问题描述
我正在尝试使用网络摄像头实时输入手势,然后处理图像以将它们提供给神经网络。我写这个处理函数是为了让手部特征看起来突出:
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 边缘检测算法,那么边缘不是很突出。
解决方法
一个简单的解决方案是使用 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 阈值之后,这就是我得到的:
如您所见,除了拇指之外,大部分手指都被清晰地检测到。光照条件使得Canny 很难计算出合适的梯度。您可以尝试通过设置来提高图像的对比度(这对我来说是最简单的解决方案),或者在使用 Canny 之前应用一些对比度增强方法,例如 Contrast Limited Adaptive Histogram Equalization (CLAHE)。不过,在使用 CLAHE 进行了几次试验后,我没有得到比上面更好的结果,但可能值得一看。祝你好运!