简介
双线性插值是指现在某个方向进行两次线性插值,之后在另外一个方向对插值的结果再进行一次线性插值。以下图为例,已知 Q 11 Q_{11} Q11, Q 12 Q_{12} Q12, Q 21 Q_{21} Q21, Q 22 Q_{22} Q22四个点的坐标和像素值、 P P P点的坐标,求 P P P点的像素值。双线性插值的做法是首先在 x x x方向上做两次线性插值获得 R 1 R_{1} R1点和 R 2 R_{2} R2点的像素值,之后在 y y y方向上做一次线性插值获得 P P P点的像素值。
代码实现
在实际处理的过程中,依次遍历目标 M a t Mat Mat所有像素点 ( x , y ) (x,y) (x,y),找到像素点 ( x , y ) (x,y) (x,y)在原 M a t Mat Mat上的对应点 ( s x , s y ) (s_{x},s_{y}) (sx,sy)。由于 s x s_{x} sx、 s y s_{y} sy的值可能不是整数,所以取最接近的四个点进行双线性插值。话不多说,直接来看c++代码。
Mat sxxcz1(Mat img, int h, int w) {
Mat img2(h, w, CV_8UC3, Scalar::all(0));
float ori_h = img.rows;
float ori_w = img.cols;
float rate_h = ori_h / h;
float rate_w = ori_w / w;
for (int i = 0; i < h; i++) {
float f_h = i * rate_h;
int s_h = cvFloor(f_h);
f_h -= s_h;
if (s_h < 0) {
f_h = 0, s_h = 0;
}
if (s_h >= ori_h - 1) {
f_h = 1, s_h = ori_h - 2;
}
for (int j = 0; j < w; j++) {
float f_w = j * rate_w;
int s_w = cvFloor(f_w);
f_w -= s_w;
if (s_w < 0) {
f_w = 0, s_w = 0;
}
if (s_w >= ori_w - 1) {
f_w = 1, s_w = ori_w - 2;
}
for (int k = 0; k < img.channels(); k++) {
img2.at<Vec3b>(i, j)[k] = (1 - f_h) * (1 - f_w) * img.at<Vec3b>(s_h, s_w)[k]
+ (1 - f_h) * f_w * img.at<Vec3b>(s_h, s_w + 1)[k]
+ f_h * (1 - f_w) * img.at<Vec3b>(s_h + 1, s_w)[k]
+ f_h * f_w * img.at<Vec3b>(s_h + 1, s_w + 1)[k];
}
}
}
return img2;
}
效果展示如下:
但是在使用上面的代码处理比较简单的图像时,放大后的图像会有明显的违和感。
针对这个问题,查阅了一下官方代码的源码,发现在计算周围四个点的权重时,官方使用的代码更合理,于是对自己的代码进行了修改。修改后的代码如下:
Mat sxxcz2(Mat img, int h, int w) {
Mat img2(h, w, CV_8UC3, Scalar::all(0));
float ori_h = img.rows;
float ori_w = img.cols;
float rate_h = ori_h / h;
float rate_w = ori_w / w;
for (int i = 0; i < h; i++) {
float f_h = ((i + 0.5) * rate_h - 0.5);
int s_h = cvFloor(f_h);
f_h -= s_h;
if (s_h < 0) {
f_h = 0, s_h = 0;
}
if (s_h >= ori_h - 1) {
f_h = 1, s_h = ori_h - 2;
}
short cbufh[2];
cbufh[0] = cv::saturate_cast<short>((1.f - f_h) * 2048);
cbufh[1] = 2048 - cbufh[0];
for (int j = 0; j < w; j++) {
float f_w = ((j + 0.5) * rate_w - 0.5);
int s_w = cvFloor(f_w);
f_w -= s_w;
if (s_w < 0) {
f_w = 0, s_w = 0;
}
if (s_w >= ori_w - 1) {
f_w = 1, s_w = ori_w - 2;
}
short cbufw[2];
cbufw[0] = cv::saturate_cast<short>((1.f - f_w) * 2048);
cbufw[1] = 2048 - cbufw[0];
for (int k = 0; k < img.channels(); k++) {
cout << (1 - f_w) * img.at<Vec3b>(s_h, s_w)[k] << endl;;
img2.at<Vec3b>(i, j)[k] = (cbufh[0] * cbufw[0] * img.at<Vec3b>(s_h, s_w)[k]
+ cbufh[0] * cbufw[1] * img.at<Vec3b>(s_h, s_w + 1)[k]
+ cbufh[1] * cbufw[0] * img.at<Vec3b>(s_h + 1, s_w)[k]
+ cbufh[1] * cbufw[1] * img.at<Vec3b>(s_h + 1, s_w + 1)[k]) >> 22;
}
}
}
return img2;
}
运行修改后的代码,图像质量明显提升。
文章来源:https://www.toymoban.com/news/detail-723328.html
总结
通过本文的撰写,自己对双线性插值算法有了更加深刻的理解。在复现代码的过程中,发现自己还是不够关注运行效率等细节上的问题,在今后的工作和生活中还是需要多加锻炼。文章来源地址https://www.toymoban.com/news/detail-723328.html
到了这里,关于双线性插值简介及C++代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!