opencv学习记录1-文件矫正

一.准备学习

1.直方图

概念:

对一幅灰度图像,其直方图反映了该图像中不同灰度级出现的统计情况。

例:其中图(a)是一幅图像,其灰度直方图可表示为图(b),其中横轴表示图像的各灰度级,纵轴表示图像中各灰度级像素的个数。

opencv学习记录1-文件矫正_第1张图片

(需要注意,灰度直方图表示了在图像中各个单独灰度级的分布,而图像对比度则取决于相邻近像素之间灰度级的关系。)

2.直方图均衡化

hist = cv2.equalizeHist(img)

概念:

直方图均衡化是一种简单有效的图像增强技术,通过改变图像的直方图来改变图像中各像素的灰度,主要用于增强动态范围偏小的图像的对比度

例:左图为原始图像,右图为直方图均衡化后的图像

opencv学习记录1-文件矫正_第2张图片

原理:

经查询得:

  • 提高图像对比度的变换函数f(x)需要满足一下条件:

  1. f(x)在0<=x<=L−1上单调递增(不要求严格单调递增),其中L表示灰度级(L=256)

  1. 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)

设原始图像为:

opencv学习记录1-文件矫正_第3张图片

计算原始图像的灰度直方图

例:(假定图像的灰度级范围是 [0, 9])

opencv学习记录1-文件矫正_第4张图片

  • 计算原始图像的像素总个数

  • 计算原始图像的灰度分布频率

例:

  • 计算原始图像的灰度累积分布频率

例:

将归一化的

乘以

再四舍五入,以使得均衡化后图像的灰度级与归一化前的原始图像一致

例:

ps:

......

  • 根据以上映射关系,参照原始图像中的像素,可以写出直方图均衡化之后的图像

例:

opencv学习记录1-文件矫正_第5张图片

均衡化后图像的灰度直方图:

opencv学习记录1-文件矫正_第6张图片

4.仿射变换与透视变换

仿射变换

原理:从一种二维坐标(x,y)到另一种二维坐标(u,v)的线性变换

对于

则矩阵T(2×3)就称为仿射变换的变换矩阵,R为线性变换矩阵,t为平移矩阵

简单来说,仿射变换就是线性变换+平移。

  • 变换后直线依然是直线,平行线依然是平行线,直线间的相对位置关系不变,因此非共线的三个对应点便可确定唯一的一个仿射变换

透视变换

原理:将二维的图片投影到一个三维视平面上,然后再转换到二维坐标下,所以也称为投影映射

简单来说就是二维→三维→二维的一个过程

opencv学习记录1-文件矫正_第7张图片

仿射变换是透视变换的子集。接下来再通过除以Z轴转换成二维坐标

opencv学习记录1-文件矫正_第8张图片

区别

透视变换相比仿射变换更加灵活,变换后会产生一个新的四边形,但不一定是平行四边形,所以需要非共线的四个点才能唯一确定,原图中的直线变换后依然是直线。因为四边形包括了所有的平行四边形,所以透视变换包括了所有的仿射变换

5.角点检测

概念

在当前的图像处理领域,角点检测算法可归纳为三类:

  1. 基于灰度图像的角点检测

  1. 基于二值图像的角点检测

  1. 基于轮廓曲线的角点检测

常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法Harris角点检测算法KLT角点检测算法SUSAN角点检测算法

关于角点的具体描述可以有几种:

  • 一阶导数(即灰度的梯度)的局部最大所对应的像素点

  • 两条及两条以上边缘的交点

  • 图像中梯度值和梯度方向的变化速率都很高的点

  • 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向

可能用到的:

Harris角点检测算法

opencv学习记录1-文件矫正_第9张图片
opencv学习记录1-文件矫正_第10张图片

我认为可以理解为:

在角点处,往大部分方向移动时,图像的灰度变化都很剧烈

常用代码:

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)
opencv学习记录1-文件矫正_第11张图片

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()
opencv学习记录1-文件矫正_第12张图片
opencv学习记录1-文件矫正_第13张图片
opencv学习记录1-文件矫正_第14张图片

3.矫正文件

opencv学习记录1-文件矫正_第15张图片

步骤:

  • 二值化

  • 查找轮廓

  • 仿射变换

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检测边缘,,结果:

opencv学习记录1-文件矫正_第16张图片

opencv学习记录1-文件矫正_第17张图片

这里卡了很久,不知道怎么确定四个角点,试过已知轮廓坐标求顶点坐标、检测主体多边形、填充图形等等等等。。,最后都没成功

搜到一个方法: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(因为左上角阴影部分不好处理)再二值化,后轮廓检测,最后四边形拟合即可得到角点,相关步骤年后再进行尝试

你可能感兴趣的:(opencv,python,计算机视觉)