找虫子~

2018-09-29  本文已影响0人  wenyilab

没错,这个项目就是来找虫子,当然是找那种很显而易见的那种,比如下图:


wenyilab

也许你说这不是明摆着一个大虫子吗,还用的着去找?

确实这个虫子是在这呢,但是要求的是用矩形框将虫子框出来,这里的难点在于虫子的触角部分很细,而右下角的logo离虫子也比较近,所以我们需要矩形既要框住虫子,又不能框住右下角的logo。

那么我们就一步一步去来解决吧~

首先我们我们需要将图片读入,转变为灰度图

#引入相关模块
import cv2
import numpy as np

img_path = "img_path"
img = cv2.imread(img_path)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

现在处理之后的效果是:


wenyilab

对该灰度图使用Sobe算子求梯度

gradx = cv2.Sobel(gray,ddepth = cv2.CV_32F,dx = 1,dy = 0)
grady = cv2.Sobel(gray,ddepth = cv2.CV_32F,dx = 0,dy = 1)
gradient = cv2.subtract(gradx,grady)

此时效果是:


wenyilab

额,sobel算子处理后的结果到底是啥,能干啥呢?
sobel算子是对图像边缘进行检测,是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度之近似值,利用快速卷积函数来提取边缘,简单有效,但其并没有将图像的主体与背景严格区分开,也就是说sobel算子没有基于图像灰度进行处理,提取的轮廓并不能令人满意(本篇的图像还是很不错的)。

在Sobel函数的第二个参数使用了cv2.CV_32F,因为opencv文档中对Sobel中的介绍中有这样的一句话:“in the case of 8-bit input images it will result in truncated derivatives”。即Sobel函数求完导数之后会有负值,而原图像是unit8(8位无符号数),所以Sobel建立的图像位数不够,会有截断,因此要使用32位有符号的数据类型,之后我们需要将上图转回原来的uint8形式。

gradient = cv2.convertScaleAbs(gradient)

效果图:


wenyilab

恩,这样看起来舒服多了,看来Sobel算子对于这张图提取的边缘信息还是很不错的。

接下来需要对上图进行高斯模糊,为后面二值化的阈值做准备

blurred = cv2.GaussianBlur(gradient,(9,9),0)

效果如下:


wenyilab

之后进行二值化和形态学处理:

(_,thresh) = cv2.threshold(blurred,90,255,cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(15,15))
closed = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel)

效果如下:


wenyilab

cv2.threshold函数是做全局形式的阈值,将整幅图像分成了非黑即白的二值图像。
形态学处理的核心就是定义结构元素,在opencv-python中,可以使用其自带的getStructuringElement函数,也可以使用Numpy的ndarray来定义一个结构函数,在本文中使用的是python-opencv内置的常量定义椭圆(MORPH_ELLIPSE)

之后我们我们对上图进行闭运算,即先膨胀后腐蚀,如果进行开运算会将虫子触角部分截断导致不能正确框出我们想要的部分。

closed = cv2.dilate(closed,None,iterations = 4)
closed = cv2.erode(closed,None,iterations = 4)

效果图如下:


wenyilab

恩,效果还可以,接下来就是找图像的轮廓,然后画出虫子的图像了。

#找出上图的轮廓 第二个参数是检索模式,第三个参数是轮廓储存,返回轮廓本身和每个轮廓对应的属性
(_,cnts,_) = cv2.findContours(closed.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_TC89_KCOS)
c = sorted(cnts,key = cv2.contourArea,reverse = True)[0]
#取找到的最小轮廓 rect = ((center_x,center_y),(width,height),angle)
rect = cv2.minAreaRect(c)
#将rect变为整数
box = np.int0(cv2.boxPoints(rect))
#画出轮廓
draw_img = cv2.drawContours(img.copy(),[box],-1,(0,0,255),3)
cv2.imshow("draw_img",draw_img)
cv2.waitKey(0)
cv2.destoryAllWindows()

最终效果如下:


wenyilab

完美~

上一篇下一篇

猜你喜欢

热点阅读