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

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

数学原理

一、线性插值

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

假设已知量为:
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;

}

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

效果展示

缩小一半后效果图

源图像

放大一倍效果图

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
线性图像处理中常用的一种,用于图像、旋转等操作。下面是Java代码实现图像旋转功能的示例: ```java public static BufferedImage rotateImage(BufferedImage image, double angle) { // 计算旋转后的图像大小 double radians = Math.toRadians(angle); double sin = Math.abs(Math.sin(radians)); double cos = Math.abs(Math.cos(radians)); int newWidth = (int) Math.floor(image.getWidth() * cos + image.getHeight() * sin); int newHeight = (int) Math.floor(image.getHeight() * cos + image.getWidth() * sin); // 创建旋转后的图像 BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, image.getType()); Graphics2D g2d = rotatedImage.createGraphics(); AffineTransform at = new AffineTransform(); at.translate((newWidth - image.getWidth()) / 2, (newHeight - image.getHeight()) / 2); at.rotate(radians, image.getWidth() / 2, image.getHeight() / 2); g2d.setTransform(at); g2d.drawImage(image, 0, 0, null); g2d.dispose(); // 双线性调整像素 BufferedImage resultImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType()); double xRatio = (double) (newWidth - 1) / (double) (image.getWidth() - 1); double yRatio = (double) (newHeight - 1) / (double) (image.getHeight() - 1); for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { int x2 = (int) Math.floor(x * xRatio); int y2 = (int) Math.floor(y * yRatio); double xDiff = (x * xRatio) - x2; double yDiff = (y * yRatio) - y2; int index1 = y2 * newWidth + x2; int index2 = index1 + 1; int index3 = index1 + newWidth; int index4 = index3 + 1; if (index4 < rotatedImage.getWidth() * rotatedImage.getHeight()) { int pixel1 = rotatedImage.getRGB(x2, y2); int pixel2 = rotatedImage.getRGB(x2 + 1, y2); int pixel3 = rotatedImage.getRGB(x2, y2 + 1); int pixel4 = rotatedImage.getRGB(x2 + 1, y2 + 1); int red = (int) (getRed(pixel1) * (1 - xDiff) * (1 - yDiff) + getRed(pixel2) * xDiff * (1 - yDiff) + getRed(pixel3) * (1 - xDiff) * yDiff + getRed(pixel4) * xDiff * yDiff); int green = (int) (getGreen(pixel1) * (1 - xDiff) * (1 - yDiff) + getGreen(pixel2) * xDiff * (1 - yDiff) + getGreen(pixel3) * (1 - xDiff) * yDiff + getGreen(pixel4) * xDiff * yDiff); int blue = (int) (getBlue(pixel1) * (1 - xDiff) * (1 - yDiff) + getBlue(pixel2) * xDiff * (1 - yDiff) + getBlue(pixel3) * (1 - xDiff) * yDiff + getBlue(pixel4) * xDiff * yDiff); int alpha = (int) (getAlpha(pixel1) * (1 - xDiff) * (1 - yDiff) + getAlpha(pixel2) * xDiff * (1 - yDiff) + getAlpha(pixel3) * (1 - xDiff) * yDiff + getAlpha(pixel4) * xDiff * yDiff); int rgb = getRGB(alpha, red, green, blue); resultImage.setRGB(x, y, rgb); } } } return resultImage; } public static int getRed(int pixel) { return (pixel >> 16) & 0xff; } public static int getGreen(int pixel) { return (pixel >> 8) & 0xff; } public static int getBlue(int pixel) { return pixel & 0xff; } public static int getAlpha(int pixel) { return (pixel >> 24) & 0xff; } public static int getRGB(int alpha, int red, int green, int blue) { return (alpha << 24) | (red << 16) | (green << 8) | blue; } ``` 其中,`rotateImage`方接收一个`BufferedImage`对象和旋转角度,返回旋转后的图像;`getRed`、`getGreen`、`getBlue`、`getAlpha`和`getRGB`方是辅助方,用于获取或设置像素的红、绿、蓝、透明度和RGB。在`rotateImage`方中,首先计算旋转后的图像大小,创建旋转后的图像,然后使用`AffineTransform`类进行旋转,最后使用双线性调整像素。在双线性中,先计算每个像素在旋转后的图像中对应的坐标,然后根据坐标获取周围四个像素的颜色,再根据双线性公式计算出新的像素颜色,最后设置到结果图像中即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值