图像缩放之双线性内插值法

今天学习了图像缩放的第二种方法:双线性内插值法。

数学原理

一、线性插值

这里我觉得讲的很清楚
用另一种方式记录一下自己的理解:
如图所示:
插值法示意图

假设已知量为:
P1P2//BC,AC=2,P2C=1,AB=2.5。(这里我直接给的是长度,没有用坐标了)
这时候我们需要求出P1B的长度。

可以得到:P1B=AB*(P2C/AC)
其中P2C/AC就是插值系数,记作α。
那么上面的链接给出的定义线性插值法:是指使用连接两个已知量的直线来确定在这两个已知量之间的一个
未知量的值的方法。其中两个已知量在上图中只得就是A和B的位置(即AB的长),未知量就是P1B的长度。

注意:这里的线P1P2,可以是在三角形内部,也可以在三角形外部,当在线段内部时,叫做线性内插值。

双线性插值

假设原图像A的大小为m*n,新图像B的大小为M*N,如果我们要求B(X,Y)处的像素值:
用最近邻法可以求得B(X,Y)在A中对应的位置为:A(x,y)=(X*(m/M),Y*(N/n))。
在最近邻法中个,我们把小数部分直接剔除了。这里我们保留下来做一下处理:
x=i+u;
y=j+v;
这里的i和j分别表示xy的整数部分,uv分别表示小数部分。下面我们来看一张图:

这里写图片描述

图中红点就是目标图像B(X,Y)与源图像对应的位置A(x,y),这时候我们先从行的方向来解释双线性插
值:我们发现红点的位置处于j和j+1位置的中间更偏向于j+1,在最近邻法中,我们如果用四舍五入的方法,
那么我们会直接用其中一个像素替代y=j+v的位置,这样的方法会使得缩放后的边缘很粗糙。根据线性插值
的原理我们可以把两个像素值加上一定的权值来确定出y=j+v的像素。这样可以很大程度上改善缩放后的图
片,即:
pixel B( ,Y)= pixel( ,y)=pixel( ,j)*k1+pixel( ,j+1)k2;//不考虑列方向
其中,k1,k2就是差值参数。
同样的对于行方向也有:
pixel B(X , )= pixel( x , )=pixel( i, )*k3+pixel( i+1 , )k4;//不考虑列方向
最后,我们将两个方向综合考虑,可以得到这样一种思路:
pixel B(X , Y)= pixel( x , y)=pixel( i,j )*α1+pixel( i ,j+1 )α2+pixel( i+1 ,j )α3+pixel( i+1 ,j+1 )α4;
其中
α1=k1*k3=(1-u)(1-v)
α2=k2*k3=u(1-v)
α3=k1*k4=(1-u)v
α4=k2*k4=uv
至此,可以得到缩放图形B(X,Y)处的像素值。

程序实现

/*****************************10—6*****************************
*功能:用双线性插值法对源图像进行缩放
*数学原理:
假设原图像A的大小为m*n,新图像B的大小为M*N
如果我们要求B(X,Y)处的像素值:
我们首先可以得到B(X,Y)在图像A中对应的位置(x,y)=(X*(m/M),Y*(N/n))
这个时候求得的x,y是小数值,我们可以通过这个小数值坐标找到距离最近的四个像素点,
假设x,y取下限整数后为_x,_y,小数部分为xp,yp,即x=_x+xp,y=_y+yp;
那么最近的四个点为:(_x,_y),(_x+1,_y),(_x,_y+1),(_x+1,_y+1)
四个系数分别为a = (1-xp)*(1-yp),b = (1-xp)*yp,c =xp*(1-yp),d =  xp*yp
最终,pixelB(X,Y)=a*pixelA(_x,_y)+b*pixelA(_x+1,_y)+c*(_x,_y+1)+d*(_x+1,_y+1)
************************************************************/
#include <opencv2\opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
    Mat image = imread("lena.jpg");//源图像

    /*缩小操作*/
    int Col_s = 2*image.cols / 3;//新的图像长宽缩小至2/3
    int Row_s = 2*image.rows / 3;

    Mat smallerImage(Row_s, Col_s, CV_8UC3);


    for (int i = 0; i < Row_s; i++){
        for (int j = 0; j < Col_s; j++){
            int x, y; float xp, yp; float a, b, c, d;
            x = i * 3 / 2;//对应坐标整数部分
            y = j * 3 / 2;
            xp = i * 3 / 2.0 - x;//对应坐标小数部分
            yp = j * 3 / 2.0 - y;
            a = (1 - xp)*(1 - yp);
            b = (1 - xp)*yp;
            c = xp*(1 - yp);
            d = xp*yp;

            smallerImage.at<Vec3b>(i, j) = a*(image.at<Vec3b>(x, y)) + b*(image.at<Vec3b>(x, y+1))
                + c*(image.at<Vec3b>(x + 1, y)) + d*(image.at<Vec3b>(x+1, y+1));
        }
    }


    /*放大操作*/
    int Col_b = 2 * image.cols ;//新的图像长宽放大2倍
    int Row_b = 2 * image.rows ;

    Mat biggerImage(Row_b, Col_b, CV_8UC3);
    int x = 0; int y = 0; float xp = 0.0; float yp = 0.0; float a = 0.0; float b = 0.0; float c = 0.0; float d = 0.0;
    for (int i = 0; i < Row_b; i++){
        for (int j = 0; j < Col_b; j++){
            x = i / 2;//对应坐标整数部分
            y = j / 2;
            xp = i / 2.0 - x;//对应坐标小数部分
            yp = j / 2.0 - y;
            a = (1 - xp)*(1 - yp);
            b = (1 - xp)*yp;
            c = xp*(1 - yp);
            d = xp*yp;

            x = x > (image.rows - 2) ? (image.rows - 2) : x;//边缘处理
            x = x < 0 ? 0 : x;
            y = y > (image.cols - 2) ? (image.cols - 2) : y;
            y = y < 0 ? 0 : y;

            biggerImage.at<Vec3b>(i, j) = a*(image.at<Vec3b>(x, y)) + b*(image.at<Vec3b>(x, y + 1))
                + c*(image.at<Vec3b>(x + 1, y)) + d*(image.at<Vec3b>(x + 1, y + 1));
        }
    }
    imshow("iamge", image);
    imshow("smallerImage", smallerImage);
    imshow("biggerImage", biggerImage);
    waitKey(0);

    return 0;

}

**这里要注意,在放大源图像时,要对边缘部分进行处理。否则会出现问题。

效果展示

缩小一半后效果图

源图像

放大一倍效果图

话说我是没看出有啥效果不同。。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值