8, Perspective transformation (slightly masked)

Posted by Mount Tropolis on Sun, 16 Jan 2022 16:09:54 +0100

Functions to be used:

1,Mat getStructuringElement(int shape, Size esize, Point anchor = Point(-1, -1));

The first parameter of this function represents the shape of the kernel. There are three shapes to choose from.

Rectangle: MORPH_RECT;

Cross shape: MORPH_CROSS;

Oval: MORPH_ELLIPSE;

The second and third parameters are the size of the kernel and the location of the anchor, respectively. Generally, before calling the erode and dilate functions, define a variable of type Mat to obtain the return value of the getStructuringElement function. For the position of the anchor Point, there is a default value of Point (- 1, - 1), indicating that the anchor Point is located at the center Point. element shape only depends on the anchor position. In other cases, the anchor only affects the offset of morphological operation results.

Kernel:

Anchor point:
 

2. morphologyEx (morphological operation)

void morphologyEx( InputArray src, OutputArray dst,int op, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() )

Parameter interpretation
src: source image Mat object
dst: target image Mat object
op: types of operations. We know from the source code that there are the following types:

enum MorphTypes{

MORPH_ERODE = 0, / / corrosion

MORPH_DILATE = 1, / / expansion

MORPH_OPEN = 2, / / open operation (in fact, the internal operation is corrosion first and then expansion)

MORPH_CLOSE = 3, / / close operation

MORPH_GRADIENT = 4, / / gradient operation

MORPH_TOPHAT = 5, / / top hat operation

MORPH_BLACKHAT = 6, / / black hat operation

MORPH_HITMISS = 7.

}

kernel: the structure element used for expansion operation. If the value is Mat(), a 3 x 3 square structure element is used by default. You can use getStructuringElement() to create the structure element
anchor: reference point, whose default value is (- 1, - 1), indicating that it is located in the center of the kernel.
borderType: edge type; the default is BORDER_CONSTANT.
borderValue: edge value. Use its default value.

3,boundingRect(InputArray points)

Points: enter information, which can be a vector containing points or a Mat.
Returns the smallest positive rectangle covering the input information.

4,HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )

Function function: extract line segments from the input image according to the given parameter requirements and put them in lines.

The first parameter, InputArray type image, is the input image.

The second parameter, lines of InputArray type, stores the output vector of the detected line after calling HoughLinesP function. Each line is represented by a vector with four elements (x_1,y_1, x_2, y_2), where, (x_1, y_1) and (x_2, y_2) are the end points of each detected line segment.
The third parameter, rho, is simply the resolution of the radius.
The fourth parameter, theta, is the angular accuracy in radians. Another way to describe it is the unit angle of the progressive dimension when searching for a straight line.
The fifth parameter, threshold, is the threshold parameter of the accumulation plane, that is, the value that a part must reach in the accumulation plane when it is recognized as a straight line in the graph. Only segments greater than the threshold can be detected and returned to the result.
The sixth parameter, minLineLength, has a default value of 0, indicating the length of the lowest line segment. The line segment shorter than this setting parameter cannot be displayed.
The seventh parameter, maxLineGap, has a default value of 0, which allows the maximum distance between points in the same line
 

The idea to solve the problem is to find the coordinates of four points to complete the perspective transformation.

Two main functions:

getPerspectiveTransform( InputArray src, InputArray dst );// Get perspective transformation matrix


warpPerspective( InputArray src, OutputArray dst, InputArray M, Size dsize,
 int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT,
  const Scalar& borderValue = Scalar());// Perspective transformation

General operation process:

(1) Gray processing, binarization and morphological operation form connected regions

(2) Contour discovery and drawing the contour of the target

(3) Line detection in the drawn contour

(4) Find the four edges and find the four corners

(5) Use the perspective transformation function to get the result

#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	Mat src = imread("D://RM//OpenCv learning / / perspective transformation / / 11 png");
	imshow("input image", src);
	//bgr 2 gray to gray image
	Mat src_gray;
	cvtColor(src, src_gray, COLOR_BGR2GRAY);
	//binary binarization
	Mat binary;
	threshold(src_gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); //THRESH_BINARY_INV is inversed after binarization
	//imshow("binary", binary);// Because there are some spots
	//Morphological closing operation: small areas can be filled
	Mat morhp_img;
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(binary, morhp_img, MORPH_CLOSE, kernel, Point(-1, -1), 3);
	//imshow("morphology", morhp_img);

	Mat dst;
	bitwise_not(morhp_img, dst);//In reverse
	imshow("dst", dst);//

	//Contour discovery
	vector<vector<Point>> contous;
	vector<Vec4i> hireachy;
	findContours(dst, contous, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
	cout << "contous.size:" << contous.size() << endl;

	//Contour drawing
	int width = src.cols;
	int height = src.rows;
	Mat drawImage = Mat::zeros(src.size(), CV_8UC3);
	cout << contous.size() << endl;
	for (size_t t = 0; t < contous.size(); t++)
	{
		Rect rect = boundingRect(contous[t]);
		if (rect.width > width / 2 && rect.height > height / 2 && rect.width < width - 5 && rect.height < height - 5)
		{
			drawContours(drawImage, contous, static_cast<int>(t), Scalar(0, 0, 255), 2, 8, hireachy, 0, Point(0, 0));
		}
	}
	imshow("contours", drawImage);//Show found contours
	//Line detection
	vector<Vec4i> lines;
	Mat contoursImg;
	int accu = min(width * 0.5, height * 0.5);
	cvtColor(drawImage, contoursImg, COLOR_BGR2GRAY);
	imshow("contours", contoursImg);

	Mat linesImage = Mat::zeros(src.size(), CV_8UC3);
	HoughLinesP(contoursImg, lines, 1, CV_PI / 180.0, accu, accu, 0);
	for (size_t t = 0; t < lines.size(); t++)
	{
		Vec4i ln = lines[t];
		line(linesImage, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);//draw a straight line
	}
	cout << "number of lines:" << lines.size() << endl;
	imshow("linesImages", linesImage);

	//Find and locate four straight lines up, down, left and right
	int deltah = 0; //Height difference
	int deltaw = 0; //Width difference
	Vec4i topLine, bottomLine; //Line definition
	Vec4i rightLine, leftLine;

	for (int i = 0; i < lines.size(); i++)
	{
		Vec4i ln = lines[i];
		deltah = abs(ln[3] - ln[1]); //Calculated height difference (y2-y1)
		//topLine
		if (ln[3] < height / 2.0 && ln[1] < height / 2.0 && deltah < accu - 1)
		{
			topLine = lines[i];
		}
		//bottomLine
		if (ln[3] > height / 2.0 && ln[1] > height / 2.0 && deltah < accu - 1)
		{
			bottomLine = lines[i];
		}
		deltaw = abs(ln[2] - ln[0]); //Calculated width difference (x2-x1)	
		//leftLine
		if (ln[0] < height / 2.0 && ln[2] < height / 2.0 && deltaw < accu - 1)
		{
			leftLine = lines[i];
		}
		//rightLine
		if (ln[0] > width / 2.0 && ln[2] > width / 2.0 && deltaw < accu - 1)
		{
			rightLine = lines[i];
		}
	}
	// Prints the coordinates of the four lines
	cout << "topLine : p1(x,y)= " << topLine[0] << "," << topLine[1] << "; p2(x,y)= " << topLine[2] << "," << topLine[3] << endl;
	cout << "bottomLine : p1(x,y)= " << bottomLine[0] << "," << bottomLine[1] << "; p2(x,y)= " << bottomLine[2] << "," << bottomLine[3] << endl;
	cout << "leftLine : p1(x,y)= " << leftLine[0] << "," << leftLine[1] << "; p2(x,y)= " << leftLine[2] << "," << leftLine[3] << endl;
	cout << "rightLine : p1(x,y)= " << rightLine[0] << "," << rightLine[1] << "; p2(x,y)= " << rightLine[2] << "," << rightLine[3] << endl;
	//Fit four lines
	float k1, k2, k3, k4, c1, c2, c3, c4;
	k1 = float(topLine[3] - topLine[1]) / float(topLine[2] - topLine[0]);
	c1 = topLine[1] - k1 * topLine[0];

	k2 = float(bottomLine[3] - bottomLine[1]) / float(bottomLine[2] - bottomLine[0]);
	c2 = bottomLine[1] - k2 * bottomLine[0];

	k3 = float(leftLine[3] - leftLine[1]) / float(leftLine[2] - leftLine[0]);
	c3 = leftLine[1] - k3 * leftLine[0];

	k4 = float(rightLine[3] - rightLine[1]) / float(rightLine[2] - rightLine[0]);
	c4 = rightLine[1] - k4 * rightLine[0];

	//Find four corners,
	Point p1;//Topline leftline upper left corner
	p1.x = static_cast<int>(c1 - c3) / k3 - k1;
	p1.y = k1 * p1.x + c1;

	Point p2;//Topline rightline top right corner
	p2.x = static_cast<int>(c1 - c4) / k4 - k1;
	p2.y = k1 * p2.x + c1;

	Point p3;//Bottomline leftline lower left corner
	p3.x = static_cast<int>(c2 - c3) / k3 - k2;
	p3.y = k2 * p3.x + c2;

	Point p4;//Bottomline rightline lower right corner
	p4.x = static_cast<int>(c2 - c4) / k4 - k2;
	p4.y = k2 * p4.x + c2;

	cout << "Point p1: (" << p1.x << "," << p1.y << ")" << endl;
	cout << "Point p2: (" << p2.x << "," << p2.y << ")" << endl;
	cout << "Point p3: (" << p3.x << "," << p3.y << ")" << endl;
	cout << "Point p4: (" << p4.x << "," << p4.y << ")" << endl;

	//Show four points
	circle(linesImage, p1, 2, Scalar(0, 255, 0), 2);
	circle(linesImage, p2, 2, Scalar(0, 255, 0), 2);
	circle(linesImage, p3, 2, Scalar(0, 255, 0), 2);
	circle(linesImage, p4, 2, Scalar(0, 255, 0), 2);
	imshow("find four points", linesImage);

	//Perspective transformation
	vector<Point2f> src_corners(4);
	src_corners[0] = p1;
	src_corners[1] = p2;
	src_corners[2] = p3;
	src_corners[3] = p4;

	Mat result_images = Mat::zeros(height * 0.7, width * 0.9, CV_8UC3);
	vector<Point2f> dst_corners(4);
	dst_corners[0] = Point(0, 0);
	dst_corners[1] = Point(result_images.cols, 0);
	dst_corners[2] = Point(0, result_images.rows);
	dst_corners[3] = Point(result_images.cols, result_images.rows);

	Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners); //Get perspective transformation matrix
	warpPerspective(src, result_images, warpmatrix, result_images.size()); //Perspective transformation
	imshow("final result", result_images);
	imwrite("D:/images/warpPerspective.png", result_images);
	waitKey(0);
	return 0;
}