OpenCV开发笔记(四十六):红胖子8分钟带你深入了解仿射变化(图文并茂+浅显易懂+程序源码)

若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105691534
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)

OpenCV开发专栏(点击传送门)

上一篇:OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)

下一篇:OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)

 

前言

      红胖子来也!!!

      图像处理中的仿射,仿射其实原理与重映射类似,其可以理解为更高级的重映射变换。

 

相关博客

OpenCV开发笔记(八):OpenCV常用操作之计时、缩放、旋转、镜像

该文章中,也同样实现了部分简单重映射效果,使用的四个函数:

  • 旋转函数1:cv::transpose,直接对矩阵进行顺时钟旋转90°
  • 旋转函数2:cv::rotate,三个枚举可以旋转90°,180°,270°
  • 翻转函数:cv::flip,xy轴翻转
  • 缩放函数:cv::resize,缩放

OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)

 

Demo

 

仿射变换

概述

仿射变换(仿射映射)是指在几何中,一个响亮空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。

一个任意的仿射都能表示为乘以一个矩阵再加上一个向量(平移)的形式(注:重映射可以理解为是一种简单的仿射变换)。

  • 旋转:rotation,线性变换;
  • 平移:translation,向量加;
  • 缩放,scale,线性变换;

原理

      仿射是用矩阵表示变换的关系,通常使用的是2 x 3的矩阵来表示仿射变换。

      整个映射关系为:

      dst = src * A + B

其中:

      A为线性变换矩阵,B为向量加矩阵;

      对应的每个像素的变换关系,则是:

      dst(x,y) = src(x,y) * A + B

      下面上图解释仿射(三个点就是函数提供计算仿射矩阵需要我们输入的点):

与重映射原理区别

仿射变换的原理与重映射不同,重映射是直接对像素点的序号进行映射,可以做些拉长,特点像素着重等等(如哈哈镜),而仿射变换是对原矩阵的标准变换,有适合各自的场景。

void warpAffine( InputArray src,
              OutputArray dst,
              InputArray M,
              Size dsize,
              int flags = INTER_LINEAR,
              int borderMode = BORDER_CONSTANT,
              const Scalar& borderValue = Scalar());
  • 参数一:InputArray类型的src,一般为cv::Mat;
  • 参数二:OutputArray类型的dst,目标图像。它的大小、类型与src相同。
  • 参数三:InputArray类型的M,即变换矩阵,大小为2 x 3。
  • 参数四:Size类型的size,表示输出图像的尺寸;
  • 参数五:int类型的interpolation,使用的插值方法;

  • 参数六:int类型的borderMode,边界处理方式;

  • 参数七:Scalar类型的borderValue,重映射后,离群点的背景,需要broderMode设置为BORDER_CONSTRANT时才有效。

计算二维三角变换矩阵函数原型

Mat getAffineTransform( InputArray src,
                     InputArray dst );
  • 参数一:InputArray类型的src,原来三个点的坐标;
  • 参数二:OutputArray类型的dst,映射后三个点的坐标;

计算二维旋转变换矩阵函数原型

Mat getRotationMatrix2D( Point2f center,
                       double angle,
                       double scale );
  • 参数一:Point2f类型的center,输入的旋转中心点;
  • 参数二:double类型的angle,旋转的角度(逆时针旋转角度,非弧度);
  • 参数三:double类型的scale,缩放系数;

 

Demo源码

void OpenCVManager::testAffineMap()
{
    QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());
    cv::Mat dstMat;
    int width = 400;
    int height = 300;

    cv::resize(srcMat, srcMat, cv::Size(width, height));

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);

    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, 
                                         srcMat.rows * 4),
                                srcMat.type());

    while(true)
    {
        windowMat = cv::Scalar(0, 0, 0);

        cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                                cv::Range(srcMat.cols * 0, srcMat.cols * 1));
        cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);

        // 第一种旋转180度
        {
            cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2, 
                                                          srcMat.rows / 2),
                                                180.0f,
                                                1.0f);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }

        // 第二种旋转45度,缩小1/2
        {
            cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2, 
                                                          srcMat.rows / 2),
                                                45.0f,
                                                0.5f);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 第三种旋转315度,缩小1/2
        {
            cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2, 
                                                          srcMat.rows / 2),
                                                315.0f,
                                                0.5f);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 第四种旋转135度,缩小1/2
        {
            cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2, 
                                                          srcMat.rows / 2),
                                                135.0f,
                                                0.5f);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 第五种旋转225度,缩小1/2
        {
            cv::Mat M = cv::getRotationMatrix2D(cv::Point(srcMat.cols / 2, 
                                                          srcMat.rows / 2),
                                                225.0f,
                                                0.5f);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 第六种使用三角点进行仿射变换,沿着对角线翻转
        {
            cv::Point2f srcTraingle[3];
            cv::Point2f dstTraingle[3];
            srcTraingle[0] = cv::Point2f(0, 0);
            srcTraingle[1] = cv::Point2f(srcMat.cols - 1, 0);
            srcTraingle[2] = cv::Point2f(0, srcMat.rows - 1);
            dstTraingle[0] = cv::Point2f(0, 0);
            dstTraingle[1] = cv::Point2f(0, srcMat.rows - 1);
            dstTraingle[2] = cv::Point2f(srcMat.cols - 1, 0);

            cv::Mat M = cv::getAffineTransform(srcTraingle, dstTraingle);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 第七种使用三角点进行仿射变换
        {
            cv::Point2f srcTraingle[3];
            cv::Point2f dstTraingle[3];
            srcTraingle[0] = cv::Point2f(0, 0);
            srcTraingle[1] = cv::Point2f(srcMat.cols - 1, 0);
            srcTraingle[2] = cv::Point2f(0, srcMat.rows - 1);
            dstTraingle[0] = cv::Point2f(srcMat.cols / 4, srcMat.rows / 4);
            dstTraingle[1] = cv::Point2f(srcMat.cols / 4 * 3, srcMat.rows / 4 );
            dstTraingle[2] = cv::Point2f(srcMat.cols / 2, srcMat.rows - 1);

            cv::Mat M = cv::getAffineTransform(srcTraingle, dstTraingle);
            dstMat = srcMat.clone();
            dstMat = cv::Scalar(0, 0, 0);
            cv::warpAffine(srcMat, dstMat, M, cv::Size(srcMat.cols, srcMat.rows));
            mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 更新
        cvui::update();
        // 显示
        cv::imshow(windowName, windowMat);
        // esc键退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

 

工程模板:对应版本号v1.41.0

      对应版本号v1.41.0

 

上一篇:OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)

下一篇:OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)


原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105691534

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值