OpenCV探索之路(八):重映射与仿射变换

重映射

重映射就是把一幅图像中某个位置的像素放置到另一个图片中指定位置的过程。

用一个数学公式来表示就是:

其中的 f 就是映射方式,也就说,像素点在另一个图像中的位置是由 f 来计算的。

在OpenCV中,用的是remap函数实现重映射。

基本重映射

#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>

using namespace cv;
using namespace std;

//基本重映射实验

int main()
{
    Mat srcImage = imread("2.jpg");

    if (!srcImage.data)
    {
        cout << "找不到这张图片!" << endl;
        return -1;
    }

    imshow("Src Pic", srcImage);

    Mat dstImage, map_x, map_y;
    dstImage.create(srcImage.size(), srcImage.type());//创建和原图一样的效果图
    map_x.create(srcImage.size(), CV_32FC1);
    map_y.create(srcImage.size(), CV_32FC1);

    //遍历每一个像素点,改变map_x & map_y的值,实现翻转180度
    for (int j = 0; j < srcImage.rows; j++)
    {
        for (int i = 0; i < srcImage.cols; i++)
        {
            map_x.at<float>(j, i) = static_cast<float>(i);
            map_y.at<float>(j, i) = static_cast<float>(srcImage.rows - j);
        }
    }

    //进行重映射操作
    remap(srcImage, dstImage, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
    imshow("重映射效果图", dstImage);  

    waitKey();
    return 0;
}

map_x与map_y分别代表目标图中的(x,y)点在原图中的x坐标(由map_x提供)与y坐标(由map_y提供)。

运行效果,图像翻转了。

仿射变换

仿射变换指的是一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。

图像进行仿射变换后,有以下几个特点:
二维图形之间的相对位置关系保持不变,平行线依旧是平行线,且直线上的点的位置顺序保持不变。

一个任意的仿射变换都可以表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。

三种常见形式:

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

仿射变换本质是一个2* 3的矩阵M乘上原图的每个坐标,得到目标图的对应点坐标。2*3矩阵M中的2表示目标点坐标的x与y,3中的第三维是平移分量。因此需要做的就是找到矩阵M,OpenCV提供 getAffineTransform 求出仿射变换, getRotationMatrix2D 来获得旋转矩阵。

这里简单说说仿射变换是怎么做到的。

现在有两幅图像(如下图),图像二是图像一经过放射变化得来的。那问题来了,我们怎么从这两个图像信息里挖掘出两图之间的映射关系?

很简单,只要在图像一种拿出三个点(1,2,3),图像二也拿出对应的三个点(1,2,3),就可以求出两图间的映射关系!

OpenCV通过两个函数的组合使用来实现仿射变换:

  • 使用warpAffine来实现简单重映射
  • 使用getRotationMatrix2D来获得旋转矩阵

#include<iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>  

using namespace std;
using namespace cv;
//仿射变换实验
int main()
{
    Mat src = imread("lol9.jpg");
    Mat dst_warp, dst_warpRotateScale;
    Point2f srcPoints[3];//原图中的三点
    Point2f dstPoints[3];//目标图中的三点  

    //第一种仿射变换的调用方式:三点法
    //三个点对的值,上面也说了,只要知道你想要变换后图的三个点的坐标,就可以实现仿射变换
    srcPoints[0] = Point2f(0, 0);
    srcPoints[1] = Point2f(0, src.rows - 1);
    srcPoints[2] = Point2f(src.cols - 1, 0);
    //映射后的三个坐标值
    dstPoints[0] = Point2f(0, src.rows*0.3);
    dstPoints[1] = Point2f(src.cols*0.25, src.rows*0.75);
    dstPoints[2] = Point2f(src.cols*0.75, src.rows*0.25);

    Mat M1 = getAffineTransform(srcPoints, dstPoints);//由三个点对计算变换矩阵
    warpAffine(src, dst_warp, M1, src.size());//仿射变换  

    //第二种仿射变换的调用方式:直接指定角度和比例
    //旋转加缩放
    Point2f center(src.cols / 2, src.rows / 2);//旋转中心
    double angle = 45;//逆时针旋转45度
    double scale = 0.5;//缩放比例  

    Mat M2 = getRotationMatrix2D(center, angle, scale);//计算旋转加缩放的变换矩阵
    warpAffine(dst_warp, dst_warpRotateScale, M2, src.size());//仿射变换  

    imshow("原始图", src);
    imshow("仿射变换1", dst_warp);
    imshow("仿射变换2", dst_warpRotateScale);
    waitKey(0);

    return 0;
}

两种仿射变换的效果如下。

有没有发现图片进行仿射变换后的背景被填充为黑色了?其实这个背景色是可以调的,像这样:

    warpAffine(dst_warp, dst_warpRotateScale, M2, src.size(), 1, 0, Scalar(11,111, 211));//利用Scalar来填充不同颜色背景

然后背景色就变成这样子了:

最后写一个对图片旋转任何角度的代码。

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define PIC_BEGIN_NUM 100  //这里定义你的起始图片编号
#define ANGLE_START -45  //旋转角度的开始
#define ANGLE_END  0  //旋转角度的结束
#define ANGLE_STEP 2 //旋转角度步长

int main(int argc, char **argv)
{
    //Read a single-channel image
    const char* filename = "lol9.jpg";
    Mat srcImg = imread(filename, 1);
    imshow("source", srcImg);
    Point center(srcImg.cols / 2, srcImg.rows / 2); //图片中心为旋转点
    char file[20];
    int count = PIC_BEGIN_NUM;
    Mat tmpimg;
    for (int tmp = ANGLE_START; tmp < ANGLE_END; tmp += ANGLE_STEP)
    {
        Mat rotMatS = getRotationMatrix2D(center, tmp, 0.5); //图片缩小到原来的0.5倍
        warpAffine(srcImg, tmpimg, rotMatS, srcImg.size(), 1, 0, Scalar(0, 0, 0));//填充黑色背景
        sprintf(file, "%d.jpg", count++);  //旋转图片以1.jpg  2.jpg 的名字格式保存
        imwrite(file, tmpimg);
    }

    waitKey(0);

    return 0;
}

这里的代码实现对图片旋转2度、4度...45度的功能,并将这些旋转后的图像保存先来。
然后文件夹下就出现旋转好的图片了!

时间: 2024-10-07 08:47:51

OpenCV探索之路(八):重映射与仿射变换的相关文章

Opencv图像识别从零到精通(30)---重映射,仿射变换

一.序言 面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换: 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标.这时就需要灰度级差值将映射的新坐标匹配到输出像素之间.最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿.这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值. 除了插值算法感觉只要了解就可以了,图像处理中比较需要理

【opencv入门】重映射 &amp; SURF特征点检测合辑

一.OpenCV重映射 1.重映射的概念简析 重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程.为了完成映射过程, 我们需要获得一些插值为非整数像素的坐标,因为源图像与目标图像的像素坐标不是一一对应的.一般情况下,我们通过重映射来表达每个像素的位置 (x,y),像这样 : g(x,y) = f ( h(x,y) ) 在这里, g() 是目标图像, f() 是源图像, 而h(x,y) 是作用于 (x,y) 的映射方法函数. 来看个例子.若有一幅图像 I ,想满足下面的条件作重映射

【OpenCV入门教程之十七】OpenCV重映射 &amp; SURF特征点检测合辑

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 本篇文章中,我们一起探讨了OpenCV中

【OpenCV新手教程之十七】OpenCV重映射 &amp;amp; SURF特征点检測合辑

本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/30974513 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本号: 2.4.9 本篇文章中,我们一起探讨了OpenCV

OpenCV2.4.10之samples_cpp_tutorial-code_learn-----ImgTrans(Laplace边缘检测和Sobel边缘检测,图像重映射)

本系列学习笔记参考自OpenCV2.4.10之opencv\sources\samples\cpp\tutorial_code和http://www.opencv.org.cn/opencvdoc/2.3.2/html/genindex.html 在图像处理中,往往需要对图像提取有效的边缘.本博文将介绍Laplace边缘检测和Sobel边缘检测,以及图像的重映射. 1.Laplace_Demo.cpp(Laplace边缘检测) Demo源码及注释如下: #include "stdafx.h&qu

hi3531的pcie atu资源重映射

1. 设置ATU 区域号寄存器为需要配置的地址转换区编号. 2. 设置ATU Region Lower Base Address Register 和ATU Region Upper Base Address Register.(在此区域内的目标地址将由区域号寄存器所在的ATU 转换) 3. 设置ATU Region Limit Address Register. 4. 设置ATU Region Lower Target Address Register 和ATU Region Upper Ta

ros名称、命名空间和重映射

一 概论 名称就是代号,ros中的节点.话题和参数的名称必须是唯一的,这很容易想到,如果你认识两个叫一样名字的人,单凭一个名字你是分辨不出来说的这个人到底是谁,所以 ros中的名称必须是唯一的. 但是,这个规定与现实不符啊,分明现实中就有这种情况啊,所以我们很容易想到两种办法 1 把两个人的名字前加点东西,如三班的A和四班的A(命名空间) 2 分别叫这两个人的小名,或者绰号.(重映射) 通过这种方式就可以解决名字冲突的问题. ros中这两种方式分别叫做命名空间和重映射. 二 命名空间 我们在安装

存储器的重映射

存储器的重映射的原因是什么? 重映射到底是怎样进行映射的? 如果不进行重映射会有什么影响? 存储器的重映射,布布扣,bubuko.com

STM32重映射(PinRemap)的使用,注意!

STM32重映射,内容和细节稍后补充,这里只说几个注意点,花了我一晚上的时间调试终于找到问题所在了... 芯片: STM32f107vct6 晶振: 25M 通过分频器与锁相环,使系统时钟为72M 背景: SPI3的重映射使用 一. 重映射函数原型: void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) STM32f107vct6不是FPGA,重映射是固定的,无法自行指定,要查表,看看映射到了哪 比如SPI