C++ OpenCV实现图像双三次插值算法

  • 前言
  • 一、图像双三次插值算法原理
  • 二、C++ OpenCV代码
    • 1.计算权重矩阵
    • 2.遍历插值
  • 3. 测试及结果

前言

近期在学习一些传统的图像处理算法,比如传统的图像插值算法等。传统的图像插值算法包括邻近插值法、双线性插值法和双三次插值法,其中邻近插值法和双线性插值法在网上都有很详细的介绍以及用c++编写的代码。但是,网上关于双三次插值法的原理介绍虽然很多,也有对应的代码,但是大多都不是很详细。因此基于自己对原理的理解,自己编写了图像双三次插值算法的c++ opencv代码,在这里记录一下。


一、图像双三次插值算法原理

首先是原理部分。图像双三次插值的原理,就是目标图像的每一个像素都是由原图上相对应点周围的4x4=16个像素经过加权之后再相加得到的。这里的加权用到的就是三次函数,这也是图像双三次插值算法名称的由来(个人猜测)。用到的三次函数如下图所示:

最关键的问题是,这个三次函数的输入和输出分别代表啥。简单来说输入就是原图对应点周围相对于这点的4x4大小区域的坐标值,大小在0~2之间,输出就是这些点横坐标或者纵坐标的权重4个横坐标、4个纵坐标,对应相乘就是4x4大小的权重矩阵,然后使用此权重矩阵对原图相对应的区域进行相乘并相加就可以得到目标图点的像素。
这里有一篇文章可以帮助大家更好地理解,《实现图像缩放功能的Matlab插值算法研究与比较》,这篇文章的系数a=-1。

首先,u和v是什么呢?举一个例子,对于一幅100x100的灰度图像,要将其放大到500x500,那么其缩放因子sx=500/100=5,sy=500/100=5。现在目标图像是500x500,需要用原图的100x100个像素值来填满这500x500个空,根据src_x=i/sx和src_y=j/sy可以得到目标像素坐标(i,j)所对应的原图像素坐标(src_x, src_y),这个src_x和src_y的小数部分就是上图中的u和v。
理解了u和v,就可以利用u和v来计算双三次插值算法的权重了。上面说了三次函数的输入是原图对应点周围相对于这点的4x4大小区域的坐标值,对于上面这幅图而言,横坐标有四个输入,分别是1+u,u,1-u,2-u纵坐标也有四个输入,分别是1+v,v,1-v,2-v,根据三次函数算出权重之后两两相乘就是对应的4x4大小的权重矩阵。
知道了怎么求权重矩阵之后,就可以遍历整幅图像进行插值了。下面是基于自己对原理的理解编写的c++ opencv代码,代码没有做优化,但是能够让大家直观地理解图像双三次插值算法。

二、C++ OpenCV代码

1.计算权重矩阵

前面说了权重矩阵就是横坐标的4个输出和纵坐标的4个输出相乘,因此只需要求出横坐标和纵坐标相对应的8个输出值就行了。

代码如下:

std::vector<double> getWeight(double c, double a = 0.5)
{//c就是u和v,横坐标和纵坐标的输出计算方式一样std::vector<double> temp(4);temp[0] = 1 + c; temp[1] = c; temp[2] = 1 - c; temp[3] = 2 - c;//y(x) = (a+2)|x|*|x|*|x| - (a+3)|x|*|x| + 1   |x|<=1//y(x) = a|x|*|x|*|x| - 5a|x|*|x| + 8a|x| - 4a  1<|x|<2std::vector<double> weight(4);weight[0] = (a * pow(abs(temp[0]), 3) - 5 * a * pow(abs(temp[0]), 2) + 8 * a * abs(temp[0]) - 4 * a);weight[1] = (a + 2) * pow(abs(temp[1]), 3) - (a + 3) * pow(abs(temp[1]), 2) + 1;weight[2] = (a + 2) * pow(abs(temp[2]), 3) - (a + 3) * pow(abs(temp[2]), 2) + 1;weight[3] = (a * pow(abs(temp[3]), 3) - 5 * a * pow(abs(temp[3]), 2) + 8 * a * abs(temp[3]) - 4 * a);return weight;
}

2.遍历插值

代码如下:

void bicubic(cv::Mat& src, cv::Mat& dst, int dst_rows, int dst_cols)
{dst.create(dst_rows, dst_cols, src.type());double sy = static_cast<double>(dst_rows) / static_cast<double>(src.rows);double sx = static_cast<double>(dst_cols) / static_cast<double>(src.cols);cv::Mat border;cv::copyMakeBorder(src, border, 1, 1, 1, 1, cv::BORDER_REFLECT_101);//处理灰度图if (src.channels() == 1){for (int i = 1; i < dst_rows + 1; ++i){int src_y = (i + 0.5) / sy - 0.5; //做了几何中心对齐if (src_y < 0) src_y = 0;if (src_y > src.rows - 1) src_y = src.rows - 1;src_y += 1;//目标图像点坐标对应原图点坐标的4个纵坐标int i1 = std::floor(src_y);int i2 = std::ceil(src_y);int i0 = i1 - 1;int i3 = i2 + 1;double u = src_y - static_cast<int64>(i1);std::vector<double> weight_x = getWeight(u);for (int j = 1; j < dst_cols + 1; ++j){int src_x = (j + 0.5) / sy - 0.5;if (src_x < 0) src_x = 0;if (src_x > src.rows - 1) src_x = src.rows - 1;src_x += 1;//目标图像点坐标对应原图点坐标的4个横坐标int j1 = std::floor(src_x);int j2 = std::ceil(src_x);int j0 = j1 - 1;int j3 = j2 + 1;double v = src_x - static_cast<int64>(j1);std::vector<double> weight_y = getWeight(v);//目标点像素对应原图点像素周围4x4区域的加权计算(插值)double pix = weight_x[0] * weight_y[0] * border.at<uchar>(i0, j0) + weight_x[1] * weight_y[0] * border.at<uchar>(i0, j1)+ weight_x[2] * weight_y[0] * border.at<uchar>(i0, j2) + weight_x[3] * weight_y[0] * border.at<uchar>(i0, j3)+ weight_x[0] * weight_y[1] * border.at<uchar>(i1, j0) + weight_x[1] * weight_y[1] * border.at<uchar>(i1, j1)+ weight_x[2] * weight_y[1] * border.at<uchar>(i1, j2) + weight_x[3] * weight_y[1] * border.at<uchar>(i1, j3)+ weight_x[0] * weight_y[2] * border.at<uchar>(i2, j0) + weight_x[1] * weight_y[2] * border.at<uchar>(i2, j1)+ weight_x[2] * weight_y[2] * border.at<uchar>(i2, j2) + weight_x[3] * weight_y[2] * border.at<uchar>(i2, j3)+ weight_x[0] * weight_y[3] * border.at<uchar>(i3, j0) + weight_x[1] * weight_y[3] * border.at<uchar>(i3, j1)+ weight_x[2] * weight_y[3] * border.at<uchar>(i3, j2) + weight_x[3] * weight_y[3] * border.at<uchar>(i3, j3);if (pix < 0) pix = 0;if (pix > 255)pix = 255;dst.at<uchar>(i - 1, j - 1) = static_cast<uchar>(pix);}}}//处理彩色图像else if (src.channels() == 3){for (int i = 1; i < dst_rows + 1; ++i){int src_y = (i + 0.5) / sy - 0.5;if (src_y < 0) src_y = 0;if (src_y > src.rows - 1) src_y = src.rows - 1;src_y += 1;int i1 = std::floor(src_y);int i2 = std::ceil(src_y);int i0 = i1 - 1;int i3 = i2 + 1;double u = src_y - static_cast<int64>(i1);std::vector<double> weight_y = getWeight(u);for (int j = 1; j < dst_cols + 1; ++j){int src_x = (j + 0.5) / sy - 0.5;if (src_x < 0) src_x = 0;if (src_x > src.rows - 1) src_x = src.rows - 1;src_x += 1;int j1 = std::floor(src_x);int j2 = std::ceil(src_x);int j0 = j1 - 1;int j3 = j2 + 1;double v = src_x - static_cast<int64>(j1);std::vector<double> weight_x = getWeight(v);cv::Vec3b pix;pix[0] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[0] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[0]+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[0] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[0]+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[0] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[0]+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[0] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[0]+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[0] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[0]+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[0] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[0]+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[0] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[0]+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[0] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[0];pix[1] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[1] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[1]+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[1] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[1]+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[1] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[1]+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[1] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[1]+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[1] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[1]+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[1] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[1]+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[1] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[1]+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[1] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[1];pix[2] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[2] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[2]+ weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[2] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[2]+ weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[2] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[2]+ weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[2] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[2]+ weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[2] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[2]+ weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[2] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[2]+ weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[2] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[2]+ weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[2] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[2];for (int i = 0; i < src.channels(); ++i){if (pix[i] < 0) pix = 0;if (pix[i] > 255)pix = 255;}dst.at<cv::Vec3b>(i - 1, j - 1) = static_cast<cv::Vec3b>(pix);}} }
}

3. 测试及结果

int main()
{cv::Mat src = cv::imread("C:\\Users\\Echo\\Pictures\\Saved Pictures\\bilateral.png");cv::Mat dst;bicubic(src, dst, 309/0.5, 338/0.5);cv::imshow("gray", dst);cv::imshow("src", src);cv::waitKey(0);
}

彩色图像(放大两倍)

C++ OpenCV实现图像双三次插值算法相关推荐

  1. 图像识别——(java)opencv(处理图像各种算法测试)

    https://note.youdao.com/ynoteshare/index.html?id=e0df335c7bba4d7633874375539c228a&type=note& ...

  2. 怎么用python编程实现二次差值多项式_双三次插值算法详解 含python实现

    一. 图像双三次插值算法原理: 假设源图像 A 大小为 m*n ,缩放后的目标图像 B 的大小为 M*N .那么根据比例我们可以得到 B(X,Y) 在 A 上的对应坐标为 A(x,y) = A( X* ...

  3. OpenCV实现图像对齐ECC算法(附完整代码)

    OpenCV实现图像对齐ECC算法 OpenCV实现图像对齐ECC算法 OpenCV实现图像对齐ECC算法 #include <opencv2/imgcodecs.hpp> #includ ...

  4. Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection)

    Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection) 理论 Feature Detection using FAST Select ...

  5. opencv 图像 抠图 算法_opencv提取轮廓与抠图

    自然图像抠图/视频抠像技术梳理(image matting, video matting)-计算机视视觉专题1 图像抠图算法学习 - Shared Sampling for Real-Time Alp ...

  6. Python基于OpenCV的图像去雾算法[完整源码&部署教程]

    1.图片识别 2.视频展示 [项目分享]Python基于OpenCV的图像去雾算法[完整源码&部署教程]_哔哩哔哩_bilibili 3.算法原理 图像增强算法常见于对图像的亮度.对比度.饱和 ...

  7. 图像的放大:双三次插值算法(C++实现)

    双线性插值算法的不足就是细节处理的不好,换句话说,就是曲线拟合得不够光滑,所以又有了双三次插值算法.双三次插值算法是基于周围的16个像素点,通过计算16个像素点的权重,累积得到增加点的像素值的. 简单 ...

  8. OpenCV每日函数 计算摄影模块(1) 图像修复算法 inpaint函数

    一.概述 该算法使用区域邻域恢复图像中的选定区域.该功能可用于去除扫描照片上的灰尘和划痕,或去除静止图像或视频中不需要的物体. 二.inpaint函数 1.函数原型 void cv::inpaint ...

  9. 图像常用的插值算法:最近邻插值、双线性插值和双三次插值算法

    图像常用的插值算法 最近邻插值算法 双线性插值算法 双三次插值(bicubic)算法 三种插值算法的优缺点 插值算法是图像缩放中的一项基本且重要的算法:在图像缩放中,输出图像像素点坐标可能对应输入图像 ...

  10. Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)

    Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++) Baumer工业相机 Baumer工业相机使用图像算法增加图像的技术背景 Baumer工业相机 ...

最新文章

  1. python操作word填表_Python 自动化办公—Word 文本操作命令
  2. lua学习笔记之编译、运行及错误
  3. ubuntu20.04安装noetic版本ros
  4. python操作mongodb之六自定义类型存储
  5. vm显示打不开 /dev/vmmon:Broken pipe
  6. html条纹填充色,HTML5/Canvas 上传图片的彩色斑马条纹遮罩效果
  7. linux cpu监控方案,Linux性能优化和监控系列(二)分析CPU性能
  8. php include 导航栏,PHP全栈开发(八):CSS Ⅹ 导航栏制作
  9. docker server 容器连接sql_借力 Docker ,三分钟搞定 MySQL 主从复制!
  10. angular1的复选框指令--checklistModel
  11. 【嵌入式】基于ARM的嵌入式Linux开发总结
  12. 双系统Win10下装Ubuntu16.04
  13. godot引擎学习10
  14. AndroidJNI 函数介绍
  15. 麦子学院深度学习视频SVM人脸识别课程代码修改及实现
  16. 【设计模式】之适配器(Adapter)
  17. 【Flutter】动画学习(一)Canvas绘制
  18. 正确的java语句,下面哪条Java语句可以正确输出“HelloJava!”?()
  19. matlab绘图函数plot调整显示参数
  20. 网站安全漏洞检测报告年度安全分析

热门文章

  1. Java阶段三:基础项目—家庭记录收支程序
  2. vue实例的参数说明
  3. postman安装与使用
  4. 【HTML】Html页面跳转的5种方式
  5. mysql 2008安装_关于SQL 2008安装时已经有实例名称。
  6. linux卸载intel驱动程序,删除 Ubuntu Intel 官方驱动包并添加 pdadoka PPA
  7. origin调整纵坐标刻度值_Origin数据图坐标刻度值特殊标注的方法
  8. 微信公众号配置JSAPI支付
  9. Java中swing修改左上角的图标
  10. Spring Cloud