resize图像插值算法

resize图像插值算法,第1张

这篇博文在写之前参考了网上的不少文章,感觉有的文章讲解比较到位,有的会误导人。


下面的代码运行结果已经和直接调用opencv算子进行对比确保结果相同,就没有深究其开源代码了。


不过根据推断,opencv在实现resize图像插值时(1)需要做像素点中心对齐 *** 作;(2)不需要做copyMakeBorder边缘复制 *** 作。



原理参考:【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值
其中双三次插值的实现参考了:C++ OpenCV实现图像双三次插值算法详解这篇文章原理讲的比较细,但代码实现有瑕疵。


目录
    • 最近邻插值
    • 双线性插值
    • 双三次插值

最近邻插值
void nearest(cv::Mat& src, cv::Mat& dst, float sx, float sy) 
{
	//由缩放因子计算输出图像的尺寸(四舍五入)
	dst = cv::Mat(round(src.rows * sy), round(src.cols * sx), src.type());
	for (int i = 0; i < dst.rows; i++)
	{
		//几何中心对齐
		float index_i = (i + 0.5) / sy - 0.5;
		//防止越界
		if (index_i < 0) index_i = 0;
		if (index_i > src.rows - 1) index_i = src.rows - 1;
		//取整
		int i_index = round(index_i);
		for (int j = 0; j < dst.cols; j++)
		{
			//几何中心对齐
			float index_j = (j + 0.5) / sx - 0.5;
			//防止越界
			if (index_j < 0) index_j = 0;
			if (index_j > src.cols - 1) index_j = src.cols - 1;
			//取整
			int j_index = round(index_j);
			dst.at<uchar>(i, j) = src.at<uchar>(i_index, j_index);
		}
	}
}
双线性插值
void linear(Mat& src, Mat& dst, float sx, float sy) 
{
	dst = Mat(round(sy*src.rows), round(sx*src.cols), src.type());
	for (int i = 0; i < dst.rows; i++) 
	{
		//几何中心对齐
		float index_i = (i + 0.5) / sy - 0.5;
		//防止越界
		if (index_i < 0) index_i = 0;
		if (index_i > src.rows - 1) index_i = src.rows - 1;
		//相邻4*4像素的行(坐标)
		int i1 = floor(index_i);
		int i2 = ceil(index_i);
		//u为得到浮点型坐标行的小数部分
		float u = index_i - i1;
		for (int j = 0; j < dst.cols; j++) 
		{
			//几何中心对齐
			float index_j = (j + 0.5) / sx - 0.5;
			//防止越界
			if (index_j < 0) index_j = 0;
			if (index_j > src.cols - 1) index_j = src.cols - 1;
			//相邻4*4像素的列(坐标)
			int j1 = floor(index_j);
			int j2 = ceil(index_j);
			//v为得到浮点型坐标列的小数部分
			float v = index_j - j1;
			dst.at<uchar>(i, j) = (1 - u)*(1 - v)*src.at<uchar>(i1, j1) + (1 - u)*v*src.at<uchar>(i1, j2) +
				u*(1 - v)*src.at<uchar>(i2, j1) + u*v*src.at<uchar>(i2, j2);
		}
	}
}
双三次插值
vector<float> getWeight(float c, float a = -1.0)
{
	//c就是u和v,横坐标和纵坐标的输出计算方式一样
	vector<float> temp = { 1 + c, c, 1 - c, 2 - c };

	//y(x) = (a+2)|x|^3- (a+3)|x|^2| + 1,  |x|<=1
	//y(x) = a|x|^3 - 5a|x|^2 + 8a|x| - 4a,  1<|x|<2
	//y(x) = 0  1<|x|<2,  otherwise
	vector<float> weight(4);
	weight[0] = a * pow(fabs(temp[0]), 3) - 5 * a * pow(fabs(temp[0]), 2) + 8 * a * fabs(temp[0]) - 4 * a;
	weight[1] = (a + 2) * pow(fabs(temp[1]), 3) - (a + 3) * pow(fabs(temp[1]), 2) + 1;
	weight[2] = (a + 2) * pow(fabs(temp[2]), 3) - (a + 3) * pow(fabs(temp[2]), 2) + 1;
	weight[3] = a * pow(fabs(temp[3]), 3) - 5 * a * pow(fabs(temp[3]), 2) + 8 * a * fabs(temp[3]) - 4 * a;

	return weight;
}


void bicubic(Mat& src, Mat& dst, float sx, float sy)
{
	int dst_cols = sx*src.cols, dst_rows = sy*src.rows;
	dst.create(dst_rows, dst_cols, src.type());

	for (int i = 0; i < dst_rows; ++i)
	{
		float index_i = (i + 0.5) / sy - 0.5; //做了几何中心对齐
		if (index_i < 0) index_i = 0;
		if (index_i > src.rows - 1) index_i = src.rows - 1;

		//目标图像点坐标对应原图点坐标的4个纵坐标
		int i0 = index_i - 1, i1 = index_i, i2 = index_i + 1, i3 = index_i + 2;
		float u = index_i - i1;
		vector<float> weight_u = getWeight(u);

		for (int j = 0; j < dst_cols; ++j)
		{
			float index_j = (j + 0.5) / sx - 0.5;
			if (index_j < 0) index_j = 0;
			if (index_j > src.cols - 1) index_j = src.cols - 1;

			//目标图像点坐标对应原图点坐标的4个横坐标
			int j0 = index_j - 1, j1 = index_j, j2 = index_j + 1, j3 = index_j + 2;
			float v = index_j - j1;
			vector<float> weight_v = getWeight(v);

			//目标点像素对应原图点像素周围4x4区域的加权计算(插值)
			float temp = 0;
			for (int s = 0; s <= 3; s++)
			{
				for (int t = 0; t <= 3; t++)
				{
					temp += src.at<uchar>(i1 + s - 1, j1 + t - 1)*weight_u[s] * weight_v[t];
				}
			}

			if (temp < 0)	temp = 0;
			if (temp > 255)	temp = 255;
			dst.at<uchar>(i, j) = temp;
		}
	}
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/578486.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存