本系列文章是OpenCV系列文章的第三篇,仍然跟随上篇内容主要聚焦于图像的一些操作
在通常情况下我们使用大小恒定的图像。但在某些情况下,我们需要使用不同分辨率的同幅图像,例如,在搜索图像中的某些内容比如脸部信息时,并不确定该内容在图像中占据的大小。这种情况下,我们需要创建一组不同的分辨率的相同图像,并在所有图像中搜索该内容。这些不同分辨率的图像被称为图像金字塔(因为当它们堆叠排列时,底部为最高分辨率图像而顶部为最低分辨率图像,看起来像金字塔)
**图像金字塔主要有两种:**
向上采样:在图像金字塔中,越靠下的分辨率越大,所以向上采样指的是从分辨率大的图像中得到分辨率小的图像
方法:
为什么要先卷积再去除呢:
是因为如果直接去除偶数行和偶数列,那势必会导致大量数据的丢失,如果我们先卷积,卷积后的值是它周围的值共同作用的,所以卷积后再去除损失的数据量比直接去除要小很多,但是还是会少一部分数据
向下采样:在图像金字塔中,越靠上的分辨率越大,所以向下采样指的是从分辨率小的图像中得到分辨率大的图像
**方法: **
我们看第一步放大后,有近四分之三的数据都是0,第二部卷积后,实际上是把不是0的数据给分配了一部分给0
那实际上,就是把原有的数据给往四周分散了。那在放大的时候,原来的数据给了其他的,那对应自身来说不也就是丢失了一部分数据。
# 引入图片
img=cv2.imread("AM.png")
cv_show(img,'img')
print (img.shape)
# 向上采样
up=cv2.pyrUp(img)
cv_show(up,'up')
print (up.shape)
# 向下采样
down=cv2.pyrDown(img)
cv_show(down,'down')
print (down.shape)
**而为了让数据尽量的少丢失点,我们又有了拉普拉斯金字塔。 **
https://blog.csdn.net/ftimes/article/details/106731558
** 在高斯金字塔中,我们提到了上采样和下采样。但是无论是上采样还是下采样都会丢失像素值,所以这两种操作并不是可逆的。也就是说,对一幅图先进行上采样再进行下采样,是无法恢复到原始状态。同样,先下采样再上采用也无法恢复到原始状态**
因此我们引入了拉普拉斯金字塔(The Laplacian pramid)
简而言之,拉普拉斯金字塔的第 i 层,是由【高斯金字塔的第 i 层】 与 【高斯金字塔中的第 i+1 层的向上采样结果】之差。
简单来说,拉普拉斯金字塔是一个 高斯差值金字塔,下面这个图就是一个拉普拉斯每一层的流程
下面这个图简化了上图的流程,我们从后往前看,G3向上取样后,G2减去它就是 L2。
而 G3加上L2就是G2。因为L2=G2-G3向上
up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(img-up_down,'img-up_down')
** 前面我们在图像形态学操作里,用cv2.morphologyEx()这个函数实现图像梯度的提取,用膨胀图像-腐蚀图像,获取一个图像中前景图像的边缘。还有我们的礼帽和黑帽一定程度也能提取图像的边缘信息。依旧我们在图像梯度中详细讲过的 sobel算子,scharr算子,laplasian算子,canny边缘检测,这些都是检测图像中边缘线条的。**
** 而本章讲的是图像轮廓,图像轮廓和图像边缘不少一回事,图像边缘不少图像轮廓!!!!图像边缘是图像中的线条,这些线条是不连续的,零散的线段,只要是由梯度,把由梯度的像素点提取出来就可以了,这是边缘检测的操作手法。而图像轮廓首先要是一个整体的,就是将边缘连接起来形成一个整体,这才叫轮廓。**
** 边缘检测主要是通过一些手段检测数字图像中明暗变化剧烈(即梯度变化比较大)像素点,偏向于图像中像素点的变化。如Canny边缘检测,结果通常保存在和原图片一样尺寸和类型的边缘图中。轮廓检测指检测图像中的对象边距,更偏向于关注上层语义对象,主要用来分析物体的形态,比如物体的周长和面积等。可以说 边缘包括轮廓。**
** 边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手,而轮廓则是一个很好的图像目标的外部特征,这种特征对于我们进行图像分析,目标识别和理解等更深层次的处理都有很重要的意义**
在OpenCV中,我们用 image, contours, hierarchy = **cv2.findContours(img,mode,method) **这个函数来得到轮廓
使用轮廓检测函数 cv2.findContours()要注意的点:
轮廓绘制函数:cv2.drawContours(img, contours, contourIdx, color [ thickness, lineType, hierarchy, maxLevel, offset] )
注意:由于该函数是在 img 的基础上绘制的,不会再重新生成一个带轮廓的新对象,所有这个函数是在原图中绘制的。所以我们要保存原图
轮廓检测步骤小结:
# 导入图片
img = cv2.imread('contours.png')
# 彩色图转灰色图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 灰色图转二值图
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv_show(thresh,'thresh')
# 检测轮廓
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 画轮廓
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show(res,'res')
我们看下面这个图片,它不是一个规则的图形,是一个不规则的图形,如果要精确的描述轮廓的话就是第三幅图
如果像要近似的表示的话,就是第二幅图,用一个矩形就可以表示
https://blog.csdn.net/SSJJRRRR/article/details/108478898
那OpenCV的 approxPolyDP 函数就可以实现这个功能。
approxPolyDP函数使用了 Douglas-Peucker算法:
在OpenCV中 cv2.approxPolyDP(cnt,epsilon,True) 就是用来得到近似轮廓
cnt 就是 findContours函数得到的 contours
epsilon就是精度
最后一个布尔值:如果为true,则闭合近似曲线(其第一个和最后一个顶点为连接的);否则,不闭合。
# 导入图片
img = cv2.imread('contours2.png')
# 转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 转为二值图
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 检测轮廓
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
# 画出轮廓
draw_img = img.copy()
res = cv2.drawContours(draw_img, [cnt], -1, (0, 0, 255), 2)
cv_show(res,'res')
# arcLength轮廓的周长,epsilon是精度
epsilon = 0.15*cv2.arcLength(cnt,True)
# 得到近似轮廓
approx = cv2.approxPolyDP(cnt,epsilon,True)
# 画出轮廓
draw_img = img.copy()
res = cv2.drawContours(draw_img, [approx], -1, (0, 0, 255), 2)
cv_show(res,'res')
x,y,w,h=boundingRect(cnt)
cnt 就是一个轮廓,x,y是矩形的左上角坐标,而(w,h)为矩形的宽度和高度
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
img:画轮廓的图片
(x,y):矩形的左上角的坐标
(x+w,y+h):矩形的右下角的坐标
(0,255,0):颜色 BGR
2:线条宽度
# 引入坐标
img = cv2.imread('contours.png')
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 转为二值图
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 检测轮廓
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
# 找到外接矩形的信息
x,y,w,h = cv2.boundingRect(cnt)
# 画矩形
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show(img,'img')
(x,y),radius = cv2.minEnclosingCircle(cnt)
cnt:就是一个轮廓
(x,y):圆心坐标
radius:半径
img = cv2.circle(img,center,radius,(0,255,0),2)
参数基本上与外接矩形都差不多
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
cv_show(img,'img')
https://blog.csdn.net/m0_37579176/article/details/116950903
模板匹配可以看作是对象检测的一种非常基本的形式。使用模板匹配,我们可以使用包含要检测对象的”模板“来检测输入图像中的对象。
也就是说,我们需要两个图像来应用模板匹配:
为了在源图像中找到模板图像,我们在源图像中从左到右和从上到下依次滑动模板:
应用模板匹配,就像在源图像上从左到右,从上到下滑动模板,在每一个位置都计算一个指标以表明这个位置处两个图像块之间匹配程度的高低
在每个(x,y)位置,都会计算一个度量来表示匹配的“好”或“坏”。通常,我们使用归一化的相关系数来确定两个图像块之间像素强度有多“相似”
相关系数有很多计算方式。
‘
对于模板 T 在源图像 I 上的每个位置,取两者重合部分的图像块,计算相似度度量结果,存储在结果矩阵 R 中。源图像中的每个(x,y)坐标在结果矩阵 R 中包含一个条目,除非模板越界
下图就是结果矩阵与源图像重叠后的图像
在这里,我们可以可视化叠加在原始图像上的结果矩阵R。注意R与原始模板大小不相同。这是因为整个模板必须在源图像的内部滑动,得到等大的两个图像块,才能计算相关性。如果模板超出了源的边界,我们将不计算相似性度量。
结果矩阵中 R 最亮的位置表示最佳匹配位置,而暗区表示该点源图像和模板图像之间的相关性很小
当模板图像中的水杯,与源图像中的水杯,两者完全重合的时候,模板图像左上角所在的源图像位置,存储的是模板与源图像相似的的最大值。
但是我们需要确保要检测的模板与源图像中检测的对象几乎完全相同。即使外观很小的偏差也会极大地影响匹配的结果。
在OpenCV中我们可以使用 cv2.matchTemplate()进行模板匹配,这个函数有三个参数:
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
- TM_CCORR:计算相关性,计算出来的值越大,越相关
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
另外值得注意的是,如果您只想检测模板图像上的特定区域,则可以为模板图像提供一个掩膜,如下所示:
**result = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED, mask) **
掩膜,即为模板图像上感兴趣的区域,用于忽略模板图像上无用的干扰的特征,即不属于检测目标的干扰特征。对于模板上你不希望被搜索的区域,掩膜值应该设置为0。对于模板图像上您要进行搜索的区域,掩膜值应该设置为255。掩膜与模板图像具有相同的维度,并且每个元素的类型也需要一致。
# 引入源图像
img = cv2.imread('lena.jpg', 0)
# 引入模板图像
template = cv2.imread('face.jpg', 0)
# 模板匹配
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
# 得到最大的像素点和最小的像素点的值和坐标
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 根据方法选择最大值或最小值的坐标
# 如果是平方差匹配TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
# 画矩形
cv2.rectangle(img2, top_left, bottom_right, 255, 2)
这次紧接者上篇内容再一次讲解了一些有关图像的操作,预计还需要一期把图像操作讲个差不多后,进入实战环节,将上述图像内容,通过实战进行一个强化训练
我是Mayphry,从一点点到亿点点,我们下次再见