Project 4:
请从图像中分割出珍珠,并检测其边缘
打开珍珠图像的原图。
待解决的问题:
图像中的珍珠大小不一,有部分重叠,并且还有很多反射的光斑,还有珍珠挡住了光以后形成的阴影部分影响检测。我要做到的是准确的检测分割出珍珠,并且准确的检测出珍珠形状的边缘。
思路及原理:
具体思路步骤如下:l
1.先将图片转化为灰度图,cv2.cvtColor(input_image, flag)函数实现图片颜色空间的转换,flag 参数决定变换类型。如 BGR->Gray flag 就可以设置为cv2.COLOR_BGR2GRAY。
2.得到灰度图以后,采用cv2.HoughCircles函数来检测珍珠的形状,并在原图上画出圆,圈出珍珠,完成检测。
cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius) image为输入图像,要求是灰度图像,circles1为输出圆向量,每个向量包括三个浮点型的元素——圆心横坐标,圆心纵坐标和圆半径。method为使用霍夫变换圆检测的算法,它的参数是CV_HOUGH_GRADIENT
dp为第一阶段所使用的霍夫空间的分辨率,dp=1时表示霍夫空间与输入图像空间的大小一致,dp=2时霍夫空间是输入图像空间的一半,以此类推
minDist为圆心之间的最小距离,如果检测到的两个圆心之间距离小于该值,则认为它们是同一个圆心
param1为边缘检测时使用Canny算子的高阈值
param2为边缘检测时使用Canny算子的低阈值
minRadius和maxRadius为所检测到的圆半径的最小值和最大值
3.对原图像进行高斯滤波去除图像中的噪声,然后采用Canny边缘检测的方法检测图像中珍珠的边缘。
Canny 边缘检测是一种非常流行的边缘检测算法,由于边缘检测很容易受到噪声影响,所以第一步是采用3*3的高斯滤波器滤掉噪声。
在OpenCV中只需要一个函数:cv2.Canny(),就可以完成以上几步。让我们看如何使用这个函数。这个函数的第一个参数是输入图像。第二和第三个分别是minVal和maxVal。
其中较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候用较小的第一个阈值用于将这些间断的边缘连接起来。可选参数中apertureSize就是Sobel算子的大小。而L2gradient参数是一个布尔值,如果为真,则使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开方),否则使用L1范数(直接将两个方向导数的绝对值相加)。
现在要确定那些边界才是真正的边界。这时我们需要设置两个阈值:minVal和maxVal。当图像的灰度梯度高于maxVal时被认为是真的边界,那些低于minVal的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下所示:
A高于阈值maxVal所以是真正的边界点,C虽然低于maxVal但高于minVal并且与A相连,所以也被认为是真正的边界点。而B就会被抛弃,因为它不仅低于maxVal而且不与真正的边界点相连。所以选择合适的maxVal和minVal对于能否得到好的结果非常重要。在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线段。
编程实现:
我依然采用Python+OpenCV的工具来完成这个项目
首先是读取原图片,并定义一个函数用来显示图片,方便在后面调用。并得到该图像的大小。
接下来对图像进行灰度图处理
然后调用cv2.HoughCircles来完成对珍珠的检测,并得到珍珠所在位置的中心坐标,以及珍珠的半径,调节相关阈值,以圆心半径画出圆,准确的圈出图中的珍珠。
接下来完成对图像边缘的检测,先进行高斯滤波,采用3*3,标准差为1的高斯滤波器。
滤波以后采用Canny边缘检测算法,检测边缘
L2gradient参数是一个布尔值,使他为真,使用更精确的L2范数进行计算,得到的结果更加准确。
最后使想要看到的图像显示出来。
结果及分析:
在这里我还对灰度图进行二值化,以及对原图进行了直方图均衡化,期望从不同角度来探究检测和分割的效果。
图像中输入的原图。接下来是经过转化以后得到的灰度图。
对灰度图进行二值化处理以后得到:
得到灰度图像的直方图
然后是均衡化以后的直方图
对灰度图进行均衡化处理以后的图像
接下来是采用了Canny边缘检测的方法检测珍珠的边缘,经过高斯滤波以后的边缘检测除去了很多杂七杂八的线条,使边缘更加简洁明朗,清晰可见。
接下来看一下高斯圆检测方法得到的10个珍珠的圆心坐标和半径大小的矩阵。
采用这些数据进行画圆,将珍珠准确地从图中分割出来。
可以看到图中珍珠检测以及边缘分割的效果非常好,符合项目预期要求。
结论:
从结果图中可以明显看出,canny算子连珍珠的倒影边缘和珍珠表面的反光部分的边缘都检测出来了,干扰的线条很少,简洁清晰明了。而对珍珠的检测,效果非常理想,每一颗珍珠都被清晰地检测了出来,用圆沿着珍珠的边缘准确的勾勒出了线条。达到了项目预期的目标。
附上所有程序代码:
#coding=utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#定义一个用来显示图片的的函数
def readPic(name1,name2):
cv2.namedWindow(name1, flags=0)
cv2.resizeWindow(name1, 500, 500)
cv2.imshow(name1,name2)
#读取图片
img = cv2.imread("C:/Users/ASUS/8/pearls.jpg",1)
print(np.shape(img)) #425*580*3
#对图像进行高斯模糊
img2 = cv2.GaussianBlur(img,(3,3),1)
#print(img)
#对图像进行灰度图处理
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#绘制灰度直方图
hist1 = cv2.calcHist([img],[0],None,[256],[0,256])
plt.figure()#新建一个图像
plt.title("Grayscale Histogram")#图像的标题
plt.xlabel("Bins")#X轴标签
plt.ylabel("# of Pixels")#Y轴标签
plt.plot(hist1)#画图
plt.xlim([0,256])#设置x坐标轴范围
#对直方图进行均衡化
equ = cv2.equalizeHist(gray)
#绘制灰度均衡化以后的直方图
hist2 = cv2.calcHist([equ],[0],None,[256],[0,256])
plt.figure()#新建一个图像
plt.title("Grayscale Histogram")#图像的标题
plt.xlabel("Bins")#X轴标签
plt.ylabel("# of Pixels")#Y轴标签
plt.plot(hist2)#画图
plt.xlim([0,256])#设置x坐标轴范围
#对图像进行二值化处理
ret,img1 = cv2.threshold(gray,200,255,cv2.THRESH_BINARY)
#对图像进行检测边缘
edges1 = cv2.Canny(img2, 10, 80, apertureSize = 3,L2gradient=True)
#edges2 = cv2.Canny(img, 10, 80, apertureSize = 3)
circles1 = cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,1,
55,param1=100,param2=25,minRadius=20,maxRadius=60)
print(circles1)
circles = circles1[0,:,:]#提取为二维
print(circles)
circles = np.uint16(np.around(circles))#四舍五入,取整
for i in circles[:]:
cv2.circle(img,(i[0],i[1]),i[2],(255,0,0),2)#画圆
cv2.circle(img,(i[0],i[1]),2,(255,0,255),5)#画圆心
#显示直方图均衡化图
readPic('equ',equ)
#显示二值图
readPic('img1',img1)
#显示灰度图
readPic('gray',gray)
#显示检测图
readPic('img',img)
#显示边缘检测图1
readPic('edges1',edges1)
#显示边缘检测图2
#('edges2',edges2)
plt.show()#显示图像
cv2.waitKey()
cv2.destroyAllWindows()
领取专属 10元无门槛券
私享最新 技术干货