web-dev-qa-db-ja.com

OpenCV(Python)で輪郭検出を改善

写真からカードを識別しようとしています。理想的な写真でやりたいことができましたが、照明などを少し変えて同じ手順を適用するのに苦労しています。問題は、次の輪郭検出をより堅牢にすることです。

テイカーが興味のある画像を作成できるようにするには、コードの大部分を共有する必要がありますが、私の質問は最後のブロックと画像にのみ関連しています。

import numpy as np
import cv2
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import math

img = cv2.imread('image.png')
img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
plt.imshow(img)

enter image description here

次に、カードが検出されます。

# Prepocess
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(1,1),1000)
flag, thresh = cv2.threshold(blur, 120, 255, cv2.THRESH_BINARY)
# Find contours
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea,reverse=True) 
# Select long perimeters only
perimeters = [cv2.arcLength(contours[i],True) for i in range(len(contours))]
listindex=[i for i in range(15) if perimeters[i]>perimeters[0]/2]
numcards=len(listindex)
# Show image
imgcont = img.copy()
[cv2.drawContours(imgcont, [contours[i]], 0, (0,255,0), 5) for i in listindex]
plt.imshow(imgcont)

enter image description here

パースペクティブが修正されました:

#plt.rcParams['figure.figsize'] = (3.0, 3.0)
warp = range(numcards)
for i in range(numcards):
    card = contours[i]
    peri = cv2.arcLength(card,True)
    approx = cv2.approxPolyDP(card,0.02*peri,True)
    rect = cv2.minAreaRect(contours[i])
    r = cv2.cv.BoxPoints(rect)

    h = np.array([ [0,0],[399,0],[399,399],[0,399] ],np.float32)
    approx = np.array([item for sublist in approx for item in sublist],np.float32)
    transform = cv2.getPerspectiveTransform(approx,h)
    warp[i] = cv2.warpPerspective(img,transform,(400,400))

# Show perspective correction
fig = plt.figure(1, (10,10))
grid = ImageGrid(fig, 111, # similar to subplot(111)
                nrows_ncols = (4, 4), # creates 2x2 grid of axes
                axes_pad=0.1, # pad between axes in inch.
                aspect=True, # do not force aspect='equal'
                )

for i in range(numcards):
    grid[i].imshow(warp[i]) # The AxesGrid object work as a list of axes.

enter image description here

それは私が私の問題を抱えていたということでした。形状の輪郭を検出したい。私が見つけた最良の方法は、グレー画像でbilateralFilterAdaptativeThresholdの組み合わせを使用することです。

fig = plt.figure(1, (10,10))
grid = ImageGrid(fig, 111, # similar to subplot(111)
                nrows_ncols = (4, 4), # creates 2x2 grid of axes
                axes_pad=0.1, # pad between axes in inch.
                aspect=True, # do not force aspect='equal'
                )
for i in range(numcards):
    image2 = cv2.bilateralFilter(warp[i].copy(),10,100,100)
    grey = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY)
    grey2 = cv2.cv.AdaptiveThreshold(cv2.cv.fromarray(grey), cv2.cv.fromarray(grey), 255, cv2.cv.CV_ADAPTIVE_THRESH_MEAN_C, cv2.cv.CV_THRESH_BINARY, blockSize=31, param1=6)
    grid[i].imshow(grey,cmap=plt.cm.binary) 

enter image description here

これは私が望むものに非常に近いですが、しかし白で閉じた輪郭を取得し、他のすべてを黒で取得するにはどうすればよいですか?

10
anderstood

キャニーを使用して遠近法補正を適用しないのはなぜですか輪郭を見つける(エッジがぼやけているように見えるため)?たとえば、質問で提供した小さな画像を使用すると(大きな画像の方が結果が良くなる可能性があります)。

enter image description here

コードの一部に基づく:

import numpy as np
import cv2

import math

img = cv2.imread('image.bmp')

# Prepocess
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
flag, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)

# Find contours
img2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True) 

# Select long perimeters only
perimeters = [cv2.arcLength(contours[i],True) for i in range(len(contours))]
listindex=[i for i in range(15) if perimeters[i]>perimeters[0]/2]
numcards=len(listindex)

card_number = -1 #just so happened that this is the worst case
stencil = np.zeros(img.shape).astype(img.dtype)
cv2.drawContours(stencil, [contours[listindex[card_number]]], 0, (255, 255, 255), cv2.FILLED)
res = cv2.bitwise_and(img, stencil)
cv2.imwrite("out.bmp", res)
canny = cv2.Canny(res, 100, 200)
cv2.imwrite("canny.bmp", canny)

まず、簡単にするために1枚のカードを除くすべてを削除してから、CannyEdge検出器を適用します。

enter image description hereenter image description here

次に、拡張/侵食、遠近法の修正、最大の輪郭の削除などを行うことができます。

3
Headcrab

右下隅の画像を除いて、次の手順が一般的に機能するはずです。

  1. バイナリマスクを拡張および侵食して、輪郭フラグメント間の1つまたは2つのピクセルギャップを埋めます。
  2. 最大抑制を使用して、シェイプの境界に沿った厚いバイナリマスクを薄いエッジに変えます。
  3. パイプラインの前半で使用したように、cvFindcontoursを使用して閉じた輪郭を識別します。メソッドによって識別された各輪郭は、閉じているかどうかをテストできます。
  4. このような問題の一般的な解決策として、アルゴリズムを試して、特定の点の周りの閉じた輪郭を見つけることをお勧めします。チェック 固定によるアクティブセグメンテーション
2
Ajay