若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105633350
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
目录
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门).
OpenCV开发专栏(点击传送门)
OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)
前言
红胖子,来也!!!
本篇章讲解重映射。
相关博客
图像检测中对图像进行旋转、镜像等操作是可以对原尺寸进行操作的,有专门的旋转、翻转函数进行处理。
《OpenCV开发笔记(八):OpenCV常用操作之计时、缩放、旋转、镜像》
该文章中,也同样实现了部分简单重映射效果,使用的四个函数:
- 旋转函数1:cv::transpose,直接对矩阵进行顺时钟旋转90°
- 旋转函数2:cv::rotate,三个枚举可以旋转90°,180°,270°
- 翻转函数:cv::flip,xy轴翻转
- 缩放函数:cv::resize,缩放
Demo
重映射
概述
重映射,按照与原图一样大小的尺寸,通过像素下标的序列号的矩阵进行映射得到结果,简而言之,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。
X轴旋转/Y轴旋转/XY轴旋转原理示意图
缩放1/2原理示意图
倾斜45度示意图
函数原型
void remap( InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
- 参数一:InputArray类型的src,一般为cv::Mat;
- 参数二:OutputArray类型的dst,目标图像。它的大小与map1相同,类型与src相同。
- 参数三:InputArray类型的map1,它有两种可能的表示对象:表示点(x,y)的第一个映射或者表示CV_16SC2 , CV_32FC1 或CV_32FC2类型的x值。
- 参数四:InputArray类型的map2,它也有两种可能的表示对象,而且他是根据map1来确定表示哪种对象。若map1表示点(x,y)时,这个参数不代表任何值,否则,表示CV_16UC1 , CV_32FC1类型的y值(第二个值)。
- 参数五:int类型的interpolation,使用的插值方法;
- 参数六:int类型的borderMode,边界处理方式;
- 参数七:Scalar类型的borderValue,重映射后,离群点的背景,需要broderMode设置为BORDER_CONSTRANT时才有效。(离群点:当图片大小为400x300,那么对应的map1和map2范围为0~399、0~299,小于0或者大于299的则为离散点,使用该颜色填充);
Demo源码
void OpenCVManager::testRemap()
{
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);
cv::Mat mapX;
cv::Mat mapY;
// 第一种x翻转
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>(col);
mapY.at<float>(row, col) = static_cast<float>(srcMat.rows - row - 1);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第二种y翻转
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>(srcMat.cols - col - 1);
mapY.at<float>(row, col) = static_cast<float>(row);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第三种x,y翻转
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>(srcMat.cols - col - 1);
mapY.at<float>(row, col) = static_cast<float>(srcMat.rows - row - 1);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第四种:右半边拉伸一倍
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>(col / 2);
mapY.at<float>(row, col) = static_cast<float>(row);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第五种,缩小1/2,并显示在中间,底色为蓝色
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
if(col < srcMat.cols / 4 || row < srcMat.rows / 4)
{
mapX.at<float>(row, col) = -1;
mapY.at<float>(row, col) = -1;
}else if(col >= srcMat.cols / 4 * 3 || row >= srcMat.rows / 4 * 3)
{
mapX.at<float>(row, col) = -1;
mapY.at<float>(row, col) = -1;
}else{
mapX.at<float>(row, col) = static_cast<float>((col - srcMat.cols / 4) * 2);
mapY.at<float>(row, col) = static_cast<float>((row - srcMat.rows / 4) * 2);
}
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第六种,矩阵旋转90度(输出必须和输入大小一样,此时可以使用resize缩放至快高对调的比例)
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>(row);
mapY.at<float>(row, col) = static_cast<float>(col);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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);
}
// 第七种,顺时针倾斜45°
{
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
mapX.at<float>(row, col) = static_cast<float>((col + (srcMat.cols * 2 - (srcMat.rows - row) - 1)) % srcMat.cols);
mapY.at<float>(row, col) = static_cast<float>(row);
}
}
dstMat.create(srcMat.size(), srcMat.type());
cv::remap(srcMat,
dstMat,
mapX,
mapY,
CV_INTER_LINEAR,
cv::BORDER_CONSTANT,
cv::Scalar(255, 0, 0));
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.40.0
对应版本号v1.40.0
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105633350