首页 文章

使用OpenCV(Python)改进轮廓检测

提问于
浏览
6

我正在尝试从照片中识别卡片 . 我设法做了我想要的理想照片,但我现在很难应用相同的程序,稍微不同的照明等 . 所以问题是关于使下面的轮廓检测更健壮 .

我需要分享我的代码的大部分内容,以便能够制作感兴趣的图像,但 my question relates only to the last block and image.

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

这非常接近我想要的,但我怎样才能改进它以获得白色的闭合轮廓,以及其他所有的黑色轮廓?

2 回答

  • 2

    除了右下角的图像,以下步骤通常应该起作用:

    • 扩展并侵蚀二进制掩码以桥接轮廓片段之间的任何一个或两个像素间隙 .

    • 使用最大抑制将沿着形状边界的厚二进制蒙版转换为细边 .

    • 如前面在管道中使用的那样,使用cvFindcontours来识别闭合轮廓 . 可以测试由该方法识别的每个轮廓的闭合 .

    • 作为此类问题的一般解决方案,我建议您尝试我的算法以找到给定点周围的闭合轮廓 . 检查active segmentation with fixation

  • 2

    为什么不在使用Canny并在找到轮廓后应用透视校正(因为它似乎模糊了边缘)?例如,使用您在问题中提供的小图片(结果在较大的图片上可能会更好):

    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)
    

    首先,为简单起见,删除除单张卡之外的所有内容,然后应用Canny边缘检测器:

    enter image description here

    enter image description here

    然后你可以扩张/侵蚀,纠正透视,去除最大的轮廓等 .

相关问题