数字图像处理之双线性插值
数字图像中实现缩放的方法有很多种,其中一种就是双线性插值,在实现图像缩放时,有两种方法来确定缩放后的图像的像素值,第一种是根据原图像中的的像素找到对应的缩放后的图像中的像素,第二种是根据缩放后的图像找到对应的原图像中的像素,如下图
但是第一种方法有缺点,因为小图中的像素点到大图中的像素点不是满射,因此大图中的点不能完全有像素值,第二种方法也有缺点,大图中的点逆映射为小图中的点时,得到的像素坐标值可能不是整数,一种办法是采用最近邻方法,即将得到的坐标值与相邻的原图像中的像素坐标值比较,取离得最近的坐标值对应的像素值作为缩放后的图像对应的坐标值的像素值,这种办法可能导致图像失真,因此采用双线性差值的办法来进行计算相应的像素值。
右侧是最一般的双线性插值,下面举一个实际的例子来说明双线性插值在图像缩放中的应用。
假设一个图像的大小是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>类型。
#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); } } } }
结果如下: