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

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

作者: Mr_Relu | 来源:发表于2019-05-11 23:37 被阅读0次

    编程环境:

    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;
    }
    

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/ujfqaqtx.html