OpenCV—python 图像矫正(基于傅里叶变换—基于透视变换)
文章目录
- 一、基于傅里叶变换的图像矫正
- 1.1 傅里叶变换原理
- 1.2 傅里叶变换过程一系列函数
- 1.3 图像矫正处理流程
- 二、旋转图像矫正
- 三、基于透视的图像矫正
- 3.1 直接变换
- 3.2 自动获取图像顶点变换
- 直接获取图像轮廓矫正
- 获取图像轮廓顶点矫正
图像校正运行环境 Anaconda| pycharm/jupyter notebook
你需要安装如下库
pip install numpy
pip install matplotlib
pip install opencv-python
一、基于傅里叶变换的图像矫正
由于拍照时,图像被旋转,为方便观看或使用,需要对旋转图像进行校正。
旋转图像矫正流程
思路借鉴 http://johnhany.net/2013/11/dft-based-text-rotation-correction/
1.1 傅里叶变换原理
2维图像的傅立叶变换可以用以下数学公式表达:
F ( k , l ) = ∑ i = 0 N − 1 ∑ j = 0 N − 1 f ( i , j ) e − i 2 π ( k i N + l j N ) F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})} F(k,l)=i=0∑N−1j=0∑N−1f(i,j)e−i2π(Nki+Nlj)
e i x = cos x + i sin x e^{ix} = \cos{x} + i\sin {x} eix=cosx+isinx
式中 f f f 是空间域(spatial domain)值, F F F 则是频域(frequency domain)值。
转换之后的频域值是复数, 因此,显示傅立叶变换之后的结果需要使用实数图像(real image) 加虚数图像(complex image), 或者幅度图像(magitude image)加相位图像(phase image)。 在实际的图像处理过程中,仅仅使用了幅度图像,因为幅度图像包含了原图像的几乎所有我们需要的几何信息。然而,如果你想通过修改幅度图像或者相位图像的方法来间接修改原空间图像,你需要使用逆傅立叶变换得到修改后的空间图像,这样你就必须同时保留幅度图像和相位图像了。
在此示例中,我将展示如何计算以及显示傅立叶变换后的幅度图像。由于数字图像的离散性,像素值的取值范围也是有限的。比如在一张灰度图像中,像素灰度值一般在0到255之间。 因此,我们这里讨论的也仅仅是离散傅立叶变换(DFT)。 如果你需要得到图像中的几何结构信息,那你就要用到它了。
在频域里面,对于一幅图像,高频部分代表了图像的细节、纹理信息;低频部分代表了图像的轮廓信息。如果对一幅精细的图像使用低通滤波器,那么滤波后的结果就剩下了轮廓了。这与信号处理的基本思想是相通的。如果图像受到的噪声恰好位于某个特定的“频率”范围内,则可以通过滤波器来恢复原来的图像。傅里叶变换在图像处理中可以做到:图像增强与图像去噪,图像分割之边缘检测,图像特征提取,图像压缩等等。
1.2 傅里叶变换过程一系列函数
1 . 将图像延展到最佳尺寸(提高运行速度)
- getOptimalDFTSize():返回给定尺寸的傅里叶最优尺寸大小,提高运行速度(需要扩充图像,填充多少由此函数计算得到:被计算的数字序列长度vecsize为2的n次幂或者能够分解成2,3,5的乘积)
- copyMakeBorder() 边界扩充函数
2 . 为傅立叶变换的结果(实部和虚部)分配存储空间. 傅立叶变换的结果是复数,这就是说对于每个原图像值,结果是两个图像值。 此外,频域值范围远远超过空间值范围, 因此至少要将频域储存在 float 格式中。 结果我们将输入图像转换成浮点类型,并多加一个额外通道来储存复数部分
3 . 进行离散傅立叶变换. 支持图像原地计算 (输入输出为同一图像):
- dtf() :对一维或者二维浮点数数组进行正向或反向傅里叶变换
4 . 将复数转换为幅度复数包含实数部分(Re)和复数部分 (imaginary - Im)。 离散傅立叶变换的结果是复数,对应的幅度可以表示为:
M = R e ( D F T ( I ) ) 2 + I m ( D F T ( I ) ) 2 2 M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2} M=2Re(DFT(I))2+Im(DFT(I))2
- magnitude() 计算二维矢量的幅值
5 . 对数尺度(logarithmic scale)缩放. 傅立叶变换的幅度值范围大到不适合在屏幕上显示。高值在屏幕上显示为白点,而低值为黑点,高低值的变化无法有效分辨。为了在屏幕上凸显出高低变化的连续性,我们可以用对数尺度来替换线性尺度:
M 1 = log ( 1 + M ) M_1 = \log{(1 + M)} M1=log(1+M)
- log() 自然对数函数
6 . 剪切和重分布幅度图象限.将新添加的像素剔除。为了方便显示,我们也可以重新分布幅度图象限位置(注:将第五步得到的幅度图从中间划开得到四张1/4子图像,将每张子图像看成幅度图的一个象限,重新分布即将四个角点重叠到图片中心)。 这样的话原点(0,0)就位移到图像中心。
7 . 归一化. 将像素值归一到 float(0,1) 再乘以 255用于显示 。
- normalize() 矩阵归一化函数
cv2.getOptimalDFTSize(vecsize)
# vecsize: 传入:image.shape[0]/image.shape[1]cv2.copyMakeBorder(src,top,bottom,left,right,borderType,dst=None)
"""src: 图像top,bottom,left,right: 上/下/左/右边扩充像素(int)borderType: 边界类型:BORDER_CONSTANT: 常量,增加的变量通通为valueBORDER_REFLICATE: 直接用边界的颜色填充,比如 : aaaaaa | abcdefgh | hhhhhhhBORDER_REFLECT: 镜像:比如 : fedcba | abcdefgh | hgfedcbBORDER_REFLECT_101:倒映,和上面类似,但在倒映时,会把边界空开:比如 : gfedcb | abcdefgh |gfedcbaBORDER_WRAP: 没有规律的,比如: cdefgh | abcdefgh | abcdefg
"""
cv2.magnitude(InputArray x, InputArray y, OutPutArray magnitude)
"""
计算输入矩阵x和y对应该的每个像素平方求和后开根号保存在输出矩阵magnitude中。
"""
1.3 图像矫正处理流程
- 获取图像的傅里叶变换图
- 二值化
- Hough直线检测
- 计算倾斜角度
- 旋转校正
import cv2
import numpy as np
import mathdef fourier_demo():#1、灰度化读取文件,img = cv2.imread('english_rotation.jpg',0)#2、图像延扩h, w = img.shape[:2]new_h = cv2.getOptimalDFTSize(h)new_w = cv2.getOptimalDFTSize(w)right = new_w - wbottom = new_h - hnimg = cv2.copyMakeBorder(img, 0, bottom, 0, right, borderType=cv2.BORDER_CONSTANT, value=0)cv2.imshow('new image', nimg)#3、执行傅里叶变换,并过得频域图像f = np.fft.fft2(nimg)fshift = np.fft.fftshift(f)magnitude = np.log(np.abs(fshift))#二值化magnitude_uint = magnitude.astype(np.uint8)ret, thresh = cv2.threshold(magnitude_uint, 11, 255, cv2.THRESH_BINARY)print(ret)cv2.imshow('thresh', thresh)print(thresh.dtype)#霍夫直线变换lines = cv2.HoughLinesP(thresh, 2, np.pi/180, 30, minLineLength=40, maxLineGap=100)print(len(lines))#创建一个新图像,标注直线lineimg = np.ones(nimg.shape,dtype=np.uint8)lineimg = lineimg * 255piThresh = np.pi/180pi2 = np.pi/2print(piThresh)for line in lines:x1, y1, x2, y2 = line[0]cv2.line(lineimg, (x1, y1), (x2, y2), (0, 255, 0), 2)if x2 - x1 == 0:continueelse:theta = (y2 - y1) / (x2 - x1)if abs(theta) < piThresh or abs(theta - pi2) < piThresh:continueelse:print(theta)angle = math.atan(theta)print(angle)angle = angle * (180 / np.pi)print(angle)angle = (angle - 90)/(w/h)print(angle)center = (w//2, h//2)M = cv2.getRotationMatrix2D(center, angle, 1.0)rotated = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)cv2.imshow('line image', lineimg)cv2.imshow('rotated', rotated)fourier_demo()
cv2.waitKey(0)
cv2.destroyAllWindows()
二、旋转图像矫正
图像旋转校正思路如下
- 读入,灰度化
- 高斯模糊
- 二值化图像
- 闭开运算
- 获取图像顶点
- 旋转矫正
import cv2
import numpy as npdef Img_Outline(input_dir):original_img = cv2.imread(input_dir)gray_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray_img, (9, 9), 0) # 高斯模糊去噪(设定卷积核大小影响效果)_, RedThresh = cv2.threshold(blurred, 165, 255, cv2.THRESH_BINARY) # 设定阈值165(阈值影响开闭运算效果)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 定义矩形结构元素closed = cv2.morphologyEx(RedThresh, cv2.MORPH_CLOSE, kernel) # 闭运算(链接块)opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel) # 开运算(去噪点)return original_img, gray_img, RedThresh, closed, openeddef findContours_img(original_img, opened):image, contours, hierarchy = cv2.findContours(opened, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)c = sorted(contours, key=cv2.contourArea, reverse=True)[1] # 计算最大轮廓的旋转包围盒rect = cv2.minAreaRect(c)angle = rect[2]print("angle",angle)box = np.int0(cv2.boxPoints(rect))draw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3)rows, cols = original_img.shape[:2]M = cv2.getRotationMatrix2D((cols / 2, rows / 2), angle, 1)result_img = cv2.warpAffine(original_img, M, (cols, rows))return result_img,draw_imgif __name__ == "__main__":input_dir = "gongjiaoka.png"original_img, gray_img, RedThresh, closed, opened = Img_Outline(input_dir)result_img,draw_img = findContours_img(original_img,opened)cv2.imshow("original_img", original_img)cv2.imshow("gray_img", gray_img)cv2.imshow("RedThresh", RedThresh)cv2.imshow("Close", closed)cv2.imshow("Open", opened)cv2.imshow("draw_img", draw_img)cv2.imshow("result_img", result_img)cv2.waitKey(0)cv2.destroyAllWindows()
三、基于透视的图像矫正
3.1 直接变换
- 获取图像四个顶点
- 形成变换矩阵
- 透视变换
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('original_img.jpg')
H_rows, W_cols= img.shape[:2]
print(H_rows, W_cols)# 原图中书本的四个角点(左上、右上、左下、右下),与变换后矩阵位置
pts1 = np.float32([[161, 80], [449, 12], [1, 430], [480, 394]])
pts2 = np.float32([[0, 0],[W_cols,0],[0, H_rows],[H_rows,W_cols],])# 生成透视变换矩阵;进行透视变换
M = cv2.getPerspectiveTransform(pts1, pts2)
dst = cv2.warpPerspective(img, M, (500,470))"""
注释代码同效
# img[:, :, ::-1]是将BGR转化为RGB
# plt.subplot(121), plt.imshow(img[:, :, ::-1]), plt.title('input')
# plt.subplot(122), plt.imshow(dst[:, :, ::-1]), plt.title('output')
# plt.show
"""cv2.imshow("original_img",img)
cv2.imshow("result",dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.2 自动获取图像顶点变换
- 以灰度图读入
- 腐蚀膨胀,闭合等操作
- 二值化图像
- 获取图像顶点
- 透视矫正
直接获取图像轮廓矫正
该方法不具有普适性,只针对比较干净对比度高的图像,只提供一种思路
from imutils.perspective import four_point_transform
import imutils
import cv2def Get_Outline(input_dir):image = cv2.imread(input_dir)gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray, (5,5),0)edged = cv2.Canny(blurred,75,200)return image,gray,edgeddef Get_cnt(edged):cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = cnts[0] if imutils.is_cv2() else cnts[1]docCnt =Noneif len(cnts) > 0:cnts =sorted(cnts,key=cv2.contourArea,reverse=True)for c in cnts:peri = cv2.arcLength(c,True) # 轮廓按大小降序排序approx = cv2.approxPolyDP(c,0.02 * peri,True) # 获取近似的轮廓if len(approx) ==4: # 近似轮廓有四个顶点docCnt = approxbreakreturn docCntif __name__=="__main__":input_dir = "gongjiaoka.png"image,gray,edged = Get_Outline(input_dir)docCnt = Get_cnt(edged)result_img = four_point_transform(image, docCnt.reshape(4,2)) # 对原始图像进行四点透视变换cv2.imshow("original", image)cv2.imshow("gray", gray)cv2.imshow("edged", edged)cv2.imshow("result_img", result_img)cv2.waitKey(0)cv2.destroyAllWindows()
获取图像轮廓顶点矫正
import cv2
import math
import numpy as npdef Img_Outline(input_dir):original_img = cv2.imread(input_dir)gray_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)blurred = cv2.GaussianBlur(gray_img, (9, 9), 0) # 高斯模糊去噪(设定卷积核大小影响效果)_, RedThresh = cv2.threshold(blurred, 165, 255, cv2.THRESH_BINARY) # 设定阈值165(阈值影响开闭运算效果)kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 定义矩形结构元素closed = cv2.morphologyEx(RedThresh, cv2.MORPH_CLOSE, kernel) # 闭运算(链接块)opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel) # 开运算(去噪点)return original_img, gray_img, RedThresh, closed, openeddef findContours_img(original_img, opened):image, contours, hierarchy = cv2.findContours(opened, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)c = sorted(contours, key=cv2.contourArea, reverse=True)[1] # 计算最大轮廓的旋转包围盒rect = cv2.minAreaRect(c) # 获取包围盒(中心点,宽高,旋转角度)box = np.int0(cv2.boxPoints(rect)) # boxdraw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3)print("box[0]:", box[0])print("box[1]:", box[1])print("box[2]:", box[2])print("box[3]:", box[3])return box,draw_imgdef Perspective_transform(box,original_img):# 获取画框宽高(x=orignal_W,y=orignal_H)orignal_W = math.ceil(np.sqrt((box[3][1] - box[2][1])**2 + (box[3][0] - box[2][0])**2))orignal_H= math.ceil(np.sqrt((box[3][1] - box[0][1])**2 + (box[3][0] - box[0][0])**2))# 原图中的四个顶点,与变换矩阵pts1 = np.float32([box[0], box[1], box[2], box[3]])pts2 = np.float32([[int(orignal_W+1),int(orignal_H+1)], [0, int(orignal_H+1)], [0, 0], [int(orignal_W+1), 0]])# 生成透视变换矩阵;进行透视变换M = cv2.getPerspectiveTransform(pts1, pts2)result_img = cv2.warpPerspective(original_img, M, (int(orignal_W+3),int(orignal_H+1)))return result_imgif __name__=="__main__":input_dir = "gongjiaoka.png"original_img, gray_img, RedThresh, closed, opened = Img_Outline(input_dir)box, draw_img = findContours_img(original_img,opened)result_img = Perspective_transform(box,original_img)cv2.imshow("original", original_img)cv2.imshow("gray", gray_img)cv2.imshow("closed", closed)cv2.imshow("opened", opened)cv2.imshow("draw_img", draw_img)cv2.imshow("result_img", result_img)cv2.waitKey(0)cv2.destroyAllWindows()
C++ 自动化透视变换:https://wangsp.blog.csdn.net/article/details/118694938
OpenCV—python 图像矫正(基于傅里叶变换—基于透视变换)相关推荐
- Python 分数阶傅里叶变换
Python 分数阶傅里叶变换 基于github上的开源库实现FRFT. https://github.com/nanaln/python_frft import frft import numpy ...
- 《OpenCv视觉之眼》Python图像处理十二 :Opencv图像轮廓提取之基于一阶导数的Roberts算法、Prewitt算法及Sobel算法
本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...
- 基于python的快速傅里叶变换FFT(二)
基于python的快速傅里叶变换FFT(二) 本文在上一篇博客的基础上进一步探究正弦函数及其FFT变换. 知识点 FFT变换,其实就是快速离散傅里叶变换,傅立叶变换是数字信号处理领域一种很重要的算 ...
- 基于python的快速傅里叶变换FFT(一)
基于python的快速傅里叶变换FFT(一) FFT可以将一个信号变换到频域.有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了.这就是很多信号分析采用FFT变换的原因. ...
- OpenCV实现基于傅里叶变换(FFT)的旋转文本校正(文字方向检测)
OpenCV实现基于傅里叶变换的旋转文本校正 from: http://johnhany.net/2013/11/dft-based-text-rotation-correction/ 发布于 201 ...
- OpenCV + python 实现人脸检测(基于照片和视频进行检测)
OpenCV + python 实现人脸检测(基于照片和视频进行检测) Haar-like 通俗的来讲,就是作为人脸特征即可. Haar特征值反映了图像的灰度变化情况.例如:脸部的一些特征能由矩形特征 ...
- python人脸识别opencv_基于python+OpenCV模块的人脸识别定位技术
什么是OpenCV模块 OpenCV是一款跨平台的视觉库,可以支持的操作系统有Linux.Windows和Mac OS操作系统,并且还提供了多种语言的接口,比如Python,java,MATLAB等常 ...
- [JavaScript]使用opencv.js实现基于傅里叶变换的频域水印(隐水印)
PS:查了多方资料,都没有提到用 JavaScript 来实现频域水印的教程,故经过笔者的实践,遂写一篇教程来简单介绍. 通过了解频域水印的相关知识,我理解了频域水印就是先将图片进行傅里叶变换,得到频 ...
- 基于 OpenCV + Python 的人脸识别上课签到系统
目录 前言 安装第三方库 第一步:采集人脸图像 (1)修改姓名学号 (2)运行capture_face.py (3)采集人脸图像 (4)查看采集到的人脸图像 第二步:训练模型 第三步:识别签到 (1) ...
最新文章
- [JS] 关于parseInt的一个小知识:[‘1‘, ‘2‘, ‘3‘].map(parseInt)
- ONES 万事联合创始人 amp; CTO 冯斌:企业服务产品的探索实践
- 识别、触达、转化、评估!百度云用ABC四招颠覆营销不可能
- 阿里巴巴开源OpenJDK长期支持版本,Java全球管理组织唯一中国企业
- 【Makefile由浅入深完全学习记录6】Makefile中变量的高级主题上
- oracle服务说明
- vhs预设_使用Linux将VHS数字化,创建Bash完成脚本,Ansible,家庭自动化等
- mysql5.7.23主主重制_MySQL-23(主从复制)
- c++中父类子类对象指针相互转换,成员函数调用方式
- uestc 方老师的分身 II
- php两个数组递归排序,PHP数组递归排序实现方法示例
- 《人月神话》读后感与读书笔记
- 智能数据中心和智慧园区:华为的单点突破与全局效应
- win10更改C盘下的用户文件夹名
- 含绝对值函数的可导性
- 纯html+css制作banner轮播图
- 数位笔的笔芯磨损到什么程度需要更换?压感笔笔芯怎么更换?
- 线上实时监测推广效果,App投放渠道数据分析
- 修改淘宝标题是否会重新发布宝贝?2014.8.23
- MacBook(m1) 通过brew安装PHP环境
热门文章
- 利用python实现ANN算法预测岩石单轴抗压强度的经验模型代码。设置岩石密度、孔隙度、施密特回弹值、动岩石参数作为输出层...
- 如何快速增加qq名片赞
- 以神奇“三”为本的逻辑与指号学----皮尔斯逻辑之三
- 微信小程序font-family中提供的十四种字体
- jsp文件打开全是黑色字体
- Android之画图
- ISP - 没有实现编码/解码功能 - VPU区别
- PQ节点-PV节点-平衡节点
- Xilinx ZCU106开发详解(Xilinx Zynq UltraScale+ MPSoC)
- 用python画皮卡丘