图片透视变换

方法一:基于approxPolyDP函数

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

double EuDis(Point pt1,Point pt2) {
	return sqrt((pt2.x - pt1.x) * (pt2.x - pt1.x) + (pt2.y - pt1.y) * (pt2.y - pt1.y));
}

void PerspectiveMat(Mat src) {
	
	Mat raw;
	//判断图像是否为灰度图
	if (src.type() != CV_8UC1) {
		cvtColor(src,raw,COLOR_BGR2GRAY);
	}
	else
	{
		src.copyTo(raw);
	}
	//二值化
	threshold(raw, raw,50,255, THRESH_BINARY_INV);
	//形态学
	Mat element = getStructuringElement(0,Size(5,5));
	morphologyEx(raw, raw,MORPH_DILATE,element,Point(-1,-1),3);
	imwrite("img2/thresh.jpg", raw);
	//获取图像轮廓
	vector<vector<Point>>contours;
	findContours(raw,contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	//绘制轮廓
	/*for (int i = 0; i < contours.size(); i++) {
		drawContours(src,contours, i, Scalar(0, 0, 255), 1, 8);
	}
	imwrite("img2/src.jpg", src);*/

	//提取图像矩形四个角点
	vector<vector<Point>>conPoly(contours.size());
	vector<Point>srcPts;
	for (int i = 0; i < contours.size(); i++) {
		//轮廓面积
		double area = contourArea(contours[i]); 
		if (area > 10000) {
			//轮廓周长
			double peri = arcLength(contours[i],true);
			approxPolyDP(contours[i],conPoly[i],0.03* peri,true);
			srcPts = { conPoly[i][0],conPoly[i][1],conPoly[i][2],conPoly[i][3] };
		}
	}
	//输出定位到的坐标
	for (int i = 0; i < srcPts.size(); i++) {
		cout << "srcPts:" << srcPts[i] << endl;
	}
	//对矩形角点(坐标)进行排序
	int width = src.cols / 2; 
	int height = src.rows / 2;
	//T_L:左上角   T_R:右上角   B_L:左下角  B_R:右下角
	int T_L, T_R, B_R, B_L;
	for (int i = 0; i < srcPts.size(); i++) {
		if (srcPts[i].x < width && srcPts[i].y < height){
			T_L = i;
		}
		if (srcPts[i].x > width && srcPts[i].y < height)
		{
			T_R = i;
		}
		if (srcPts[i].x > width && srcPts[i].y > height)
		{
			B_R = i;
		}
		if (srcPts[i].x < width && srcPts[i].y > height)
		{
			B_L = i;
		}
	}
	//在原图上显示排序后的点
	/*circle(src, srcPts[T_L], 10, Scalar(0, 0, 255), -1);
	circle(src, srcPts[T_R], 10, Scalar(0, 255, 255), -1);
	circle(src, srcPts[B_R], 10, Scalar(255, 0, 0), -1);
	circle(src, srcPts[B_L], 10, Scalar(0, 255, 0), -1);
	imwrite("img2/src.jpg", src);*/

	/*
	变换后,图像的长和宽应该变为:
	长 = max(变换前左边长,变换前右边长)
	宽 = max(变换前上边长,变换前下边长)
	设变换后图像的左上角位置为原点位置。
	*/
	double LeftHeight = EuDis(srcPts[T_L], srcPts[B_L]);
	double RightHeight = EuDis(srcPts[T_R], srcPts[B_R]);
	cout << "LeftHeight:" << LeftHeight << "  " << "RightHeight:" << RightHeight << endl;
	double MaxHeight = max(LeftHeight, RightHeight);

	double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
	double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
	cout << "UpWidth:" << UpWidth << "  " << "DownWidth:" << DownWidth << endl;
	double MaxWidth = max(UpWidth, DownWidth);

	//这里使用的顺序是左上、右上、右下、左下顺时针顺序。SrcAffinePts、DstAffinePts要一一对应
	Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
	Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth-6,0),Point2f(MaxWidth-6,MaxHeight),Point2f(0,MaxHeight) };

	Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);

	Mat DstImg;
	warpPerspective(src, DstImg, M, Point(MaxWidth, MaxHeight));
	imwrite("img2/DstImg.jpg", DstImg);
	imwrite("img2/src.jpg", src);

	//imshow("Dst", DstImg);
	//imshow("src", src);
	waitKey(0);
}
int main() {
	Mat img;
	img = imread("1.bmp");
	PerspectiveMat(img);
}

方法二:基于霍夫直线检测

#include<iostream>
#include<opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	//加载图像
	Mat src = imread("1.bmp");
	//Mat src = imread("1.tiff");
	if (src.empty())
	{
		cout << "no image!" << endl;
		return -1;
	}
	//imshow("src", src);
	//二值化
	Mat gray, binary;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("binary", binary);
	//形态学处理,消除微小颗粒
	Mat closeImg;

	Mat kern1 = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(binary, closeImg, MORPH_CLOSE, kern1, Point(-1, -1));
	imshow("close", closeImg);
	bitwise_not(closeImg, closeImg);
	imshow("not", closeImg);
	//寻找轮廓
	int width = src.cols;
	int height = src.rows;
	vector<vector<Point>>contours;
	vector<Vec4i>hie;
	Mat mask = Mat::zeros(src.size(), CV_8UC3);
	findContours(closeImg, contours, hie, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
	for (size_t i = 0; i < contours.size(); i++)
	{
		Rect rect = boundingRect(contours[i]);
		if (rect.width > width / 2 && rect.height > height / 2 && rect.width < src.cols - 5)
		{
			drawContours(mask, contours, static_cast<int>(i), Scalar(0, 0, 255), 2, 8, hie, 0, Point());
		}
	}
	imshow("mask", mask);
	//检测直线
	Mat gray_mask;
	int accu =min(0.09* mask.cols, 0.09 * mask.rows);
	cout << "accu:" << accu << endl;
	cvtColor(mask, gray_mask, COLOR_BGR2GRAY);
	imshow("gray_mask", gray_mask);
	vector<Vec4i>lines;
	HoughLinesP(gray_mask, lines, 1, 3.1415 / 180.0, accu, accu, 0);
	Mat mask_line = Mat::zeros(mask.size(), CV_8UC3);
	for (size_t i = 0; i < lines.size(); i++)
	{
		line(mask_line, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("lines", mask_line);
	cout << "直线数量:" << lines.size() << endl;

	//寻找并定位上下左右四条直线
	int deltaH = 0;//定义高度差
	int deltaW = 0;//宽度差
	Vec4i topLine, bottomLine, leftLine, rightLine;
	for (int i = 0; i < lines.size(); i++)
	{
		Vec4i ln = lines[i];
		deltaH = abs(ln[3] - ln[1]);
		deltaW = abs(ln[2] - ln[0]);
		//double slope = (ln[3] - ln[1]) / (ln[2] - ln[0] + 0.00001);
		if (ln[1] < height / 2.0 && ln[3] < height / 2.0 && deltaH < accu - 1)
		{
				topLine = lines[i];				
		}
		if (ln[1] > height / 2.0 && ln[3] > height / 2.0 && deltaH < accu - 1)
		{
				bottomLine = lines[i];
		}
		if (ln[0] < width / 2.0 && ln[2] < width / 2.0 && deltaW < accu - 1)
		{
				leftLine = lines[i];
		}
		if (ln[0] > width / 2.0 && ln[2] > width / 2.0 && deltaW < accu - 1)
		{
				rightLine = lines[i];
		}
	}
	cout << "topLine:" << topLine[0] << "," << topLine[1] << ";" << topLine[2] << "," << topLine[3] << endl;
	cout << "bottomLine:" << bottomLine[0] << "," << bottomLine[1] << ";" << bottomLine[2] << "," << bottomLine[3] << endl;
	cout << "leftLine:" << leftLine[0] << "," << leftLine[1] << ";" << leftLine[2] << "," << leftLine[3] << endl;
	cout << "rightLine:" << rightLine[0] << "," << rightLine[1] << ";" << rightLine[2] << "," << rightLine[3] << endl;
	
	
	//拟合四条直线方程
    //分母不能为0
	//top
	cout << "float(topLine[3] - topLine[1]):" << float(topLine[3] - topLine[1]) << endl;
	float k1 = float(topLine[3] - topLine[1]) / float(topLine[2] - topLine[0] + 0.00001) ;
	float c1 = topLine[1] - k1 * topLine[0];
	cout << "k1=" << k1 << ",c1=" << c1 << endl;
	//bottom
	float k2 = float(bottomLine[3] - bottomLine[1]) / float(bottomLine[2] - bottomLine[0] + 0.00001);
	float c2 = bottomLine[1] - k2 * bottomLine[0];
	cout << "k2=" << k2 << ",c2=" << c2 << endl;
	//left
	float k3 = float(leftLine[3] - leftLine[1]) / float(leftLine[2] - leftLine[0] + 0.00001);
	float c3 = leftLine[1] - k3 * leftLine[0];
	cout << "k3=" << k3 << ",c3=" << c3 << endl;
	//right
	float k4 = float(rightLine[3] - rightLine[1]) / float(rightLine[2] - rightLine[0] + 0.00001);
	float c4 = rightLine[1] - k4 * rightLine[0];
	cout << "k4=" << k4 << ",c4=" << c4 << endl;
	//计算四角
	Point pt1, pt2, pt3, pt4;
	//左上
	pt1.x = static_cast<int>((c1 - c3) / (k3 - k1));
	pt1.y = static_cast<int>(k1 * pt1.x + c1);
	//右上
	pt2.x = static_cast<int>((c4 - c1) / (k1 - k4));
	pt2.y = static_cast<int>(k1 * pt2.x + c1);
	//左下
	pt3.x = static_cast<int>((c2 - c3) / (k3 - k2));
	pt3.y = static_cast<int>(k2 * pt3.x + c2);
	//右下
	pt4.x = static_cast<int>((c4 - c2) / (k2 - k4));
	pt4.y = static_cast<int>(k2 * pt4.x + c2);
	circle(mask_line, pt1, 3, Scalar(0, 255, 0), -1, 8);
	circle(mask_line, pt2, 3, Scalar(0, 255, 0), -1, 8);
	circle(mask_line, pt3, 3, Scalar(0, 255, 0), -1, 8);
	circle(mask_line, pt4, 3, Scalar(0, 255, 0), -1, 8);
	imshow("mask_line_point", mask_line);
	imwrite("img2/a.jpg", mask_line);
	cout << "pt1(x, y)=" << pt1.x << "," << pt1.y << endl;
	cout << "pt2(x, y)=" << pt2.x << "," << pt2.y << endl;
	cout << "pt3(x, y)=" << pt3.x << "," << pt3.y << endl;
	cout << "pt4(x, y)=" << pt4.x << "," << pt4.y << endl;
	//透视变换
	//输入点
	Point2f src_corners[4];
	src_corners[0] = pt1;
	src_corners[1] = pt2;
	src_corners[2] = pt3;
	src_corners[3] = pt4;
	//输出点
	Point2f dst_corners[4];
	dst_corners[0] = Point2f(static_cast<float>(0), static_cast<float>(0));
	dst_corners[1] = Point2f(static_cast<float>(width), static_cast<float>(0));
	dst_corners[2] = Point2f(static_cast<float>(0), static_cast<float>(height));
	dst_corners[3] = Point2f(static_cast<float>(width), static_cast<float>(height));
	Mat resultImg = Mat::zeros(src.size(), CV_8UC3);
	//Mat warpMat(3,3,CV_32F);
	Mat warpMat = getPerspectiveTransform(src_corners, dst_corners);
	cout << warpMat.type() << endl;
	warpPerspective(src, resultImg, warpMat, resultImg.size());
	//resultImg.convertTo(resultImg, CV_8U);
	imshow("result", resultImg);
	imwrite("img2/resultImg.jpg", resultImg);

	waitKey(0);
	return 0;
}

 以上两种方式转载于其他博客,时间太久了未能找到出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值