OpenCV学习 基础图像操作(十三):像素重映射

基础概念

映射的数学概念

定义:映射,或者射影,在数学及相关的领域还用于定义函数。函数是从非空数集到非空数集的映射,而且只能是一对一映射或多对一映射。

映射的成立条件简单的表述就是:

1.定义域的遍历性:X中的每个元素x在映射的值域中都有对应对象

2.对应的唯一性:定义域中的一个元素只能与映射值域中的一个元素对应

定义域也称为原象集,值域也称为象集。

OpenCV学习 基础图像操作(十三):像素重映射_第1张图片

图中A到B的为映射,B到A为逆映射。

常见的映射关系

单设:对于任意的x_i,有且仅有唯一的y_i=f(x_i),则f为单射。

OpenCV学习 基础图像操作(十三):像素重映射_第2张图片

满射:对于任意的y_i,存在y_i=f(x) \ \ \ \ \ \ x \in X,则f为满射。

OpenCV学习 基础图像操作(十三):像素重映射_第3张图片

双射:对于x_i \in X,y_i \in Y,有且仅有y_i = f(x_i),x_i=f^{-1}(y_i),即x_i,y_i一一对应,则f为双射。

双射既满足单射,也满足满射。

OpenCV学习 基础图像操作(十三):像素重映射_第4张图片

像素映射

简单说就是把输入图片中个像素按照一定的规则映射到另外一张图像的对应位置上,形成一张新的图像。

g(x,y) = f(h(x,y))

f(x,y)为原图中的像素点,通过映射函数h(x,y)映射到目标图像g(x,y)中。

如当h(x,y) = (I.cols-x,y),则可以实现图片的左右镜像对称。

通常像素映射需要建立映射表,建立映射表的方式有两种:

1.前向映射:即以原图像中的(x,y)为起点,根据映射关系(f_x(x),f_y(y))\rightarrow (x',y')找到目标图像中的坐标,然后将原图像中的像素值搬到目标图像中的对应位置坐标上。

2.后向映射:即以目标图像中的(x',y')为起点,根据映射关系的逆运算(f'_x(x'),f'_y(y))\rightarrow (x,y)找到原图像中的坐标,然后将原图像中的像素值搬到目标图像中的对应位置坐标上。

由于像素映射可以必须保证是满射,但是无法保证满足单射,因此采用后向映射更为合理。

AIP简介

像素重映射

因为映射关系是(x_1,y_1)\rightarrow (x_2,y_2),所以可以拆解为两部分来看,即x坐标的映射和y坐标的映射,因此API中的map1是x映射表,map2是y映射表。简单来看真个API的功能可以表示下图,即根据原图与映射表生成新的图像,下图为顺时针旋转九十。

OpenCV学习 基础图像操作(十三):像素重映射_第5张图片

映射表的尺度大小与原始图像相同,但是只保存x坐标或y坐标信息,所以是单通道的,且坐标在计算时会出现小数的情况,因此opencv要求映射表的数据类型为 CV_32FC1。

Remap(
InputArray src,
OutputArray dst,
InputArray map1,//x映射表  CV_32FC1
InputArray map2,//y映射表
int interpolation,//选择的插值方法,常见的为线性插值,可选双线性插值和立方插值等
int borderMode,//BORDER_CONSTANT
const Scalar borderValue, //固定值填充边缘时的颜色
)

代码与实践

#include
#include
#include

using namespace std;
using namespace cv;

void update_map(Mat& map_x,Mat& map_y,const Mat& src ,Size size, int flag)
{
	map_x.create(size, CV_32FC1); //创建映射表
	map_y.create(size, CV_32FC1);

	float x_scale = src.cols*1.0/size.width;
	float y_scale =	src.rows*1.0 / size.height;
	float x, y;

	flag = flag % 3;

    //填充映射表
	for (int row = 0; row < size.height; row++)
	{
		for (int col = 0; col < size.width; col++)
		{
			switch (flag)
			{
			case 0: //左右翻转
				x = src.cols - col * x_scale;  // x = X - x'
				y = row * y_scale;
				
				map_x.at(row, col) = x;
				map_y.at(row, col) = y;
				break;
			case 1://上下翻转
				x = col * x_scale;
				y = src.rows - row * y_scale;  //y = Y -y'

				map_x.at(row, col) = x;
				map_y.at(row, col) = y;
				break;

			case 2://旋转180°
				x = row *  y_scale * src.cols /src.rows;				//x = y'*(X/Y)
				y = src.rows -  col * x_scale *src.rows/src.cols;       //y = X-x'/(X/Y)

				map_x.at(row, col) = x;
				map_y.at(row, col) = y;
				break;

			default:
				x = col * x_scale;
				y = row * y_scale;

				map_x.at(row, col) = x;
				map_y.at(row, col) = y;

				break;
			}
		}
	}

}


int main(int argc,char** argv)
{
	Mat src = imread("33.jpg");

	if (!src.data)
	{
		printf("打开图像失败!\n");
		return -1;
	}


	char input_win[] = "input ";
	char output_win[] = "output";
	namedWindow(input_win,CV_WINDOW_AUTOSIZE);
	namedWindow(output_win, CV_WINDOW_AUTOSIZE);

	imshow(input_win, src);

	Mat map_x, map_y,out_img;
	int flag = 0;
	while (true)
	{
		flag = waitKey(5000);
		if (flag == 27)
		{
			break;
		}
		update_map(map_x, map_y, src, Size(300, 400), flag);

        //根据映射表完成映射
		remap(src, out_img, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(255, 255, 255));

		
		imshow(output_win, out_img);
	
	}
	
	return 0;

}

 

 

你可能感兴趣的:(Opecv,计算机视觉,opencv)