传统图片超分算法——双三次插值 (Bicubic)、附C++源码

这篇具有很好参考价值的文章主要介绍了传统图片超分算法——双三次插值 (Bicubic)、附C++源码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

呼,花了一个下午,终于是写完加调试完了所有的代码。

双三次插值介绍

之前我写的这篇博客中讲了什么是超分,并实现了单线性插值算法和双线性插值算法。在这里将再介绍一种插值算法——双三次插值算法。

首先,双三次插值法需要参考16个点(4x4),因此插值效果会比双线性插值法要好,但同时时间开销也会更大。在 OpenCV 中,可在 cv::resize 函数中使用 cv::INTER_CUBIC 选项选择使用双三次插值算法改变图像大小。

在学习的过程中,我参考了这篇博客,其中的插值算法写成表达式的形式为:
f ( x , y ) = ∑ i = 0 3 ∑ j = 0 3 f ( x i , y j ) W ( x − x i ) W ( y − y j ) f(x,y)=\sum_{i=0}^3\sum_{j=0}^3f(x_i,y_j)W(x-x_i)W(y-y_j) f(x,y)=i=03j=03f(xi,yj)W(xxi)W(yyj)
其中,(x,y) 表示待插值的像素点的坐标,f(x,y)表示经过计算待插值像素点应该插入的值, ( x i , y j ) (x_i,y_j) (xi,yj) i , j = 0 , 1 , 2 , 3 i,j=0,1,2,3 i,j=0,1,2,3 表示待插值点附近的 4x4 领域的点。

W W W函数称为 BiCubic 函数。与该博客中不同的是,原博客中的像素点可以是浮点数,而在 OpenCV 中坐标只能为整数。因此在这里 W W W 函数需要做个变换。

原博客中的 W W W 函数:
W ( x ) = { ( a + 2 ) ∣ x ∣ 3 − ( a + 3 ) ∣ x ∣ 2 + 1 ∣ x ∣ ≤ 1 a ∣ x ∣ 3 − 5 a ∣ x ∣ 2 + 8 a ∣ x ∣ − 4 a 1 < ∣ x ∣ < 2 0 e l s e W(x)=\left\{ \begin{matrix} (a+2)|x|^3-(a+3)|x|^2+1 \qquad|x|\le1\\ a|x|^3-5a|x|^2+8a|x|-4a\qquad1<|x|<2\\ 0\qquad \qquad \qquad else\\ \end{matrix} \right. W(x)= (a+2)x3(a+3)x2+1x1ax35ax2+8ax4a1<x<20else

在这里使用的 W W W 函数:
W ( x ) = { ( a + 2 ) ∣ x / t ∣ 3 − ( a + 3 ) ∣ x / t ∣ 2 + 1 ∣ x ∣ ≤ t a ∣ x / t ∣ 3 − 5 a ∣ x / t ∣ 2 + 8 a ∣ x / t ∣ − 4 a t < ∣ x ∣ < 2 t 0 e l s e W(x)=\left\{ \begin{matrix} (a+2)|x/t|^3-(a+3)|x/t|^2+1 \qquad |x|\le t\\ a|x/t|^3-5a|x/t|^2+8a|x/t|-4a\qquad t<|x|<2t \\ 0\qquad \qquad \qquad else\\ \end{matrix} \right. W(x)= (a+2)x/t3(a+3)x/t2+1xtax/t35ax/t2+8ax/t4at<x<2t0else

其中 t t t 为超分放大倍数, a a a 为指定的值,OpenCV 源码中给的是 -0.75。

代码实现

首先是需要循环遍历每个像素点,逐个计算像素点的值。

进一步地,如何计算像素点的值呢?例如,下图中绿色的点是原图像上的像素点,红色的点是待计算的点,则用黄色框起来的点为参考的像素点。假设其坐标为(i,j)(在dst上,即保存超分结果的图像上),以行为例,则参考src的行号如下图所标注。对于列也是同理。因此我们知道了上述表达式中 x i x_i xi y j y_j yj 的所有取值。
传统图片超分算法——双三次插值 (Bicubic)、附C++源码 W ( x − x i ) W(x-x_i) W(xxi) 中的 x − x i x-x_i xxi 表示当前像素点距离参考像素点的 x x x 方向上的距离( y y y 方向同理)。根据 W W W 函数的表达式可以判断出,这个距离应该要小于两倍的 t i m e s times times (放大倍数)。以左边的参考点为例(向右也是同理),在dst 图像中,对于离待计算点最近的左边的像素点,它们的 x x x 方向上的距离为 i % t i m e s i\%times i%times 。又因为在 dst 图中,两个绿色的点之间的间距为放大倍数,因此计算点离最左边的参考点的距离为 i % t i m e s + t i m e s i\%times+times i%times+times。同时考虑到右侧的点,可以写成更加一般的形式: i % t i m e s − i ∗ t i m e s i\%times-i*times i%timesitimes。其中 i = − 1 , 0 , 1 , 2 i=-1,0,1,2 i=1,0,1,2 。对于列也是同理。

(注:在代码中,为了与逐个枚举像素点的循环变量区分开,此处的 i j 会写成 r c。其中 r 代表 row,表示行; c 代表 col, 表示列。)

以下是完整的代码:

//权重计算函数
//输入:x:自变量的值  times: 图片超分倍数
//返回值:W(x)计算之后的值
double W(int x,int times){
    
    x=std::abs(x);

    //OpenCV 中给的是 -0.75
    double a=-0.75;
    double abs_=std::abs((double)x/(double)times);
    
    if(x>=2*times) return 0.0;
    else if(x>times){
        double ans=a*(abs_)*(abs_)*(abs_)-5*a*(abs_)*(abs_)+8*a*(abs_)-4*a;
        return ans;
    }
    else{
        double ans=(a+2)*(abs_)*(abs_)*(abs_)-(a+3)*(abs_)*(abs_)+1;
        return ans;
    }

    //不会执行到这里的,哈哈
    return -1.0f;
}

//双三次插值
//输入:原图像(单通道),超分倍数
//返回值:超分后的图像
cv::Mat biCubicInterp(const cv::Mat &src, int times){
    cv::Mat dst=cv::Mat::zeros(cv::Size(src.cols*times,src.rows*times),CV_8UC1);

    for(int i=0;i<dst.rows;i++){
        for(int j=0;j<dst.cols;j++){
            double val=0.0;

            //利用周围16个像素点计算插值
            for(int r=-1;r<=2;r++){
                //如果参考点超出图像范围,则舍弃
                if(i/times+r<0 || i/times+r>=src.rows) continue;
                
                for(int c=-1;c<=2;c++){
                    //如果参考点超出图像范围,则舍弃
                    if(j/times+c<0 || j/times+c>=src.cols) continue;
                    
                    val+=(src.at<uchar>(i/times+r,j/times+c)*W(i%times-r*times,times)*W(j%times-c*times,times));
                }
            }

            //防止越界溢出
            if(val>255) val=255;
            if(val<0) val=-val;
            
            dst.at<uchar>(i,j)=(unsigned char)val;
        }
    }
    return dst;
}

运行结果

对一张 100x100 分辨率的图片,分别用双线性插值法和双三次插值法放大8倍,处理成 800x800 分辨率的图片。运行结果如下:

双线性插值法:
传统图片超分算法——双三次插值 (Bicubic)、附C++源码
双三次插值法:
传统图片超分算法——双三次插值 (Bicubic)、附C++源码仔细观察可以发现,在像素值发生突变的位置(如数字 5 的周围),双线性插值法超分后的图像会出现方块效应,而使用双三次插值法超分后的图像就显得比较光滑,但是时间开销也会更大。

算法复杂度分析

双线性插值法 O ( 3 n 2 ) O(3n^2) O(3n2)

双三次插值法 O ( 16 n 2 ) O(16n^2) O(16n2)文章来源地址https://www.toymoban.com/news/detail-466607.html

到了这里,关于传统图片超分算法——双三次插值 (Bicubic)、附C++源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 低照度图片增强——传统算法

    光照因素一直是影响成像质量的一个关键因素,夜间等光照环境较差的条件下的图片往往细节丢失、分辨不清,信噪比低下。低照度图像增强是指通过一系列算法和技术,增强在低照度或弱光条件下拍摄的图像的可视化质量。本文主要介绍一些传统的低照度图像增强算法,给

    2024年02月08日
    浏览(28)
  • 基于Matlab的插值问题(Lagrange插值法、三次插值多项式)

    要求 1、 利用Lagrange插值公式 L n ( x ) = ∑ k = 0 n ( ∏ i = 0 , i ≠ k n x − x i x k − x i ) y k {L_n}(x) = sumlimits_{k = 0}^n {left( {prodlimits_{i = 0,i ne k}^n {frac{{x - {x_i}}}{{{x_k} - {x_i}}}} } right)} {y_k} L n ​ ( x ) = k = 0 ∑ n ​ ( i = 0 , i  = k ∏ n ​ x k ​ − x i ​ x − x i ​ ​ ) y k ​ 编写出

    2024年02月07日
    浏览(31)
  • 分段三次hermit插值

    一、插值函数建立 设函数 y = F ( x ) y=F(x) y = F ( x ) 在区间 [ a , b ] [a,b] [ a , b ] 上有定义,且已知在离散点 a = x 0 x 1 . . . x n = b a=x_0x_1...x_n = b a = x 0 ​ x 1 ​ ... x n ​ = b 上的值 y 0 , y 1 , . . . y n , y_0,y_1,...y_n, y 0 ​ , y 1 ​ , ... y n ​ , f ( x ) f(x) f ( x ) 在 [ x j , x j + 1 ] [x_j,x_

    2024年02月11日
    浏览(31)
  • 三次样条插值——三弯矩方法

             三次样条插值方法,是将一个曲线函数分成多段,每相邻的两个标准点就是一个三次多项式函数.也就是说,n+1个标准点,共有 n 个三次函数.求解分段时共有4*n个未知系数  其相邻的分段函数之间连续,一阶导连续,二阶导也连续。 因此  每个分段三次样条函数要

    2024年01月23日
    浏览(31)
  • 牛顿插值法、拉格朗日插值法、三次插值、牛顿插值多项式、拉格朗日插值多项式

    两点式线性插值 调用Matlab库函数 拉格朗日二次插值: 牛顿二次插值 结果分析:通过对比不同插值方法,可以看到在一定范围内(高次会出现龙格现象),插值次数越高,截断误差越小(插值结果越接近于真实函数值);同时,对于相同次数的插值,由于不同的插值方法它们

    2024年02月11日
    浏览(36)
  • 三次、五次多项式插值(附代码)

      三次、五次多项式插值在工程实践中很常见。求解多项式的系数最直接的方法是根据端点处的约束条件,列出线性方程组,再写成矩阵方程AX=B,然后用通用的方法(如高斯消元法、LU分解等)解矩阵方程。   本博文利用matlab符号计算的功能,给出三次、五次多项式插值的

    2024年02月06日
    浏览(39)
  • 【数学建模笔记】【第三讲】拉格朗日插值法,牛顿插值法,分段三次埃尔米特插值法及其MATLAB实践

    温馨提示:本文共有3748字,阅读并理解全文大概需要15-20分钟 数模比赛中,常常需要根据已知的函数点进行数据、模型的处理和分析,而有时候现有的数据是极少的,不足以支撑分析的进行,这时就 需要使用一些数学的方法,“模拟产生”一些新的但又比较靠谱的值来满足

    2024年02月05日
    浏览(37)
  • 分段三次Hermite插值的原理、matlab代码和Python代码

    目录 一、分段三次Hermite插值 二、分段三次Hermite插值多项式的推导 三、分段三次Hermite插值的matlab实现  四、分段三次Hermite插值的Python实现 已知,求一个分段函数H(x),使其满足: (1) (2)在每个子区间  上,H(x)是次数不超过3的多项式。 称满足上述条件的函数H(x)为分段

    2024年02月05日
    浏览(30)
  • Opencv实现的三次样条曲线(Cubic Spline)插值

    1.样条曲线简介 样条曲线(Spline)本质是分段多项式实函数,在实数范围内有: S:[a,b]→R ,在区间 [a,b] 上包含 k 个子区间[ti−1,ti],且有: a=t0t1⋯tk−1tk=b(1) 对应每一段区间 i 的存在多项式: Pi:[ti−1,ti]→R,且满足于: S(t)=P1(t) , t0≤tt1,S(t)=P2(t) , t1≤tt2,⋮S(t)=Pk(t) , 

    2024年02月06日
    浏览(28)
  • 三次样条插值(python完美实现,三种形式都有!)

    使用的是公式法迭代,没有用牛顿,我认为更加精准,牛顿只是方便手算误差自然大。 ----以上为个人思考与见解,有误请指点,有想法也可联系交流!                ~~~~~~~~~~~~~~                             谢谢观看!

    2024年02月08日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包