前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >『带你学算法』详解OpenCV中Reszie操作与原理

『带你学算法』详解OpenCV中Reszie操作与原理

作者头像
小宋是呢
发布2020-12-10 10:45:10
2.2K0
发布2020-12-10 10:45:10
举报
文章被收录于专栏:深度应用深度应用

小宋说:在进行图片数据处理时,经常会用到图片Reszie的操作。由于是基于OpenCV的Resize接口,所以并不了解内部原理,所以这篇文章将详细讲解一下具体操作与原理。

1 OpenCV中Reszie使用

1.1 Resize接口

OpenCV支持不同的编程语言,下面是对不同语言Resize的操作:

C++:

代码语言:javascript
复制
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)  

Python:

代码语言:javascript
复制
cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst  

C:

代码语言:javascript
复制
void cvResize(const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR)  

1.2 参数说明:

src - 原图

dst - 目标图像。当参数dsize不为0时,dst的大小为size;否则,它的大小需要根据src的大小,参数fx和fy决定。dst的类型(type)和src图像相同

dsize - 目标图像大小。当dsize为0时,它可以通过以下公式计算得出:

所以,参数dsize和参数(fx, fy)不能够同时为0。

fx - 水平轴上的比例因子。当它为0时,计算公式如下:

fy - 垂直轴上的比例因子。当它为0时,计算公式如下:

interpolation - 插值方法

2 OpenCV中Reszie原理

2.1 插值方法介绍

插值方法。共有5种:

1)INTER_NEAREST - 最近邻插值法

2)INTER_LINEAR - 双线性插值法(默认)

3)INTER_AREA - 基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。

4)INTER_CUBIC - 基于4x4像素邻域的3次插值法

5)INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值

2.2 插值原理详解

这里以常用的双线性插值法为例详细介绍:

双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。

搞懂双线性之前需要先单线性插值法:

已知数据 (x0, y0) 与 (x1, y1),要计算 [x0, x1] 区间内某一位置 x 在直线上的y值。

其实就是用x和x0,x1的距离作为一个权重,用于y0和y1的加权。

小宋说:其实这个操作也很直观,线性就是离哪个点近结果就趋近于哪点,这就是所谓的加权操作。双线性插值本质上就是在两个方向上做线性插值。

双线性插值

在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。见下图:

假如我们想得到未知函数 f 在点 P = (x, y) 的值,假设我们已知函数 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四个点的值。最常见的情况,f就是一个像素点的像素值。首先在 x 方向进行线性插值,得到

然后在 y 方向进行线性插值,得到

综合起来就是双线性插值最后的结果:

2.3 双线性插值代码实现

由相邻的四像素(2*2)计算得出,公式如下:

代码实现基于C++,如下:

代码语言:javascript
复制
    cv::Mat matSrc, matDst1, matDst2;
	matSrc = cv::imread("lena.jpg", 2 | 4);
	matDst1 = cv::Mat(cv::Size(800, 1000), matSrc.type(), cv::Scalar::all(0));
	matDst2 = cv::Mat(matDst1.size(), matSrc.type(), cv::Scalar::all(0));
	double scale_x = (double)matSrc.cols / matDst1.cols;
	double scale_y = (double)matSrc.rows / matDst1.rows;


    uchar* dataDst = matDst1.data;
	int stepDst = matDst1.step;
	uchar* dataSrc = matSrc.data;
	int stepSrc = matSrc.step;
	int iWidthSrc = matSrc.cols;
	int iHiehgtSrc = matSrc.rows;


	for (int j = 0; j < matDst1.rows; ++j)
	{
		float fy = (float)((j + 0.5) * scale_y - 0.5);
		int sy = cvFloor(fy);
		fy -= sy;
		sy = std::min(sy, iHiehgtSrc - 2);
		sy = std::max(0, sy);


		short cbufy[2];
		cbufy[0] = cv::saturate_cast<short>((1.f - fy) * 2048);
		cbufy[1] = 2048 - cbufy[0];


		for (int i = 0; i < matDst1.cols; ++i)
		{
			float fx = (float)((i + 0.5) * scale_x - 0.5);
			int sx = cvFloor(fx);
			fx -= sx;
 
			if (sx < 0) {
				fx = 0, sx = 0;
			}
			if (sx >= iWidthSrc - 1) {
				fx = 0, sx = iWidthSrc - 2;
			}
 
			short cbufx[2];
			cbufx[0] = cv::saturate_cast<short>((1.f - fx) * 2048);
			cbufx[1] = 2048 - cbufx[0];


			for (int k = 0; k < matSrc.channels(); ++k)
			{
				*(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + sy*stepSrc + 3*sx + k) * cbufx[0] * cbufy[0] + 
					*(dataSrc + (sy+1)*stepSrc + 3*sx + k) * cbufx[0] * cbufy[1] + 
					*(dataSrc + sy*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[0] + 
					*(dataSrc + (sy+1)*stepSrc + 3*(sx+1) + k) * cbufx[1] * cbufy[1]) >> 22;
			}
		}
	}
	cv::imwrite("linear_1.jpg", matDst1);


	cv::resize(matSrc, matDst2, matDst1.size(), 0, 0, 1);


	cv::imwrite("linear_2.jpg", mat);

3 使用Reszie注意事项:

  1. dsize和fx/fy不能同时为0,要么你就指定好dsize的值,让fx和fy空置直接使用默认值,就像 resize(img, imgDst, Size(30,30)); 要么你就让dsize为0,指定好fx和fy的值,比如fx=fy=0.5,那么就相当于把原图两个方向缩小一倍!
  2. 至于最后的插值方法,正常情况下使用默认的双线性插值就够用了。 几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值; 但是效率和效果成反比,所以根据自己的情况酌情使用。
  3. 正常情况下,在使用之前dst图像的大小和类型都是不知道的,类型从src图像继承而来,大小也是从原图像根据参数计算出来。但是如果你事先已经指定好dst图像的大小,那么你可以通过下面这种方式来调用函数:resize(src, dst, dst.size(), 0, 0, interpolation);

-1 参考

-1.1 https://blog.csdn.net/kokozeng1995/article/details/78534842

-1.2 https://blog.csdn.net/fengbingchun/article/details/17335477

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 OpenCV中Reszie使用
    • 1.1 Resize接口
      • 1.2 参数说明:
      • 2 OpenCV中Reszie原理
        • 2.1 插值方法介绍
          • 2.2 插值原理详解
            • 2.3 双线性插值代码实现
            • 3 使用Reszie注意事项:
            • -1 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档