栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

OpenCV实现图像校正功能

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

OpenCV实现图像校正功能

一、 需求分析

首先是需求:

1、利用 OpenCV 里面的仿射变换函 数实现对图像进行一些基本的变换,如平移、旋转、缩放
2、学习透视变换原理,对一个矩形进行透视变换,并将变换结果绘制出来。先调用 OpenCV 函数实现透视变换,自己编写代码实现透视变换。
3、识别一张倾斜拍摄的纸张,找出轮廓,提取出该纸张的位置
4、 假设你已通过图像处理的算法找到发生形变的纸张的位置,那么对这个倾斜 纸张进行变换,得到纸张的垂直视图,实现文档校准。

然后是分析:

1、首先要调用OpenCV的函数对图像进行平移、旋转、缩放变换,然后要进行仿射变换和透视变换。
2、编程实现仿射变换和透视变换,注意到仿射变换是透视变换的一种,因此只需实现透视变换
3、 实现文档校准:

(1)滤波。考虑到文档中的字(噪点),同时采用均值滤波和闭运算滤波。
(2)边缘提取。利用库函数提取边缘信息
(3)边缘识别。利用经典霍夫变换,获得边界方程,并且计算出文档的四个角的坐标
(4)透视变换。调用库函数,实现文档校准

5、由于前三个需求与最后一个需求的源码放在同一个工程中显得不合适,因此,我将前三个需求的代码和注释放在了工程:作业2_2中,开发环境是win10 vs2017,openCV3.43

二、 实现

注意:

以下的函数全部写在标头.h文件中,要在在main中调用标头.h文件中的函数才能完成功能
还有就是图片输入的路径要改好。

1、工程:作业2_2的实现

(1)调用OpenCV内的函数,编写了一个main_transform函数,在主函数调用它,输入图片后,同时将图片缩小、平移、旋转、透视和仿射变换,并且将图片展示和保存下来(实际上后来openCV的仿射、透视我注释掉了,不用它自带的函数了)
都是直接调用函数,没什么好说的。

下面分别是旋转、透视、平移、缩小、仿射的效果图:

(2)手动实现仿射、透视变换函数 toushibianhuan和toushibianhuan_gai_fangshebianhuan,并在main_transform中调用他们。

透视变换实现:

注意到仿射变换是透视变换的特殊情况,因此只要实现了透视就可以实现仿射。

透视函数的实现:

首先使用getPerspectiveTransform来获取变换矩阵,然后看透视函数

toushibianhuan函数需要三个输入参数:

  • 第一个参数:透视变换输入的图像矩阵,Mat
  • 第二个参数:输出图像容器矩阵,Mat
  • 第三个参数:变换矩阵,Mat

进入函数后,首先定义出一个位置矩阵position_maxtri用以刻画变换前图像的位置,利用矩阵元素积,乘以变换矩阵后算出变换后的四个角的位置矩阵。

用Max、Min函数计算出图像最高点、最低点,进而算出图像的高和宽

然后,重点来了,定义、更新计算出两个重映射矩阵。Map1是从原图的x—>新图x的映射,Map2是从原图y—>新图y的映射。




#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace cv;


Mat comMatC(Mat Matrix1, Mat Matrix2, Mat &MatrixCom)
{
 CV_Assert(Matrix1.cols == Matrix2.cols);
 MatrixCom.create(Matrix1.rows + Matrix2.rows, Matrix1.cols, Matrix1.type());
 Mat temp = MatrixCom.rowRange(0, Matrix1.rows);
 Matrix1.copyTo(temp);
 Mat temp1 = MatrixCom.rowRange(Matrix1.rows, Matrix1.rows + Matrix2.rows);
 Matrix2.copyTo(temp1);
 return MatrixCom;
}


void toushibianhuan(Mat input_image, Mat &output, Mat tp_Translater_maxtri)
{
 int qiu_max_flag;
 int j;
 int i;
 //定义顶点位置矩阵
 Mat position_maxtri(3, 4, CV_64FC1, Scalar(1));
 position_maxtri.at < double >(0, 0) = 0;
 position_maxtri.at < double >(1, 0) = 0;
 position_maxtri.at < double >(1, 1) = 0;
 position_maxtri.at < double >(0, 2) = 0;
 position_maxtri.at < double >(1, 2) = input_image.rows;
 position_maxtri.at < double >(0, 3) = input_image.cols;
 position_maxtri.at < double >(1, 3) = input_image.rows;
 position_maxtri.at < double >(0, 1) = input_image.cols;
 Mat new_corner = tp_Translater_maxtri * position_maxtri;
 //打印并监视三个矩阵
 cout << "coner_maxtri" << new_corner << ";" << endl << endl;
 cout << "pos_maxtri" << position_maxtri << ";" << endl << endl;
 cout << "T_maxtri" << tp_Translater_maxtri << ";" << endl << endl;
 //为了计算图像高度,先初始化最高最低、最左最右点
 double max_kuan = new_corner.at < double >(0, 0) / new_corner.at < double >(2, 0);
 double min_kuan = new_corner.at < double >(0, 0) / new_corner.at < double >(2, 0);
 double max_gao = new_corner.at < double >(1, 0) / new_corner.at < double >(2, 0);
 double min_gao = new_corner.at < double >(1, 0) / new_corner.at < double >(2, 0);
 for (qiu_max_flag = 1; qiu_max_flag < 4; qiu_max_flag++)
 {
 max_kuan = max(max_kuan,
 new_corner.at < double >(0, qiu_max_flag) / new_corner.at < double >(2, qiu_max_flag));
 min_kuan = min(min_kuan,
 new_corner.at < double >(0, qiu_max_flag) / new_corner.at < double >(2, qiu_max_flag));
 max_gao = max(max_gao,
 new_corner.at < double >(1, qiu_max_flag) / new_corner.at < double >(2, qiu_max_flag));
 min_gao = min(min_gao,
 new_corner.at < double >(1, qiu_max_flag) / new_corner.at < double >(2, qiu_max_flag));
 }
 //创建向前映射矩阵 map1, map2
 output.create(int(max_gao - min_gao), int(max_kuan - min_kuan), input_image.type());
 Mat map1(output.size(), CV_32FC1);
 Mat map2(output.size(), CV_32FC1);
 Mat tp_point(3, 1, CV_32FC1, 1);
 Mat point(3, 1, CV_32FC1, 1);
 tp_Translater_maxtri.convertTo(tp_Translater_maxtri, CV_32FC1);
 Mat Translater_inv = tp_Translater_maxtri.inv();
 //核心步骤,将映射阵用矩阵乘法更新出来
 for (i = 0; i < output.rows; i++)
 {
 for (j = 0; j < output.cols; j++)
 {
 point.at(0) = j + min_kuan;
 point.at(1) = i + min_gao;
 tp_point = Translater_inv * point;
 map1.at(i, j) = tp_point.at(0) / tp_point.at(2);
 map2.at(i, j) = tp_point.at(1) / tp_point.at(2);
 }
 }

 remap(input_image, output, map1, map2, CV_INTER_LINEAR);
}


void toushibianhuan_gai_fangshebianhuan(Mat input_image, Mat &output, Mat Translater_maxtri)
{
 int width = 0;
 int height = 0;
 Mat tp_Translater_maxtri;
 Mat position_maxtri(3, 4, CV_64FC1, Scalar(1));
 Mat one_vector(1, 3, CV_64FC1, Scalar(0));
 one_vector.at(0, 2) = 1.;
 comMatC(Translater_maxtri, one_vector, tp_Translater_maxtri);
 position_maxtri.at < double >(1, 1) = 0;
 position_maxtri.at < double >(0, 2) = 0;
 position_maxtri.at < double >(0, 0) = 0;
 position_maxtri.at < double >(1, 0) = 0;
 position_maxtri.at < double >(0, 3) = input_image.cols;
 position_maxtri.at < double >(1, 3) = input_image.rows;
 position_maxtri.at < double >(0, 1) = input_image.cols;
 position_maxtri.at < double >(1, 2) = input_image.rows;
 Mat new_corner = tp_Translater_maxtri * position_maxtri;
 cout << "coner_maxtri" << new_corner << ";" << endl << endl;
 cout << "pos_maxtri" << position_maxtri << ";" << endl << endl;
 cout << "T_maxtri" << tp_Translater_maxtri << ";" << endl << endl;
 double max_kuan = new_corner.at < double >(0, 0) / new_corner.at < double >(2, 0);
 double min_kuan = new_corner.at < double >(0, 0) / new_corner.at < double >(2, 0);
 double max_gao = new_corner.at < double >(1, 0) / new_corner.at < double >(2, 0);
 double min_gao = new_corner.at < double >(1, 0) / new_corner.at < double >(2, 0);
 for (int flag = 1; flag < 4; flag++)
 {
 max_kuan = max(max_kuan, new_corner.at < double >(0, flag) / new_corner.at < double >(2, flag));
 min_kuan = min(min_kuan, new_corner.at < double >(0, flag) / new_corner.at < double >(2, flag));
 max_gao = max(max_gao, new_corner.at < double >(1, flag) / new_corner.at < double >(2, flag));
 min_gao = min(min_gao, new_corner.at < double >(1, flag) / new_corner.at < double >(2, flag));
 }
 output.create(int(max_gao - min_gao), int(max_kuan - min_kuan), input_image.type());
 Mat map1(output.size(), CV_32FC1);
 Mat map2(output.size(), CV_32FC1);
 Mat tp_point(3, 1, CV_32FC1, 1);
 Mat point(3, 1, CV_32FC1, 1);
 tp_Translater_maxtri.convertTo(tp_Translater_maxtri, CV_32FC1);
 Mat Translater_inv = tp_Translater_maxtri.inv();
 for (int i = 0; i < output.rows; i++)
 {
 for (int j = 0; j < output.cols; j++)
 {
 point.at(1) = i + min_gao;
 point.at(0) = j + min_kuan;
 tp_point = Translater_inv * point;
 map1.at(i, j) = tp_point.at(0) / tp_point.at(2);
 map2.at(i, j) = tp_point.at(1) / tp_point.at(2);
 }
 }
 remap(input_image, output, map1, map2, CV_INTER_LINEAR);
}


void main_transform(float angle, int right_translate, int down_translate,
 const char* road_read_image, float x_tobe, float y_tobe)
{
 Point2f input_image1[3] = { Point2f(50,50),Point2f(200,50),Point2f(50,200) };
 Point2f dst1[3] = { Point2f(0,100),Point2f(200,50),Point2f(180,300) };
 Point2f input_image[4] = { Point2f(100,50),Point2f(100,550),Point2f(350,50),Point2f(350,550) };
 Point2f dst[4] = { Point2f(100,50),Point2f(340,550),Point2f(350,80),Point2f(495,550) };
 Mat kernel2 = getPerspectiveTransform(input_image, dst);
 Mat kernel = getAffineTransform(input_image1, dst1);
 Mat one_vector(1, 3, CV_64FC1, Scalar(0));
 Mat Temp_kernel;
 one_vector.at(0, 2) = 1.;
 comMatC(kernel, one_vector, Temp_kernel);
 float all_tobe = x_tobe / 2 + y_tobe / 2;
 Mat old_image = imread(road_read_image);
 Mat new_min_image;
 Mat new_translation_image;
 Mat rotate_image;
 Mat translater(2, 3, CV_32F, Scalar(0));
 Mat rotater;
 Mat fangshe_image;
 Mat toushi_image;
 vector compression_params;
 resize(old_image, new_min_image, Size(), x_tobe, y_tobe, INTER_CUBIC);
 translater.at(0, 0) = 1;
 translater.at(1, 1) = 1;
 translater.at(0, 2) = right_translate;
 translater.at(1, 2) = down_translate;
 warpAffine(new_min_image, new_translation_image, translater,
 Size(new_min_image.cols*1.5, new_min_image.rows*1.5));
 Point rotate_center = Point(new_translation_image.cols / 3, new_translation_image.rows / 2);
 rotater = getRotationMatrix2D(rotate_center, angle, all_tobe);
 warpAffine(new_translation_image, rotate_image, rotater, Size(),
 INTER_CUBIC | CV_WARP_FILL_OUTLIERS, BORDER_CONSTANT, Scalar(0));
 //warpAffine(new_translation_image, fangshe_image, kernel, Size(new_translation_image.cols*1.5, new_translation_image.rows*1.5));
 //这是OpenCV自带的仿射变换.........
 compression_params.push_back(IMWRITE_PNG_COMPRESSION);
 toushibianhuan_gai_fangshebianhuan(new_translation_image, fangshe_image, kernel);
 toushibianhuan(fangshe_image, toushi_image, kernel2);

 //warpPerspective(fangshe_image, toushi_image, kernel2, Size(new_translation_image.cols, new_translation_image.rows));
 //这是openCV的透视变换
 compression_params.push_back(9);
 namedWindow("new_min_image");
 imshow("new_min_image", new_min_image);
 imwrite("task2_1放缩.png", old_image, compression_params);
 namedWindow("new_translation_image");
 imshow("new_translation_image", new_translation_image);
 bool flags = imwrite("task2_1平移.png", new_translation_image, compression_params);
 namedWindow("rotate_image");
 imshow("rotate_image", rotate_image);
 imwrite("task2_1旋转.png", rotate_image, compression_params);
 namedWindow("fangshe_image");
 imshow("fangshe_image", fangshe_image);
 imwrite("task2_1仿射.png", fangshe_image, compression_params);
 namedWindow("toushi_image");
 imshow("toushi_image", toushi_image);
 imwrite("task2_1透视.png", toushi_image, compression_params);
 printf("%d", flags);

}


Point2f getCrossPoint(Vec4i LineA, Vec4i LineB)
{
 double ka, kb;
 //求出LineA斜率
 ka = (double)(LineA[3] - LineA[1]) / (double)(LineA[2] - LineA[0]); 
 //求出LineB斜率
 kb = (double)(LineB[3] - LineB[1]) / (double)(LineB[2] - LineB[0]); 


 Point2f crossPoint;
 crossPoint.x = (ka*LineA[0] - LineA[1] - kb * LineB[0] + LineB[1]) / (ka - kb);
 crossPoint.y = (ka*kb*(LineA[0] - LineB[0]) + ka * LineB[1] - kb * LineA[1]) / (ka - kb);
 return crossPoint;
}


void input_solve(const char* image_road)
{
 //定义保存图像参数向量
 vector compression_params;
 compression_params.push_back(IMWRITE_PNG_COMPRESSION);
 compression_params.push_back(9);
 //获取闭运算滤波的核
 Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
 Mat new_min_image;
 Mat last_kernel;
 //获取灰度图
 Mat old_image = imread(image_road,0);
 vectorlines;
 vectorconers;
 vectorlines_2pt(10);
 Point pt1, pt2,pt3,pt4,pt5,pt6;
 Mat last_image;
 Mat new_min_image2;
 resize(old_image, new_min_image, Size(), 0.5, 0.5, INTER_CUBIC);
 resize(old_image, new_min_image2, Size(), 0.5, 0.5, INTER_CUBIC);
 //闭运算滤波
 morphologyEx(new_min_image, new_min_image, MORPH_CLOSE, element);
 blur(new_min_image,new_min_image,Size(10,10));
 Canny(new_min_image, new_min_image,8.9,9,3 );
 HoughLines(new_min_image,lines,1,CV_PI/180,158,0,0);
 //利用这个循环,可以绘制霍夫变换获取直线的效果图,但是为了简洁性我暂时删去了创建窗口绘制的代码
 for (rsize_t i = 0 ; i < lines.size(); i++)
 {
 if (i!=lines.size()-2)
 {
 float zhongxinjuli = lines[i][0], theta = lines[i][1];
 double cos_theta = cos(theta), sin_theta = sin(theta);
 double x0 = zhongxinjuli * cos_theta, y0 = zhongxinjuli * sin_theta;
 pt1.x = cvRound(x0 - 1000 * sin_theta);
 pt1.y = cvRound(y0 + 1000 * cos_theta);
 pt2.x = cvRound(x0 + 1000 * sin_theta);
 pt2.y = cvRound(y0 - 1000 * cos_theta);
 line(new_min_image, pt1, pt2, Scalar(255, 255, 255), 1, LINE_AA);
 }
 }
 //获取霍夫变换直线的交点
 for (rsize_t flag = 0,flag2=0; flag < lines.size(); flag++)
 {
 if (flag != lines.size() - 2)
 {
 float zx_juli = lines[flag][0], theta2 = lines[flag][1];
 double cos_theta2 = cos(theta2), sin_theta2 = sin(theta2);
 double x1 = zx_juli * cos_theta2, y1 = zx_juli * sin_theta2;
 lines_2pt[flag2][0]= cvRound(x1 - 1000 * sin_theta2);
 lines_2pt[flag2][1] = cvRound(y1 +1000 * cos_theta2);
 lines_2pt[flag2][2] = cvRound(x1 + 1000 * sin_theta2);
 lines_2pt[flag2][3] = cvRound(y1 - 1000 * cos_theta2);
 flag2++;
 }
 }
 for(int flag3=0;flag3<4;flag3++)
 {
 cout << "line_vector=" <





 

#include 
#include 
#include 
#include 
#include "标头.h"
using namespace std;
using namespace cv;
int main()
{
 main_transform(90, 0, 100, "task2.png", 0.5, 0.5);
 input_solve("task2.png");
 waitKey(0);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持考高分网。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/62389.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号