OpenCV 实现重新映射(53)

返回:OpenCV系列文章目录(持续更新中......)

上一篇:OpenCV 实现霍夫圆变换(52)
下一篇 :OpenCV实现仿射变换(54)

目标

在本教程中,您将学习如何:

一个。使用 OpenCV 函数 cv::remap 实现简单的重新映射例程。

理论

什么是重映射?

  • 它是从图像中的一个位置获取像素并将它们定位在新图像中的另一个位置的过程。

  • 为了完成映射过程,可能需要对非整数像素位置进行一些插值,因为源图像和目标图像之间并不总是存在一对一的像素对应关系。

  • 我们可以表示每个像素位置的重新映射(x,y)如:

    哪里g()是重新映射的图像,f()源图像和ℎ(x,y)是操作的映射函数(x,y).

  • 让我们举个简单的例子。想象一下,我们有一个图像我而且,比如说,我们想做一个重新映射,以便:

    会发生什么?很容易看出,图像会在x方向。例如,考虑输入图像:

观察红色圆圈相对于 x 如何改变位置(考虑到x水平方向):

  • 在 OpenCV 中,函数 cv::remap 提供了一个简单的重新映射实现。

C++代码

  • 这个程序是做什么的?

    • 加载图像
    • 每秒将 4 个不同的重新映射过程中的 1 个应用于图像,并在窗口中无限期地显示它们。
    • 等待用户退出程序
    • 教程代码如下所示。您也可以从这里下载
    cpp 复制代码
     
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    #include <iostream>
     
    using namespace cv;
     
    void update_map( int &ind, Mat &map_x, Mat &map_y );
     
    int main(int argc, const char** argv)
    {
     CommandLineParser parser(argc, argv, "{@image |chicky_512.png|input image name}");
     std::string filename = parser.get<std::string>(0);
     Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );
     if (src.empty())
     {
     std::cout << "Cannot read image: " << filename << std::endl;
     return -1;
     }
     
     Mat dst(src.size(), src.type());
     Mat map_x(src.size(), CV_32FC1);
     Mat map_y(src.size(), CV_32FC1);
     
     const char* remap_window = "Remap demo";
     namedWindow( remap_window, WINDOW_AUTOSIZE );
     
     int ind = 0;
     for(;;)
     {
     update_map(ind, map_x, map_y);
     remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );
     
     imshow( remap_window, dst );
     
     char c = (char)waitKey( 1000 );
     if( c == 27 )
     {
     break;
     }
     }
     return 0;
    }
     
    void update_map( int &ind, Mat &map_x, Mat &map_y )
    {
     for( int i = 0; i < map_x.rows; i++ )
     {
     for( int j = 0; j < map_x.cols; j++ )
     {
     switch( ind )
     {
     case 0:
     if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 )
     {
     map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;
     map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;
     }
     else
     {
     map_x.at<float>(i, j) = 0;
     map_y.at<float>(i, j) = 0;
     }
     break;
     case 1:
     map_x.at<float>(i, j) = (float)j;
     map_y.at<float>(i, j) = (float)(map_x.rows - i);
     break;
     case 2:
     map_x.at<float>(i, j) = (float)(map_x.cols - j);
     map_y.at<float>(i, j) = (float)i;
     break;
     case 3:
     map_x.at<float>(i, j) = (float)(map_x.cols - j);
     map_y.at<float>(i, j) = (float)(map_x.rows - i);
     break;
     default:
     break;
     } // end of switch
     }
     }
     ind = (ind+1) % 4;
    }

解释

  • 加载图像

    cpp 复制代码
     
     Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );
     if (src.empty())
     {
     std::cout << "Cannot read image: " << filename << std::endl;
     return -1;
     }
  • 创建目标映像和两个映射矩阵(对于 x 和 y )

    cpp 复制代码
    Mat dst(src.size(), src.type());
     Mat map_x(src.size(), CV_32FC1);
     Mat map_y(src.size(), CV_32FC1);
  • 创建一个窗口以显示结果

    cpp 复制代码
     const char* remap_window = "Remap demo";
     namedWindow( remap_window, WINDOW_AUTOSIZE );
  • 建立循环。每隔 1000 毫秒,我们就会更新映射矩阵(mat_xmat_y),并将它们应用于我们的源图像:

    cpp 复制代码
     int ind = 0;
     for(;;)
     {
     update_map(ind, map_x, map_y);
     remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );
     
     imshow( remap_window, dst );
     
     char c = (char)waitKey( 1000 );
     if( c == 27 )
     {
     break;
     }
     }
cpp 复制代码
void update_map( int &ind, Mat &map_x, Mat &map_y )
{
 for( int i = 0; i < map_x.rows; i++ )
 {
 for( int j = 0; j < map_x.cols; j++ )
 {
 switch( ind )
 {
 case 0:
 if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 )
 {
 map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;
 map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;
 }
 else
 {
 map_x.at<float>(i, j) = 0;
 map_y.at<float>(i, j) = 0;
 }
 break;
 case 1:
 map_x.at<float>(i, j) = (float)j;
 map_y.at<float>(i, j) = (float)(map_x.rows - i);
 break;
 case 2:
 map_x.at<float>(i, j) = (float)(map_x.cols - j);
 map_y.at<float>(i, j) = (float)i;
 break;
 case 3:
 map_x.at<float>(i, j) = (float)(map_x.cols - j);
 map_y.at<float>(i, j) = (float)(map_x.rows - i);
 break;
 default:
 break;
 } // end of switch
 }
 }
 ind = (ind+1) % 4;
}

结果

  1. 编译上面的代码后,您可以执行它,并给出一个图像路径作为参数。例如,使用下图:
  1. 这是将其减小到一半大小并居中的结果:
  1. 把它颠倒过来:
  1. 在 x 方向上反映它
  1. 在两个方向上反映它:

参考文献:

1、《Remapping》------Ana Huamán

相关推荐
水如烟3 分钟前
孤能子视角:三十六计之金蝉脱壳——中断与撤离
人工智能
月光船幽幽4 分钟前
构建多节点耦合动力学仿真框架
人工智能·科技·拓扑学
一个小猴子`12 分钟前
CUDA全局内存
c++·cuda编程
ysa05103012 分钟前
【板子】树上启发式合并
数据结构·c++·笔记·算法
私人珍藏库21 分钟前
[Android] 聊天记录微备份 -聊天记录备份+本地数据备份
android·人工智能·app·软件·多功能
C137的本贾尼23 分钟前
第15章:模型的好与坏——过拟合、欠拟合与验证
人工智能
咖啡屋和酒吧27 分钟前
干细胞认知偏差纠正:回归科学本质的三大方向
人工智能·回归·精选
Simple_core27 分钟前
cmake模板和问题总结
c++
无情的西瓜皮29 分钟前
MCP 协议实战:从零搭建一个 MCP 服务器(Node.js 版)
vue.js·人工智能·typescript·node.js
学究天人31 分钟前
数学公理体系大全:第一章 命题逻辑:真值之舞
人工智能·算法·机器学习·数学建模·动态规划·图论·抽象代数