CV图像基本操作【3】——仿射变换与变形(affine tran

2019-05-11  本文已影响0人  Mr_Relu

编程环境:

VS + OpenCV + C++
完整代码已经更新至GitHub,欢迎fork~GitHub链接


声明:创作不易,未经授权不得复制转载
statement:No reprinting without authorization


内容

-设计一个函数WarpAffine,可以对图像进行任意的二维仿射变换(用2*3矩阵表示);
-采用双线性插值进行重采样;
-可以只考虑输入图像为3通道,8位深度的情况;
-函数接口可以参考OpenCV的warpAffine函数;
-调用WarpAffine,实现绕任意中心的旋转函数Rotate;

原理简介

记[x’, y’]=f([x, y])为像素坐标的一个映射,实现f所表示的图像形变。f的逆映射为:


image.png
image.png
image.png

一、仿射变换具体实现

1. 参考opencv定义函数接口等,如下:
image.png

-my_warpAffine,可以对图像进行任意的二维仿射变换(用2*3数组表示矩阵),
-CalcRotationMatrix,函数得到进行指定变换的数组;
//注意二维数组应定义成double类型

2. 对于绕任意中心进行旋转,需要求取正变换的逆矩阵,正变换矩阵如下:
image.png
image.png
3. 而后设计双线性插值的实现:
image.png
由于映射辉源图坐标后可能会发生越界,需注意处理:异常如下:
image.png
处理越界code
if (x_ < 0 || y_ < 0||x_>=src.rows||y_>=src.cols) {
                for (int c = 0; c < 3; c++) {
                    dst.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(0);
                }
            }
            else {
                for (int c = 0; c < 3; c++) {
                    //计算双线性插值
                    //左上角坐标(X1,Y1)
                    int X1 = (int)x_;
                    int Y1 = (int)y_;
                    //四个顶点像素值
                    //注意访问越界
                    if (X1 == (src.rows - 1) || Y1 == (src.cols - 1)||X1==0||Y1==0) {
                        dst.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(src.at<Vec3b>(X1, Y1)[c]);
                    }
                    else {
                        int aa = src.at<Vec3b>(X1, Y1)[c];
                        int bb = src.at<Vec3b>(X1, Y1+1)[c];
                        int cc = src.at<Vec3b>(X1+1, Y1)[c];
                        int dd = src.at<Vec3b>(X1+1, Y1+1)[c];

                        double dx = x_ - X1;
                        double dy = y_ - Y1;
                        double h1 = aa + dx * (bb - aa);
                        double h2 = cc + dx * (dd - cc);
                        dst.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(h1+dy*(h2-h1));
                    } 
                }
4. 测试结果如下:
image.png
image.png
image.png

二、变形具体实现

函数设计:

Mat changeShape(const Mat &src)

注意中心归一化后和还原:
image.png

结果测试如下:


image.png
image.png

code

Mat changeShape(const Mat &src) {
    Mat imgAffine = Mat::zeros(src.rows, src.cols, src.type()); 
    int row = imgAffine.rows, col = imgAffine.cols;
    for (int x = 0; x < row; x++) {
        for (int y = 0; y < col; y++) {
            
            double X = x / ((row - 1) / 2.0) - 1.0;
            double Y = y / ((col - 1) / 2.0) - 1.0;
            double r = sqrt(X * X + Y * Y);
            
            if (r >= 1) {
                imgAffine.at<Vec3b>(x, y)[0] = saturate_cast<uchar>(src.at<Vec3b>(x, y)[0]);
                imgAffine.at<Vec3b>(x, y)[1] = saturate_cast<uchar>(src.at<Vec3b>(x, y)[1]);
                imgAffine.at<Vec3b>(x, y)[2] = saturate_cast<uchar>(src.at<Vec3b>(x, y)[2]);
            }
            else {

                double theta = 1.0 + X * X + Y * Y - 2.0*sqrt(X * X + Y * Y);//修改不用(1-r)*(1-r)
                double x_ = cos(theta)*X - sin(theta)*Y;
                double y_ = sin(theta)*X + cos(theta)*Y;

                x_ = (x_ + 1.0)*((row - 1) / 2.0);
                y_ = (y_ + 1.0)*((col - 1) / 2.0);

                
                if (x_ < 0 || y_ < 0||x_>=src.rows||y_>=src.cols) {
                    for (int c = 0; c < 3; c++) {
                        imgAffine.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(0);
                    }
                }
                else {
                    //左上角坐标(X1,Y1)
                    //计算双线性插值   
                    int X1 = (int)x_;
                    int Y1 = (int)y_;

                    for (int c = 0; c < 3; c++) {   
                        if (X1 == (src.rows - 1) || Y1 == (src.cols - 1)) {
                            imgAffine.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(src.at<Vec3b>(X1, Y1)[c]);
                        }
                        else {
                            //四个顶点像素值
                            //注意访问越界
                            int aa = src.at<Vec3b>(X1, Y1)[c];
                            int bb = src.at<Vec3b>(X1, Y1 + 1)[c];
                            int cc = src.at<Vec3b>(X1 + 1, Y1)[c];
                            int dd = src.at<Vec3b>(X1 + 1, Y1 + 1)[c];

                            double dx = x_ - (double)X1;
                            double dy = y_ - (double)Y1;
                            double h1 = aa + dx * (bb - aa);
                            double h2 = cc + dx * (dd - cc);
                            imgAffine.at<Vec3b>(x, y)[c] = saturate_cast<uchar>(h1 + dy * (h2 - h1));
                        }
                    }
                }
            }   
        }
    }
    return imgAffine;
}
上一篇下一篇

猜你喜欢

热点阅读