在 openCV 中使用 approxPolyDP() 检测矩形不准确

sat*_* 29 9 python opencv

作为包含一系列要处理的图像的程序的一部分,我首先需要先检测一个绿色矩形。我正在尝试编写一个不使用颜色遮罩的程序,因为图像上的照明/眩光会使找到合适的 HSV 范围变得困难。

(ps:我已经有两个基于这个“程序”的问题,但这个问题与那些无关。这不是跟进,我想解决一个单独的问题。)

我使用了标准的矩形检测技术:利用 findContours() 和 approxPolyDp() 方法。我添加了一些摆脱不必要矩形的约束(例如 aspectRatio>2.5: 因为我想要的矩形显然是“最宽的”并且 area>1500,以丢弃随机的小矩形)。

import numpy as np
import cv2 as cv

img = cv.imread("t19.jpeg")

width=0 
height=0

start_x=0 
start_y=0
end_x=0 
end_y=0


output = img.copy()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)


#threshold
th = cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,9,2)

cv.imshow("th",th)



#rectangle detection

contours, _ = cv.findContours(th, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

for contour in contours:

    approx = cv.approxPolyDP(contour, 0.01* cv.arcLength(contour, True), True)
    
    cv.drawContours(img, [approx], 0, (0, 0, 0), 5)
    
    x = approx.ravel()[0]
    y = approx.ravel()[1]

    x1 ,y1, w, h = cv.boundingRect(approx)
    a=w*h    
    if len(approx) == 4 and x>15  :
            
        aspectRatio = float(w)/h
        if  aspectRatio >= 2.5 and a>1500:          
          print(x1,y1,w,h)
          width=w
          height=h   
          start_x=x1
          start_y=y1
          end_x=start_x+width
          end_y=start_y+height      
          cv.rectangle(output, (start_x,start_y), (end_x,end_y), (0,0,255),3)
          cv.putText(output, "rectangle "+str(x1)+" , " +str(y1-5), (x1, y1-5), cv.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))
          
cv.imshow("op",output)

print("start",start_x,start_y)
print("end", end_x,end_y)
print("width",width)
print("height",height)
Run Code Online (Sandbox Code Playgroud)

它对所有图像都完美无缺,除了一个

在此处输入图片说明

我使用自适应阈值来创建阈值,该阈值由 findContours() 方法使用。我尝试显示阈值和输出,它看起来像这样:

在此处输入图片说明

其他图像的阈值看起来也相似……​​所以我无法确定矩形检测过程中到底出了什么问题。

我尝试过的一些调整:

  1. 更改自适应参数方法中的最后两个参数。我尝试了 11,1 、 9,1 ,对于它们两个,阈值中的矩形看起来更突出:但在这种情况下,输出根本没有检测到矩形。
  2. 我已经忽略了 otsu 阈值,因为它不适用于我的大约 4 个测试图像。

我究竟可以在矩形检测程序中调整什么来检测这个矩形?

我还要求,如果可能的话,只对这个方法稍作修改,而不是一些全新的方法。正如我所提到的,这种方法对我所有的其他测试图像都非常有效,如果新建议的方法对这个图像有效而对其他图像失败,那么我会发现自己回到这里问为什么它失败了。

编辑:abss 建议的方法适用于该图像,但失败了:

4

1、远

其他测试图片:

1、正常

2

3

9,part1

9,part2

abs*_*bss 5

您可以通过在阈值后添加这行代码来轻松完成

kernel = cv.getStructuringElement(cv.MORPH_RECT,(3,3))
th = cv.morphologyEx(th,cv.MORPH_OPEN,kernel)
Run Code Online (Sandbox Code Playgroud)

这将消除图像中的噪声。您可以查看此链接以进一步了解morphologyEx https://docs.opencv.org/master/d9/d61/tutorial_py_morphological_ops.html

我得到的结果如下所示 在此处输入图片说明


Kni*_*ked 5

我对您的代码进行了一些修改,以便它适用于您的所有测试图像。您可能需要根据绿色的 HSV 颜色范围过滤一些误报(因为您的目标始终是绿色阴影)。或者,您可以考虑这样一个事实:您的 ROI 轮廓的子层次结构将是外部轮廓的 > 0.4 倍左右。以下是修改内容:

  1. 使用 DoG 对有用轮廓进行阈值处理
  2. 将 arcLength 乘数更改为 0.5 而不是 0.1,因为方角不平滑
  3. cv2.RETR_CCOMP 获得 2 级层次结构
  4. 将 ApproxPolyDP 移入内部以提高效率
  5. 轮廓过滤器面积更改为 600 以过滤所有测试图像的 ROI
  6. 删除了一些不必要的代码

检查您可能拥有的所有其他测试图像并相应地修改参数。

img = cv2.imread("/path/to/your_image")

width=0 
height=0

start_x=0 
start_y=0
end_x=0 
end_y=0

output = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

gw, gs, gw1, gs1, gw2, gs2 = (3,1.0,7,3.0, 3, 2.0)

img_blur = cv2.GaussianBlur(gray, (gw, gw), gs)
g1 = cv2.GaussianBlur(img_blur, (gw1, gw1), gs1)
g2 = cv2.GaussianBlur(img_blur, (gw2, gw2), gs2)
ret, thg = cv2.threshold(g2-g1, 127, 255, cv2.THRESH_BINARY)

contours, hier = cv2.findContours(thg, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

img_cpy = img.copy()

width=0 
height=0

start_x=0 
start_y=0
end_x=0 
end_y=0

for i in range(len(contours)):
    
    if hier[0][i][2] == -1:
        continue
        
    x ,y, w, h = cv2.boundingRect(contours[i])
    a=w*h    
    aspectRatio = float(w)/h
    if  aspectRatio >= 2.5 and a>600:          
        approx = cv2.approxPolyDP(contours[i], 0.05* cv2.arcLength(contours[i], True), True)
        if len(approx) == 4 and x>15  :
            width=w
            height=h   
            start_x=x
            start_y=y
            end_x=start_x+width
            end_y=start_y+height      
            cv2.rectangle(img_cpy, (start_x,start_y), (end_x,end_y), (0,0,255),3)
            cv2.putText(img_cpy, "rectangle "+str(x)+" , " +str(y-5), (x, y-5), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))
          
plt.imshow(img_cpy)

print("start",start_x,start_y)
print("end", end_x,end_y)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述