Opencv-C++笔记 (15) : 像素重映射 与 图像扭曲
文章目录
- 一、重映射简介
- 二、图像扭曲
一、重映射简介
重映射,就是把一幅图像中某位置的像素放置到另一图像指定位置的过程。即:
在重映射过程中,图像的大小也可以同时发生改变。此时像素与像素之间的关系就不是一一对应关系,因此在重映射过程中,可能会涉及到像素值的插值计算。
Remap(
InputArray src, 输入图像(灰度图或真彩图均可)
OutputArray dst, 输出图像(要求大小和xmap,ymap相同,通道数目及数据类型和src相同)
InputArray map1, x 映射表 CV_32FC1/CV_32FC2
InputArray map2, y 映射表
int interpolation, 选择的插值方法,常见线性插值,可选择立方等
int borderMode, BORDER_CONSTANT
const Scalar borderValue color
)
头文件 quick_opencv.h:声明类与公共函数
#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;class QuickDemo {
public:...void remap_Demo(Mat& image1);void MLS(Mat& src, std::vector<Point> p, std::vector<Point> q);void MLS(Mat& src, int* p, int* q, int rows, int cols);
};
主函数调用该类的公共成员函数
#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;int main(int argc, char** argv) {Mat src = imread("D:\\Desktop\\pandas_small22.png");if (src.empty()) {printf("Could not load images...\n");return -1;}QuickDemo qk;qk.remap_Demo(src);vector<Point> p{Point(30, 147), Point(147, 147), Point(268, 147), Point(112, 148),Point(186, 148), Point(98, 316), Point(211, 316)};vector<Point> q{ Point(28, 209), Point(126, 143), Point(282, 26), Point(71, 236), Point(136, 240), Point(79, 313), Point(190, 310)};qk.MLS(src1, p, q);int p_array[7][2] = { {30, 147}, {147, 147}, {268, 147}, {112, 148}, {186, 148}, {98, 316}, {211, 316} };int q_array[7][2] = { {28, 209}, {126, 143}, {282, 26}, {71, 236}, {136, 240}, {79, 313}, {190, 310} };qk.MLS(src1, (int *)p_array, (int*)q_array, 7, 2);waitKey(0);destroyAllWindows();return 0;
}
源文件 quick_demo.cpp:实现类与公共函数
void update_map(Mat& image, int index, Mat& x_map, Mat& y_map) {int height = image.rows;int width = image.cols;double h_41 = height * 0.25;double h_43 = height * 0.75;double w_41 = width * 0.25;double w_43 = width * 0.75;for (int h = 0; h < height; h++) {float* x_ptr = x_map.ptr<float>(h);float* y_ptr = y_map.ptr<float>(h);for (int w = 0; w < width; w++) {switch (index){case 0:if (h > h_41 && h < h_43 && w>w_41 && w < w_43) {*x_ptr++ = 2 * (w - w_41 + 0.5);*y_ptr++ = 2 * (h - h_41 + 0.5);}else{*x_ptr++ = 0;*y_ptr++ = 0;}break;case 1:*x_ptr++ = width - w - 1;*y_ptr++ = h;break;case 2:*x_ptr++ = w;*y_ptr++ = height - h - 1;break;case 3:*x_ptr++ = width - w - 1;*y_ptr++ = height - h - 1;break;}}}}
void QuickDemo::remap_Demo(Mat& image) {Mat dst, x_map, y_map;int index = 0;x_map.create(image.size(), CV_32FC1);y_map.create(image.size(), CV_32FC1);int c = 0;while (true){c = waitKey(400);if ((char)c==27){break;}index = c % 4;update_map(image,index, x_map, y_map);remap(image, dst, x_map, y_map, INTER_LINEAR, BORDER_CONSTANT, Scalar(255, 0, 0));imshow("remap", dst);}
}
如上两个函数,update_map,用于更新remap的具体映射方法,remap_Demo为调用函数。
二、图像扭曲
MLS算法 图像扭曲 Image Deformation Using Moving Least Squares 论文。
最小二乘法(MLS)对图像进行变形 python 实现
Point NewPoint(Point V, vector<Point> p, vector<Point> q){vector<float>W;Point p_star, q_star = Point(0, 0);for (int i = 0; i <= p.size() - 1; i++){float temp;if (p[i] == V){temp = INT_MAX;}else{temp = 1.0 / (((p[i].x - V.x) * (p[i].x - V.x)) + ((p[i].y - V.y) * (p[i].y - V.y)));}W.push_back(temp);}float px = 0, py = 0, qx = 0, qy = 0, W_sum = 0;for (int i = 0; i <= W.size() - 1; i++){px += W[i] * p[i].x;py += W[i] * p[i].y;qx += W[i] * q[i].x;qy += W[i] * q[i].y;W_sum += W[i];}p_star.x = px / W_sum;p_star.y = py / W_sum;q_star.x = qx / W_sum;q_star.y = qy / W_sum;vector<Point> p_hat, q_hat;for (int i = 0; i <= p.size() - 1; i++){p_hat.push_back(p[i] - p_star);q_hat.push_back(q[i] - q_star);}Mat pi_hat_t_ = Mat::zeros(2, 1, CV_32FC1);Mat_<float> pi_hat_t = pi_hat_t_;Mat pi_hat_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> pi_hat = pi_hat_;Mat M_1_ = Mat::zeros(2, 2, CV_32FC1);Mat_<float> M_1 = M_1_;for (int i = 0; i <= p_hat.size() - 1; i++){pi_hat_t.at<float>(0, 0) = p_hat[i].x;pi_hat_t.at<float>(1, 0) = p_hat[i].y;pi_hat.at<float>(0, 0) = p_hat[i].x;pi_hat.at<float>(0, 1) = p_hat[i].y;M_1 += pi_hat_t * W[i] * pi_hat;}Mat_<float> M_1_inv = M_1.inv();M_1 = M_1_inv;Mat pj_hat_t_ = Mat::zeros(2, 1, CV_32FC1);Mat_<float> pj_hat_t = pj_hat_t_;Mat qj_hat_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> qj_hat = qj_hat_;Mat M_2_ = Mat::zeros(2, 2, CV_32FC1);Mat_<float> M_2 = M_2_;for (int j = 0; j <= q.size() - 1; j++){pj_hat_t.at<float>(0, 0) = p_hat[j].x;pj_hat_t.at<float>(1, 0) = p_hat[j].y;qj_hat.at<float>(0, 0) = q_hat[j].x;qj_hat.at<float>(0, 1) = q_hat[j].y;M_2 += W[j] * pj_hat_t * qj_hat;}Mat_<float> M = M_1 * M_2;//ok//cout << "M = " << M << endl;Point x_p_star = V - p_star;Mat M_x_p_star_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> M_x_p_star = M_x_p_star_;M_x_p_star.at<float>(0, 0) = x_p_star.x;M_x_p_star.at<float>(0, 1) = x_p_star.y;Mat M_q_star_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> M_q_star = M_q_star_;M_q_star.at<float>(0, 0) = q_star.x;M_q_star.at<float>(0, 1) = q_star.y;Mat_<float> Lv = M_x_p_star * M + M_q_star;return Point(Lv.at<float>(0, 0), Lv.at<float>(0, 1));
}void QuickDemo::MLS(Mat& src, std::vector<Point> p, std::vector<Point> q){double time0 = static_cast<double>(getTickCount());Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){Point old = Point(j, i);Point new_point = NewPoint(old, p, q);//cout << "old = " << old << "\tnew = " << new_point << endl;dst.at<Vec3b>(i, j) = src.at<Vec3b>(abs(new_point.y), abs(new_point.x));}}double time1 = static_cast<double>(getTickCount());cout << "Total cost time is " << ((time1 - time0) / getTickFrequency()) << "seconds" << endl;imshow("dst_msl", dst);
}
重载函数
Point NewPoint(Point V, float* W, int* p, int* q , float* p_hat, float* q_hat, int rows, int cols) {Point p_star, q_star = Point(0, 0);float temp = 0;float px = 0, py = 0, qx = 0, qy = 0, W_sum = 0;for (int i = 0; i < rows; i++) {int p_0 = *(p + i * cols);int p_1 = *(p + i * cols + 1);if (!(p_0 == V.x && p_1 == V.y)) {temp = 1.0 / (((p_0 - V.x) * (p_0 - V.x)) + ((p_1 - V.y) * (p_1 - V.y)));}else {temp = INT_MAX;}W[i] = temp;px += temp * p_0;py += temp * p_1;qx += temp * (*(q + i * cols));qy += temp * (*(q + i * cols + 1));W_sum += temp;}p_star.x = px / W_sum;p_star.y = py / W_sum;q_star.x = qx / W_sum;q_star.y = qy / W_sum;for (int i = 0; i < rows; i++) {*(p_hat + i * cols) = *(p + i * cols) - p_star.x;*(p_hat + i * cols + 1) = *(p + i * cols + 1) - p_star.y;*(q_hat + i * cols) = *(q + i * cols) - p_star.x;*(q_hat + i * cols + 1) = *(q + i * cols + 1) - p_star.y;}// ====================================Mat pi_hat_t_ = Mat::zeros(2, 1, CV_32FC1);Mat_<float> pi_hat_t = pi_hat_t_;Mat pi_hat_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> pi_hat = pi_hat_;Mat M_1_ = Mat::zeros(2, 2, CV_32FC1);Mat_<float> M_1 = M_1_;// ====================================Mat pj_hat_t_ = Mat::zeros(2, 1, CV_32FC1);Mat_<float> pj_hat_t = pj_hat_t_;Mat qj_hat_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> qj_hat = qj_hat_;Mat M_2_ = Mat::zeros(2, 2, CV_32FC1);Mat_<float> M_2 = M_2_;// ====================================for (int i = 0; i < rows; i++) {float p_hat_x = *(p_hat + i * cols);float p_hat_y = *(p_hat + i * cols + 1);pi_hat_t.at<float>(0, 0) = p_hat_x;pi_hat_t.at<float>(1, 0) = p_hat_y;pi_hat.at<float>(0, 0) = p_hat_x;pi_hat.at<float>(0, 1) = p_hat_y;M_1 += pi_hat_t * W[i] * pi_hat;pj_hat_t.at<float>(0, 0) = p_hat_x;pj_hat_t.at<float>(1, 0) = p_hat_y;qj_hat.at<float>(0, 0) = *(q_hat + i * cols);qj_hat.at<float>(0, 1) = *(q_hat + i * cols + 1);M_2 += pj_hat_t * W[i] * qj_hat;}Mat_<float> M_1_inv = M_1.inv();M_1 = M_1_inv;Mat_<float> M = M_1 * M_2;//=====================================//// 如下为总公式计算////======================================Point x_p_star = V - p_star;Mat M_x_p_star_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> M_x_p_star = M_x_p_star_;M_x_p_star.at<float>(0, 0) = x_p_star.x;M_x_p_star.at<float>(0, 1) = x_p_star.y;Mat M_q_star_ = Mat::zeros(1, 2, CV_32FC1);Mat_<float> M_q_star = M_q_star_;M_q_star.at<float>(0, 0) = q_star.x;M_q_star.at<float>(0, 1) = q_star.y;Mat_<float> Lv = M_x_p_star * M + M_q_star;return Point(Lv.at<float>(0, 0), Lv.at<float>(0, 1));}void QuickDemo::MLS(Mat& src, int* p, int* q, int rows, int cols) {double time0 = static_cast<double>(getTickCount());Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);assert(7 == rows); // 若断言失败请修改如下三个数组的长度为rowsfloat W[7] = { 0 }; // 权重长度为p数组长度:rows=7float p_hat[7][2] = { 0 }; // p_hat长度为p数组长度:rows=7float q_hat[7][2] = { 0 }; // q_hat长度为p数组长度:rows=7for (int i = 0; i < src.rows; i++) {for (int j = 0; j < src.cols; j++) {Point new_point = NewPoint(Point(j, i), W, p, q, (float*)p_hat, (float*)p_hat, rows, cols);//cout << "old = " << old << "\tnew = " << new_point << endl;dst.at<Vec3b>(i, j) = src.at<Vec3b>(abs(new_point.y), abs(new_point.x));//cout << "src.at<uchar> = " << src.at<Vec3b>(new_point.y,new_point.x) << endl;}}double time1 = static_cast<double>(getTickCount());cout << "Total cost time is " << ((time1 - time0) / getTickFrequency()) << "seconds" << endl;imshow("dst_msl", dst);
}
————
鸣谢与拓展阅读:
使用范例 记录四图像处理之瘦脸 MLS算法 C++实现
OpenCV局部变形算法探究添加链接描述
基于移动最小二乘(MLS)的图像扭曲刚性变形python实现
使用重映射实现图像的局部扭曲 来实现 图像增强。
相关文章:

Opencv-C++笔记 (15) : 像素重映射 与 图像扭曲
文章目录 一、重映射简介二、图像扭曲 一、重映射简介 重映射,就是把一幅图像中某位置的像素放置到另一图像指定位置的过程。即: 在重映射过程中,图像的大小也可以同时发生改变。此时像素与像素之间的关系就不是一一对应关系,因…...

【Java】UWB高精度工业人员安全定位系统源码
基于VueSpring boot前后端分离架构开发的一套UWB技术高精度定位系统源码。 UWB高精度人员定位系统提供实时定位、电子围栏、轨迹回放等基础功能以及各种拓展功能,用户可根据实际需要任意选择搭配拓展功能。该系统简易部署,方便使用,实时响应。UWB高精度定…...
文本NLP噪音预处理(加拼写检查)
最近总结修改了下预处理方法,记录下 首先download需要的依赖 pip install pyenchantpip install nltk pyenchant 是用来检测拼写正确的,如果你的文本里面可能包含非正确拼写的单词,那就忽略它,nltk用来做分词的。 python -m nlt…...

[Docker实现测试部署CI/CD----自由风格的CI操作[最终架构](5)]
目录 11、自由风格的CI操作(最终)Jenkins容器化实现方案修改 docker.sock 权限修改 Jenkins 启动命令后重启 Jenkins构建镜像推送到Harbor修改 daemon.json 文件Jenkins 删除构建后操作Jenkins 添加 shell 命令重新构建 Jenkins通知目标服务器拉取镜像目…...

纯JS+Vue实现一个仪表盘
在使用canvas的时候发现数值变化,每次都要重新渲染,值都从0开始,这和我的需求冲突。 1. 先绘制基本的圆环背景,利用border-color和border-radius将正方形变成基本的圆环。 <div class"circle"><div class&qu…...
标定(内参、外参)
在计算机视觉中,特别是在相机标定和立体视觉领域,内参(intrinsic parameters)和外参(extrinsic parameters)是非常重要的概念。它们与相机的几何属性和姿态有关。 内参(Intrinsic Parameters&am…...

基于ffmpeg与SDL的视频播放库
由于工作需要,自己封装的基于ffmpeg的视频编解码库,显示采用了SDL库。可以播放本地文件或网络流,支持多端口播放,支持文字叠加,截图、视频录制等等。 头文件代码: #pragma once #ifdef __DLLEXPORT #defin…...

基于二进制草蝉优化算法选择特征并使用 KNN 进行训练(Matlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨💻4 Matlab代码 💥1 概述 基于二进制草蝉优化算法选择特征并使用KNN(K-Nearest Neighbors,K最近邻算法)进行训练是一种…...

14-4_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP组播
文章目录 1. UDP组播的特性2. UDP 组播实例程序的功能3. 组播功能的程序实现4. 源码4.1 可视化UI设计4.2 mainwindow.h4.3 mainwindow.cpp 1. UDP组播的特性 下图简单表示了组播的原理。UDP 组播是主机之间“一对一组”的通信模式,当多个客户端加入由一个组播地址定…...

ai图片合成软件帮你创造个性绚丽
嘿!悄悄告诉你一个小秘密,现在有一款超酷的软件,它能让你的图片变得活灵活现,就像跳出了屏幕一样!没错,这就是ai图片制作软件!想象一下,你拍摄了一张美丽的风景照片,但总…...
git 版本回退
git 没有push之前,可以用git reset --mixed回退,就是把add 的内容和commit的内容都撤销 在push之后,你只有2种操作 1.git reset 退回到你想要的那个版本 有配置选项 如果是soft就是当前版本删掉,之前改的代码保留,ha…...
使用Jackson自定义序列化操作(Jackson – Custom Serializer)
目录 Standard Serialization of an Object GraphCustom Serializer on the ObjectMapperCustom Serializer on the Class Standard Serialization of an Object Graph Data NoArgsConstructor AllArgsConstructor public class Item {public int id;public String itemName;p…...
Python-元组
元组(Tuples)详解 在Python中,元组(Tuples)是一种有序的数据类型,它可以包含任意类型的元素,包括数字、字符串、列表等。与列表相似,元组也是用来存储一组数据,但与列表…...

快速转换PDF文件: Python和PyMuPDF教程
解决问题 有时候将文档上传Claude2做分析,有大小限制,所以需要切割pdf文档为几个小点的文档,故才有了本文章。 如何用Python和PyMuPDF制作你想要大小的PDF? PDF是一种广泛使用的文件格式,可以在任何设备上查看和打印…...

规划模型Matlab代码
文章目录 数学规划定义一般形式分类 1.线性规划(linear programming)2.非线性规划(nonlinear programming)3. 整数规划(integer programming)4. 0-1规划(0-1 programming)5. 最大最小化模型6. 多目标规划模型7.敏感性分析(对权重)[例题] 数学规划定义 数…...

用html+javascript打造公文一键排版系统11:改进单一附件说明排版
一、用htmljavascript打造公文一键排版系统10中的一个bug 在 用htmljavascript打造公文一键排版系统10:单一附件说明排版 中,我们对附件说明的排版函数是: function setAtttDescFmt(p) {var t p;var a ;if (-1 ! t.indexOf(:))//是半角冒…...

snap xxx has “install-snap“ change in progress
error description * 系重复安装,进程冲突 solution 展示snap的改变 然后sudo snap abort 22即可终止该进程 之后重新运行install command~~ PS: ubuntu有时候加载不出来,执行resolvectl flush-caches,清除dns缓存…...
Elasticsearch 性能调优指南
目录 1、通用优化策略 1.1 通用最小化法则 1.2 职责单一原则 1.3 其他 2、写性能调优 2.1 基本原则 2.2 优化手段 2.2.1 增加 flush 时间间隔, 2.2.2 增加refresh_interval的参数值 2.2.3 增加Buffer大小, 2.2.4 关闭副本 2.2.5 禁用swap 2…...
学习Boost一:学习方法和学习目的
学习目的 Boost 的学习目的: 因为从知乎和CSND上根据了解内容来看,Boost作为一个历史悠久的开源库,已经脱离了一个单纯的库的概念了,他因庞大的涉及面应当被称之为库集。 并且,因为boost库优秀的试用反馈和开发人员的…...

c语言每日一练(1)
前言: 每日一练系列,每一期都包含5道选择题,2道编程题,博主会尽可能详细地进行讲解,令初学者也能听的清晰。每日一练系列会持续更新,暑假时三天之内必有一更,到了开学之后,将看学业情…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...