图像金字塔
图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。
图像金字塔如下图所示:
那我们为什么要做图像金字塔呢?这就是因为改变像素大小有时候并不会改变它的特征,比方说给你看1000万像素的图片,你能知道里面有个人,给你看十万像素的,你也能知道里面有个人,但是对计算机而言,处理十万像素可比处理1000万像素要容易太多了。
高斯金字塔
向下采样方法(缩小):
1 16 [ 1 4 6 4 1 4 16 24 16 4 6 24 36 24 6 4 16 24 16 4 1 4 6 4 1 ] \frac{1}{16}\left[\begin{array}{ccccc} {1} & {4} & {6} & {4} & {1} \\ {4} & {16} & {24} & {16} & {4} \\ {6} & {24} & {36} & {24} & {6} \\ {4} & {16} & {24} & {16} & {4} \\ {1} & {4} & {6} & {4} & {1} \end{array}\right] 161 1464141624164624362464162416414641
分为两步:将 G i G_{i} Gi与高斯内核卷积,将所有偶数行和列去除(这也是池化的思想)。
拉普拉斯金字塔
那向上采样怎么做呢?流程如下所示:
[ 10 30 56 96 ] ⇒ [ 10 0 30 12 0 0 0 0 0 56 0 96 0 0 0 0 0 ] \left[\begin{array}{cc} {10} & {30} \\ {56} & {96} \end{array}\right] \Rightarrow \left[\begin{array}{cccc} {10} & {0} & {30} & {\frac{12}{0}} \\ {0} & {0} & {0} & {0} \\ {56} & {0} & {96} & {0} \\ {0} & {0} & {0} & {0} \end{array}\right] [10563096]⇒ 1005600000300960012000
- 将图像在每个方向扩大为原来的两倍,新增的行和列以0填充。
- 使用先前同样的内核(乘以4)与放大后的图像卷积,获得近似值。
读入图像
img=cv2.imread("aa.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)
这里就不给完整代码了,网上随便down一个图像应该都会可以的。
对一张图片先进行上采样,再进行下采样得到的结果与原图是不一样的,因为上采样拿0填充再平均,下采样又损失了一些信息。
代码实现:
down=cv2.pyrDown(img) # 下采样
down_up=cv2.pyrUp(down) # 上采样
l_1=img-down_up # 查看变换前后之间的差别
cv_show(l_1,'l_1')
图像轮廓
- 边缘与轮廓的不同:
边缘是零零散散的,但是轮廓是一个整体。
- OpenCv轮廓检测函数
cv2.findContours(img,mode,method) # OpenCv轮廓检测函数
参数:
- mode:轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓;
- RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次;
- method:轮廓逼近方法 。 # 通常情况下使用这个。
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
- 为了更高的准确率,使用二值图像。
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换为灰度图
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 大于127取255,小于取0。
cv_show(thresh,'thresh')
- 做完简单的处理之后,我们才能够用检测函数来检测轮廓,这样才能更好地检测出来。
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
- 第一个输入是二值处理完的图像。
- 第二个输入选择轮廓检测的模式。
- 第三个输入也是选择轮廓模式。
第一个返回值是轮廓二值信息,第二个返回值是轮廓二值信息,第三个是一个层级信息。
- 绘制轮廓
# 传入绘制图像,轮廓,轮廓索引,所画线条颜色模式,线条厚度
# 注意需要copy,要不原图会变。。。
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)
cv_show(res,'res')
- 轮廓特征
cnt = contours[0] #取第几个轮廓
- 面积
cv2.contourArea(cnt)
- 周长,True表示闭合的
cv2.arcLength(cnt,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')
对于轮廓我们还可以做一些额外的操作,比如外接矩形,外接圆,外界椭圆等等。
- 边界矩形
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')
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print ('轮廓面积与边界矩形比',extent)
外接圆
(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')
- 模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有六种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AXB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1)。
# 模板匹配
img = cv2.imread('lean.jpg',0)
template = cv2.imread('face.jpg',0)
h, w = template.shape[:2]
- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
- TM_CCORR:计算相关性,计算出来的值越大,越相关
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
公式:https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
res.shape
res = cv2.mathTemplate(img, template, 1)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth)
print (method)
res = cv2.matchTemplate(img, template, method)
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)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
- 匹配多个对象
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
h, w = template.shape[:2]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于%80的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # *号表示可选参数
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
代码项目参考:https://github.com/ZhiqiangHo/Opencv-Computer-Vision-Practice-Python-
我的微信公众号名称:小小何先生
公众号介绍:主要研究分享深度学习、机器博弈、强化学习等相关内容!期待您的关注,欢迎一起学习交流进步!