昨天和今天没有学习,有个学弟找我帮忙做个小demo,说要交差应付一下作业。想法是测量一下图片中近似于圆柱的物体的直径。因为我之前写过一个雷达图像的点云目标检测,我当时想这俩差不多,我应该俩小时就能写完,毕竟网上很多东西都是现成的,一边抄一点就够了,但是还是有很多坑要踩,过程中发现自己的代码编写能力真实差的雅痞。
边缘探测
之前的雷达云图实际不存在边缘探测的问题,这也是我后来意识到的问题。雷达云图之前的探测就是转为灰度图,然后二值化,形态学操作,再找边缘就可以了。但是我在做这个的时候发现两者之间区别还是很大的,我要是想把目标图片也二值化的结果很糟糕,我就在网上找了类似的。发现他没有二值化,直接高斯滤波之后就findcounter了。结果竟然挺棒的。很简单,就是直接用canny边缘算子滤波之后得到结果再寻找边缘。
这里是参考的代码
然后我就尝试了一下之后发现确实不用很复杂:
- 转为灰度图
- 高斯滤波
- 边缘检测
- 膨胀腐蚀
- 寻找边界
def detect_cnts(img):
# 读取输入图片
image = img
# 输入图片灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 对灰度图片执行高斯滤波
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# 二值化
# 对滤波结果做边缘检测获取目标
edged = cv2.Canny(gray, 50, 100)
# 使用膨胀和腐蚀操作进行闭合对象边缘之间的间隙
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
# 在边缘图像中寻找物体轮廓(即物体)
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts) # 提取轮廓
# 对轮廓按照从左到右进行排序处理
(cnts, _) = contours.sort_contours(cnts)
return cnts
该函数返回的就是探测到的边界
旋转图像
探测到边界之后理论上直接对外界矩形进行测量宽度就可以了,但是我的目标不是很规则,他是有点奇形怪状的,外接矩形的宽度和他实际的直径投影差的太多了,就得想办法。我一开始想根据外界矩形,把内部切割成几个部分重新检测得到结果。但是我发现最难的是再opencv里图像旋转的函数我不知道是怎么运作的,我自己写的点旋转函数和图像旋转并不重合,很重要的原因在于如果想要旋转后的图像中心不变,是需要对M矩阵进行平移变化的,我不懂。所以我没法知道矩形旋转后的坐标,就没法切割。于是想法变成了先把原图像按照外界矩形框旋转了再说。这里仅涉及到了图片旋转之后中心变了的问题的问题。这个方法也是我在网上找的。
def rotate_bound(image, angle):
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle t