图像的三次B样条插值原理与C++实现
01 前言
前文我们讲过图像中最常用的三种插值算法:最邻近插值、双线性插值、双三次插值。
常见图像插值算法的原理与C++实现https://mp.weixin.qq.com/s/G9xXJKwuIHm4KyDndVKRxw
双三次插值算法的C++实现与SSE指令优化https://mp.weixin.qq.com/s/B7ocbmh2Uep9205DA8yaJw
插值的本质,就是使用周围点的值来计算插值点的值,如下图所示,红点的值已知,黑点的值未知,那么通过一定算法,使用黑点周围红点的值来计算黑点的值,就是插值。
在图像中也是类似的,整型坐标点的像素值已知,浮点型坐标点的像素值未知,所以如果想求浮点型坐标点的像素值,则需要使用其周围整型坐标点的像素值来计算,如下图所示:
02 三次B样条插值原理
图像处理中几种常见的插值算法基本都是取浮点型坐标点周围的n*n个整型坐标点的像素值进行加权和,从而得到该浮点型坐标点的像素值,不同插值算法的主要区别在于权重计算方法,如下式(其中W为权重):
三次B样条插值与双三次插值的原理几乎一样,区别仅在于插值的基函数不一样,所以我们先来复习一下双三次插值原理。
如下图,假设想求图像中浮点型坐标点(x', y')的像素值。
双三次插值算法使用点(x', y')周围4*4个整型点的像素值来计算点(x', y')的像素值。如下式,其中I表示像素值,W表示权重。
权重W(i,j)的计算如下式,其中a取值范围-1~0之间,一般取固定值-0.5,p(i,j).x和p(i,j).y分别表示点p(i,j)的x坐标、y坐标。
上式中函数w(d)通常被称为双三次插值的基函数,三次B样条插值除了基函数与双三次插值不一样,其它都相同,其基函数如下:
03 几种常见插值算法的总结与比较
最邻近插值算法
最邻近插值取离浮点型坐标点的最近点像素值作为其像素值,也可以看成使用浮点型坐标点周围2*2个整型点的像素值来计算其像素值,不过只有最靠近的那个点权重为1,其余3个点权重系数都为0。
双线性插值算法
双线性插值与最邻近插值类似,同样使用浮点型坐标点周围2*2个整型点的像素值来计算其像素值,不过其周围每个整型点的权重都不为0。
双三次插值算法
双三次插值使用浮点型坐标点周围4*4个整型点的像素值来计算其像素值。
三次B样条插值算法
三次B样条插值除了基函数与双三次插值不一样,其它都相同。
下面我们取d从-2.5到2.5,数据点间隔0.01,分别画出以上几种插值算法的基函数曲线。从曲线可以知道,d越小,也即周围整型点越靠近插值点,那么该整型点的权重越大。从曲线平滑度来看:三次B样条插值>双三次插值>双线性插值>最邻近插值。
04 几种常见插值算法的代码实现
最近邻插值算法的代码实现
最邻近插值取离浮点型坐标点的最近点像素值作为其像素值,也即相当于对浮点型x、y坐标分别四舍五入,从而得到最近点的整型坐标。
//src--uchar型图像数据
//x_float--浮点型的x坐标
//y_float--浮点型的y坐标
uchar nearst_inner(Mat src, float x_float, float y_float)
{int x = (int)(x_float + 0.5); //四舍五入int y = (int)(y_float + 0.5); //四舍五入return src.ptr<uchar>(y)[x];
}
双线性插值算法的代码实现
//基函数
float line_w_f(float x)
{return (abs(x) <= 1) ? (1 - abs(x)) : 0;
}//src--uchar型图像数据
//x_float--浮点型的x坐标
//y_float--浮点型的y坐标
uchar line_inner(Mat src, float x_float, float y_float)
{int x = floor(x_float);int y = floor(y_float);float a0_0 = line_w_f(x - x_float) * line_w_f(y - y_float);float a0_1 = line_w_f(x + 1 - x_float) * line_w_f(y - y_float);float a1_0 = line_w_f(x - x_float) * line_w_f(y + 1 - y_float);float a1_1 = line_w_f(x + 1 - x_float) * line_w_f(y + 1 - y_float);float sum = src.ptr<uchar>(y)[x] * a0_0 + src.ptr<uchar>(y)[x + 1] * a0_1 + src.ptr<uchar>(y + 1)[x] * a1_0 + src.ptr<uchar>(y + 1)[x + 1] * a1_1;return ((uchar)sum);
}
双三次插值算法的代码实现
//基函数
float cubic_w_f(float x, float a)
{if (x <= 1){return 1 - (a + 3) * x * x + (a + 2) * x * x * x;}else if (x < 2){return -4 * a + 8 * a * x - 5 * a * x * x + a * x * x * x;}return 0.0;
}//计算权重系数
void cal_cubic_coeff(float x, float y, float* coeff)
{float u = x - floor(x) + 1;float v = y - floor(y) + 1;float a = -0.15;float A[4];A[0] = cubic_w_f(abs(u), a);A[1] = cubic_w_f(abs(u - 1), a);A[2] = cubic_w_f(abs(u - 2), a);A[3] = cubic_w_f(abs(u - 3), a);for (int s = 0; s < 4; s++){float C = cubic_w_f(abs(v - s), a);coeff[s * 4] = A[0] * C;coeff[s * 4 + 1] = A[1] * C;coeff[s * 4 + 2] = A[2] * C;coeff[s * 4 + 3] = A[3] * C;}
}//双三次插值
uchar cubic_inner(Mat src, float x_float, float y_float, float a)
{float coeff[16];cal_cubic_coeff(x_float, y_float, coeff); //计算权重系数float sum = 0.0;int x0 = floor(x_float) - 1;int y0 = floor(y_float) - 1;for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){sum += coeff[i * 4 + j] * src.ptr<uchar>(y0 + i)[x0 + j];}}return ((uchar)sum);
}
三次B样条插值算法的代码实现
//基函数
float bpline_w_f(float x)
{if (x <= 1){return 2.0/3.0 - (1.0 - x/2.0)*x*x;}else if (x > 1 && x <= 2){return (2.0 - x) * (2.0 - x) * (2.0 - x) / 6.0;;}return 0.0;
}//计算权重系数
void cal_bpline_coeff(float x, float y, float* coeff)
{float u = x - floor(x) + 1;float v = y - floor(y) + 1;float A[4];A[0] = bpline_w_f(abs(u));A[1] = bpline_w_f(abs(u - 1));A[2] = bpline_w_f(abs(u - 2));A[3] = bpline_w_f(abs(u - 3));for (int s = 0; s < 4; s++){float C = bpline_w_f(abs(v - s));coeff[s * 4] = A[0] * C;coeff[s * 4 + 1] = A[1] * C;coeff[s * 4 + 2] = A[2] * C;coeff[s * 4 + 3] = A[3] * C; }}//三次B样条插值
uchar bpline_inner(Mat src, float x_float, float y_float)
{float coeff[16];cal_bpline_coeff(x_float, y_float, coeff); //计算权重系数float sum = 0.0;int x0 = floor(x_float) - 1;int y0 = floor(y_float) - 1;for (int i = 0; i < 4; i++){for (int j = 0; j < 4; j++){ sum += coeff[i * 4 + j] * src.ptr<uchar>(y0 + i)[x0 + j];}}return ((uchar)sum);
}
05 几种常见插值算法应用于图像缩放的结果对比
将以上几种插值算法分别应用于Lena图像的缩放,测试代码如下:
//src -- 输入图像
//dst -- 输出图像
//row_m -- 行的缩放倍数
//col_m -- 列的缩放倍数
//inner_type -- 插值类型 0:最邻近,1:双线性,2:双三次,3:三次B样条
void resize_img(Mat src, Mat& dst, float row_m, float col_m, int inner_type)
{const int row = (int)(src.rows * row_m);const int col = (int)(src.cols * col_m);const float x_a = 1.0 / col_m;const float y_a = 1.0 / row_m;Mat dst_tmp = Mat::zeros(row, col, CV_8UC1);if (inner_type == 0){for (int i = 0; i < row; i++){uchar* p = dst_tmp.ptr<uchar>(i);float y = i * y_a;for (int j = 0; j < col; j++){float x = j * x_a;p[j] = nearst_inner(src, x, y);}}}else if (inner_type == 1){for (int i = 0; i < row; i++){uchar* p = dst_tmp.ptr<uchar>(i);float y = i * y_a;for (int j = 0; j < col; j++){float x = j * x_a;p[j] = line_inner(src, x, y);}}}else if (inner_type == 2){for (int i = 0; i < row; i++){uchar* p = dst_tmp.ptr<uchar>(i);float y = i * y_a;for (int j = 0; j < col; j++){float x = j * x_a;p[j] = cubic_inner(src, x, y, -0.5);}}}else{for (int i = 0; i < row; i++){uchar* p = dst_tmp.ptr<uchar>(i);float y = i * y_a;for (int j = 0; j < col; j++){float x = j * x_a;p[j] = bpline_inner(src, x, y);}}}dst_tmp.copyTo(dst);
}//测试函数,调用以上图像缩放,并显示结果
void resize_img_test(void)
{Mat img = imread("image/lena.png", CV_LOAD_IMAGE_GRAYSCALE);float mul = 3.0; //宽和高的放大倍数都为3Mat img_resize_nearst, img_resize_line, img_resize_cubic, img_resize_bpline;resize_img(img, img_resize_nearst, mul, mul, 0);resize_img(img, img_resize_line, mul, mul, 1);resize_img(img, img_resize_cubic, mul, mul, 2);resize_img(img, img_resize_bpline, mul, mul, 3);imshow("img", img);imshow("img_resize_nearst", img_resize_nearst); //最近邻imshow("img_resize_line", img_resize_line); //双线性imshow("img_resize_cubic", img_resize_cubic); //双三次imshow("img_resize_bpline", img_resize_bpline); //三次B样条waitKey();
}
运行以上代码,分别使用几种插值算法对Lena的宽、高都放大3倍,得到结果如下:
原图
最近邻插值
双线性插值
双三次插值
三次B样条插值
由以上结果可知放大图像之后:
马赛克现象:最近邻插值>双线性插值≥双三次插值>三次B样条插值
边缘锯齿现象:最近邻插值>双线性插值≥双三次插值>三次B样条插值
插值后模糊度:最近邻插值<双线性插值≤双三次插值<三次B样条插值
为了对比更明显一点,我们把Lena图像中帽子边缘区域截取出来对比:
微信公众号如下,会不定期更新精彩内容,欢迎关注:
图像的三次B样条插值原理与C++实现相关推荐
- 十三种基于直方图的图像全局二值化算法原理、实现、代码及效果。
图像二值化的目的是最大限度的将图象中感兴趣的部分保留下来,在很多情况下,也是进行图像分析.特征提取与模式识别之前的必要的图像预处理过程.这个看似简单的问题,在过去的四十年里受到国内外学者的广泛关注,产 ...
- GANs系列:用于图像风格迁移的CycleGAN网络原理解读
CycleGAN论文:https://arxiv.org/pdf/1703.10593.pdf 一.前言 目前关于GAN应用,比较有意思的应用就是GAN用在图像风格迁移,图像降噪修复,图像超分辨率了, ...
- 插值法(最邻近,双线性,双三次)的原理及实现
插值法(最邻近,双线性,双三次)的原理及实现 常用的插值方法有最邻近插值法.双现象插值法和双三次插值法等,主要用于图像的放大或缩小. 缩小图像(或称为下采样(subsampled) 或降采样(down ...
- 十三种基于直方图的图像全局二值化算法原理、实现、代码及效果
十三种基于直方图的图像全局二值化算法实现 1. 什么是基于直方图的图像全局二值化算法 2. 灰度平均值 3. 百分比阈值(P-Tile法) 3. 基于双峰的阈值 3.1 基于平均值的阈值 3.2 基于 ...
- 怎么解释三线圈直流电机工作原理更好?
简 介: 针对网络上小型直流电机原理说明视频存在的问题,通过CSDN视频制作系统进行阐释.进一步把TEASOFT中的文稿-视频转换功能进行完善,实现快速零后期视频制作. 经过这次测试,为今后MOOC课 ...
- 十三种基于直方图的图像全局二值化算法原理、实现、代码及效果(转)
源:十三种基于直方图的图像全局二值化算法原理.实现.代码及效果.
- c语言是以文件为单位编译,c语言从头开始(三:编译器工作原理) (我们还可以自己编写头文件后缀是xx.h并把它当前代码文件所在目录我们要用就可以直接通过下面代码使用)...
c语言从头开始(三:编译器工作原理) [[枫歌枫歌]c语言从头开始(三:编译器工作原理)]https://toutiao.com/group/6568056688174170628/?iid=1590 ...
- 基于序列图像的三维体绘的视线投射算法
基于序列图像的三维体绘的视线投射算法(Ray Casting) 一.体绘算法步骤 1.体数据的生成: 将序列图像的象素数据部分剥离出来(如果是JPG等压缩类型的数据,还需要先解压缩),按照相对的上 ...
- QuaggaJS在给定图像中定位条形码的工作原理
QuaggaJS在给定图像中定位条形码的工作原理 一.介绍 二.步骤 1. 创建图像的二进制表示 2. 将图像切成网格(20 x 15个单元) 3. 提取每个细胞的骨架 4. 组件标记 5. 确定组件 ...
- 《网络攻防》实验三:免杀原理与实践
<网络攻防>实验三:免杀原理与实践 世界上公认的第一个在个人电脑上广泛流行的病毒是1986年初诞生的大脑(C-Brain)病毒,编写该病毒的是一对巴基斯坦兄弟,两兄弟经营着一家电脑公司,以 ...
最新文章
- acm经典题Mark
- 【PP】长交期计划(LTP)简介
- Aupera:FPGA让视频编码与AI结合水到渠成
- 安德鲁斯Selector简介
- 欢迎使用CSDN-markdown编辑器保存
- 起点计算机网,《零起点计算机》网第5课.pdf
- web端log4net输出错误日志到mysql
- MYSQL常用操作(一)之设置ROOT密码,连接,常用命令
- 二叉树的遍历 C/C++语言实现
- html文件变成巨大,巨大的JavaScript HTML5 blob(从大ArrayBuffers)在客户端构建一个巨大的文件...
- python循环结构高一信息技术_高中信息技术《循环结构1》优质课教学设计、教案...
- java坦克大战思路总结
- 微信支付v2升级v3注意事项
- OSChina 周四乱弹 —— 月中发工资还没到家……
- 网页加载过程+性能优化+安全
- Spring MVC实现查找酒店用例
- 2022-2028年中国滚动轴承行业市场发展规模及投资机会分析报告
- 一对一 视频聊天源码,不要小瞧社交平台的盈利方式
- 《中学科技》期刊简介及投稿邮箱
- 什么叫做石英表_机械表与石英表的区别是什么?