ওপেনসিভি দিয়ে টেবিল গেম কার্ডের চিত্র থেকে আর্টওয়ার্কটি বের করুন


10

আমি অজগরটিতে একটি ছোট স্ক্রিপ্ট লিখেছিলাম যেখানে আমি প্লে কার্ডের অংশটি কেবল শিল্পকর্মের প্রতিনিধিত্ব করে বাকী সমস্ত অংশ মুছে ফেলার চেষ্টা করছি। আমি দোরগোড়ানোর বিভিন্ন পদ্ধতি চেষ্টা করেছিলাম কিন্তু সেখানে যেতে পারিনি। এছাড়াও নোট করুন যে আমি আর্টওয়ার্কের অবস্থানটি কেবল ম্যানুয়ালি রেকর্ড করতে পারি না কারণ এটি সবসময় একই অবস্থান বা আকারে থাকে না, তবে সবসময় একটি আয়তক্ষেত্রাকার আকারে যেখানে সমস্ত কিছুই কেবল পাঠ্য এবং সীমানায় থাকে।

এখানে চিত্র বর্ণনা লিখুন

from matplotlib import pyplot as plt
import cv2

img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,binary = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)

binary = cv2.bitwise_not(binary)
kernel = np.ones((15, 15), np.uint8)

closing = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)

plt.imshow(closing),plt.show()

বর্তমান আউটপুটটি আমার কাছে পাওয়া সবচেয়ে কাছের জিনিস। আমি সঠিক পথে থাকতে পারি এবং সাদা অংশগুলির চারপাশে একটি আয়তক্ষেত্র আঁকতে আরও কিছুটা ঝাঁকুনির চেষ্টা করতে পারি, তবে আমি মনে করি না এটি একটি টেকসই পদ্ধতি:

বর্তমান আউটপুট

একটি সর্বশেষ নোট হিসাবে, নীচের কার্ডগুলি দেখুন, সমস্ত ফ্রেম হুবহু একই আকার বা অবস্থান নয় তবে সবসময় কেবলমাত্র পাঠ্য এবং চারপাশে সীমানা যুক্ত একটি শিল্পকর্ম রয়েছে। এটি খুব ভালভাবে কাটতে হবে না, তবে স্পষ্টতই শিল্পটি কার্ডের একটি "অঞ্চল", কিছু অঞ্চল সহ অন্যান্য অঞ্চল দ্বারা ঘিরে রয়েছে। আমার লক্ষ্যটি আমি যতটা পারি শিল্পকর্মের অঞ্চলটি ক্যাপচার করার চেষ্টা করা।

এখানে চিত্র বর্ণনা লিখুন

এখানে চিত্র বর্ণনা লিখুন


"নারকোয়েবা" কার্ড থেকে আপনি কোন ধরণের আউটপুট অপেক্ষা করেন? এটির নিয়মিত আকারের সীমানাও নেই। এছাড়াও, আমি মনে করি না যে ব্যবহারকারীদের সহায়তা ব্যতীত কোনও সমাধান রয়েছে a
বুরাক

আপনি যা করতে পারেন তা হ'ল বাউন্ডিং পয়েন্টগুলিতে ক্লিক করা, সেই পয়েন্টগুলিকে নিকটস্থ সনাক্তকৃত কোণে মিলিয়ে উন্নত করা, তারপরে পয়েন্টগুলির মধ্যে প্রান্তগুলির ভিত্তিতে আকারটি সন্ধান করুন। আমি এখনও সন্দেহ করি যে এই অ্যালগরিদমের একটি ভাল বাস্তবায়ন বেশিরভাগ সময় সম্পাদন করবে। প্রান্ত সনাক্তকরণ প্রান্তিককরণ সামঞ্জস্য করা এবং পয়েন্টগুলির মধ্যে লাইনের বক্রতা সম্পর্কে ইঙ্গিত প্রদান (বাম ক্লিক: সরাসরি, ডান ক্লিক: বক্র, সম্ভবত?) সাফল্যের সম্ভাবনা বাড়িয়ে তুলতে পারে।
বুড়ক

1
আমি নারকোয়েবা কার্ডে আরও একটি ভাল উদাহরণ যুক্ত করেছি। আপনি দেখতে পাচ্ছেন যে আমি কার্ডের আর্টওয়ার্ক অঞ্চলে আগ্রহী, এটি 100% সুনির্দিষ্ট হতে হবে না। আমি আমার মতামত, এমন কিছু রূপান্তর থাকতে হবে যা আমাকে কথা বলার জন্য বিভিন্ন 'অঞ্চলে' কার্ড বিভক্ত করতে দেয়।
ওয়ার্লোলজ

আমি মনে করি আপনি প্রথমে 2 টি ধরণের চিত্রগুলি ক্রপ করতে পারেন (সম্ভবত 4 প্রকার? তথ্য হিসাবে দেওয়া হয়েছে, চিত্রটি উপরে বা ডানদিকে প্রদর্শিত হবে) এবং এটিতে চিত্রের পাঠ্য আছে কিনা তা পরীক্ষা করতে ওপেনসিভি ব্যবহার করতে পারেন। সুতরাং শস্য -> ফিল্টার -> ফলাফল -> কাটা প্রান্ত প্রয়োজন হলে ওপেনসিভি আরও ভাল ফলাফল করা সহজ।
ইলপ্রুপ

উত্তর:


3

আমি চিত্রের রৈখিক অংশগুলি সনাক্ত করতে হাগ লাইন রূপান্তর ব্যবহার করেছি। সমস্ত লাইন ক্রসিংগুলি সমস্ত সম্ভাব্য আয়তক্ষেত্রগুলি তৈরি করতে ব্যবহৃত হত, যাতে অন্যান্য ক্রসিং পয়েন্ট থাকে না। যেহেতু আপনি যে কার্ডটির সন্ধান করছেন সেটি সেই আয়তক্ষেত্রগুলির মধ্যে সর্বদা বৃহত্তম (কমপক্ষে আপনি সরবরাহিত নমুনাগুলিতে), তাই আমি বিজয়ী হিসাবে কেবল সেই আয়তক্ষেত্রগুলির মধ্যে বৃহত্তমটি বেছে নিয়েছি। স্ক্রিপ্টটি ব্যবহারকারীর মিথস্ক্রিয়া ছাড়াই কাজ করে।

import cv2
import numpy as np
from collections import defaultdict

def segment_by_angle_kmeans(lines, k=2, **kwargs):
    #Groups lines based on angle with k-means.
    #Uses k-means on the coordinates of the angle on the unit circle 
    #to segment `k` angles inside `lines`.

    # Define criteria = (type, max_iter, epsilon)
    default_criteria_type = cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER
    criteria = kwargs.get('criteria', (default_criteria_type, 10, 1.0))
    flags = kwargs.get('flags', cv2.KMEANS_RANDOM_CENTERS)
    attempts = kwargs.get('attempts', 10)

    # returns angles in [0, pi] in radians
    angles = np.array([line[0][1] for line in lines])
    # multiply the angles by two and find coordinates of that angle
    pts = np.array([[np.cos(2*angle), np.sin(2*angle)]
                    for angle in angles], dtype=np.float32)

    # run kmeans on the coords
    labels, centers = cv2.kmeans(pts, k, None, criteria, attempts, flags)[1:]
    labels = labels.reshape(-1)  # transpose to row vec

    # segment lines based on their kmeans label
    segmented = defaultdict(list)
    for i, line in zip(range(len(lines)), lines):
        segmented[labels[i]].append(line)
    segmented = list(segmented.values())
    return segmented

def intersection(line1, line2):
    #Finds the intersection of two lines given in Hesse normal form.
    #Returns closest integer pixel locations.
    #See https://stackoverflow.com/a/383527/5087436

    rho1, theta1 = line1[0]
    rho2, theta2 = line2[0]

    A = np.array([
        [np.cos(theta1), np.sin(theta1)],
        [np.cos(theta2), np.sin(theta2)]
    ])
    b = np.array([[rho1], [rho2]])
    x0, y0 = np.linalg.solve(A, b)
    x0, y0 = int(np.round(x0)), int(np.round(y0))
    return [[x0, y0]]


def segmented_intersections(lines):
    #Finds the intersections between groups of lines.

    intersections = []
    for i, group in enumerate(lines[:-1]):
        for next_group in lines[i+1:]:
            for line1 in group:
                for line2 in next_group:
                    intersections.append(intersection(line1, line2)) 
    return intersections

def rect_from_crossings(crossings):
    #find all rectangles without other points inside
    rectangles = []

    # Search all possible rectangles
    for i in range(len(crossings)):
        x1= int(crossings[i][0][0])
        y1= int(crossings[i][0][1])

        for j in range(len(crossings)):
            x2= int(crossings[j][0][0])
            y2= int(crossings[j][0][1])

            #Search all points
            flag = 1
            for k in range(len(crossings)):
                x3= int(crossings[k][0][0])
                y3= int(crossings[k][0][1])

                #Dont count double (reverse rectangles)
                if (x1 > x2 or y1 > y2):
                    flag = 0
                #Dont count rectangles with points inside   
                elif ((((x3 >= x1) and (x2 >= x3))and (y3 > y1) and (y2 > y3) or ((x3 > x1) and (x2 > x3))and (y3 >= y1) and (y2 >= y3))):    
                    if(i!=k and j!=k):    
                        flag = 0

            if flag:
                rectangles.append([[x1,y1],[x2,y2]])

    return rectangles

if __name__ == '__main__':
    #img = cv2.imread('TAJFp.jpg')
    #img = cv2.imread('Bj2uu.jpg')
    img = cv2.imread('yi8db.png')

    width = int(img.shape[1])
    height = int(img.shape[0])

    scale = 380/width
    dim = (int(width*scale), int(height*scale))
    # resize image
    img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA) 

    img2 = img.copy()
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray,(5,5),cv2.BORDER_DEFAULT)

    # Parameters of Canny and Hough may have to be tweaked to work for as many cards as possible
    edges = cv2.Canny(gray,10,45,apertureSize = 7)
    lines = cv2.HoughLines(edges,1,np.pi/90,160)

    segmented = segment_by_angle_kmeans(lines)
    crossings = segmented_intersections(segmented)
    rectangles = rect_from_crossings(crossings)

    #Find biggest remaining rectangle
    size = 0
    for i in range(len(rectangles)):
        x1 = rectangles[i][0][0]
        x2 = rectangles[i][1][0]
        y1 = rectangles[i][0][1]
        y2 = rectangles[i][1][1]

        if(size < (abs(x1-x2)*abs(y1-y2))):
            size = abs(x1-x2)*abs(y1-y2)
            x1_rect = x1
            x2_rect = x2
            y1_rect = y1
            y2_rect = y2

    cv2.rectangle(img2, (x1_rect,y1_rect), (x2_rect,y2_rect), (0,0,255), 2)
    roi = img[y1_rect:y2_rect, x1_rect:x2_rect]

    cv2.imshow("Output",roi)
    cv2.imwrite("Output.png", roi)
    cv2.waitKey()

আপনার দেওয়া নমুনাগুলির সাথে ফলাফলগুলি:

Image1

Image2

Image3

লাইন ক্রসিংগুলি সন্ধানের কোডটি এখানে পাওয়া যাবে: হফলাইনস ওপেনসিভি ব্যবহার করে দুটি রেখার ছেদ বিন্দুটি সন্ধান করুন

আপনি এখানে হফ লাইনের বিষয়ে আরও পড়তে পারেন ।


2
কঠোর পরিশ্রমের জন্য ধন্যবাদ। আপনার উত্তরটি আমি যা খুঁজছিলাম is আমি জানতাম এখানে হাফ লাইন্স একটি বড় ভূমিকা পালন করবে। আমি এটি ব্যবহার করতে কয়েকবার চেষ্টা করেছি কিন্তু আপনার সমাধানের কাছে যেতে পারিনি। আপনি মন্তব্য হিসাবে, পন্থাগুলি সাধারণকরণের জন্য প্যারামিটারগুলিতে কয়েকটি টুইট করতে হবে তবে যুক্তি দুর্দান্ত এবং শক্তিশালী।
ওয়ার্লোলজ

1
আমি মনে করি এটি এই ধরণের সমস্যার এক দুর্দান্ত সমাধান, কোনও ব্যবহারকারীর ইনপুট প্রয়োজন নেই। সাবাস !!
মেটো

@ মেটো - আমি এখানে করা কাজের প্রশংসা করি তবে আমি ব্যবহারকারীর ইনপুট অংশটিকে সম্মত করি না । এটি কেবলমাত্র একটি উপনাম যা আপনি রানটাইমের সময় ইনপুট দেন বা ফলাফলটি দেখার পরে প্রান্তিক পরিবর্তন করেন change
বুড়াক

1
@ বারাক - আমি একই সেটিংস সরবরাহিত সমস্ত নমুনা চালাতে সক্ষম হয়েছি, তাই আমি ধরে নিচ্ছি যে অন্যান্য কার্ডগুলির বেশিরভাগই কাজ করবে। সুতরাং theshold সেটিংসটি একবারে তৈরি করতে হবে।
এম মার্টিন

0

আমরা জানি যে কার্ডগুলির এক্স এবং y অক্ষের সাথে সোজা সীমানা রয়েছে। আমরা এটি চিত্রের অংশগুলি বের করতে ব্যবহার করতে পারি। নিম্নলিখিত কোডটি চিত্রটিতে অনুভূমিক এবং উল্লম্ব রেখা সনাক্তকরণ কার্যকর করে।

import cv2
import numpy as np

def mouse_callback(event, x, y, flags, params):
    global num_click
    if num_click < 2 and event == cv2.EVENT_LBUTTONDOWN:
        num_click = num_click + 1
        print(num_click)
        global upper_bound, lower_bound, left_bound, right_bound
        upper_bound.append(max(i for i in hor if i < y) + 1)
        lower_bound.append(min(i for i in hor if i > y) - 1)
        left_bound.append(max(i for i in ver if i < x) + 1)
        right_bound.append(min(i for i in ver if i > x) - 1)

filename = 'image.png'
thr = 100  # edge detection threshold
lined = 50  # number of consequtive True pixels required an axis to be counted as line
num_click = 0  # select only twice
upper_bound, lower_bound, left_bound, right_bound = [], [], [], []
winname = 'img'

cv2.namedWindow(winname)
cv2.setMouseCallback(winname, mouse_callback)

img = cv2.imread(filename, 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
bw = cv2.Canny(gray, thr, 3*thr)

height, width, _ = img.shape

# find horizontal lines
hor = []
for i in range (0, height-1):
    count = 0
    for j in range (0, width-1):
        if bw[i,j]:
            count = count + 1
        else:
            count = 0
        if count >= lined:
            hor.append(i)
            break

# find vertical lines
ver = []
for j in range (0, width-1):
    count = 0
    for i in range (0, height-1):
        if bw[i,j]:
            count = count + 1
        else:
            count = 0
        if count >= lined:
            ver.append(j)
            break

# draw lines
disp_img = np.copy(img)
for i in hor:
    cv2.line(disp_img, (0, i), (width-1, i), (0,0,255), 1)
for i in ver:
    cv2.line(disp_img, (i, 0), (i, height-1), (0,0,255), 1)

while num_click < 2:
    cv2.imshow(winname, disp_img)
    cv2.waitKey(10)
disp_img = img[min(upper_bound):max(lower_bound), min(left_bound):max(right_bound)]
cv2.imshow(winname, disp_img)
cv2.waitKey()   # Press any key to exit
cv2.destroyAllWindows()

অন্তর্ভুক্ত করতে আপনাকে কেবল দুটি ক্ষেত্র ক্লিক করতে হবে। একটি নমুনা ক্লিকের ক্ষেত্র এবং সম্পর্কিত ফলাফল নীচে রয়েছে:

লাইন result_of_lines

অন্যান্য চিত্র থেকে প্রাপ্ত ফলাফল:

result_2 result_3


0

আমি মনে করি না প্রতিটি কার্ডের রঙ, মাত্রা, অবস্থান এবং জমিনগুলির গতিশীল প্রকৃতির কারণে traditionalতিহ্যবাহী চিত্র প্রক্রিয়াকরণ কৌশলগুলি ব্যবহার করে আর্টওয়ার্ক আরওআই স্বয়ংক্রিয়ভাবে ক্রপ করা সম্ভব। আপনি যদি স্বয়ংক্রিয়ভাবে এটি করতে চান তবে আপনাকে মেশিন / গভীর শিক্ষার সন্ধান করতে হবে এবং নিজের শ্রেণিবদ্ধকে প্রশিক্ষণ দিতে হবে। পরিবর্তে, এখানে একটি চিত্র থেকে একটি স্ট্যাটিক আরওআই নির্বাচন এবং ক্রপ করার জন্য একটি ম্যানুয়াল পদ্ধতি রয়েছে।

ধারণাটি হ'ল cv2.setMouseCallback()মাউসটি ক্লিক করা হয়েছে বা প্রকাশ হয়েছে কিনা তা সনাক্ত করতে ইভেন্ট হ্যান্ডলারগুলি ব্যবহার করা । এই বাস্তবায়নের জন্য, আপনি বাম মাউস বোতামটি ধরে রেখে এবং পছন্দসই ROI নির্বাচন করতে টান দিয়ে আর্টওয়ার্ক আরওআই নিষ্কাশন করতে পারবেন। একবার আপনি কাঙ্ক্ষিত আরওআই নির্বাচন করে নিলে, cক্রপ করতে টিপুন এবং আরওআই সংরক্ষণ করুন। ডান মাউস বোতামটি ব্যবহার করে আপনি আরওআইকে পুনরায় সেট করতে পারেন।

সংরক্ষণ করা আর্টওয়ার্ক আরওআই

কোড

import cv2

class ExtractArtworkROI(object):
    def __init__(self):
        # Load image
        self.original_image = cv2.imread('1.png')
        self.clone = self.original_image.copy()
        cv2.namedWindow('image')
        cv2.setMouseCallback('image', self.extractROI)
        self.selected_ROI = False

        # ROI bounding box reference points
        self.image_coordinates = []

    def extractROI(self, event, x, y, flags, parameters):
        # Record starting (x,y) coordinates on left mouse button click
        if event == cv2.EVENT_LBUTTONDOWN:
            self.image_coordinates = [(x,y)]

        # Record ending (x,y) coordintes on left mouse button release
        elif event == cv2.EVENT_LBUTTONUP:
            # Remove old bounding box
            if self.selected_ROI:
                self.clone = self.original_image.copy()

            # Draw rectangle 
            self.selected_ROI = True
            self.image_coordinates.append((x,y))
            cv2.rectangle(self.clone, self.image_coordinates[0], self.image_coordinates[1], (36,255,12), 2)

            print('top left: {}, bottom right: {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
            print('x,y,w,h : ({}, {}, {}, {})'.format(self.image_coordinates[0][0], self.image_coordinates[0][1], self.image_coordinates[1][0] - self.image_coordinates[0][0], self.image_coordinates[1][1] - self.image_coordinates[0][1]))

        # Clear drawing boxes on right mouse button click
        elif event == cv2.EVENT_RBUTTONDOWN:
            self.selected_ROI = False
            self.clone = self.original_image.copy()

    def show_image(self):
        return self.clone

    def crop_ROI(self):
        if self.selected_ROI:
            x1 = self.image_coordinates[0][0]
            y1 = self.image_coordinates[0][1]
            x2 = self.image_coordinates[1][0]
            y2 = self.image_coordinates[1][1]

            # Extract ROI
            self.cropped_image = self.original_image.copy()[y1:y2, x1:x2]

            # Display and save image
            cv2.imshow('Cropped Image', self.cropped_image)
            cv2.imwrite('ROI.png', self.cropped_image)
        else:
            print('Select ROI before cropping!')

if __name__ == '__main__':
    extractArtworkROI = ExtractArtworkROI()
    while True:
        cv2.imshow('image', extractArtworkROI.show_image())
        key = cv2.waitKey(1)

        # Close program with keyboard 'q'
        if key == ord('q'):
            cv2.destroyAllWindows()
            exit(1)

        # Crop ROI
        if key == ord('c'):
            extractArtworkROI.crop_ROI()
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.