【问题标题】:How can I remove double lines detected along the edges?如何删除沿边缘检测到的双线?
【发布时间】:2021-06-29 15:45:25
【问题描述】:

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

    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)

输入和输出图像如下:

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

【问题讨论】:

  • 能分享一下原图吗?还有你想保留哪些行?
  • 为什么要在阈值之后应用 Canny? Canny 对二进制图像应用毫无意义,您可以使用更便宜的算法来查找二进制图像的边缘(例如图像减去图像的侵蚀)。 Canny 旨在应用于灰度图像,它可以巧妙地区分重要边缘和不重要边缘。
  • @Prefect :非常抱歉上次没有分享输入图像。现在我在 Q 的编辑部分添加了一个输入及其对应的输出。

标签: python opencv image-processing edge-detection canny-operator


【解决方案1】:

一个简单的解决方案是在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, mask=None, seedPoint=(int(0), int(0)), 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, mask=None, seedPoint=(int(0), int(0)), newVal=(255))
cv2.floodFill(holes, mask=None, seedPoint=(int(10), int(rows-10)), newVal=(255))
cv2.floodFill(holes, mask=None, seedPoint=(int(cols-10), int(10)), newVal=(255))
cv2.floodFill(holes, mask=None, seedPoint=(int(cols-10), int(rows-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)

这些是孤立的孔和手面具:

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

# 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)

这是结果:

【讨论】:

  • 您好,感谢您的回答。非常抱歉上次没有分享输入图像。现在我在 Q 的编辑部分添加了一个输入及其相应的输出。你能看一次吗?
  • 您的解决方案对输入图像效果不佳。
【解决方案2】:

看起来你走对了,但正如@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 阈值之后,我得到了:

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

【讨论】:

  • 您好,感谢您的建议。我会明白的。在您的解决方案中,根本没有检测到掌纹。实际上,美国手语识别将有 26 种手势。仅从轮廓来看,多个手势看起来非常相似。所以还需要检测掌纹。
  • 你的问题只是关于手指周围的双线。您能否根据您的需要更新您的问题,例如输入和预期输出?
  • 我的意思是边缘,整个 - 不仅仅是轮廓(外边缘)。可能是,我没有正确表达。无论如何,在我原来的 Q 中进行了编辑。
猜你喜欢
  • 2019-01-29
  • 1970-01-01
  • 2016-10-04
  • 2018-10-05
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多