OpenCV 指南2:如何在图像中进行边缘检测?
在上篇文章中,我们已经学习了 OpenCV 的基础知识。我们已经看到了如何执行图像和视频调整大小、裁剪等,这是 OpenCV 教程的第二部分。本文,我们主要关注 OpenCV 的高级部分,它将涵盖以下提到的问题
在上篇文章中,我们已经学习了 OpenCV 的基础知识。我们已经看到了如何执行图像和视频调整大小、裁剪等,这是 OpenCV 教程的第二部分。
本文,我们主要关注 OpenCV 的高级部分,它将涵盖以下提到的问题。但是,如果你是 OpenCV 的新手或觉得这很难理解,那么请访问OpenCV 基础指南第1部分并复习基础知识,然后再回到此文。
我们思考的问题是:
如何在图像中进行边缘检测?
如何在图像中进行轮廓检测?
如何在图像中执行颜色空间?
如何处理颜色通道?
如何模糊图像?
如何使用位运算符?
如何在图像的每个像素点绘制颜色的直方图?
1)如何在图像中进行边缘检测?
1.1. Canny:对于这里的边缘检测,我们将使用 cv.Canny 方法。输入图像为原始图像,thereshold-1 即像素值低于150 被视为非边缘,threshold-2 即像素值高于175 被视为有效边缘。如果该值在 150 和 175 之间,那么如果边缘像素与有效边缘相连,则仅将其视为有效边缘。original_img=cv.imread("/content/drive/MyDrive/Colab Notebooks/Opencv/NCS/hana-lshin-qUhu8zjm38w-unsplash(1).jpg")
cv2_imshow(original_img)
print(" Edge detection in the Original images ")
#We will find the edges in the image as below
Edge_Org=cv.Canny(original_img,150,175)
cv2_imshow(Edge_Org)
上述代码的输出:原图:
原始图像的边缘:
现在我们将尝试通过使其模糊来找到原始图像的边缘。博客中进一步介绍了如何使图像模糊。
通过比较原始图像和模糊图像之间的边缘检测。我们知道图像中的边缘检测随着图像模糊程度的增加而减少。1.2. 拉普拉斯算子在拉普拉斯边缘检测方法中,我们将计算源图像的 (x,y) 像素的第二个梯度。当 ksize>1 时,查看下面提到的源图像的拉普拉斯公式:
当 ksize=1 时,拉普拉斯算子是通过使用以下 3×3 孔径过滤图像来计算:
其中 ksize:用于计算二阶导数滤波器的孔径大小。大小必须是正数和奇数。lap=cv.Laplacian(original_img,cv.CV_64F)
lap=np.uint8(np.absolute(lap))
cv2_imshow(lap)
上述代码的输出:拉普拉斯边缘检测:
1.3.Sobel边缘检测在计算拉普拉斯算子时,我们计算了称为 Sobel 的二阶导数。因此,在 sobel 检测中,我们将计算 sobelx(也称为水平 Sobel 导数)和 sobely(也称为垂直 Sobel 导数)。我们可以通过输入图像与大小为 3*3 的内核进行卷积,来计算 Sobelx 和 sobely(但我们可以根据需要更改内核大小)。拉普拉斯公式中的 Sobelx 和 sobely:
提到的卷积的 3*3 矩阵是:
Sobel 边缘检测的代码:#Sobel
Sobelx=cv.Sobel(Gray_1,cv.CV_64F,1,0)
Sobely=cv.Sobel(Gray_1,cv.CV_64F,0,1)
Cpmbine_Sobel=cv.bitwise_or(Sobelx,Sobely)
print(" SobelX Edge Detection")
cv2_imshow(Sobelx)
print(" Sobely Edge Detection")
cv2_imshow(Sobely)
print(" ombine Sobel Edge Detection")
cv2_imshow(Cpmbine_Sobel)
上述代码的输出:Sobelx:
Sobely:
结合Sobel:
1.4. 腐蚀和膨胀
1.4.1 腐蚀它对于去除小的白噪声很有用。用于分离两个连接的对象等。怎么运作:内核(奇数大小的矩阵(3,5,7)与图像卷积。仅当内核下的所有像素都为 1 时,原始图像中的像素(1 或 0)才会被视为 1,否则它会被腐蚀(使其为零)。因此,根据内核的大小,将丢弃边界附近的所有像素。因此,前景对象的厚度或大小会减少,或者只是图像中的白色区域会减少。1.4.2 膨胀:在去除噪声等情况下,腐蚀之后是膨胀。因为,腐蚀去除了白噪声,但它也缩小了我们的对象。所以我们膨胀它。由于噪音消失了,它们不会回来,但我们的对象区域会增加。它也可用于连接对象的损坏部分。这个怎么运作:内核(奇数大小的矩阵(3,5,7)与图像卷积如果内核下至少有一个像素为“1”,则原始图像中的像素元素为“1”。它增加了图像中的白色区域或前景对象的大小增加Dilate 和 Erode 的代码演练:#Dilating the image i.e., It will incrases the thickness of the edges
Dilat_img=cv.dilate(Edge_blur,(7,7),iterations=3)
cv2_imshow(Dilat_img)
#We can restore to the original edges by using the erode
print(" ")
Erode_img=cv.erode(Dilat_img,(7,7),iterations=3)
cv2_imshow(Erode_img)
上述代码的输出:将图像输入到 cv.dilate():
Dilate : 它会增加边缘的厚度
腐蚀:这里的输入图像是 cv.dilate 输出。所以腐蚀输出将恢复 Dilate 的输入图像。
2)如何在图像中进行轮廓检测?
有时,如果我们对图像执行边缘检测和轮廓检测,则两者的输出可能看起来相似。但与边缘检测相比,轮廓检测通常会更详细。cv.findContours 函数中的一些重要参数是:Image :输入图像应该是二进制作为源,一个8位的单通道图像。非零像素被视为 1。零像素保持 0,因此图像被视为二进制。轮廓检索模式:1)cv.RETR_TREE →它将给出图像中的分层轮廓。cv.RETR_EXTERNAL →它将给出图像中唯一的外部轮廓。cv.RETR_LIST →它将给出图像中存在的所有轮廓。轮廓逼近方法:1)CHAIN_APPROX_NONE →它将给出图像中存在的所有计数器。2)CHAIN_APPROX_SIMPLE →它将给出图像中的重要轮廓,例如,如果我们有线,它只会给出图像中的起点和终点,而在 CHAIN_APPROX_NONE 的情况下,我们得到所有点。现在让我们来看看它实际上是如何工作的。Org_img=cv.imread("/content/drive/MyDrive/Colab Notebooks/Opencv/NCS/olena-sergienko-UQLGR8otAEs-unsplash(1).jpg")
Canny_img=cv.Canny(Org_img,100,150)
cv2_imshow(Canny_img)
contours,hierarchies= cv.findContours(Canny_img,cv.RETR_LIST,cv.CHAIN_APPROX_NONE)
print(" Number of the contours in the image: ",len(contours))
上图的输出:
现在我们将模糊图像并查看轮廓的数量blur_img=cv.GaussianBlur(Org_img,(5,5),cv.BORDER_DEFAULT)
#if image has the pixel 175 then it is consider as the 255(white)
blur_Canny=cv.Canny(blur_img,150,175)
cv2_imshow(blur_Canny)
# As we have the blur image edges as a Input to the findContours so the number of the contours is also decreases.
contours,hierarchies= cv.findContours(blur_Canny,cv.RETR_LIST,cv.CHAIN_APPROX_NONE)
print(" Number of the contours in the image: ",len(contours))
上述代码的输出:
通过观察图像和它的总轮廓,我们知道模糊图像中的轮廓数量比原始图像少。现在我们将尝试在空白图像上绘制图像的轮廓cv2_imshow(Canny_img)
contours,hierarchies= cv.findContours(Canny_img,cv.RETR_LIST,cv.CHAIN_APPROX_SIMPLE)
print(len(contours))
print(len(hierarchies))
Blank_contours=np.zeros((600,600,3),dtype='uint8')
print(" Plotting the Countours on the blank")
cv.drawContours(Blank_contours,contours,-1,(0,0,255),1)
cv2_imshow(Blank_contours)
上述代码的输出:
在空白图像上绘制轮廓: