Arithmetic and geometrical transforms

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.

Arithmetic transform

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:

Arithmetic transform

Output of different lineal blending between two images

Geometrical transforms

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.

Note

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:

Geometrical transforms

Output of the geometrical transform performed to correct the perspective

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset