【OpenCV】-重映射

简介: 【OpenCV】-重映射
1、重映射的概念

重映射是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。为了完成重映射过程,需要获得一些插值为非整数像素的坐标,因为源图像与目标图像像素坐标不是一一对应的。如下:

g(x,y)=f(h(x,y))

g()是目标图像,f()是源图像,而h(x,y)是作用于(x,y)的映射方法函数。

2、实现重映射:remap()函数

remap()函数会根据指定的映射形式,将源图像进行重映射几何变换:

dst(x,y)=src(mapx(x,y),mapy(x,y))

void remap(InputArray src,OutputArray dst,InputArray map1,InputArray map2,int interpolation,intborderMode=BORDER_CONSTANT,const Scalar& borderValue=Scalar())

第一个参数:输出图像

第二个参数:函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型

第三个参数:InputArray类型的 map1,有两种可能的表示对象

表示点(x,y)的第一映射

表示CV_16SC2,CV_32FC1,CV_32FC2类型的X值

第四个参数:InputArray类型的 map2,有两种可能的表示对象

若map1表示点(x,y)时,这个参数不代表任何值

表示CV_16UC1,CV_32FC1类型的Y值

第五个参数:int 类型的interpolation,插值方式,所选的插值方式如下:

INTER_NEAREST——最近邻插值

INTER_LINEAR——双线性插值(默认值)

INTER_CUBIC——双三次样条插值

INTER_LANCZOS4——Lanczos插值

第六个参数:边界模式

第七个参数:const Scalar&类型的borderValue,当有常数边界使用的值,其默认值Scalar(),即默认值为0

3、基本重映射

说明:下面是精简后的以remap函数为核心的示例程序

#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
  Mat srcImage, dstImage;
  Mat map_x, map_y;
  //载入原始图像
  srcImage = imread("E:\\Pec\\car.jpg", 1);
  imshow("【原始图】", srcImage);
  //创建和原始图一样的效果图,x重映射图,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
  for (int j = 0; j < srcImage.rows; j++)
  {
    for (int i = 0; i < srcImage.cols; i++)
    {
            //通过at获取像素值
      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(0);
}

4、实现多重映射
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage;
Mat g_map_x, g_map_y;
int update_map(int key);
static void ShowHelpText();
int main()
{
  system("color 2F");
  ShowHelpText();
  //载入原始图像
  g_srcImage = imread("E:\\Pec\\car.jpg", 1);
  imshow("【原始图】", g_srcImage);
  //创建和原始图一样的效果图,x重映射图,y重映射图
  g_dstImage.create(g_srcImage.size(), g_srcImage.type());
  g_map_x.create(g_srcImage.size(), CV_32FC1);
  g_map_y.create(g_srcImage.size(), CV_32FC1);
  namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);
  imshow(WINDOW_NAME, g_srcImage);
  while (1)
  {
    int key = waitKey(0);
    if ((key & 255) == 27)
    {
      cout << "程序退出......" << endl;
      break;
    }
    update_map(key);
    //根据按下键盘按键来更新map_x & map_y的值,然后调用remap()进行重映射
    remap(g_srcImage, g_dstImage, g_map_x, g_map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
    imshow(WINDOW_NAME, g_dstImage);
  }
  return 0;
}
int update_map(int key)
{
  //双层循环,遍历每一个像素点,改变map_x和Map_y
  for (int j = 0; j < g_srcImage.rows; j++)
  {
    for (int i = 0; i < g_srcImage.cols; i++)
    {
      switch (key)
      {
        case '1'://按键1按下,进行第一重映射操作
          if (i > g_srcImage.cols*0.25&&i<g_srcImage.cols*0.75&&j>g_srcImage.rows*0.25&&j < g_srcImage.rows*0.75)
          {
            g_map_x.at<float>(j, i) = 2 * (i - g_srcImage.cols*0.25) + 0.5;
            g_map_y.at<float>(j, i) = 2 * (j - g_srcImage.rows*0.25) + 0.5;
          }
          else
          {
            g_map_x.at<float>(j, i) = 0;
            g_map_y.at<float>(j, i) = 0;
          }
          break;
        case '2': //按键2按下,进行第二重映射操作
          g_map_x.at<float>(j, i) = static_cast<float>(i);
          g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j);
          break;
        case '3':
          g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols-i);
          g_map_y.at<float>(j, i) = static_cast<float>(j);
          break;
        case '4':
          g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols - i);
          g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j);
          break;
      }
    }
  }
  return 1;
}
static void ShowHelpText()
{
  cout << "\n\n\n\t欢迎来到重映射示例程序\n";
  cout << "\n\n\n\t按键操作如下:" ;
  cout << "\n\n\n\t键盘按下【ESC】-退出程序" ;
  cout << "\n\n\n\t键盘按下【1】-第一种映射方式,缩小2倍" ;
  cout << "\n\n\n\t键盘按下【2】-第二种映射方式,y方向翻转" ;
  cout << "\n\n\n\t键盘按下【3】-第三种映射方式,x方向反转" ;
  cout << "\n\n\n\t键盘按下【4】-第四种映射方式,x,y方向反转" ;
}

第一种映射方式,缩小2倍

第二种映射方式,y方向翻转

第三种映射方式,x方向反转

第四种映射方式,x,y方向反转

相关文章
|
存储 Linux 开发工具
Rockchip系列之浅度分析UART接口系列(1)
Rockchip系列之浅度分析UART接口系列(1)
1282 1
|
机器学习/深度学习 人工智能 数据可视化
【好物分享】onnx-modifier:可视化操作助力模型更改,让你不再为更改模型烦恼!
【好物分享】onnx-modifier:可视化操作助力模型更改,让你不再为更改模型烦恼!
2182 0
【好物分享】onnx-modifier:可视化操作助力模型更改,让你不再为更改模型烦恼!
h264编码一帧所用时间
h264编码一帧所用时间
507 0
h264编码一帧所用时间
|
存储 Shell Linux
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
【Shell 命令集合 系统设置 】Linux 生成并更新内核模块的依赖 depmod命令 使用指南
388 0
|
存储 API 图形学
Unity精华☀️二、到底是什么原因导致“万向锁”?旋转翻车的终极解析!
Unity精华☀️二、到底是什么原因导致“万向锁”?旋转翻车的终极解析!
磁盘爆满如何解决?
磁盘爆满如何解决:df -h 查看磁盘空间的使用情况,释放磁盘空间、增加磁盘空间
|
Web App开发 网络协议 Android开发
Android平台一对一音视频通话方案大比拼:WebRTC VS RTMP VS RTSP,谁才是王者?
【9月更文挑战第4天】本文详细对比了在Android平台上实现一对一音视频通话时常用的WebRTC、RTMP及RTSP三种技术方案。从技术原理、性能表现与开发难度等方面进行了深入分析,并提供了示例代码。WebRTC适合追求低延迟和高质量的场景,但开发成本较高;RTMP和RTSP则在简化开发流程的同时仍能保持较好的传输效果,适用于不同需求的应用场景。
1092 2
|
网络安全
openstack云平台创建卷以及使用卷挂载
在阿里云上,创建一个名为test-lv的10GB卷,并与实例关联。通过SSH登录,检查磁盘,进行分区和格式化,挂载到/mnt并写入test文件。当卷空间不足时,先分离卷,然后扩展至15GB。重新连接实例,发现原有数据丢失,需重新分区和格式化。重启云主机后,新扩容的5GB显示为/vdb2。
470 1
openstack云平台创建卷以及使用卷挂载
|
机器学习/深度学习 人工智能 PyTorch
LLM 大模型学习必知必会系列(四):LLM训练理论篇以及Transformer结构模型详解
LLM 大模型学习必知必会系列(四):LLM训练理论篇以及Transformer结构模型详解
LLM 大模型学习必知必会系列(四):LLM训练理论篇以及Transformer结构模型详解

热门文章

最新文章