24

私が使用している画像:

https://dl.dropbox.com/u/454490/1%20%28Small%29.JPG

この画像の各ボックスを見つけようとしています。見つかったボックスの位置/サイズがほぼ正しい限り、結果は100%正確である必要はありません。正方形の検出の例で遊んでから、輪郭、境界ボックス、コーナー、およびボックスの中心を取得することができました。

私がここで遭遇しているいくつかの問題があります:

  1. 描画された線の内側と外側の両方で、外接する長方形が検出されます。
  2. いくつかの無関係なコーナー/センターが検出されます。
  3. 特にネストされたボックスを考慮に入れる場合、コーナー/中心を関連する輪郭/境界ボックスと一致させる方法がわかりません。

コードから得られた画像:https://dl.dropbox.com/u/454490/testresult.jpg

上記の画像を生成するために使用しているコードは次のとおりです。

import numpy as np
import cv2
from operator import itemgetter
from glob import glob
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 makebin(gray):
    bin = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 2)
    return cv2.bitwise_not(bin)
def find_squares(img):
    img = cv2.GaussianBlur(img, (11, 11), 0)
    squares = []
    points = []`
    for gray in cv2.split(img):
        bin = makebin(gray)
        contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        corners = cv2.goodFeaturesToTrack(gray,len(contours)*4,0.2,15)
        cv2.cornerSubPix(gray,corners,(6,6),(-1,-1),(cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS,10, 0.1))
        for cnt in contours:
            cnt_len = cv2.arcLength(cnt, True)
            if len(cnt) >= 4 and cv2.contourArea(cnt) > 200:
                rect = cv2.boundingRect(cnt)
                if rect not in squares:
                    squares.append(rect)
    return squares, corners, contours
if __name__ == '__main__':
    for fn in glob('../1 (Small).jpg'):
        img = cv2.imread(fn)
        squares, corners, contours = find_squares(img)
        for p in corners:
            cv2.circle(img, (p[0][0],p[0][3]), 3, (0,0,255),2)
        squares = sorted(squares,key=itemgetter(1,0,2,3))
        areas = []
        moments = []
        centers = []
        for s in squares:
            areas.append(s[2]*s[3])
            cv2.rectangle( img, (s[0],s[1]),(s[0]+s[2],s[1]+s[3]),(0,255,0),1)
        for c in contours:
            moments.append(cv2.moments(np.array(c)))
        for m in moments:
            centers.append((int(m["m10"] // m["m00"]), int(m["m01"] // m["m00"])))
        for cent in centers:
            print cent
            cv2.circle(img, (cent[0],cent[1]), 3, (0,255,0),2)
        cv2.imshow('squares', img)
        ch = 0xFF & cv2.waitKey()
        if ch == 27:
            break
    cv2.destroyAllWindows()             
4

3 に答える 3

17

出発点として、より単純なアプローチを提案します。たとえば、形態学的勾配は、強いエッジの優れた局所検出器として機能することができ、そのしきい値は単純になる傾向があります。次に、小さすぎるコンポーネントを削除できます。これは、問題に対しても比較的簡単です。あなたの例では、残りの各連結成分は単一のボックスであるため、この場合、問題は解決されます。

この簡単な手順で得られるものは次のとおりです。

ここに画像の説明を入力してください

赤い点はコンポーネントの図心を表しているので、黄色の点が悪い場合は、黄色の点に含まれている別のボックスをそこから成長させることができます。

これを実現するためのコードは次のとおりです。

import sys
import numpy
from PIL import Image, ImageOps, ImageDraw
from scipy.ndimage import morphology, label

def boxes(orig):
    img = ImageOps.grayscale(orig)
    im = numpy.array(img)

    # Inner morphological gradient.
    im = morphology.grey_dilation(im, (3, 3)) - im

    # Binarize.
    mean, std = im.mean(), im.std()
    t = mean + std
    im[im < t] = 0
    im[im >= t] = 1

    # Connected components.
    lbl, numcc = label(im)
    # Size threshold.
    min_size = 200 # pixels
    box = []
    for i in range(1, numcc + 1):
        py, px = numpy.nonzero(lbl == i)
        if len(py) < min_size:
            im[lbl == i] = 0
            continue

        xmin, xmax, ymin, ymax = px.min(), px.max(), py.min(), py.max()
        # Four corners and centroid.
        box.append([
            [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)],
            (numpy.mean(px), numpy.mean(py))])

    return im.astype(numpy.uint8) * 255, box


orig = Image.open(sys.argv[1])
im, box = boxes(orig)

# Boxes found.
Image.fromarray(im).save(sys.argv[2])

# Draw perfect rectangles and the component centroid.
img = Image.fromarray(im)
visual = img.convert('RGB')
draw = ImageDraw.Draw(visual)
for b, centroid in box:
    draw.line(b + [b[0]], fill='yellow')
    cx, cy = centroid
    draw.ellipse((cx - 2, cy - 2, cx + 2, cy + 2), fill='red')
visual.save(sys.argv[3])
于 2013-01-10T02:34:51.057 に答える
15

私はあなたがすでに答えを得ているのを見ます。しかし、この問題を解決するために、OpenCVで利用できるはるかに単純で短くて優れた方法があると思います。

輪郭を見つけると同時に、輪郭の階層も見つけます。等高線の階層は、異なる等高線間の関係です。

したがって、コードで使用したフラグは、cv2.RETR_TREEすべての階層関係を提供します。

cv2.RETR_LIST階層は提供されませんcv2.RETR_EXTERNALが、外部の輪郭のみが提供されます。

あなたに最適なのはcv2.RETR_CCOMP、すべての輪郭と2レベルの階層関係を提供することです。つまり、外側の輪郭は常に親であり、内側の穴の輪郭は常に子です。

階層の詳細については、次の記事をお読みください:輪郭-5:階層

したがって、等高線の階層は4要素の配列であり、最後の要素がその親へのインデックスポインタです。If a contour has no parent, it is external contour and it has a value -1。内側の輪郭の場合、それは子であり、その親を指す値があります。この機能を問題に活用します。

import cv2
import numpy as np

# Normal routines
img = cv2.imread('square.JPG')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,50,255,1)

# Remove some small noise if any.
dilate = cv2.dilate(thresh,None)
erode = cv2.erode(dilate,None)

# Find contours with cv2.RETR_CCOMP
contours,hierarchy = cv2.findContours(erode,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

for i,cnt in enumerate(contours):
    # Check if it is an external contour and its area is more than 100
    if hierarchy[0,i,3] == -1 and cv2.contourArea(cnt)>100:
        x,y,w,h = cv2.boundingRect(cnt)
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

        m = cv2.moments(cnt)
        cx,cy = m['m10']/m['m00'],m['m01']/m['m00']
        cv2.circle(img,(int(cx),int(cy)),3,255,-1)

cv2.imshow('img',img)
cv2.imwrite('sofsqure.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

結果 :

ここに画像の説明を入力してください

于 2013-01-11T14:03:50.997 に答える
0

この質問は、Pythonの画像認識に関連しています。解決策はsqures.pyデモにあります。

于 2013-01-10T00:39:25.653 に答える