双线性插值理论与代码实例
1. 双线性插值
假设源图像大小为mxn,目标图像为axb。那么两幅图像的边长比分别为:m/a和n/b。注意,通常这个比例不是整数,编程存储的时候要用浮点型。目标图像的第(i,j)个像素点(i行j列)可以通过边长比对应回源图像。其对应坐标为(i*m/a,j*n/b)。显然,这个对应坐标一般来说不是整数,而非整数的坐标是无法在图像这种离散数据上使用的。双线性插值通过寻找距离这个对应坐标最近的四个像素点,来计算该点的值(灰度值或者RGB值)。
若图像为灰度图像,那么(i,j)点的灰度值的数学计算模型是:
f(x,y)=b1+b2x+b3y+b4xy
其中b1,b2,b3,b4是相关的系数。关于其的计算过程如下如下:
如图,已知Q12,Q22,Q11,Q21,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对R1和R2两个点进行插值,这个很简单,然后根据R1和R2对P点进行插值,这就是所谓的双线性插值。
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。
假如我们想得到未知函数 在点 的值,假设我们已知函数 在 , , , 及 四个点的值。
首先在 x 方向进行线性插值,得到
然后在 y 方向进行线性插值,得到
这样就得到所要的结果 ,
如果选择一个坐标系统使得 的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为
或者用矩阵运算表示为
与这种插值方法名称不同的是,这种插值方法的结果通常不是线性的,它的形式是
- {\displaystyle b_{1}+b_{2}x+b_{3}y+b_{4}xy.\,}
常数的数目都对应于给定的 f 的数据点数目
- {\displaystyle b_{1}=f(0,0)}
- {\displaystyle b_{2}=f(1,0)-f(0,0)}
- {\displaystyle b_{3}=f(0,1)-f(0,0)}
- {\displaystyle b_{4}=f(1,1)-f(1,0)-f(0,1)+f(0,0)}
线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。
2.存在的问题
这部分的前提是,你已经明白什么是双线性插值并且在给定源图像和目标图像尺寸的情况下,可以用笔计算出目标图像某个像素点的值。当然,最好的情况是你已经用某种语言实现了网上一大堆博客上原创或转载的双线性插值算法,然后发现计算出来的结果和matlab、openCV对应的resize()函数得到的结果完全不一样。
那这个究竟是怎么回事呢?
其实答案很简单,就是坐标系的选择问题,或者说源图像和目标图像之间的对应问题。
按照网上一些博客上写的,源图像和目标图像的原点(0,0)均选择左上角,然后根据插值公式计算目标图像每点像素,假设你需要将一幅5x5的图像缩小成3x3,那么源图像和目标图像各个像素之间的对应关系如下:
只画了一行,用做示意,从图中可以很明显的看到,如果选择右上角为原点(0,0),那么最右边和最下边的像素实际上并没有参与计算,而且目标图像的每个像素点计算出的灰度值也相对于源图像偏左偏上。
那么,让坐标加1或者选择右下角为原点怎么样呢?很不幸,还是一样的效果,不过这次得到的图像将偏右偏下。
最好的方法就是,两个图像的几何中心重合,并且目标图像的每个像素之间都是等间隔的,并且都和两边有一定的边距,这也是matlab和openCV的做法。如下图:
如果你不懂我上面说的什么,没关系,只要在计算对应坐标的时候改为以下公式即可,
int x=(i+0.5)*m/a-0.5
int y=(j+0.5)*n/b-0.5
instead of
int x=i*m/a
int y=j*n/b
利用上述公式,将得到正确的双线性插值结果
总结:
总结一下,我得到的教训有这么几条。
1.网上的一些资料有的时候并不靠谱,自己还是要多做实验。
2.不要小瞧一些简单的、基本的算法,让你写你未必会写,而且其中可能还藏着一些玄妙。
3.要多动手编程,多体会算法,多看大牛写的源码(虽然有的时候很吃力,但是要坚持看)。
源码:
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);}}}
}//sx=1.2,sy=1.6
//图像基本信息输出int image_height = image.size().height;int image_width = image.size().width;cout << "Image Info: height:" << image_height << " width:" << image_width << endl;//===============================================================================================================================//图像处理-----双线性插值cout << "Please input the Sx and Sy:" << endl;float Sx, Sy;cin >> Sx >> Sy;cout << "Sx = " << Sx << "; Sy = " << Sy << ";" << endl;Mat final_img;int final_img_height, final_img_width;final_img_height = image.size().height * Sx;final_img_width = image.size().width * Sy;final_img.create(final_img_height, final_img_width, CV_8UC1);int y, x;int x1, x2, y1, y2;float temp1, temp2;for (y = 0; y < final_img_height; y++){for (x = 0; x < final_img_width; x++){x1 = (int)(x / Sx);x2 = x1 + 1;y1 = (int)(y / Sy);y2 = y1 + 1;//判断边界if (y2 >= image_height || x2 >= image_width){final_img.at<uchar>(y, x) = image.at<uchar>(y1, x1);continue;}temp1 = (x2 - x / Sx) * image.at<uchar>(y1, x1) + (x / Sx - x1) * image.at<uchar>(y1, x2);temp2 = (x2 - x / Sx) * image.at<uchar>(y2, x1) + (x / Sx - x1) * image.at<uchar>(y2, x2);final_img.at<uchar>(y, x) = (uchar)((y2 - y / Sy) * temp1 + (y / Sy - y1) * temp2);}}//===============================================================================================================================//显示处理前后的图像namedWindow("original_image");imshow("original_image", image);namedWindow("final_image");cout << "Final image Info: height:" << final_img.size().height << " width:" << final_img.size().width << endl;imshow("final_image", final_img);
//sx=1.2,sy=1.2
双线性插值理论与代码实例相关推荐
- 扩展卡尔曼滤波(EKF)理论讲解与实例(matlab、python和C++代码)
扩展卡尔曼滤波(EKF)理论讲解与实例(matlab.python和C++代码) 文章目录 扩展卡尔曼滤波(EKF)理论讲解与实例(matlab.python和C++代码) 理论讲解 KF和EKF模型 ...
- php获取数据3中方式,PHP面向对象之3种数据访问方式详解(代码实例)
PHP面向对象之3种数据访问方式详解(代码实例) 本文目标 掌握PHP中数据访问的3种方式的定义和作用 1.public 2.protected 3.private (一).数据访问的3种方式 1.P ...
- Python——字典类型理论及应用实例
字典类型字典类型理论及应用实例 一.字典类型理论 二.字典类型应用实例 一.字典类型理论 1.使用大括号({})建立,每一个元素是一个键值对,使用方法如下: {<键1>:<值1> ...
- VINS理论与代码详解2——单目视觉跟踪
VINS理论与代码详解2--单目视觉跟踪 一.Feature_tracker文件夹中 首先讲第一部分,也就是纯粹的图像处理部分内容,在论文中的第IV点观测值预处理的A部分视觉前端处理,为了更好的理解代 ...
- 无迹(损)卡尔曼滤波(UKF)理论讲解与实例
无迹(损)卡尔曼滤波(UKF)理论讲解与实例 文章目录 无迹(损)卡尔曼滤波(UKF)理论讲解与实例 理论讲解 模型对比 UT变换 UKF算法步骤 预测部分 更新部分 应用实例 CTRV模型 预测处理 ...
- 常见的PCA、tSNE、UMAP降维及聚类基本原理及代码实例
常见的降维方法基本原理及代码实例 0.前言:什么时候要降维聚类?降维目的-方法概述 1.PCA(主成分分析) 1.1PCA概念 1.2 PCA代码实例 2.tSNE 2.1tSNE概念 2.2 tSN ...
- python画烟花_python烟花效果的代码实例
天天敲代码的朋友,有没有想过代码也可以变得很酷炫又浪漫?今天就教大家用Python模拟出绽放的烟花,工作之余也可以随时让程序为自己放一场烟花秀. 这个有趣的小项目并不复杂,只需一点可视化技巧,100余 ...
- python烟花效果的代码实例|CSDN创作打卡
python烟花效果的代码实例 一.整体概念梳理 二.基本知识:用Python和Tkinter设计烟花 三.使用Tkinter模拟 四.全部代码 一.整体概念梳理 在本篇文章里小编给大家整理的是关于p ...
- php 3 3公派算法代码,PHP常见算法合集代码实例
许多人都说 算法是程序的核心,一个程序的好于差,关键是这个程序算法的优劣,下面是一些常用的算法和实例,大家可以好好学习下 一.文件夹遍历 function allFile($path = __DIR_ ...
最新文章
- 【Java】 leetCode 删除链表中等于给定值 val 的所有节点。
- linux下各个目录里面都装了什么
- C#垃圾回收学习总结
- 开关电源中的反馈电阻
- java 单例设计_Java 之单例设计模式
- CVX学习笔记(转载
- 远程监控系统集成方案
- HttpClient的3种超时
- VS2005项目的安装与布署,包括卸载
- iOS集成支付宝H5支付实现跳转与回调的解决方案
- java 如何将word 转换为ftl_3种方法轻松将PDF转换为Word文档,办公必备
- 微信读书 《围城》笔记
- 利用C++求坐标系中两点间距离
- 用AD9画51单片机的最小系统
- mfc 控件显示 被遮挡_MFC控件显示和隐藏的问题
- python基础ppt_python基础知识(三)
- webrtc QOS方法一(NACK实现)
- 浅谈ES6基础——Promise
- 怎么把MP4视频进行压缩
- 2. 样式,大纲和目录
热门文章
- CYQ.Data 轻量数据访问层(八) 自定义数据表实现绑定常用的数据控件(中)
- ArcGIS Engine 刷新问题
- NSURLSessionDataTask与NSOperationQueue实现多文件断点下载(任意时刻终止进程,重启应用,自动重启下载)...
- Spring Security认证过程
- SQL Server 中关于EXCEPT和INTERSECT的使用方法
- javase总结报告
- MySQL limit 优化,百万至千万级快速分页:复合索引
- linux学习工作记录----配置基于ip的虚拟主机
- 玩的起也要输的起 。。没什么,照样支持你。。
- html日历显示不完整,求html代码,显示日历和时间的代码