7

यदि वर्गों ने छवि में क्षेत्र को जोड़ा है, तो मैं उन्हें कैसे पहचान सकता हूं।उन्नत वर्ग पहचान (कनेक्टेड क्षेत्र के साथ)

मैं विधि OpenCV C++/Obj-C: Advanced square detection

में उल्लेख किया है यह अच्छी तरह से काम नहीं किया परीक्षण किया है।

कोई अच्छा विचार?

squares that has Connected region

import cv2 
import numpy as np 

def angle_cos(p0, p1, p2): 
    d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float') 
    return abs(np.dot(d1, d2)/np.sqrt(np.dot(d1, d1)*np.dot(d2, d2))) 

def find_squares(img): 
    squares = [] 
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
    # cv2.imshow("gray", gray) 

    gaussian = cv2.GaussianBlur(gray, (5, 5), 0) 

    temp,bin = cv2.threshold(gaussian, 80, 255, cv2.THRESH_BINARY) 
    # cv2.imshow("bin", bin) 

    contours, hierarchy = cv2.findContours(bin, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) 

    cv2.drawContours(gray, contours, -1, (0, 255, 0), 3) 

    #cv2.imshow('contours', gray) 
    for cnt in contours: 
     cnt_len = cv2.arcLength(cnt, True) 
     cnt = cv2.approxPolyDP(cnt, 0.02*cnt_len, True) 
     if len(cnt) == 4 and cv2.contourArea(cnt) > 1000 and cv2.isContourConvex(cnt): 
      cnt = cnt.reshape(-1, 2) 
      max_cos = np.max([angle_cos(cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4]) for i in xrange(4)]) 
      if max_cos < 0.1: 
       squares.append(cnt) 
    return squares 

if __name__ == '__main__': 
    img = cv2.imread('123.bmp') 

    #cv2.imshow("origin", img) 

    squares = find_squares(img) 
    print "Find %d squres" % len(squares) 
    cv2.drawContours(img, squares, -1, (0, 255, 0), 3) 
    cv2.imshow('squares', img) 

    cv2.waitKey() 

मैं opencv उदाहरण में कुछ विधि का उपयोग करें, लेकिन परिणाम अच्छा नहीं है।

उत्तर

12

एक वाटरशेड को लागू करने पर दूरी आधारित रूपांतरण रूपांतरण वस्तुओं अलग कर देगा: इतना है कि शीर्ष पर गुलाबी आयत अलग किए बिना छोड़ दी है

enter image description here

सीमा पर वस्तुओं हैंडलिंग, हमेशा समस्याग्रस्त है, और अक्सर खारिज कर दिया है कोईबातनही।

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

import sys 
import cv2 
import numpy 
import random 
from scipy.ndimage import label 

def segment_on_dt(img): 
    dt = cv2.distanceTransform(img, 2, 3) # L2 norm, 3x3 mask 
    dt = ((dt - dt.min())/(dt.max() - dt.min()) * 255).astype(numpy.uint8) 
    dt = cv2.threshold(dt, 100, 255, cv2.THRESH_BINARY)[1] 
    lbl, ncc = label(dt) 

    lbl[img == 0] = lbl.max() + 1 
    lbl = lbl.astype(numpy.int32) 
    cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), lbl) 
    lbl[lbl == -1] = 0 
    return lbl 


img = cv2.cvtColor(cv2.imread(sys.argv[1]), cv2.COLOR_BGR2GRAY) 
img = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)[1] 
img = 255 - img # White: objects; Black: background 

ws_result = segment_on_dt(img) 
# Colorize 
height, width = ws_result.shape 
ws_color = numpy.zeros((height, width, 3), dtype=numpy.uint8) 
lbl, ncc = label(ws_result) 
for l in xrange(1, ncc + 1): 
    a, b = numpy.nonzero(lbl == l) 
    if img[a[0], b[0]] == 0: # Do not color background. 
     continue 
    rgb = [random.randint(0, 255) for _ in xrange(3)] 
    ws_color[lbl == l] = tuple(rgb) 

cv2.imwrite(sys.argv[2], ws_color) 

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

enter image description here

# Fit ellipse to determine the rectangles. 
wsbin = numpy.zeros((height, width), dtype=numpy.uint8) 
wsbin[cv2.cvtColor(ws_color, cv2.COLOR_BGR2GRAY) != 0] = 255 

ws_bincolor = cv2.cvtColor(255 - wsbin, cv2.COLOR_GRAY2BGR) 
lbl, ncc = label(wsbin) 
for l in xrange(1, ncc + 1): 
    yx = numpy.dstack(numpy.nonzero(lbl == l)).astype(numpy.int64) 
    xy = numpy.roll(numpy.swapaxes(yx, 0, 1), 1, 2) 
    if len(xy) < 100: # Too small. 
     continue 

    ellipse = cv2.fitEllipse(xy) 
    center, axes, angle = ellipse 
    rect_area = axes[0] * axes[1] 
    if 0.9 < rect_area/float(len(xy)) < 1.1: 
     rect = numpy.round(numpy.float64(
       cv2.cv.BoxPoints(ellipse))).astype(numpy.int64) 
     color = [random.randint(60, 255) for _ in xrange(3)] 
     cv2.drawContours(ws_bincolor, [rect], 0, color, 2) 

cv2.imwrite(sys.argv[3], ws_bincolor) 
+0

+1 - अच्छा काम। –

+2

हाँ, सच, वास्तव में अच्छा दृष्टिकोण, +1। इमेज इंडेक्स को सही प्रारूप में लाने के लिए यह बेहद मुश्किल है, मैं आपके द्वारा यहां दिखाए गए एक ही ज़िप अनपैक विधि का उपयोग करता था, लेकिन मुझे हाल ही में एहसास हुआ कि यह ट्रांसपोज़िंग और कॉपी करने से बहुत धीमी हो सकती है (गति महत्वपूर्ण होनी चाहिए ..) दुर्भाग्यवश, एक ओपनसीवी अपवाद (मेरे लिए कम से कम) से बचने के लिए प्रतिलिपि की आवश्यकता होती है .. – fraxel

+0

अंतर यह है कि numpy डिफ़ॉल्ट रूप से '(y, x)' coords के साथ काम करता है, और ओपनसीवी '(x, y)' की अपेक्षा करता है। @fraxel मैंने प्रदर्शन को माप नहीं लिया, लेकिन यह संभावना है कि उस विशिष्ट बिंदु में अद्यतन कोड बेहतर होगा। – mmgp

2

समाधान 1:

अपनी छवि को चौड़ा करना जुड़ा घटकों को हटाने के लिए। पता चला घटकों के रूप में खोजें। कुछ उपाय (उदा। अनुपात परिधि/क्षेत्र) पेश करके आयताकार नहीं हैं जो समरूप को हटा दें।

यह समाधान सीमाओं से जुड़े आयतों का पता नहीं लगाएगा।

समाधान 2:

चौड़ा करना जुड़ा घटकों को हटाने के लिए। समोच्च खोजें। अपने अंक को कम करने के लिए लगभग समोच्च (आयताकार समोच्च के लिए 4 अंक होना चाहिए)। जांचें कि समोच्च रेखाओं के बीच कोण 90 डिग्री है। समोच्चों को हटा दें जिनके पास 90 डिग्री नहीं है।

यह सीमाओं से जुड़े आयतों के साथ समस्या को हल करना चाहिए।

1

आप तीन समस्या है:

  1. आयतों बहुत सख्त आयतों नहीं हैं (किनारों अक्सर कुछ हद तक घुमावदार हैं)
  2. उनमें से एक बहुत कुछ कर रहे हैं।
  3. वे अक्सर जुड़े होते हैं।

ऐसा लगता है कि अपने सभी rects अनिवार्य रूप से एक ही आकार (?) कर रहे हैं, और बहुत ओवरलैप नहीं, लेकिन पूर्व प्रसंस्करण उन्हें जुड़ा हुआ है।

इस स्थिति के लिए दृष्टिकोण मैं कोशिश करेगा है:

  1. dilate अपनी छवि को कुछ समय (के रूप में भी @krzych ने सुझाव दिया) - इस कनेक्शन हटा दिए जाएंगे, लेकिन थोड़ा छोटा rects में परिणाम।
  2. label और find_objects पर scipy का उपयोग करें - अब आप छवि में प्रत्येक शेष ब्लॉब के लिए स्थिति और टुकड़ा जानते हैं।
  3. प्रत्येक आयत के केंद्र, अभिविन्यास, चौड़ाई और ऊंचाई को खोजने के लिए minAreaRect का उपयोग करें।

आप चरण 3 का उपयोग कर सकते हैं।यह जांचने के लिए कि क्या ब्लॉब एक ​​वैध आयताकार है या नहीं, इसके क्षेत्र, आयाम अनुपात या किनारे से निकटता ..

यह एक अच्छा दृष्टिकोण है, जैसा कि हम मानते हैं कि प्रत्येक ब्लॉब एक ​​आयताकार है, इसलिए minAreaRect मिलेगा हमारे न्यूनतम संलग्न आयताकार के लिए पैरामीटर। इसके अलावा हम पूरी तरह से निष्क्रिय होने पर humoments जैसे कुछ ब्लॉब का परीक्षण कर सकते हैं।

यहां मैं कार्रवाई में सुझाव दे रहा था, लाल रंग में दिखाए गए सीमा टक्कर मैच।

enter image description here

कोड:

import numpy as np 
import cv2 
from cv2 import cv 
import scipy 
from scipy import ndimage 

im_col = cv2.imread('jdjAf.jpg') 
im = cv2.imread('jdjAf.jpg',cv2.CV_LOAD_IMAGE_GRAYSCALE) 

im = np.where(im>100,0,255).astype(np.uint8) 
im = cv2.erode(im, None,iterations=8) 
im_label, num = ndimage.label(im) 
for label in xrange(1, num+1): 
    points = np.array(np.where(im_label==label)[::-1]).T.reshape(-1,1,2).copy() 
    rect = cv2.minAreaRect(points) 
    lines = np.array(cv2.cv.BoxPoints(rect)).astype(np.int) 
    if any([np.any(lines[:,0]<=0), np.any(lines[:,0]>=im.shape[1]-1), np.any(lines[:,1]<=0), np.any(lines[:,1]>=im.shape[0]-1)]): 
     cv2.drawContours(im_col,[lines],0,(0,0,255),1) 
    else: 
     cv2.drawContours(im_col,[lines],0,(255,0,0),1) 

cv2.imshow('im',im_col) 
cv2.imwrite('rects.png',im_col) 
cv2.waitKey() 

मुझे लगता है कि Watershed और distanceTransform दृष्टिकोण @mmgp द्वारा प्रदर्शन छवि के आधार पर विभाजन के लिए स्पष्ट रूप से बेहतर है, लेकिन इस सरल दृष्टिकोण अपनी आवश्यकताओं पर निर्भर करता है प्रभावी हो सकता है।

+0

आपको बहुत बहुत धन्यवाद, मुझे लगता है कि विधि तेजी से तो जल विधि है, तो आप कोड पर कुछ टिप्पणी दे सकते हैं? "ndimage.label" का अर्थ क्या है? भी "np.array (np.where (im_label == लेबल) [:: - 1])। टी। ताज़ाप (-1,1,2) .copy()"? एवर ~ – Yang

+0

के लिए धन्यवाद "भी ([np.any (रेखाएं [:, 0] <= 0), np.any (रेखाएं [:, 0]> = im.shape [1] -1), एनपी। कोई भी (रेखाएं [:, 1] <= 0), np.any (रेखाएं [:, 1]> = im.shape [0] -1)]) "मैं आसानी से समझ नहीं पा रहा हूँ.धन्यवाद! – Yang

+1

@Yang - हे, निश्चित रूप से एक शॉट है: 'ndimage.label (im)' छवि को विभाजित करने के लिए प्रयोग किया जाता है: प्रत्येक अनकनेक्टेड ब्लॉब्स मानों को अनुक्रमिक रूप से एक पूर्णांक द्वारा प्रतिस्थापित किया जाता है, जिसके परिणामस्वरूप एक नई लेबल वाली छवि 'im_label' होती है। 'np.where (im_label == लेबल) 'यह नई लेबल वाली छवि लेता है और उस छवि में प्रत्येक पिक्सेल के सूचकांक देता है जो लेबल के बराबर होता है - यानी। एक ही ब्लॉब के लिए सभी इंडेक्स वैल्यू - ध्यान दें कि हम ब्लॉब्स के माध्यम से एक बार में एक लेबल मान पर विचार कर रहे हैं। '.T.reshape (-1,1,2) .copy() 'डेटा को सही प्रारूप में प्राप्त करने के लिए एक त्रुटि है जिसे 'minAreaRect' – fraxel

संबंधित मुद्दे