An arithmetic transform changes the value of an image pixel and it is applied point to point, whereas a geometrical transform changes the position of the image pixels. Thus, points in an image get a new position in the output image without changing their intensity values. Examples of arithmetic transforms may be addition, subtraction, and division between images. Examples of geometrical transforms are scaling, translation, and rotation of images. More complex transformations are to solve the barrel and cushion deformations of an image produced by an optical lens.
In OpenCV, there are several functions to perform arithmetic and geometrical transforms. Here we show two examples for image addition and perspective transformation by means of the functions addWeighted
and warpPerspective
respectively.
The function addWeighted
performs a linear combination of two images, that is, addition of two weighted images to carry out a linear blending. The function void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1)
has two input images as the first and third parameters with their weights (second and fourth parameter). Then, the output image is the sixth parameter. The fifth parameter, gamma
, is a scalar added to each sum. The last parameter dtype
is optional and refers to the depth of the output image; when both input images have the same depth, it can be set to -1
.
The following LinearBlend
example shows how to perform a linear blending between two images:
#include "opencv2/highgui/highgui.hpp"
using namespace cv;
using namespace std;
int main()
{
double alpha = 0.5, beta, input;
Mat src1, src2, dst;
//Read images (same size and type )
src1 = imread("baboon.jpg");
src2 = imread("lena.jpg");
//Create windows
namedWindow("Final Linear Blend", CV_WINDOW_AUTOSIZE );
//Perform a loop with 101 iteration for linear blending
for(int k = 0; k <= 100; ++k ){
alpha = (double)k/100;
beta = 1 - alpha;
addWeighted( src2, alpha, src1, beta, 0.0, dst );
imshow( "Final Linear Blend", dst );
cvWaitKey(50);
}
namedWindow("Original Image 1", CV_WINDOW_AUTOSIZE );
namedWindow("Original Image 2", CV_WINDOW_AUTOSIZE );
imshow( "Original Image 1", src1 );
imshow( "Original Image 2", src2 );
cvWaitKey(); // Wait for key press
return 0; // End
}
The code explanation is given here: the example first reads two images, src1= baboon.jpg
and src2= lena.jpg
, and then performs a total of 101 linear combinations with different values of the weights alpha
and beta
. The first linear combination or blend is with alpha
equal to zero, and therefore it is the src1
image. The value of alpha
increases in the loop while the value of beta
decreases. Therefore, the src2
image is combined and superimposed onto the src1
image. This produces a morphing effect and the baboon.jpg
image gradually changes into a different image, that is, into lena.jpg
. The following screenshot shows the output of several linear blending steps at iterations 1
, 10
, 20
, 30
, 40
, 50
, 70
, 85
, and 100
:
The function warpPerspective, void ocl::warpPerspective(const oclMat& src, oclMat& dst, const Mat& M, Size dsize, int flags=INTER_LINEAR)
performs a perspective transformation on an image. It has the input or source image src
as the first parameter and the output or destination image dst
as the second parameter. Then, the third parameter is a 2 x 3 transformation matrix obtained from the getPerspectiveTransform
function, which calculates a perspective transform from the positions of four points in the two images in four pairs of corresponding points. The fourth parameter of warpPerspective
is the size of the output image and the last parameter is the interpolation method. By default, the interpolation method is linear, INTER_LINEAR
; other methods supported are nearest neighbor
INTER_NEAREST
and cubic INTER_CUBIC
.
The following Geometrical_Transform
example performs a perspective transformation to the input image img.jpg
.
For full details of the example, refer to N. Amin, Automatic perspective correction for quadrilateral objects, at https://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/.
#include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> using namespace cv; using namespace std; Point2f centerpoint(0,0); Point2f computeIntersect(Vec4i a,Vec4i b){ int x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3]; if (float d = ((float)(x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4))) { Point2f pnt; pnt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; pnt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; return pnt; } else return Point2f(-1, -1); } void sortCorners(vector<Point2f>& corner_points, Point2f centerpoint) { vector<Point2f> top, bot; for (int i = 0; i < corner_points.size(); i++) { if (corner_points[i].y < centerpoint.y) top.push_back(corner_points[i]); else bot.push_back(corner_points[i]); } Point2f tl = top[0].x > top[1].x ? top[1] : top[0]; Point2f tr = top[0].x > top[1].x ? top[0] : top[1]; Point2f bl = bot[0].x > bot[1].x ? bot[1] : bot[0]; Point2f br = bot[0].x > bot[1].x ? bot[0] : bot[1]; corner_points.clear(); corner_points.push_back(tl); corner_points.push_back(tr); corner_points.push_back(br); corner_points.push_back(bl); } int main(){ Mat src = imread("img.jpg"); if (src.empty()) return -1; Mat dst = src.clone(); Mat bw; cvtColor(src, bw, CV_BGR2GRAY); Canny(bw, bw, 100, 100, 3); vector<Vec4i> lines; HoughLinesP(bw, lines, 1, CV_PI/180, 70, 30, 10); vector<Point2f> corner_points; for (int i = 0; i < lines.size(); i++) { for (int j = i+1; j < lines.size(); j++) { Point2f pnt = computeIntersect(lines[i], lines[j]); if (pnt.x >= 0 && pnt.y >= 0) corner_points.push_back(pnt); } } vector<Point2f> approx; approxPolyDP(Mat(corner_points), approx, arcLength(Mat(corner_points), true) * 0.02, true); if (approx.size() != 4) { cout << "The object is not quadrilateral!" << endl; return -1; } //Get center point for (int i = 0; i < corner_points.size(); i++) centerpoint += corner_points[i]; centerpoint *= (1. / corner_points.size()); sortCorners(corner_points, centerpoint); //Draw lines for (int i = 0; i < lines.size(); i++) { Vec4i v = lines[i]; line(dst, Point(v[0], v[1]), Point(v[2], v[3]), CV_RGB(0,255,0)); } //Draw corner points circle(dst, corner_points[0], 3, CV_RGB(255,0,0), 2); circle(dst, corner_points[1], 3, CV_RGB(0,255,0), 2); circle(dst, corner_points[2], 3, CV_RGB(0,0,255), 2); circle(dst, corner_points[3], 3, CV_RGB(255,255,255), 2); //Draw mass center points circle(dst, centerpoint, 3, CV_RGB(255,255,0), 2); //Calculate corresponding points for corner points Mat quad = Mat::zeros(src.rows, src.cols/2, CV_8UC3); vector<Point2f> quad_pnts; quad_pnts.push_back(Point2f(0, 0)); quad_pnts.push_back(Point2f(quad.cols, 0)); quad_pnts.push_back(Point2f(quad.cols, quad.rows)); quad_pnts.push_back(Point2f(0, quad.rows)); // Draw corresponding points circle(dst, quad_pnts[0], 3, CV_RGB(255,0,0), 2); circle(dst, quad_pnts[1], 3, CV_RGB(0,255,0), 2); circle(dst, quad_pnts[2], 3, CV_RGB(0,0,255), 2); circle(dst, quad_pnts[3], 3, CV_RGB(255,255,255), 2); Mat transmtx = getPerspectiveTransform(corner_points, quad_pnts); warpPerspective(src, quad, transmtx, quad.size()); //Create windows and display results namedWindow("Original Image", CV_WINDOW_AUTOSIZE ); namedWindow("Selected Points", CV_WINDOW_AUTOSIZE ); namedWindow("Corrected Perspertive", CV_WINDOW_AUTOSIZE ); imshow("Original Image", src); imshow("Selected Points", dst); imshow("Corrected Perspertive", quad); waitKey(); //Wait for key press return 0; //End }
The code explanation is given here: the example first reads the input image (img.jpg
) and calculates the key points of the region of interest or object to perform the perspective transformation. The key points are the corner points of the object. The algorithm only works for quadrilateral objects. The methods to calculate corners (Canny operator and Hough transforms) are explained in Chapter 4, What's in the Image, Segmentation. The points corresponding to the object corners are the corners of the output image. These points are shown with circles on the original image. The dimension of the output image is set to the same height and half the width of the input image. Finally, the image with the corrected object is visualized. The perspective correction uses a linear transform, INTER_LINEAR
. The following screenshot shows the output of the algorithm: