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

Python 如何删除沿边缘检测到的双线?,python,opencv,image-processing,edge-detection,canny-operator,Python,Opencv,Image Processing,Edge Detection,Canny Operator,我正试图通过网络摄像头获取手势的实时输入,然后对图像进行处理,将其输入神经网络。我编写此处理函数是为了使手部特征看起来突出: 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_THRES

我正试图通过网络摄像头获取手势的实时输入,然后对图像进行处理,将其输入神经网络。我编写此处理函数是为了使手部特征看起来突出:

    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边缘检测算法,那么边缘不是很突出。

一个简单的解决方案是用
白色
填充背景,然后用
黑色
使用
cv2。泛光填充
,如下所示:

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)
# 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)
# 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)
结果是:

如果要获得手的实心遮罩,可以尝试填充手轮廓内的孔,也可以使用
整体填充
和一些图像算法,如下所示:

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)
# 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)
# 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)
这些是隔离孔和手罩:

然后,您可以通过处理
轮廓
、过滤小面积斑点并近似矩形来检测
边界矩形
,如下所示:

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)
# 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)
# 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阈值之后,我得到了以下结果:


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

你能分享一下原始图像吗?还有,您希望保留哪些行?为什么要在阈值之后应用Canny?Canny应用于二值图像是毫无意义的,您可以使用更便宜的算法来查找二值图像的边缘(例如,图像减去图像的腐蚀)。Canny适用于灰度图像,它擅长区分重要边缘和非重要边缘。@Perfect:非常抱歉,我上次没有共享输入图像。现在我在Q的编辑部分添加了一个输入及其相应的输出。嗨,谢谢你的回答。非常抱歉,上次我没有共享输入图像。现在,我在Q的编辑部分添加了一个输入及其相应的输出。您能看一下吗?您的解决方案与输入图像不匹配。您好,谢谢您的建议。我看看。在您的解决方案中,根本检测不到手掌线。实际上,美国手语识别将有26种手势。仅从轮廓上看,多个手势看起来非常相似。所以也需要检测手掌线。你的问题只是手指周围的双线。请您根据您的需要更新您的问题,例如,输入和预期输出?我指的是边缘,不仅仅是轮廓(外边缘)。可能是,我表达得不恰当。总之,在我的原始Q中进行了编辑。