Opencv-C++ メモ (15): ピクセルの再マッピングと画像の歪み

1. リマッピングの概要

再マッピングは、ある画像内の特定の位置にあるピクセルを別の画像内の指定された位置に配置するプロセスです。つまり、
ここに画像の説明を挿入
再マッピング プロセス中に、イメージのサイズも同時に変更される可能性があります。このとき、ピクセル間の関係は 1 対 1 の対応ではないため、リマッピング処理にはピクセル値の補間計算が含まれる場合があります。

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);
};

main 関数はクラスのパブリック メンバー関数を呼び出します。

#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);
	}
}

上記 2 つの関数 update_map は、remap の特定のマッピング方法を更新するために使用され、remap_Demo は呼び出し関数です。
ここに画像の説明を挿入

2. 画像の歪み

MLS アルゴリズムの画像歪み 移動最小二乗法ペーパーを使用した画像変形。
最小二乗法 (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);
}
————

ここに画像の説明を挿入
謝辞と参考資料:
サンプル レコードを使用して 4 つの画像処理のフェイスリフト MLS アルゴリズムを記録するC++ 実装
OpenCV ローカル変形アルゴリズムの探索リンクの説明を追加する移動最小二乗法 (MLS)
に基づく画像の歪み 剛体変形 Python の実装再マッピングを使用してローカル歪みを実現する画像を強調して画像を強調します。

おすすめ

転載: blog.csdn.net/jiyanghao19/article/details/132104535