图像处理
1. 图像阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)
-
src: 输入图,只能输入单通道图像,通常来说为灰度图
-
dst: 输出图
-
thresh: 阈值
-
maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
-
type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
-
cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0
-
cv2.THRESH_BINARY_INV THRESH_BINARY的反转
-
cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
-
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
-
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转
2.图像平滑:滤波-filter
均值滤波:取卷积核为n阶全一矩阵,计算中心点卷积核范围内的均值
方框滤波:取卷积核为n阶全一矩阵,计算中心点卷积核范围内的均值,归一化可选:true则等于均值滤波,false则不取均值,超过255取255
高斯滤波:高斯分布即正态分布,卷积核内距离中心点越大权重越大,高斯分布标准差可选
中值滤波:取中心点卷积核内中值
3. 形态学
腐蚀:多用于二值图像,卷积核内取最大值:0--黑色|255--白色
膨胀:多用于二值图像,卷积核内取最小值
OpenCV---膨胀与腐蚀 - 山上有风景 - 博客园 (cnblogs.com)
腐蚀与膨胀也可以对彩色图进行处理,但是效果不怎么样,一般不用
上述的模板或者成卷积核封装入opencv中的腐蚀和膨胀操作,如果想自己操作的话可以利用opencv中的getStructuringElement函数来实现,其内部是生成一个mat(指定的形状和size)
开运算:先腐蚀再膨胀
闭运算:先膨胀再腐蚀
梯度运算:膨胀-腐蚀,抽象的“梯度”
高帽:显示毛刺,原图-开运算
黑帽:显示瑕疵,闭运算-原图
imageH1 = numpy.hstack((img, errosion, dilate, openOp)) imageH2 = numpy.hstack((closeOp, grad, tophat, blackHat)) image = numpy.vstack((imageH1, imageH2)) image = cv2.resize(image, (0, 0), fx=0.3, fy=0.3) _cvShow("image", image)
4. 图像梯度
Sobel算子:dx|dy,卷积核--dx_A = [[-1,0,1], [-2,0,2], [-1,0,1]],dy_B = A^T
Scharr算子:dx|dy,放大差异
Laplacian算子:无dx||dy,对变化更敏感,L = [[0,1,0],[1,-4,1],[0,1,0]]
5. Canny边缘检测
步骤
-
1) 使用高斯滤波器,以平滑图像,滤除噪声。
-
2) 计算图像中每个像素点的梯度强度和方向。
-
3) 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。
-
4) 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。
-
5) 通过抑制孤立的弱边缘最终完成边缘检测。
(图像噪点严重可先做一次中值滤波,再进行Canny边缘检测)
6. 图像金字塔
高斯金字塔:向下采样-高斯内核卷积;向上采样:扩充矩阵+高斯内核卷积
拉普拉斯金字塔:原图-up_down
7. 图像轮廓:多用二值数据(二值 != 灰度图)
cv2.findContours(img,mode,method)
mode:轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓;
- RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:最常用,检索所有的轮廓,并重构嵌套轮廓的整个层次;
method:轮廓逼近方法(常用)
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
# 轮廓检测多用二值 import cv2 import numpy img_ori = cv2.imread("../pics/contours.png") img = cv2.cvtColor(img_ori, cv2.COLOR_BGR2GRAY) # 注意转换灰度图和读取灰度图的参数不一样 ret, thresh = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY) # 127为阈值,255为maxVal def _show(str, img, size): cv2.imshow(str, cv2.resize(img, (0, 0), fx=size, fy=size)) cv2.moveWindow(str, 500, 200) cv2.waitKey(0) cv2.destroyAllWindows() def _drawContours(img, contours, which): draw_img = img.copy() # 直接=赋值相当于指针,还是会被修改 res = cv2.drawContours(draw_img, contours, which, (0, 0, 255), 2) # -1/0 # draw_img---在哪里画,这个图像是否灰度无所谓,(个人)甚至不是原图也行,就是没什么意义 # 这个函数会直接修改原图,故先复制一下,防止后续没法用了 # -1表示画出所有轮廓 # 0,0,255---bgr,这里是使用红色来画,简单来讲就是用什么颜色取描绘轮廓 # 2---线条宽度 return res binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) # binary是二值图像,contours存储轮廓信息,hierarchy是层次数据(暂时不用) _drawContours(img_ori, contours, -1) images = [img, img, img, img, img] for i in range(5): images[i] = _drawContours(img_ori, contours, i - 1) h1 = numpy.hstack((img_ori, images[0], images[1])) h2 = numpy.hstack((images[2], images[3], images[4])) res = numpy.vstack((h1, h2)) _show("contours", res, 0.5)
轮廓特征
cnt = contours[0] #面积 cv2.contourArea(cnt) #周长,True表示闭合的 cv2.arcLength(cnt,True)
轮廓近似:以直代曲
# 轮廓近似:以直代曲 def _imiContours(contours, lenRate, i): cnt = contours[i] epsilon = lenRate * cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) # cnt:轮廓 # epsilon:阈值,超过这个值由多条直线代替曲线 # true:闭合 return _drawContours(img_ori, [approx], -1) imis = [img_ori, _drawContours(img_ori, contours, 0), _imiContours(contours, 0.1, 0), _imiContours(contours, 0.01, 0)] h1 = numpy.hstack((imis[0], imis[1])) h2 = numpy.hstack((imis[2], imis[3])) res = numpy.vstack((h1, h2)) _show("res", res, 1)
边界图形
cnt = contours[0] # 外接矩形 x, y, w, h = cv2.boundingRect(cnt) img = img_ori.copy() rec = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) # img_ori,如果是在灰度图上标识只能用白色((255,255,255))来画线,否则看不到 # 外接圆 (x, y), radius = cv2.minEnclosingCircle(cnt) img = img_ori.copy() circle = cv2.circle(img, (int(x), int(y)), int(radius), (0, 255, 0), 2) # x,y,radius只能是整数 res = numpy.hstack((img_ori, rec, circle)) _show("res", res, 1)
8. 傅里叶变换
傅里叶分析之掐死教程(完整版)更新于2014.06.06 - 知乎 (zhihu.com)
这个写得非常好,有机会要多读几遍
9. 模板匹配
# 模板匹配:类比串的模式匹配 # opencv提供了各种函数来计算像素点之间的关系,如方差、相关系数等 import cv2 import matplotlib.pyplot as plt # 流程: # 1. 读入图片+模板 # 2. 依次(每像素)比较模板和图片,计算模板和图片比较区域像素值的差异并记录,返回结果应该是[picw-temw+1]*[pich-temh+1]的ndarray # 3. 遍历返回结果得到最有可能的点及其坐标(点在返回结果中的坐标值就是点在图像中的坐标值) # 另,应该明白,如果将返回的数据画出,则图中最量或最暗的点(值越大或者越小)就是相似度最高的部分所在位置 import numpy img = cv2.imread("../pics/lena.jpg") template = cv2.imread("../pics/face.jpg") h, w = template.shape[:2] # :2---取横坐标全部,纵坐标前两列,如果是2*2的矩阵,则w,h就分别为2*1的矩阵了 methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] # 图像匹配的所有参数计算方法,应用归一化操作的方法通常表现更好 def _show(str, img, size): cv2.imshow(str, cv2.resize(img, (0, 0), fx=size, fy=size)) cv2.moveWindow(str, 500, 200) cv2.waitKey(0) cv2.destroyAllWindows() temp = numpy.hstack((img, img)) images = [temp, temp, temp, temp, temp, temp] # 设置图像尺寸 plt.figure(figsize=(20,16), dpi=300) for meth, i in zip(methods, range(len(methods))): img2 = img.copy() b, g, r = cv2.split(img2) img2 = cv2.merge((r, g, b)) # 匹配方法的真值 method = eval(meth) # 如eval('1+1') = 2,而这里cv2.TM_CCOEFF是类似枚举类型,具有具体的整数值的,这样可以直接得到 print(method) res = cv2.matchTemplate(img, template, method) # 这里method不能直接输入字符串 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # method下pic和temp之间的相似关系值:min, max;以及位置,显然min_loc和max_loc都是二维的 # 如果是平方差匹配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, (0, 0, 255), 2) plt.subplot(2, 6, 2 * i + 1) plt.imshow(res, cmap='gray') plt.xticks([]), plt.yticks([]) # 隐藏坐标轴 plt.subplot(2, 6, 2 * i + 2) plt.imshow(img2, cmap='gray') plt.xticks([]), plt.yticks([]) plt.show()
匹配多个对象
# 多对象匹配:使用相关系数计算:越接近于1就越像 img_rgb = cv2.imread('../pics/mario.jpg') img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) template = cv2.imread('../pics/mario_coin.jpg', 0) h, w = template.shape[:2] res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) threshold = 0.8 # 取匹配程度大于%80的坐标 loc = numpy.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), 1) _show("res", img_rgb, 1)
10. 直方图
import cv2 import matplotlib.pyplot as plt img = cv2.imread("../pics/lena.jpg") # 0---gray b, g, r = cv2.split(img) img2 = cv2.merge((r, g, b)) imgGray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) hist = cv2.calcHist(imgGray, [0], None, [256], [0, 256]) color = ('b', 'g', 'r') plt.figure(dpi=150) grid = plt.GridSpec(2, 6) plt.subplot(grid[0, 0:2]) plt.imshow(img2) plt.subplot(grid[0, 3:5]) plt.imshow(imgGray, cmap='gray') plt.subplot(grid[1, 1:5]) plt.hist(imgGray.ravel(), 256, color='black') 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()