一.准备学习
1.直方图
概念:
对一幅灰度图像,其直方图反映了该图像中不同灰度级出现的统计情况。
例:其中图(a)是一幅图像,其灰度直方图可表示为图(b),其中横轴表示图像的各灰度级,纵轴表示图像中各灰度级像素的个数。
(需要注意,灰度直方图表示了在图像中各个单独灰度级的分布,而图像对比度则取决于相邻近像素之间灰度级的关系。)
2.直方图均衡化
hist = cv2.equalizeHist(img)
概念:
直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度。
例:左图为原始图像,右图为直方图均衡化后的图像
原理:
经查询得:
提高图像对比度的变换函数f(x)需要满足一下条件:
f(x)在0<=x<=L−1上单调递增(不要求严格单调递增),其中L表示灰度级(L=256)
f(x)的范围是[0,L−1]
当图像直方图完全均匀分布的时候,此时图像的熵是最大的(随机变量每个值的概率都相同时,概率最大),图像对比度是最大的。所以,理想情况下,图像经过变换函数f(x)变换后,直方图能够均匀分布,此时对比度是最大的。
理论基础:
为讨论方便起见,以 r 和 s 分别表示归一化了的原图像灰度和经直方图均衡化后的图像灰度
(因为归一化了,所以 r 和 s 的取值在0到1之间)
当 r = s = 0时,表示黑色;当 r = s = 1时,表示白色;当 r, s ∈(0, 1)时,表示像素灰度在黑白之间变化。
(所谓直方图均衡化,其实是根据直方图对像素点的灰度值进行变换,属于点操作范围。换言之,即:已知r,求其对应的s。)
3.手工实现直方图均衡化
步骤:(资料源于csdn)
设原始图像为:
计算原始图像的灰度直方图
例:(假定图像的灰度级范围是 [0, 9])
计算原始图像的像素总个数
计算原始图像的灰度分布频率
例:
计算原始图像的灰度累积分布频率
例:
将归一化的
乘以
再四舍五入,以使得均衡化后图像的灰度级与归一化前的原始图像一致
例:
ps:
......
根据以上映射关系,参照原始图像中的像素,可以写出直方图均衡化之后的图像
例:
均衡化后图像的灰度直方图:
4.仿射变换与透视变换
仿射变换
原理:从一种二维坐标(x,y)到另一种二维坐标(u,v)的线性变换
对于
则矩阵T(2×3)就称为仿射变换的变换矩阵,R为线性变换矩阵,t为平移矩阵
简单来说,仿射变换就是线性变换+平移。
变换后直线依然是直线,平行线依然是平行线,直线间的相对位置关系不变,因此非共线的三个对应点便可确定唯一的一个仿射变换
透视变换
原理:将二维的图片投影到一个三维视平面上,然后再转换到二维坐标下,所以也称为投影映射
简单来说就是二维→三维→二维的一个过程
仿射变换是透视变换的子集。接下来再通过除以Z轴转换成二维坐标
区别
透视变换相比仿射变换更加灵活,变换后会产生一个新的四边形,但不一定是平行四边形,所以需要非共线的四个点才能唯一确定,原图中的直线变换后依然是直线。因为四边形包括了所有的平行四边形,所以透视变换包括了所有的仿射变换
5.角点检测
概念
在当前的图像处理领域,角点检测算法可归纳为三类:
基于灰度图像的角点检测
基于二值图像的角点检测
基于轮廓曲线的角点检测
常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法
关于角点的具体描述可以有几种:
一阶导数(即灰度的梯度)的局部最大所对应的像素点
两条及两条以上边缘的交点
图像中梯度值和梯度方向的变化速率都很高的点
角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向
可能用到的:
Harris角点检测算法
我认为可以理解为:
在角点处,往大部分方向移动时,图像的灰度变化都很剧烈
常用代码:
cornerHarris(src, blockSize, ksize,k[, dst[, borderType]])
形态学变换 morphologyEx函数
高级形态学变换:
开运算:MORPH_OPEN
先腐蚀,再膨胀,可清除一些小东西(亮的),放大局部低亮度的区域
闭运算:MORPH_CLOSE
先膨胀,再腐蚀,可清除小黑点
形态学梯度:MORPH_GRADIENT
膨胀图与腐蚀图之差,提取物体边缘
顶帽:MORPH_TOPHAT
原图像-开运算图,突出原图像中比周围亮的区域
黑帽:MORPH_BLACKHAT
闭运算图-原图像,突出原图像中比周围暗的区域
膨胀:MORPH_DILATE
二.实例
1.手动实现直方图均衡化
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = 'C:/Users/DELL/Desktop/1.jpg'
def def_equalizehist(img, L = 256):
img = cv2.imread(img, 0)
h, w =img.shape
#计算原始图像的像素点总个数
hist = cv2.calcHist([ img ], [0], None, [256], [0, 256])
#计算原始图像的灰度分布频率
hist[0 : 255] = hist[0 : 255] / ( h * w )
#计算原始图像的灰度累积分布频率
sum_hist = np.zeros(hist.shape)
for i in range(256):
sum_hist[i] = sum(hist[0 : i + 1])
#乘上灰度级再四舍五入,使得均衡化后图像的灰度级与归一化前的原始图像一致
equal_hist = np.zeros( sum_hist.shape )
for i in range(256):
equal_hist[i] = int(((L - 1) - 0) * sum_hist[i] + 0.5)
new_img = img.copy()
for i in range(h):
for j in range(w):
new_img[i, j] = equal_hist[img[i, j]]
cv2.imshow("new", new_img)
cv2.imshow("before", img)
cv2.waitKey()
return [new_img, equal_hist]
def_equalizehist(img)
2.绘制像素值直方图
import matplotlib.pyplot as plt
import cv2
import numpy as np
img = cv2.imread('C:/Users/DELL/Desktop/1.jpg', 0)
b = cv2.calcHist([img[0]], [0], None, [256], [0, 256])
b[0 : 255] = b[0 : 255] / (h * w)
g = cv2.calcHist([img[1]], [0], None, [256], [0, 256])
g[0 : 255] = g[0 : 255] / (h * w)
r = cv2.calcHist([img[2]], [0], None, [256], [0, 256])
r[0 : 255] = r[0 : 255] / (h * w)
plt.plot(b, color='b')
plt.show()
plt.plot(g, color='g')
plt.show()
plt.plot(r, color='r')
plt.show()
3.矫正文件
步骤:
二值化
查找轮廓
仿射变换
import cv2
import numpy as np
#读取图像
img = cv2.imread('C:/Users/DELL/Desktop/img.png')
cv2.imshow('img', img)
#膨胀
img=cv2.dilate(img, None, 1)
img=cv2.dilate(img, None, 1)
img=cv2.dilate(img, None, 1)
cv2.imshow('result', img)
#canny检测
binary = cv2.Canny(img, 80, 160)
#膨胀
binary = cv2.dilate(binary, None, 1)
cv2.imshow('binary', binary)
cv2.waitKey()
试图多次膨胀去掉文字干扰后,再canny检测边缘,,结果:
这里卡了很久,不知道怎么确定四个角点,试过已知轮廓坐标求顶点坐标、检测主体多边形、填充图形等等等等。。,最后都没成功
搜到一个方法:Hough直线检测(法一
#hough直线检测
lines = cv2.HoughLines(binary, rho = 1, theta = 1 * np.pi/180, threshold=120, srn=0, stn = 0, min_theta=1, max_theta=2)
for i in range(0, len(lines)):
rho, theta = lines[i][0][0], lines[i][0][1]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow('Hough_line', img)
img_p = img.copy()
lines_p = cv2.HoughLinesP(binary, rho = 1, theta = np.pi/180, threshold = 50, minLineLength= 30, maxLineGap=10)
for i in range(len(lines_p)):
x_1, y_1, x_2, y_2 = lines_p[i][0]
cv2.line(binary, (x_1, y_1), (x_2, y_2), (0, 255, 0), 2)
cv2.imshow('Hough_line_p', binary)
还搜到一个比较暴力的方法:(法二
def order_points(pts):
#一共四个坐标点
rect = np.zeros((4, 2), dtype = "float32")
#按顺序找到对应坐标,分别对应0,1,2,3->左上,右上,右下,左下
#计算左上,右下
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
#计算右上,左下
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def point_trans(img, pts):
#获取坐标点
rect = order_points(pts)
(tl, tr, br, bl) = rect
#计算宽高
wA = np.sqrt(((br[0] - bl[0]) ** 2)+((br[1] - bl[1]) ** 2))
wB = np.sqrt(((tr[0] - tl[0]) ** 2)+((tr[1] - tl[1]) ** 2))
maxW = max(int(wA), int(wB))
hA = np.sqrt(((tr[0] - br[0]) ** 2)+((tr[1] - br[1]) ** 2))
hB = np.sqrt(((tl[0] - bl[0]) ** 2)+((tl[1] - bl[1]) ** 2))
maxH = max(int(hA), int(hB))
#变换后对应的坐标位置
dst = np.array([
[0, 0],[maxW - 1, 0], [maxW - 1, maxH - 1], [0, maxH - 1]], dtype = "float32")
#计算变换矩阵
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(img, M, (maxW, maxH))
#返回变换后结果
return warped
#读取图像
img = cv2.imread('C:/Users/DELL/Desktop/img.png')
#cv2.imshow('img', img)
#灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#膨胀
img=cv2.dilate(img, None, 1)
img=cv2.dilate(img, None, 1)
img=cv2.dilate(img, None, 1)
#canny边缘检测
binary = cv2.Canny(img, 70, 220)
#cv2.imshow('binary', binary)
#轮廓检测
#[0]表示只需要轮廓即可
cnts, hie = cv2.findContours(binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(cnts)
docCnt = None
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)
#遍历每一个轮廓
for c in cnts:
#近似
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, False)
print(approx)
#准备仿射变换
if len(approx) == 4:
docCnt = approx
break
#变换
warped = point_trans(gray, docCnt)
cv2.imshow('trans', warped)
最后都没成功,(。
看到别人成功的方法如下:先转化成hsv(因为左上角阴影部分不好处理)再二值化,后轮廓检测,最后四边形拟合即可得到角点,相关步骤年后再进行尝试