如何删除沿边缘检测到的双线?

问题描述

我正在尝试使用网络摄像头实时输入手势,然后处理图像以将它们提供给神经网络。我写这个处理函数是为了让手部特征看起来突出:

    img = cv2.imread('hand.png')
   
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray,(5,5),2)

    th3 = cv2.adaptiveThreshold(blur,10,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY_INV,11,2)
    ret,res = cv2.threshold(th3,225,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) 
    res = cv2.Canny(res,100,200) 
    cv2.imshow("Canny",res)

输入和输出图像如下:

enter image description here

enter image description here

很明显,沿着边缘(整个手,不仅仅是轮廓)检测到双线,而不是一条。我想让他们单身。如果我只应用 Canny 边缘检测算法,那么边缘不是很突出。

解决方法

一个简单的解决方案是使用 flood-fill white 背景,然后使用 black 使用 cv2.floodFill,如下所示:

import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "hand.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Convert the image to Grayscale:
binaryImage = cv2.cvtColor(inputImage,cv2.COLOR_BGR2GRAY)

# Flood fill bakcground (white + black):
cv2.floodFill(binaryImage,mask=None,seedPoint=(int(0),int(0)),newVal=(255))

cv2.floodFill(binaryImage,newVal=(0))

cv2,imshow("floodFilled",binaryImage)
cv2.waitKey(0)

结果如下:

如果您想获得手部的实体蒙版,您可以尝试填充手部轮廓内的孔,同样使用 flood-fill 和一些图像算法,如下所示:

# image path
path = "D://opencvImages//"
fileName = "hand.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Convert the image to Grayscale:
binaryImage = cv2.cvtColor(inputImage,cv2.COLOR_BGR2GRAY)

# Isolate holes on input image:
holes = binaryImage.copy()
# Get rows and cols from input:
(rows,cols) = holes.shape[:2]

# Remove background via flood-fill on 4 outermost corners
cv2.floodFill(holes,newVal=(255))
cv2.floodFill(holes,seedPoint=(int(10),int(rows-10)),seedPoint=(int(cols-10),int(10)),newVal=(255))

# Get holes:
holes = 255 - holes
# Final image is original imput + isolated holes:
mask = binaryImage + holes

# Deep copy for further results:
maskCopy = mask.copy()
maskCopy = cv2.cvtColor(maskCopy,cv2.COLOR_GRAY2BGR)

这些是隔离孔和手罩:

然后您可以通过处理 bounding rectangle、过滤小区域斑点并近似为矩形来检测 contours,如下所示:

# Find the big contours/blobs on the processed image:
contours,hierarchy = cv2.findContours(mask,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

# Get bounding rectangles:
for c in contours:

    # Filter contour by area:
    blobArea = cv2.contourArea(c)
    maxArea = 100

    if blobArea > maxArea:

        # Approximate the contour to a polygon:
        contoursPoly = cv2.approxPolyDP(c,3,True)
        # Get the polygon's bounding rectangle:
        boundRect = cv2.boundingRect(contoursPoly)

        # Get the dimensions of the bounding rect:
        rectX = boundRect[0]
        rectY = boundRect[1]
        rectWidth = boundRect[2]
        rectHeight = boundRect[3]

        # Draw rectangle:
        color = (0,255,0)
        cv2.rectangle(maskCopy,(int(rectX),int(rectY)),(int(rectX + rectWidth),int(rectY + rectHeight)),color,3)

        cv2.imshow("Bounding Rectangle",maskCopy)
        cv2.waitKey(0)

结果如下:

,

看起来您走对了路,但正如@CrisLuengo 提到的,Canny 应用于灰度图像而不是二进制图像。这是一种方法。

import numpy as np
import matplotlib.pyplot as plt
import cv2


img_gray = cv2.imread('hand.png',0)

sigma = 2
threshold1=30
threshold2=60

img_blur = cv2.GaussianBlur(img_gray,(5,5),sigmaX=sigma,sigmaY=sigma)
res = cv2.Canny(img_blur,threshold1=threshold1,threshold2=threshold2)
fig,ax = plt.subplots(1,2,sharex=True,sharey=True)
ax[0].imshow(img_gray,cmap='gray')
ax[1].imshow(res,cmap='gray')
plt.show()

在玩弄高斯滤波器的参数和 Canny 阈值之后,这就是我得到的:

fingers

如您所见,除了拇指之外,大部分手指都被清晰地检测到。光照条件使得Canny 很难计算出合适的梯度。您可以尝试通过设置来提高图像的对比度(这对我来说是最简单的解决方案),或者在使用 Canny 之前应用一些对比度增强方法,例如 Contrast Limited Adaptive Histogram Equalization (CLAHE)。不过,在使用 CLAHE 进行了几次试验后,我没有得到比上面更好的结果,但可能值得一看。祝你好运!