如何用Python方式在图像中找到篮球?
我最近在做一个小项目,想要找到图片中的篮球。过去几周我尝试了很多方法,比如使用hough.circles和变换等,但总是无法把篮球从图片中分离出来,无论是参考代码还是我自己尝试的都没能成功。
这是我用的一个示例照片:
这是我尝试的一个简单的圆形查找代码的结果:
有没有人知道我哪里出错了,怎么才能做对?
这是我正在调整的代码:
import cv2
import cv2.cv as cv # here
import numpy as np
def draw_circles(storage, output):
circles = np.asarray(storage)
for circle in circles:
Radius, x, y = int(circle[0][3]), int(circle[0][0]), int(circle[0][4])
cv.Circle(output, (x, y), 1, cv.CV_RGB(0, 255, 0), -1, 8, 0)
cv.Circle(output, (x, y), Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)
orig = cv.LoadImage('basket.jpg')
processed = cv.LoadImage('basket.jpg',cv.CV_LOAD_IMAGE_GRAYSCALE)
storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)
#use canny, as HoughCircles seems to prefer ring like circles to filled ones.
cv.Canny(processed, processed, 5, 70, 3)
#smooth to reduce noise a bit more
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 7, 7)
cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 2, 32.0, 30, 550)
draw_circles(storage, orig)
cv.imwrite('found_basketball.jpg',orig)
2 个回答
1
以下是一些想法:
- 首先按颜色过滤,这样可以简化图像。如果你特别想找一个橙色的篮球,可以先把其他颜色去掉。我建议使用HSI颜色空间,而不是RGB,但无论如何,你都应该能排除那些和你训练的篮球颜色差距较大的颜色。
- 试试用Sobel或者其他不需要手动参数的边缘检测方法。把边缘图像显示出来,看看它看起来是否“正确”。
- 允许边缘稍微弱一些。在灰度图像中,篮球和球员深色球衣之间的对比没有白色内衣和黑色球衣之间的对比那么明显。
- 如果物体在横截面上只是名义上是圆形的,但实际上是拉长的或者边缘有噪声,Hough变换可能会产生意想不到的结果。我通常会自己写Hough算法,没有用过OpenCV的实现,所以不太确定该改哪个参数,但你可以试着允许边缘模糊一些。
- 也许可以去掉平滑操作。无论如何,试着在寻找边缘之前先进行平滑,而不是反过来。
- 尝试自己写一个粗略的Hough算法。虽然快速实现可能没有OpenCV的实现灵活,但通过亲自动手,你可能会发现问题的根源。
3
我同意其他人的看法,使用篮球的颜色来识别它是个不错的方法。下面是一些简单的代码来实现这个功能:
import cv2
import numpy as np
im = cv2.imread('../media/basketball.jpg')
# convert to HSV space
im_hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
# take only the orange, highly saturated, and bright parts
im_hsv = cv2.inRange(im_hsv, (7,180,180), (11,255,255))
# To show the detected orange parts:
im_orange = im.copy()
im_orange[im_hsv==0] = 0
# cv2.imshow('im_orange',im_orange)
# Perform opening to remove smaller elements
element = np.ones((5,5)).astype(np.uint8)
im_hsv = cv2.erode(im_hsv, element)
im_hsv = cv2.dilate(im_hsv, element)
points = np.dstack(np.where(im_hsv>0)).astype(np.float32)
# fit a bounding circle to the orange points
center, radius = cv2.minEnclosingCircle(points)
# draw this circle
cv2.circle(im, (int(center[1]), int(center[0])), int(radius), (255,0,0), thickness=3)
out = np.vstack([im_orange,im])
cv2.imwrite('out.png',out)
结果:
我假设:
- 场景中总是只有一个篮球
- 篮球是场景中最显眼的橙色物体
在这些假设下,如果我们找到任何颜色正确的物体,就可以认为它是篮球,并为它拟合一个圆。这种方法完全不需要进行圆形检测。
从上面的图片可以看到,有一些较小的橙色元素(比如短裤上的颜色)可能会影响我们对篮球半径的估计。代码使用了一种叫做opening
的操作(先进行erosion
再进行dilation
),来去除这些干扰。这在你的示例图片中效果很好。不过对于其他图片,可能需要用不同的方法,比如使用圆形检测,或者根据轮廓的形状和大小,或者如果我们在处理视频的话,可以追踪篮球的位置。
我在一个随机的短篮球视频上运行了这段代码(只做了视频方面的修改),结果效果还不错(虽然不是特别好,但也还可以)。