1.目的
线性插值的目的是什么?
线性插值一般是用在如图像缩放,区域缩放美图瘦脸大眼等中.
以图像缩放为例.
设缩放前图像坐标为(x,y),缩放后图像坐标为(x',y').
前向变换.png
我们现在已知缩放前图像信息和缩放系数,目的是求的缩放后的图像的灰度值.
那么这个灰度值怎么求?
目标图像灰度值就是根据目标像素在原图中的位置的灰度值线性插值得到的.
后向变换
根据缩放后图像的像素位置(x',y')求得缩放前图像中的像素位置就是后向变换(x,y).
根据公式,后向变换计算出来的坐标一般不是整数。最邻近插值就是把后向变换的坐标取整,获得原图坐标处的像素值作为目标图像的像素值。
上述变换公式都假定原图和目标图像的坐标原点在左上角,前向变换没有任何问题,但是后向变换就存在问题了。
如下图所示,
假设把5x5的图像缩小为3x3的,那么原图与目标图像之间的坐标对应关系如下图所示:这里只画出第一行!
image.png
其中,黑色的原图,红色的框是目标图像。
变换后的图像的3个像素平均占据在原图的5个像素的前3个位置上,但是由于目标图像的3个像素起始点选在了原图的左上角,导致原图最后一个像素坐标没有以及最后一行的像素坐标都没有使用到。为了充分利用原图每一个像素坐标,应该保持两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,如下图所示。
image.png
最终后向变换公式为
image.png
2.线性插值公式
在求得图像缩放后的坐标之后,就需要缩放后坐标对应的像素灰度值了。
image.png
设目标图像P点(x,y),
现在要利用变换前坐标(x',y')最邻近的4个坐标点(左上 右上 左下 右下)的像素灰度值拟合出P点的像素值.
(x’, y’)是P点坐标变换前像素位置的整数部分.
(u, v)是P点坐标变换前像素位置x',y'取整后的小数部分.
线性插值是以距离为权重的一种插值方式.
image.png
3.程序
以图像缩放为例
// 双线性插值
#if 1
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// src_im缩放前图像
// dst_im 缩放后图像
void interpolate(
Mat src_im,
double scale_x,
double scale_y,
Mat& dst_im)
{
for (int i = 0; i < dst_im.rows; i++)
{
for (int j = 0; j < dst_im.cols; j++)
{
//浮点数 变换前坐标
double before_x = double(j+0.5)/scale_x - 0.5f;
double before_y = double(i+0.5)/scale_y - 0.5;
//获得变换前最近的四个顶点,取整
int top_y = static_cast<int>(before_y);
int bottom_y = top_y + 1;
int left_x = static_cast<int>(before_x);
int right_x = left_x + 1;
//计算变换前坐标的小数部分
double u = before_x - left_x;
double v = before_y - top_y;
// 如果计算的原始图像的像素大于真实原始图像尺寸
if ( (top_y >= src_im.rows-1) && (left_x >= src_im.cols-1))//右下角
{
for (size_t k = 0; k < src_im.channels(); k++)
{
dst_im.at<Vec3b>(i,j)[k]
= (1.-u)*(1.-v)*src_im.at<Vec3b>(top_y,left_x)[k];
}
}
else if (top_y >= src_im.rows - 1)//最后一行
{
for (size_t k = 0; k < src_im.channels(); k++)
{
dst_im.at<Vec3b>(i,j)[k]
= (1.-u)*(1.-v)*src_im.at<Vec3b>(top_y, left_x)[k]
+ (1.-v)*u*src_im.at<Vec3b>(top_y,right_x)[k];
}
}
else if (left_x >= src_im.cols - 1)//最后一列
{
for (size_t k = 0; k < src_im.channels(); k++)
{
dst_im.at<Vec3b>(i,j)[k]
= (1.-u)*(1.-v)*src_im.at<Vec3b>(top_y,left_x)[k]
+ (v)*(1.-u)*src_im.at<Vec3b>(bottom_y,left_x)[k];
}
}
else
{
for (size_t k = 0; k < src_im.channels(); k++)
{
dst_im.at<Vec3b>(i,j)[k]
= (1.-u)*(1.-v)*src_im.at<Vec3b>(top_y,left_x)[k]
+ (1.-v)*(u)*src_im.at<Vec3b>(top_y,right_x)[k]
+ (v)*(1.-u)*src_im.at<Vec3b>(bottom_y,left_x)[k]
+ (u)*(v)*src_im.at<Vec3b>(bottom_y,right_x)[k];
}
}
}
}
}
int main()
{
Mat im = imread("timg.jpg",1);
if (im.empty())
{
cerr << "读取图片失败..." << endl;
return -1;
}
//缩放系数
const double sx = 3;
const double sy = 3;
//resize()函数测试
Mat resize_im;
resize(im, resize_im, Size(0, 0), sx, sy, INTER_LINEAR);
imwrite("resize_im.jpg",resize_im);
// 自定义resize函数实现
int result_H = static_cast<int>(im.rows * sy);
int result_W = static_cast<int>(im.cols * sx);
Mat result_im = Mat::zeros(cv::Size(result_W, result_H), im.type());
if (sx == 1 && sy == 1)//不做缩放运算,直接返回原图
{
result_im = im;
return 0;
}
interpolate(im, sx, sy, result_im);
imwrite("result_im.jpg",result_im);
//做差比较结果
Mat check_im;
subtract(resize_im, result_im, check_im);
imwrite("check_im.jpg",check_im);
std::cout<<"finished!"<<std::endl;
return 0;
}
#endif
结果:
timg.jpg
resize_im.jpg
result_im.jpg
check_im.jpg
两幅图像相减图像为全黑表示像素值相同.
网友评论