数字图像中实现缩放的方法有很多种,其中一种就是双线性插值,在实现图像缩放时,有两种方法来确定缩放后的图像的像素值,第一种是根据原图像中的的像素找到对应的缩放后的图像中的像素,第二种是根据缩放后的图像找到对应的原图像中的像素,如下图
但是第一种方法有缺点,因为小图中的像素点到大图中的像素点不是满射,因此大图中的点不能完全有像素值,第二种方法也有缺点,大图中的点逆映射为小图中的点时,得到的像素坐标值可能不是整数,一种办法是采用最近邻方法,即将得到的坐标值与相邻的原图像中的像素坐标值比较,取离得最近的坐标值对应的像素值作为缩放后的图像对应的坐标值的像素值,这种办法可能导致图像失真,因此采用双线性差值的办法来进行计算相应的像素值。
右侧是最一般的双线性插值,下面举一个实际的例子来说明双线性插值在图像缩放中的应用。
假设一个图像的大小是485x647,放大分别放大1.3倍和1.7倍,即485x1.3=630.5,647x1.7=1099.9,根据四舍五入的原则确定放大后的图像为631x1100,接下来就是计算放大后图像各个位置的像素值,例如计算放大后图像位于(136,345)位置的像素值,则136/1.3=104.615,345/1.7=202.941,这里由于示例的原因取小数点后三位,则原图像中相邻的四个位置分别是(104,202),(104,203),(105,202),(105,203)这四个点,
如图我画出了这个点对应的周围的四个点,203.941-203=0.941
所以f(R1)=(1-0.941)xf(104,203)+0.941xf(104,204),
f(R2)=(1-0.941)xf(105,203)+0.941xf(105,204),
104.615-104=0.615
所以f(P)=(1-0.615)xf(R1)+0.615xf(R2),
其中f表示的那一点的像素值,这样就计算出了f(P),实际上就是放大后图像(136,345)处对应的像素值。
下面还是先看Mat的存储形式。Mat和Matlab里的数组格式有点像,但一般是二维向量,如果是灰度图,一般存放<uchar>类型;如果是RGB彩色图,存放<Vec3b>类型。
单通道灰度图数据存放格式:
多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:
注意通道的顺序反转了:BGR。通常情况内存足够大的话图像的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,这中情况在访问时可以提供很大方便。可以用 isContinuous()函数来判断图像数组是否为连续的。
下面介绍两种访问mat中像素值的方法
第一种:
uchar* p = I.ptr<uchar>(i);//获得第i行的指针
p[j]得到的就是第i行第j个像素,注意j指的是从前往后数第j个元素,不是上图标的column j,例如j=4,则表示上图的column 1的绿色值。
第二种
I.at<Vec3b>(i,j)[k],表示的是第i行第j列的第k个值,例如i=0,j=1,k=1表示上图中的row 0,column 1的绿色值。
介绍完这些,下面是我写的缩放的源代码了
#include<iostream> #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> using namespace cv; using namespace std; int x, y; void scale(Mat &srcmat, Mat &desmat, double sx, double sy); int main(int argc ,char** argv){ Mat srcimg = imread("opencv.jpg", IMREAD_COLOR);// Read the file if (!srcimg.data) // Check for invalid input { cout << "Could not open or find the image" << std::endl; return -1; } double sx = 0, sy = 0; cout << "please input scale x and scale y" << endl; cin >> sx >> sy; namedWindow("source img",WINDOW_AUTOSIZE); imshow("source img", srcimg); y = int(srcimg.cols*sy), x = int(srcimg.rows*sx); Mat scaimg(x, y, CV_8UC3, Scalar::all(0)); scale(srcimg, scaimg, sx, sy); imshow("scaled img", scaimg); waitKey(); return 0; } void scale(Mat &srcmat, Mat &desmat, double sx, double sy){ int nc = x, nl = y,srccol=0,srcrow=0; double alph = 0.0, beta = 0.0; for (int i = 0; i < nc; i++){ uchar* desdata = desmat.ptr<uchar>(i); for (int j = 0; j < nl; j++){ srcrow = int(i / sx); //下面的的几个if是判断放大后图像对应到原图像的坐标是否越界的 if (srcrow >= srcmat.rows-1){ srcrow = srcmat.rows - 2; } alph = i / sx - srcrow; if (alph >= 1) alph = 1; srccol = int(j / sy); if (srccol >= srcmat.cols-1) srccol = srcmat.cols-2; beta = j / sy - srccol; if (beta >= 1) beta=1; for (int k = 0; k < 3; k++){ double kk=srcmat.at<Vec3b>(srcrow, srccol)[k] + beta*(srcmat.at<Vec3b>(srcrow, srccol+1)[k] - srcmat.at<Vec3b>(srcrow, srccol)[k]); double jj = srcmat.at<Vec3b>(srcrow+1, srccol)[k] + beta*(srcmat.at<Vec3b>(srcrow+1, srccol + 1)[k] - srcmat.at<Vec3b>(srcrow+1, srccol)[k]); desdata[j*3+k] = kk + alph*(jj - kk); } } } }
结果如下:
参考:http://en.wikipedia.org/wiki/Bilinear_interpolation
http://blog.csdn.net/xiaowei_cqu/article/details/7771760