发布时间:2023-03-31 文章分类:电脑基础 投稿人:樱花 字号: 默认 | | 超大 打印

Ⅰ. 边缘检测算法

0x01.Canny边缘检测

Canny边缘检测算法是由4步构成,分别介绍如下:

第一步:噪声去除
由于边缘检测很容易受到噪声的影响,所以首先使用【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化高斯滤波器去除噪声,在图像平滑那一章节中已经介绍过。

第二步:计算图像梯度

对平滑后的图像使用 Sobel 算子计算水平方向和竖直方向的一阶导数( 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 和  【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 )。根据得到的这两幅梯度图( 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 和  【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 )找到边界的梯度和方向,公式如下:

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

如果某个像素点是边缘,则其梯度方向总是垂直与边缘垂直。

梯度方向被归为四类:垂直,水平,和两个对角线方向。

第三步:非极大值抑制

在获得梯度的方向和大小之后,对整幅图像进行扫描,去除那些非边界上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下图所示:

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

A点位于图像的边缘,在其梯度变化方向,选择像素点B和C,用来检验A点的梯度是否为极大值,若为极大值,则进行保留,否则A点被抑制,最终的结果是具有“细边”的二进制图像。

第四步:滞后阈值

现在要确定真正的边界。 我们设置两个阈值:【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 和 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化 。 
当图像的灰度梯度高于【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化时被认为是真的边界, 低于 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化的边界会被抛弃。如果介于两者之间的话,就要看这个点是否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是就抛弃。如下图:

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 如上图所示,A 高于阈值 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化所以是真正的边界点,C 虽然低于【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化但高于 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因为低于 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化而且不与真正的边界点相连。所以选择合适的 【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化对于能否得到好的结果非常重要。(指定的越大,标准越高,指定的越小,标准越低)

💬API

canny = cv2.Canny(image, threshold1, threshold2)

💎示例:

img = cv2.imread('sun.jpg')
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
v1 = cv2.Canny(img, 80, 150)
v2 = cv2.Canny(img, 50, 100)
res = np.hstack((v1,v2))
cv2.imshow('res', res)
cv2.waitKey(0)  # 等待时间,毫秒级,0表示按任意键终止
cv2.destroyAllWindows()

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 Ⅱ. 图像轮廓

0x00 轮廓检测

💬API

cv2.findContours(img,mode,method)

mode:轮廓检索模式

method:轮廓逼近方法

Ⅲ. 直方图

0x00 灰度直方图

原理
直方图是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。

其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。

图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素个数。

这种直方图中,横坐标的左侧为较暗的区域,而右侧为较亮的区域。因此一张较暗图片的直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。

📌 注意:直方图是根据灰度图进行绘制的,而不是彩色图像。  


假设有一张图像的信息(灰度值 0 - 255,已知数字的范围包含 256 个值,于是可以按一定规律将这个范围分割成子区域(也就是 bins)。如:

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 然后再统计每一个 bin(i) 的像素数目。可以得到下图(其中 x 轴表示 bin,y 轴表示各个 bin 中的像素个数):

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

直方图的一些术语和细节

直方图的意义

直方图的计算和绘制
我们使用OpenCV中的方法统计直方图,并使用matplotlib将其绘制出来。

💬API

cv2.calcHist(images,channels,mask,histSize,ranges[,hist[,accumulate]])

参数:

💎示例:

绘制灰度直方图

# 1 直接以灰度图的方式读入
img = cv2.imread('sun.jpg',0)#0表示灰度图
# 2 统计灰度图
hist = cv2.calcHist([img], [0], None, [256], [0,256])
# 3 绘制灰度图
plt.figure(figsize=(10,6),dpi=100)
plt.plot(hist)
plt.grid()
plt.show()

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 绘制彩色直方图

img = cv2.imread('sun.jpg')
color=('b', 'g', 'r')
for i,col in enumerate(color):
    histr =cv2.calcHist([img],[i], None,[256],[0,256])
    plt.plot(histr,color=col)
    plt.xlim([0,256])
plt.show()

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

0x01 掩膜

掩膜是用选定的图像、图形或物体,对要处理的图像进行遮挡,来控制图像 处理的区域。

在数字图像处理中,我们通常使用二维矩阵数组进行掩膜。掩膜是由0和1组成一个二进制图像,利用该掩膜图像要处理的图像进行掩膜,其中1值的区域被处理,0 值区域被屏蔽,不会处理。

掩膜的主要用途是:

掩膜在遥感影像处理中使用较多,当提取道路或者河流,或者房屋时,通过一个掩膜矩阵来对图像进行像素过滤,然后将我们需要的地物或者标志突出显示出来。

我们使用cv.calcHist()来查找完整图像的直方图。

如果要查找图像某些区域的直方图,该怎么办?

只需在要查找直方图的区域上创建一个白色的掩膜图像,否则创建黑色, 然后将其作为掩码mask传递即可。

💎示例:

# 1. 直接以灰度图的方式读入
img = cv2.imread('sun.jpg', 0)
# 2. 创建蒙版
mask = np.zeros(img.shape[:2], np.uint8)
mask[400:650, 200:500] = 255
# 3.掩模
masked_img = cv2.bitwise_and(img,img,mask = mask)
# 4. 统计掩膜后图像的灰度图
mask_histr = cv2.calcHist([img],[0],mask,[256],[1,256])
# 5. 图像展示
fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(img,cmap=plt.cm.gray)
axes[0,0].set_title("Original image")
axes[0,1].imshow(mask,cmap=plt.cm.gray)
axes[0,1].set_title("Masking data")
axes[1,0].imshow(masked_img,cmap=plt.cm.gray)
axes[1,0].set_title("Post-mask data")
axes[1,1].plot(mask_histr)
axes[1,1].grid()
axes[1,1].set_title("Grayscale histogram")
plt.show()

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 Ⅳ. 直方图均衡化

0x01 原理与应用
想象一下,如果一副图像中的大多数像素点的像素值都集中在某一个小的灰度值值范围之内会怎样呢?

如果一幅图像整体很亮,那所有的像素值的取值个数应该都会很高。所以应该把它的直方图做一个横向拉伸(如下图),就可以扩大图像像素值的分布范围,提高图像的对比度,这就是直方图均衡化要做的事情。
“直方图均衡化”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在更广泛灰度范围内的分布。

直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。

这种方法提高图像整体的对比度,特别是有用数据的像素值分布比较接近时,在X光图像中使用广泛,可以提高骨架结构的显示,另外在曝光过度或不足的图像中可以更好的突出细节。

使用opencv进行直方图统计时,使用的是:

dst = cv.equalizeHist(img)

返回:

💎 示例:

img = cv2.imread('sun.jpg', 0)
dst = cv2.equalizeHist(img)
plt.hist(dst.ravel(),256)
plt.show()

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

0x01 自适应的直方图均衡化

上述的直方图均衡,我们考虑的是图像的全局对比度。 在进行完直方图均衡化之后,图片背景的对比度被改变了,但是我们丢失了很多信息,所以在许多情况下,这样做的效果并不好。如下图所示,对比下两幅图像中雕像的画面,由于太亮我们丢失了很多信息。

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 
为了解决这个问题, 需要使用自适应的直方图均衡化。

此时, 整幅图像会被分成很多小块,这些小块被称为“tiles”(在 OpenCV 中 tiles 的 大小默认是 8x8),然后再对每一个小块分别进行直方图均衡化。 所以在每一个的区域中, 直方图会集中在某一个小的区域中)。

但如果有噪声的话,噪声会被放大。为了避免这种情况的出现要使用对比度限制。对于每个小块来说,如果直方图中的 bin 超过对比度的上限的话,就把 其中的像素点均匀分散到其他 bins 中,然后在进行直方图均衡化。

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

 最后,为了 去除每一个小块之间的边界,再使用双线性差值,对每一小块进行拼接。

💬API

cv.createCLAHE(clipLimit, tileGridSize)

参数:

💎示例:

img = cv2.imread('sun.jpg', 0)
dst = cv2.equalizeHist(img)
plt.hist(dst.ravel(),256)
clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
res_clahe = clahe.apply(img)
res = np.hstack((img, dst, res_clahe))
cv2.imshow('res',res)

【OpenCV】 Canny边缘检测 | 图像轮廓检测 | 直方图均衡化

总结

1. 灰度直方图:

cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])

2. 掩膜
   创建蒙版,透过mask进行传递,可获取感兴趣区域的直方图

3. 直方图均衡化:增强图像对比度的一种方法
cv.equalizeHist( ): 输入是灰度图像,输出是直方图均衡图像

4. 自适应的直方图均衡
将整幅图像分成很多小块,然后再对每一个小块分别进行直方图均衡化,最后进行拼接
clahe = cv.createCLAHE(clipLimit, tileGridSize)