透视校正图像

一.问题描述
拍摄或者扫描图像不是规则的矩形,会对后期处理产生不好的影响,需要通过透视变换矫正得到正确的形状。
适用场景:对于机器学习、深度学习中的一些算法,算法本身很好,但对源图的要求却很高,有些图像摆放不正,若通过透视矫正,可以提高检测的准确率。
在这里插入图片描述
就是把左边的图像进行透视矫正变成右边的图像。

二.用到的API
透视变换warpPerspective
PS:书《Opencv3 编程入门》那本书中,是仿射变换,用于图像的旋转等等,一开始我还搞混了。
关于透视变换warpPerspective的介绍参考博客:
https://blog.csdn.net/i_chaoren/article/details/78324184

三.解决的思路
二值分割+形态学方法+Hough直线检测+透视变换

四.程序演示

#include <opencv2/opencv.hpp>
#include <math.h>
#include <iostream>
using namespace cv;
using namespace std;

int main(int argc,char** argv)
{
	Mat srcImage = imread("E:\\pictures\\47.jpg");
 	if (srcImage.empty())
 	{
 		cout << "图像读取错误!" << endl;
  		return -1;
 	}
 	namedWindow("【1】原图", WINDOW_AUTOSIZE);
 	imshow("【1】原图", srcImage);

	//二值处理
 	Mat srcImage_gray, srcImage_binary, dstImage;
 	cvtColor(srcImage, srcImage_gray, COLOR_BGR2GRAY);
 	threshold(srcImage_gray, srcImage_binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
 	//THRESH_BINARY_INV  对象设置为黑色,背景为白色
 	//THRESH_BINARY  对象设置为白色,背景为黑色
 	imshow("【2】二值化后", srcImage_binary);

	//形态学操作
 	//目的:去除掉二值化后的噪点
 	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
 	morphologyEx(srcImage_binary, dstImage, MORPH_CLOSE, kernel, Point(-1, -1), 3);//闭操作可以去掉图像中小点点,噪点
 	imshow("【3】形态学操作后", dstImage);

	//发现轮廓
 	bitwise_not(dstImage, dstImage, Mat());
 	vector<vector<Point>> contours;
 	vector<Vec4i> hireachy;
 	findContours(dstImage, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());

	//绘制轮廓
 	int width = srcImage.cols;
 	int height = srcImage.rows;
 	Mat drawImage = Mat::zeros(srcImage.size(), CV_8UC3);
 	for (size_t t = 0; t < contours.size(); t++)
 	{
 		Rect rect = boundingRect(contours[t]);
  		if (rect.width > width / 2 && rect.width < width - 5)  //找出我们需要的轮廓
  		{
  			drawContours(drawImage, contours, static_cast<int>(t), Scalar(0, 0, 255), 2, 8, hireachy, 0, Point());
  		}
 	}
 	imshow("【4】绘制轮廓后", drawImage);

	//霍夫直线检测
 	vector<Vec4i> lines;
 	Mat contoursImage;
 	int minLineLength = min(width * 0.5, height * 0.5); //表示最低线段长度
 	cvtColor(drawImage, contoursImage, COLOR_BGR2GRAY);
 	HoughLinesP(contoursImage, lines, 1, CV_PI / 180.0, minLineLength, minLineLength, 0);
 	Mat LinesImage = Mat::zeros(srcImage.size(), CV_8UC3);
 	for (size_t t = 0; t < lines.size(); t++)
 	{
 		Vec4i In = lines[t];
  		line(LinesImage, Point(In[0], In[1]), Point(In[2], In[3]), Scalar(0, 0, 255), 2, 8, 0);
 	}
 	cout << "线段的个数:" << lines.size() << endl;//这里寻找的线段多了无所谓,重合了后仍是那条线段
 	imshow("【5】霍夫直线检测后", LinesImage);

	//寻找与定位上下左右四条直线
 	int height_difference = 0; //定义高度差
 	Vec4i topLine, bottomLine;
 	Vec4i leftLine, rightLine;
 	for (int i = 0; i < lines.size(); i++)
 	{
 		Vec4i In = lines[i];
  		height_difference = abs(In[3] - In[1]); //取绝对值
  		if (In[3] < height / 2.0 && In[1] < height / 2.0 && height_difference < minLineLength - 1)
  		{
  			topLine = lines[i];
  		}
  		if (In[3] > height / 2.0 && In[1] > height / 2.0 && height_difference < minLineLength - 1)
  		{
  			bottomLine = lines[i];
  		}
  		if (In[0] < width / 2.0 && In[2] < width / 2.0)
  		{
  			leftLine = lines[i];
  		}
  		if (In[0] > width / 2.0 && In[2] > width / 2.0)
  		{
  			rightLine = lines[i];
  		}
 	}
 	cout << "top line:p1(x,y) = " << topLine[0] << "," << topLine[1] << " ;p2(x, y) = " << topLine[2] << "," << topLine[3] << endl;
 	cout << "bottomLine line:p1(x,y) = " << bottomLine[0] << "," << bottomLine[1] << " ;p2(x, y) = " << bottomLine[2] << "," << bottomLine[3] << endl;
 	cout << "leftLine line:p1(x,y) = " << leftLine[0] << "," << leftLine[1] << " ;p2(x, y) = " << leftLine[2] << "," << leftLine[3] << endl;
 	cout << "rightLine line:p1(x,y) = " << rightLine[0] << "," << rightLine[1] << " ;p2(x, y) = " << rightLine[2] << "," << rightLine[3] << endl;

	//上一步操作寻找到了轮廓的四条直线
 	//下面求出四条直线相交的四个顶点,左上角,右上角,左下角,右下角
 	//这一步求出四条直线的方程,通过 y=kx+c
 	//y=k1x+c1
 	float k1, c1;
 	k1 = float(topLine[3] - topLine[1]) / float(topLine[2] - topLine[0]);
 	c1 = topLine[1] - k1 * topLine[0];
 	//y = k2x + c2
 	float k2, c2;
 	k2 = float(bottomLine[3] - bottomLine[1]) / float(bottomLine[2] - bottomLine[0]);
 	c2 = bottomLine[1] - k2 * bottomLine[0];
 	//y = k3x + c3
 	float k3, c3;
 	k3 = float(leftLine[3] - leftLine[1]) / float(leftLine[2] - leftLine[0]);
 	c3 = leftLine[1] - k3 * leftLine[0];
 	//y = k4x + c4
 	float k4, c4;
 	k4 = float(rightLine[3] - rightLine[1]) / float(rightLine[2] - rightLine[0]);
 	c4 = rightLine[1] - k4 * rightLine[0];

	//上一步操作求出了上下左右四条直线的方程
 	//这一步求出四条直线相交的四个交点,左上角,右上角,左下角,右下角
 	//理论基础:两条直线求交点:y=k1x+c1,y = k3x + c3
 	//                          k1x+c1 = k3x + c3   得出:x = (c1-c3) / (k3-k1),
 	//                          算出x后代入两条中任一直线求出y的值,若匹配的点不对,就换另一条直线
 	Point p1;//左上角:topLine与leftLine交点
 	p1.x = static_cast<int>((c1 - c3) / (k3 - k1));
 	p1.y = static_cast<int>(k1 * p1.x + c1);
 	Point p2;//右上角:topLine与rightLine交点
 	p2.x = static_cast<int>((c1 - c4) / (k4 - k1));
 	p2.y = static_cast<int>(k1 * p2.x + c1);
 	Point p3;//左下角:leftLine与bottomLine交点
 	p3.x = static_cast<int>((c2 - c3) / (k3 - k2));
 	p3.y = static_cast<int>(k2 * p3.x + c2);
 	Point p4;//右下角:rightLine与bottomLine交点
 	p4.x = static_cast<int>((c2 - c4) / (k4 - k2));
 	p4.y = static_cast<int>(k2 * p4.x + c2);
 	cout << "p1(x,y) = " << p1.x << "," << p1.y << endl;
 	cout << "p2(x,y) = " << p2.x << "," << p2.y << endl;
 	cout << "p3(x,y) = " << p3.x << "," << p3.y << endl;
 	cout << "p4(x,y) = " << p4.x << "," << p4.y << endl;

	//绘制出四个顶点,直观的显示出来,方便检查
 	circle(LinesImage, p1, 2, Scalar(255, 0, 0), 2, 8, 0);
 	circle(LinesImage, p2, 2, Scalar(255, 0, 0), 2, 8, 0);
 	circle(LinesImage, p3, 2, Scalar(255, 0, 0), 2, 8, 0);
 	circle(LinesImage, p4, 2, Scalar(255, 0, 0), 2, 8, 0);
 	imshow("【6】四个顶点", LinesImage);

	//进行透视变换
 	vector<Point2f> srcImage_corners(4); 
 	//这里注意透视变换的点坐标的顺序,是从左上角开始,以顺时针的顺序,而之前p1是以左上角右上角左下角右下角的顺序
	srcImage_corners[0] = p1;
 	srcImage_corners[1] = p2;
 	srcImage_corners[2] = p4;
 	srcImage_corners[3] = p3;
 	vector<Point2f> dstImage_corners(4);//顺序与上面对应
 	dstImage_corners[0] = Point(0,0);
 	dstImage_corners[1] = Point(width,0);
 	dstImage_corners[2] = Point(width, height);
 	dstImage_corners[3] = Point(0, height);
 	//获取透视变换矩阵
 	Mat resultImage;
 	Mat warpmatrix = getPerspectiveTransform(srcImage_corners, dstImage_corners);
 	warpPerspective(srcImage, resultImage, warpmatrix, resultImage.size(), INTER_LINEAR);
 	imshow("【7】效果图", resultImage);

	waitKey(0);
 	return 0;
}

输出结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发布了25 篇原创文章 · 获赞 0 · 访问量 447

猜你喜欢

转载自blog.csdn.net/qq_45445740/article/details/103765599
今日推荐