Opencv-C++笔记 (15) : 像素重映射 与 图像扭曲

news2025/1/14 1:05:25

文章目录

  • 一、重映射简介
  • 二、图像扭曲

一、重映射简介

重映射,就是把一幅图像中某位置的像素放置到另一图像指定位置的过程。即:
在这里插入图片描述
在重映射过程中,图像的大小也可以同时发生改变。此时像素与像素之间的关系就不是一一对应关系,因此在重映射过程中,可能会涉及到像素值的插值计算。

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);               // 若断言失败请修改如下三个数组的长度为rows
	float W[7] = { 0 };              // 权重长度为p数组长度:rows=7
	float p_hat[7][2] = { 0 };       // p_hat长度为p数组长度:rows=7
	float q_hat[7][2] = { 0 };       // q_hat长度为p数组长度:rows=7
	for (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实现
使用重映射实现图像的局部扭曲 来实现 图像增强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/838671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[MAUI]模仿微信“按住-说话”的交互实现

今天使用这个控件&#xff0c;做一个模仿微信“按住-说话”的小功能&#xff0c;最终效果如下&#xff1a; 使用.NET MAUI实现跨平台支持&#xff0c;本项目可运行于Android、iOS平台。 创建页面布局 新建.NET MAUI项目&#xff0c;命名HoldAndSpeak MainPage.xaml中创建一个…

【敏捷开发】测试驱动开发(TDD)

测试驱动开发&#xff08;Test-Driven Development&#xff0c;简称TDD&#xff09;是敏捷开发模式中的一项核心实践和技术&#xff0c;也是一种设计方法论。TDD有别于以往的“先编码&#xff0c;后测试”的开发模式&#xff0c;要求在设计与编码之前&#xff0c;先编写测试脚本…

windows部署springboot项目 jar项目 (带日志监听和开机自起脚本)

windows部署springboot项目 jar项目 &#xff08;带日志监听&#xff09; 1.把项目打包成jar包&#xff0c;本例演示打包后的jar文件名为demo.jar ———————————————— 2.需要装好java环境&#xff0c;配置好JAVA_HOME&#xff0c;CLASSPATH&#xff0c;PATH等…

Idea中侧面栏不见了,如何设置?

一、打开idea点击File然后点击Setting 二、点击Appearance,然后划到最下面&#xff0c;勾选Show tool windows bars和Side-by-side layout on the left 三、侧面栏目正常显示

在java中如何使用openOffice进行格式转换,word,excel,ppt,pdf互相转换

1.首先需要下载并安装openOffice,下载地址为&#xff1a; Apache OpenOffice download | SourceForge.net 2.安装后&#xff0c;可以测试下是否可用&#xff1b; 3.build.gradle中引入依赖&#xff1a; implementation group: com.artofsolving, name: jodconverter, version:…

避免安装这5种软件,手机广告频繁弹窗且性能下降

在我们使用手机的日常生活中&#xff0c;选择合适的应用软件对于保持良好的使用体验至关重要。然而&#xff0c;有些软件可能会给我们带来不必要的麻烦和困扰。特别是那些频繁弹窗广告、导致手机性能下降的应用程序&#xff0c;我们应该尽量避免安装它们。 首先第一种&#xf…

STM32H5开发(5)----串口打印配置

STM32H5开发----4.开发板介绍 概述样品申请硬件准备生成例程配置调试口代码生成配置项目配置调试配置串口重定向打印测试结果 概述 在使用STM32CUBEIDE开发STM32H5项目时&#xff0c;串口打印被证明是一项极其有益的调试工具&#xff0c;能够在开发过程中实时输出信息和调试数…

0基础学习VR全景平台篇 第78篇:全景相机-拍摄VR全景

新手入门圆周率科技&#xff0c;成立于2012年&#xff0c;是中国最早投身嵌入式全景算法研发的团队之一&#xff0c;亦是全球市场占有率最大的全景算法供应商。相继推出一体化智能屏、支持一键高清全景直播的智慧全景相机--Pilot Era和Pilot One&#xff0c;为用户带来实时畅享…

【LeetCode】516. 最长回文子序列

文章目录 1. 思路讲解1.1 创建dp表1.2 状态转移方程1.3 不需考虑边界问题 2. 整体代码 1. 思路讲解 1.1 创建dp表 此题采用动态规划的方法&#xff0c;创建一个二维dp表&#xff0c;dp[i][j]表示s[i, j]中最大回文子序列的长度。且我们人为规定 i 是一定小于等于 j 的。 1.2…

jsqlparser 安装和使用

jsqlparser是sql语句解析工具&#xff0c;可以解析sql并分析语法。 安装 <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.3</version> </dependency>使用 String s …

Devart dbForge Studio for MySQL Crack

Devart dbForge Studio for MySQL Crack dbForge Studio for MySQL是一个用于MySQL和MariaDB数据库开发、管理和管理的通用GUI工具。IDE允许您通过直观的界面创建和执行查询、开发和调试存储例程、自动化数据库对象管理、分析表数据。MySQL客户端提供了数据和模式比较和同步工具…

CS 144 Lab Four 收尾 -- 网络交互全流程解析

CS 144 Lab Four 收尾 -- 网络交互全流程解析 引言Tun/Tap简介tcp_ipv4.cc文件配置信息初始化cs144实现的fd家族体系基于自定义fd体系进行数据读写的adapter适配器体系自定义socket体系自定义事件循环EventLoop模板类TCPSpongeSocket详解listen_and_accept方法_tcp_main方法_in…

【docker】docker-compose服务编排

目录 一、服务编排概念二、docker compose2.1 定义2.2 使用步骤2.3 docker-compose安装2.4 docker-compose卸载 三、编排示例 一、服务编排概念 1.微服务架构的应用系统中一般包含若干个微服务&#xff0c;每个微服务一般都会部署多个实例&#xff0c;如果每个微服务都要手动启…

数学建模—多元线性回归分析

第一部分&#xff1a;回归分析的介绍 定义&#xff1a;回归分析是数据分析中最基础也是最重要的分析工具&#xff0c;绝大多数的数据分析问题&#xff0c;都可以使用回归的思想来解决。回归分析的人数就是&#xff0c;通过研究自变量X和因变量Y的相关关系&#xff0c;尝试去解释…

MySQL的常用函数大全

一、字符串函数 常用函数&#xff1a; 函数功能CONCAT(s1, s2, …, sn)字符串拼接&#xff0c;将s1, s2, …, sn拼接成一个字符串LOWER(str)将字符串全部转为小写UPPER(str)将字符串全部转为大写LPAD(str, n, pad)左填充&#xff0c;用字符串pad对str的左边进行填充&#xff0…

在家下载Springer、IEEE、ScienceDirect等数据库论文的论文下载工具

Springer、IEEE、ScienceDirec数据库是我们查找外文文献常用数据库&#xff0c;当我们没有数据库使用权限的时该如何下载这些数据库的学术论文呢&#xff1f;下面就讲解一下在家下载数据库学术文献的论文下载工具。 一、查找下载外文文献&#xff0c;我们可以谷歌学术检索&…

MyBatis-XML映射文件

XML映射文件 规范 XML映射文件的名称与Mapper接口名称一致&#xff08;EmpMapper对应EmpMpper.xml&#xff09;&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09; ​​​ 在maven项目结构中所有的配置文件都在resources目录之下&…

【css问题】flex布局中,子标签宽度超出父标签宽度,导致布局出现问题

场景&#xff1a;文章标题过长时&#xff0c;只显示一行&#xff0c;且多余的部分用省略号显示。 最终效果图&#xff1a; 实现时&#xff0c;flex布局&#xff0c;出现问题&#xff1a; 发现text-overflow: ellipsis不生效&#xff0c;省略符根本没有出现。 而且因为设置了 …

【C++】——内存管理

目录 回忆C语言内存管理C内存管理方式new deleteoperator new与operator delete函数new和delete的实现原理定位new表达式(placement-new)malloc/free和new/delete的区别 回忆C语言内存管理 void Test() {int* p1 (int*)malloc(sizeof(int));free(p1);int* p2 (int*)calloc(4…

树的层次遍历

层次遍历简介 广度优先在面试里出现的频率非常高&#xff0c;整体属于简单题。而广度优先遍历又叫做层次遍历&#xff0c;基本过程如下&#xff1a; 层次遍历就是从根节点开始&#xff0c;先访问根节点下面一层全部元素&#xff0c;再访问之后的层次&#xff0c;类似金字塔一样…