OpenCV के साथ टेबल गेम कार्ड छवि से कलाकृति निकालें


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()

वर्तमान आउटपुट निकटतम चीज है जो मुझे मिल सकती है। मैं सही रास्ते पर हो सकता हूं और सफेद भागों के चारों ओर एक आयत बनाने के लिए कुछ और तकरार करने की कोशिश कर सकता हूं, लेकिन मुझे नहीं लगता कि यह एक स्थायी तरीका है:

मौजूदा उत्पादन

अंतिम नोट के रूप में, नीचे दिए गए कार्ड देखें, सभी फ़्रेम बिल्कुल समान आकार या स्थिति नहीं हैं, लेकिन हमेशा केवल पाठ और उसके चारों ओर की सीमाओं के साथ कलाकृति का एक टुकड़ा होता है। यह सुपर ठीक कटौती करने की जरूरत नहीं है, लेकिन स्पष्ट रूप से कला कार्ड का एक "क्षेत्र" है, जो कुछ पाठ वाले अन्य क्षेत्रों से घिरा हुआ है। मेरा लक्ष्य कलाकृति के क्षेत्र पर कब्जा करने की कोशिश करना है और साथ ही मैं कर सकता हूं।

यहाँ छवि विवरण दर्ज करें

यहाँ छवि विवरण दर्ज करें


"नारकोमेब" कार्ड से आप किस तरह के आउटपुट का इंतजार करते हैं? इसकी एक नियमित आकार की सीमा भी नहीं है। इसके अलावा, मुझे नहीं लगता कि उपयोगकर्ता सहायता के बिना कोई समाधान है।
बुरक

आप जो सबसे अच्छा कर सकते हैं वह है बाउंडिंग पॉइंट्स पर क्लिक करना, उन पॉइंट्स को नज़दीकी कॉर्नर पर मिलान करके बढ़ाना, फिर पॉइंट्स के आधार पर किनारों पर आधारित आकृति का पता लगाना। मुझे अभी भी संदेह है कि इस एल्गोरिथ्म का एक अच्छा कार्यान्वयन अधिकांश समय पूरा करेगा। एज डिटेक्शन थ्रेशोल्ड को एडजस्ट करना और पॉइंट्स के बीच लाइन की वक्रता के बारे में संकेत देना (लेफ्ट क्लिक: स्ट्रेट, राइट क्लिक: कर्व्ड, हो सकता है?) रियल टाइम पर सफलता की संभावना को बढ़ा सकता है।
बुरक

1
मैंने नारकोमेब कार्ड में एक बेहतर उदाहरण जोड़ा। जैसा कि आप देख सकते हैं कि मैं कार्ड के कलाकृति क्षेत्र में दिलचस्पी रखता हूं, यह 100% सटीक नहीं है। मेरी राय है, कुछ ऐसे परिवर्तन होने चाहिए जो मुझे बोलने के लिए अलग-अलग 'क्षेत्रों' में एक कार्ड को विभाजित करने की अनुमति दें।
वारुणोलज़

मुझे लगता है कि आप पहली बार छवियों को 2 प्रकारों (शायद 4 प्रकारों के रूप में प्रदान कर सकते हैं; जैसा कि जानकारी प्रदान की गई है, छवि wil शीर्ष या दाएं तरफ दिखाती है) और opencv का उपयोग करके यह जांचने के लिए कि क्या छवि में पाठ है। इसलिए फसल -> फिल्टर -> परिणाम -> यदि आवश्यक हो तो बेहतर परिणाम बनाने के लिए ओपनकेव के लिए कट एज आसान है।
elprup

जवाबों:


3

मैंने छवि के रैखिक भागों का पता लगाने के लिए Hough लाइन रूपांतरण का उपयोग किया। सभी लाइनों के क्रॉसिंग का उपयोग सभी संभव आयतों के निर्माण के लिए किया गया था, जिसमें अन्य क्रॉसिंग बिंदु नहीं होते हैं। चूंकि आप जिस कार्ड की तलाश कर रहे हैं, वह हमेशा उन आयतों में से सबसे बड़ा होता है (कम से कम आपके द्वारा दिए गए नमूनों में), मैंने बस उन आयतों में से सबसे बड़ी को विजेता के रूप में चुना। स्क्रिप्ट उपयोगकर्ता के संपर्क के बिना काम करती है।

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

छवि 3

लाइन क्रॉसिंग खोजने के लिए कोड यहां पाया जा सकता है: houghlines opencv का उपयोग करके खींची गई दो लाइनों का प्रतिच्छेदन बिंदु ढूंढें

आप यहाँ Hough Lines के बारे में अधिक पढ़ सकते हैं ।


2
कड़ी मेहनत के लिए धन्यवाद। आपका जवाब है कि मैं क्या देख रहा था। मुझे पता था कि हूप लाइन्स यहां एक बड़ी भूमिका निभाएंगी। मैंने खुद को इस्तेमाल करने के लिए कई बार कोशिश की लेकिन आपके समाधान के करीब नहीं पहुंच सका। जैसा कि आपने टिप्पणी की, दृष्टिकोण को सामान्य बनाने के लिए मापदंडों पर कुछ ट्विक करने होंगे लेकिन तर्क महान और शक्तिशाली है।
वारुलेल्ज़

1
मुझे लगता है कि इस तरह की समस्या के लिए यह एक महान समाधान है, किसी उपयोगकर्ता इनपुट की आवश्यकता नहीं है। वाहवाही!!
मेटो

@ मीटो - मैं यहां किए गए काम की सराहना करता हूं, लेकिन मैं कोई उपयोगकर्ता इनपुट भाग सहमत नहीं हूं । यह सिर्फ एक उर्फ ​​है कि क्या आप रनटाइम पर इनपुट करते हैं या परिणाम को देखने के बाद थ्रेसहोल्ड बदलते हैं।
बुरक

1
@Burak - मैं एक ही सेटिंग्स के साथ आपूर्ति किए गए सभी नमूनों को चलाने में सक्षम था, इसलिए मैं मान रहा हूं, कि अधिकांश अन्य कार्ड भी काम करेंगे। इसलिए थ्रेसहोल्ड सेटिंग्स को केवल एक बार करना होगा।
एम। मार्टिन

0

हम जानते हैं कि कार्ड की एक्स और वाई कुल्हाड़ियों के साथ सीधी सीमाएं हैं। हम इसका उपयोग छवि के कुछ हिस्सों को निकालने के लिए कर सकते हैं। निम्नलिखित कोड छवि में क्षैतिज और ऊर्ध्वाधर रेखाओं का पता लगाता है।

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

मुझे नहीं लगता कि प्रत्येक कार्ड के लिए रंगों, आयामों, स्थानों और बनावट की गतिशील प्रकृति के कारण पारंपरिक छवि प्रसंस्करण तकनीकों का उपयोग करके आरओआई को स्वचालित रूप से क्रॉप करना संभव है। यदि आप इसे स्वचालित रूप से करना चाहते हैं, तो आपको मशीन / गहन शिक्षण में देखना होगा और अपने स्वयं के क्लासिफायर को प्रशिक्षित करना होगा। इसके बजाय, यहाँ एक छवि से स्थिर ROI का चयन और फसल करने के लिए एक मैनुअल तरीका है।

cv2.setMouseCallback()माउस का क्लिक किया गया है या जारी किया गया है या नहीं, यह पता लगाने के लिए हैंडलर का उपयोग करना और घटना करना है। इस कार्यान्वयन के लिए, आप बाईं माउस बटन को दबाकर और वांछित ROI का चयन करने के लिए ड्रैग करके कलाकृति ROI निकाल सकते हैं। एक बार जब आप वांछित आरओआई का चयन कर लेते हैं, तो cफसल को दबाएं और आरओआई को बचाएं। आप सही माउस बटन का उपयोग करके ROI को रीसेट कर सकते हैं।

बची कलाकृति ROIs

कोड

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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.