目录
一、图像轮廓
在Opencv中,图像轮廓函数是:cv2.findContours(image, mode, method)
Mode:轮廓检索模式
RETR_EXTRENAL:只检索最外面的轮廓
RETR_LIST:检索所有的轮廓,并保存在一条链表中
RETR_CCOMP:检索所有的轮廓,并将他们组织成两层,顶层是各部分的外界边界,第二层是空洞边界
RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次
Method:轮廓逼近方法
CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
CHAIN_APPROX_SIMPLE:压缩水平的,垂直的和斜的部分,也就是函数只保留他们的终点部分。
为了使结果更准确,推荐使用二值图。
代码实现:
import cv2 as cv
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
img = cv.imread('../de/c.jpg')
grey = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 变成灰度图
ret, thresh = cv.threshold(grey, 127, 255, cv.THRESH_BINARY) # 转化为二值图
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE) # contours返回的轮廓
# 绘制图片
draw_img = img.copy() # drawContours函数会对原图进行操作,会改变原图,所以需要copy
res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), 2) # -1表示绘制所以轮廓,也可指定其他值;(0, 0, 255)表示绘制轮廓的颜色。cv2是BGR。2表示绘制轮廓线的宽度
show('res', res)
实现效果:
二、模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图象被覆盖的地方)的差异,这个差异的计算方式在opencv中有六种,然后将每次计算的结果放在一个矩阵里,在为输出,假如原图像的大小是A×B,模板的大小是a×b,则输出的矩阵大小为(A-a+1,B-b+1)
cv2.TM_SQDIFF:计算平方不同,计算出来的数值越小越相关
cv2.TM_CCORR:计算相关性,计算出来的数值越大越相关
cv2.TM_CCOEFF:计算相关系数,计算出来的数值越大越相关
cv2.TM_SQDIFF_NORMED:计算归一化平方不同,计算出的数值越接近0越相关
cv2.TM_CCORR_NORMED:计算归一化相关性,计算出的数值越接近1越相关
cv.TM_CCOEFF_NORMED:计算归一化相关系数,计算出的数值越接近1越相关
(1)匹配单个目标
我所用的模板和图片
代码实现:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cat = cv.imread('../de/cat.jpg', 0)
cat_face = cv.imread('../de/cat_face.jpg', 0)
print(cat_face.shape)
h, w = cat_face.shape[:2]
print(h, w)
methods = ['cv.TM_SQDIFF', 'cv.TM_CCORR', 'cv.TM_CCOEFF', 'cv.TM_SQDIFF_NORMED', 'cv.TM_CCORR_NORMED', 'cv.TM_CCOEFF_NORMED' ]
for meth in methods:
cat2 = cat.copy()
method = eval(meth)
print(method)
res = cv.matchTemplate(cat, cat_face, method)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) # 返回的分别是最小值、最大值、最小值坐标和最大值坐标
# 判断是否进行TM_SQDIFF或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
if meth in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0]+w, top_left[1]+h) # 矩形框的右下坐标
# 画矩形
cv.rectangle(cat2, top_left, bottom_right, 0, 2) # 参数意义:图片,左上坐标,右下坐标,颜色(0, 0, 0),线条粗细
plt.subplot(2, 3, method+1), plt.imshow(cat2, 'gray')
plt.xticks([]), plt.yticks([])
plt.title(meth)
plt.show()
实现效果:
可以看到有些匹配的效果并不是很好,在实际中尽可能使用有归一化处理的方法
(2)匹配多个目标
使用的模板和图片:
代码实现:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
fish = cv.imread('../de/fish.png')
fish_gray = cv.cvtColor(fish, cv.COLOR_BGR2GRAY)
fish_1 = cv.imread('../de/fish_1.png', 0)
h, w = fish_1.shape[:2]
res = cv.matchTemplate(fish_gray, fish_1, cv.TM_CCOEFF_NORMED)
threshold = 0.99
loc = np.where(res >= threshold) # 提取归一化相关系数大于等于threshold
for pt in zip(*loc[::-1]): # *表示可选参数
bottom_right = (pt[0] + w, pt[1] + h)
cv.rectangle(fish, pt, bottom_right, (0, 0, 255), 1)
show('fish', fish)
具体效果:
三、直方图
灰度直方图是关于灰度级分布的函数,是对图像中灰度级分布的统计。灰度直方图是将数字图像中的所有像素,按照灰度值的大小,统计其出现的频率。灰度直方图是灰度级的函数,它表示图像中具有某种灰度级的像素的个数,反映了图像中某种灰度出现的频率。
oepncv中灰度直方图函数为:cv2.calcHist(images,channels,mask,histSize,ranges)
images:原图像图像格式为uint8或者float32,当传入图像要用[]括起来
channels:同样也要括起来,如果是灰度图它的值就是[0],如果是彩色图它的值就是[0],[1],[2],分别代表BGR
mask:掩模图像,统计整个图像就是None,假如你想统计图像的某个部分的直方图就可以制作一个掩模图像来使用
histSize:BIN的数目,也要用[]。常为[256]
Ranges:像素值范围常为[0, 255]
(1)图片直方图
实现灰度直方图和彩色图片的直方图代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cat = cv.imread('../de/cat.jpg', 0)
hist = cv2.calcHist([cat], [0], None, [256], [0, 255]) # 灰度直方图
plt.plot(hist)
plt.show()
cat = cv.imread('../de/cat.jpg')
color = ('b', 'g', 'r')
for i, col in enumerate(color):
hist = cv2.calcHist([cat], [i], None, [256], [0, 255]) # 彩色图片的直方图
plt.plot(hist, color=col)
plt.xlim([0, 256])
plt.show()
灰度直方图:
彩色图片的直方图:
(2)创建mask
创建mask具体实现代码如下:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
mask = np.zeros(cat.shape[:2], np.uint8)
mask[250:650, 500:900] = 255
cat = cv.imread('../de/cat.jpg', 0)
mask_cat = cv.bitwise_and(cat, cat, mask=mask) # 与操作
hist_full = cv.calcHist([cat], [0], None, [256], [0, 255])
his_mask = cv.calcHist([cat], [0], mask, [256], [0, 255])
plt.subplot(221), plt.imshow(cat, 'gray'), plt.title('cat')
plt.subplot(222), plt.imshow(mask, 'gray'), plt.title('mask')
plt.subplot(223), plt.imshow(mask_cat, 'gray'), plt.title('mask_cat')
plt.subplot(224), plt.plot(hist_full), plt.plot(his_mask), plt.title('hist')
plt.show()
具体效果对比图如下:
(3)均衡化直方图
原理在可以搜到很多,我就不废话了,直接上实现代码。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cat = cv.imread('../de/cat.jpg', 0)
cat = cv.resize(cat, (0, 0), -1, 0.4, 0.4)
equ_cat = cv.equalizeHist(cat)
plt.subplot(221), plt.hist(cat.ravel(), 256), plt.title('cat')
plt.subplot(222), plt.hist(equ_cat.ravel(), 256), plt.title('equ_cat')
plt.show()
res = np.hstack((cat, equ_cat))
show('res', res)
均衡化后的直方图对比:
原图对比:
(4)自适应直方图均衡化
直方图均衡化可能会导致某些细节消失,所以就有了自适应直方图均衡化, 它的原理就是把图片分成几个部分,分别进行均衡化。
代码实现:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
def show(name, img):
cv.imshow(name, img)
cv.waitKey(0)
cat = cv.imread('../de/cat.jpg', 0)
cat = cv.resize(cat, (0, 0), -1, 0.4, 0.4)
equ_cat = cv.equalizeHist(cat)
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) # 自适应均衡化方法
cat_clahe = clahe.apply(cat) # 应用到图片
res = np.hstack((cat, equ_cat, cat_clahe))
show('res', res)
原图、均衡化和自适应均衡化对比图:
可以看出自适应均衡化能保留更多的纹理。