前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >数字图像放大算法

数字图像放大算法

作者头像
小白学视觉
发布于 2019-10-24 06:35:04
发布于 2019-10-24 06:35:04
1.7K00
代码可运行
举报
运行总次数:0
代码可运行

上一篇推送中,为大家介绍了几种图像处理算法总结的方法,在本次推送中,二白继续为大家介绍余下的方法。

1.图像放大算法

图像放大有许多算法,其关键在于对未知像素使用何种插值方式。以下我们将具体分析几种常见的算法,然后从放大后的图像是否存在色彩失真,图像的细节是否得到较好的保存,放大过程所需时间是否分配合理等多方面来比较它们的优劣。

当把一个小图像放大的时候,比如放大400%,我们可以首先依据原来的相邻4个像素点的色彩值,按照放大倍数找到新的ABCD像素点的位置并进行对应的填充,但是它们之间存在的大量的像素点,比如p点的色彩值却是不可知的,需要进行估算。

图5原始图像的相邻4个像素点分布图

图6 图像放大4倍后已知像素分布图

1)最临近点插值算法(Nearest Neighbor)

最邻近点插值算法是最简单也是速度最快的一种算法,其做法是將放大后未知的像素点P,將其位置换算到原始影像上,与原始的邻近的4周像素点A,B,C,D做比较,令P点的像素值等于最靠近的邻近点像素值即可。如上图中的P点,由于最接近D点,所以就直接取P=D。

这种方法会带来明显的失真。在A,B中点处的像素值会突然出现一个跳跃,这就是出现马赛克和锯齿等明显走样的原因。最临近插值法唯一的优点就是速度快。

2)双线性插值算法(Bilinear Interpolation)

其做法是將放大后未知的像素点P,將其位置换算到原始影像上,计算的四個像素点A,B,C,D对P点的影响(越靠近P点取值越大,表明影响也越大),其示意图如下。

图7 双线性插值算法示意图

其具体的算法分三步: 第一步插值计算出AB两点对P点的影响得到e点的值。

图8 线性插值算法求值示意图

对线性插值的理解是这样的,对于AB两像素点之间的其它像素点的色彩值,认定为直线变化的,要求e点处的值,只需要找到对应位置直线上的点即可。换句话说,A,B间任意一点的值只跟A,B有关。

第二步,插值计算出CD两点对P点的影响得到f点的值。

第三步,插值计算出ef两点对P点的影响值。

双线性插值算法由于插值的结果是连续的,所以视觉上会比最邻近点插值算法要好一些,不过运算速度稍微要慢一点,如果讲究速度,是一个不错的折衷。

3)双立方插值算法(Bicubic Interpolation)

双立方插值算法与双线性插值算法类似,对于放大后未知的像素点P,将对其影响的范围扩大到邻近的16个像素点,依据对P点的远近影响进行插值计算,因P点的像素值信息来自16个邻近点,所以可得到较细致的影像,不过速度比较慢。

图 9双立方插值的附近4个临近点

好了,在介绍完了这些基础知识后,我们接下来讲解如何实现这些算法。

2.最临近点插值缩放

图10 最邻近点插值放大(2倍)处理效果

(由于Word版面原因,您看到的图像被word自动缩放成合适的宽度了)

最临近插值的的思想很简单。对于通过反向变换得到的的一个浮点坐标,对其进行简单的取整,得到一个整数型坐标,这个整数型坐标对应的像素值就是目的像素的像素值,也就是说,取浮点坐标最邻近的左上角点(对于DIB是右上角,因为它的扫描行是逆序存储的)对应的像素值。可见,最邻近插值简单且直观,但得到的图像质量不高。

下面给出算法的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void CBMPSampleDlg::NearestNeighbourInterpolation(float fWRatio, float fHRatio)
{
//打开旧图像
         CDib srcDib;
         srcDib.AttachMapFile("1.bmp", TRUE);
         //获取旧的高和宽
         LONG lOldHeight = srcDib.m_lpBMIH->biHeight;
         LONG lOldWidth = srcDib.m_lpBMIH->biWidth;
         //保存旧的图像的字节
         WORD wOldBitCount = srcDib.m_lpBMIH->biBitCount;
         //计算新的高和宽
         //四舍五入
         LONG lNewHeight = LONG ( lOldHeight * fHRatio + 0.5 );
         LONG lNewWidth = LONG ( lOldWidth * fWRatio + 0.5 );
         CSize size;
         size.cx = lNewWidth;
         size.cy = lNewHeight;
         //创建图像
         m_Dib.CreatDib( size, wOldBitCount );
         //获取旧图像每行字节数
         LONG lOldLineBytes = WIDTHBYTES( lOldWidth * wOldBitCount );
         //获取新图像每行的字节数
         LONG lNewLineBytes = WIDTHBYTES( lNewWidth * wOldBitCount );
         //计算填充的字节
         int iNewFillBytes = lNewLineBytes - lNewWidth * ( wOldBitCount / 8 );
         int iOldFillBytes = lOldLineBytes - lOldWidth * ( wOldBitCount / 8 );
         //指定图像大小,新图像的大小有不同
         m_Dib.m_dwSizeImage = lNewLineBytes * lNewHeight;
         //分配空间
         m_Dib.AllocImage();
         //定位到数据的开头,也就是图像最后一行的行首
         LPRGBTRIPLE DRgb = ( LPRGBTRIPLE ) m_Dib.m_lpImage;
         LPRGBTRIPLE SRgb = ( LPRGBTRIPLE ) srcDib.m_lpImage;
         //将每行的头指针存储起来
         PRGBTRIPLE *pNewRow = (PRGBTRIPLE *)malloc(sizeof(PRGBTRIPLE) * lNewHeight);
         PRGBTRIPLE * pOldRow = (PRGBTRIPLE *)malloc(sizeof(PRGBTRIPLE) * lOldHeight);
         LONG srcCol = 0,srcRow = 0;
         for (int row = 0 ; row < lNewHeight ; row ++)
         {
                   pNewRow[row] = PRGBTRIPLE( (PBYTE)DRgb + row * lNewLineBytes );
         }
         for (int row = 0 ; row < lOldHeight ; row ++)
         {
                   pOldRow[row] = PRGBTRIPLE ( (PBYTE)SRgb + row * lOldLineBytes );
         }
         for ( LONG row = 0 ; row < lNewHeight ; row ++ )
         {
                   //对每一行进行处理
                   for ( LONG col = 0 ; col < lNewWidth  ; col ++ )
                   {
                            //计算在源图像中的坐标srcCol 和srcRow
                            //四舍五入
                            srcCol = (LONG) floor(col / fWRatio);
                            srcRow = (LONG) floor(row / fHRatio);
                            //判断计算出来的坐标是否在源图像中
                            if ( ( ( srcCol >= 0 ) && ( srcCol < lOldWidth ) ) && ( ( srcRow >= 0 ) && ( srcRow < lOldHeight ) ) )
                            {
                                     //定位指针到源图像的位置
                                     //复制像素的值
                                     *(pNewRow[row] + col) = *(pOldRow[srcRow] + srcCol);
                            }
                   }
         }
    //释放申请的内存
         free( pNewRow );
         free( pOldRow );
         DrawPic();
         m_Dib.CopyToMapFile("最邻近法插值.bmp");
}

图中的fWRatio, fHRatio分别为水平方向和垂直方向的缩放系数,如果大于1则为放大,如果大于0小于1则为缩小。

3.双线性插值缩放

图11 双线性插值放大(2倍)处理效果

(由于Word版面原因,您看到的图像被word自动缩放成合适的宽度了)

下面给出双线性插值的具体实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//双线性插值法
void  CBMPSampleDlg::BiLinearInterpolation(float fWRatio, float fHRatio)
{
         //打开旧图像
         CDib  srcDib;
         srcDib.AttachMapFile("1.bmp",  TRUE);
         //获取旧的高和宽
         LONG  lOldHeight = srcDib.m_lpBMIH->biHeight;
         LONG  lOldWidth = srcDib.m_lpBMIH->biWidth;
         //保存旧的图像的字节
         WORD  wOldBitCount = srcDib.m_lpBMIH->biBitCount;
         //计算新的高和宽
         //四舍五入
         LONG  lNewHeight = LONG ( lOldHeight * fHRatio + 0.5 );
         LONG  lNewWidth = LONG ( lOldWidth * fWRatio + 0.5 );
         CSize  size;
         size.cx  = lNewWidth;
         size.cy  = lNewHeight;
         //创建图像
         m_Dib.CreatDib(  size, wOldBitCount );
         //获取旧图像每行字节数
         LONG  lOldLineBytes = WIDTHBYTES( lOldWidth * wOldBitCount );
         //获取新图像每行的字节数
         LONG  lNewLineBytes = WIDTHBYTES( lNewWidth * wOldBitCount );
         //计算填充的字节
         int  iNewFillBytes = lNewLineBytes - lNewWidth * ( wOldBitCount / 8 );
         int  iOldFillBytes = lOldLineBytes - lOldWidth * ( wOldBitCount / 8 );
         //指定图像大小,新图像的大小有不同
         m_Dib.m_dwSizeImage  = lNewLineBytes * lNewHeight;
         //分配空间
         m_Dib.AllocImage();
         //定位到数据的开头,也就是图像最后一行的行首
         LPRGBTRIPLE  DRgb = ( LPRGBTRIPLE ) m_Dib.m_lpImage;
         LPRGBTRIPLE  SRgb = ( LPRGBTRIPLE ) srcDib.m_lpImage;
         //将每行的头指针存储起来
         PRGBTRIPLE  *pNewRow = (PRGBTRIPLE *)malloc( sizeof(PRGBTRIPLE) * lNewHeight );
         PRGBTRIPLE  * pOldRow = (PRGBTRIPLE *)malloc( sizeof(PRGBTRIPLE) * lOldHeight );
         for  (int row = 0 ; row < lNewHeight ; row ++)
         {
                  pNewRow[row]  = PRGBTRIPLE( (PBYTE)DRgb + row * lNewLineBytes );
         }
         for  (int row = 0 ; row < lOldHeight ; row ++)
         {
                   pOldRow[row]  = PRGBTRIPLE ( (PBYTE)SRgb + row * lOldLineBytes );
         }
         float  p = 0 , q = 0;
         LONG  PACol = 0,PARow = 0;
         PRGBTRIPLE  PA = NULL ,PB = NULL , PC = NULL , PD = NULL , PDst = NULL;
         for  ( LONG row = 0 ; row < lNewHeight ; row ++ )
         {
                   //对每一行进行处理
                   for  ( LONG col = 0 ; col < lNewWidth  ;  col ++ )
                   {
                            //计算在源图像中的坐标
                            PACol  = (LONG) floor( col / fWRatio );
                            PARow  = (LONG) floor( row / fHRatio );
                            //计算P和Q
                            p  = col / fWRatio - PACol;
                            q  = row / fHRatio - PARow;
                            //判断计算出来的坐标是否在源图像中
                            if  ( ( ( PACol >= 0 ) && ( PACol < lOldWidth -1 ) ) && ( (  PARow >= 0 ) && ( PARow < lOldHeight -1 ) ) )
                            {
                                     //获得四周的像素指针
                                     //PA即 指向A点的指针,如下图为各个点的分布
                                     //                         A       |         C
                                     //                                  q
                                     //                          -p Dst
                                     //                                          
                                     //                         B                 D
                                     //
                                     PA  = pOldRow[PARow] + PACol;
                                     PB  = pOldRow[PARow + 1] + PACol;
                                     PC  = pOldRow[PARow] + PACol + 1;
                                     PD  = pOldRow[PARow + 1] + PACol + 1;
                                     //需要确定的像素的指针,在目标图像中
                                     PDst  = pNewRow[row] + col;
                                     //对目标像素的分量进行计算
                                     //Blue
                                     PDst->rgbtBlue  = BYTE( ( 1 - p ) * ( 1 - q ) * PA->rgbtBlue \
                                                          + ( 1 - p ) * q * PB->rgbtBlue  \                                                                    +  p  * ( 1- q ) * PC->rgbtBlue \
                                                                    +  p  * q * PD->rgbtBlue );
                                     if(PDst->rgbtBlue  < 0)
                                     {
                                               PDst->rgbtBlue  = 0;
                                     }
                                     if  (PDst->rgbtBlue > 255)
                                     {
                                               PDst->rgbtBlue  = 255;
                                     }
                                     //Green
                                     PDst->rgbtGreen  = BYTE( ( 1 - p ) * ( 1 - q ) * PA->rgbtGreen \
                                               +  ( 1 - p ) * q * PB->rgbtGreen \
                                               +  p * ( 1- q ) * PC->rgbtGreen \
                                               +  p * q * PD->rgbtGreen );
                                     if(PDst->rgbtGreen  < 0)
                                     {
                                               PDst->rgbtGreen  = 0;
                                     }
                                     if  (PDst->rgbtGreen > 255)
                                     {
                                               PDst->rgbtGreen  = 255;
                                     }
                                     //Red
                                     PDst->rgbtRed  = BYTE( ( 1 - p ) * ( 1 - q ) * PA->rgbtRed \
                                               +  ( 1 - p ) * q * PB->rgbtRed \
                                               +  p * ( 1- q ) * PC->rgbtRed \
                                               +  p * q * PD->rgbtRed );
                                     if(PDst->rgbtRed  < 0)
                                     {
                                               PDst->rgbtRed  = 0;
                                     }
                                     if  (PDst->rgbtRed > 255)
                                     {
                                               PDst->rgbtRed  = 255;
                                     }
                            }                          
                  }
         }
         free(  pNewRow );
         free(  pOldRow );
         DrawPic();
         m_Dib.CopyToMapFile("双线性插值法.bmp");
}

图像处理算法到这里就告一段落了,六种方法,足以解决常见的一些图像处理算法问题。对初学者二白来说,这次知识量很大,在初步掌握这几种方法后,要多多复习哦。(不多说啦,小白师兄又给派活了,大家下期再见!)

本文转载自xizero00博客,并进行修改,仅用作学习交流,如有侵权,请联系后台删除 https://blog.csdn.net/xizero00/article/details/6631209

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小白学视觉 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
常用图像插值方法概述
https://github.com/AndroidDeveloperLB/AndroidJniBitmapOperations
音视频开发进阶
2021/05/10
1.7K0
常用图像插值方法概述
图像插值算法及其实现
sensor、codec、display device都是基于pixel的,高分辨率图像能呈现更多的detail,由于sensor制造和chip的限制,我们需要用到图像插值(scaler/resize)技术,这种方法代价小,使用方便。同时,该技术还可以放大用户希望看到的感兴趣区域。图像缩放算法往往基于插值实现,常见的图像插值算法包括最近邻插值(Nearest-neighbor)、双线性插值(Bilinear)、双立方插值(bicubic)、lanczos插值、方向插值(Edge-directed interpolation)、example-based插值、深度学习等算法。
233333
2020/10/29
1.7K0
图像插值算法及其实现
【GAMES101-现代计算机图形学课程笔记】Lecture 09 Shading 3 (纹理映射)
这里补充一下上一节遗漏的一丢丢知识点,见下图。左边是渲染后的平面图,右边是对应的纹理。另外无论纹理平面原始有多大,最后都会被映射在$U-V$坐标,又称纹理坐标,并且规定坐标范围是0~1。
marsggbo
2020/06/10
1K0
图像处理之三种常见双立方插值算法
详解几种常见的双立方插值技术!好东西记得分享 图像插值技术概述图像插值技术在图像几何变换、透视变换等过程中是必不可少的技术环节,可以说像素插值方法最终决定变换之后的图像质量高低。常见的插值方法有临近点插值双线性插值双立方插值内插值三角插值等插值方法。 其中双立方插值效果比较好而在很多高质量图像变换中得到广泛应用,根据插值之后效果的不一样的,双立方插值可以分为几种插值方式。首先来看一下双立方插值基本解释与说明。双立方插值计算涉及到16个像素点,其中(i’, j’)表示待计算像素点在源图像中的包含小数部分的像
OpenCV学堂
2019/07/16
2.3K0
视频技术快览 0x0 - 图像基础和前处理
视频行业常见的分辨率有 QCIF(176x144)、CIF(352x288)、D1(704x576 或 720x576),还有 360P(640x360)、720P(1280x720)、1080P(1920x1080)、4K(3840x2160)、8K(7680x4320)等。
Cellinlab
2023/05/17
7420
视频技术快览 0x0 - 图像基础和前处理
常用的像素操作算法:Resize、Flip、RotateResizeFlipRotate总结
图像缩放有多种算法。最为简单的是最临近插值算法,它是根据原图像和目标图像的尺寸,计算缩放的比例,然后根据缩放比例计算目标像素所依据的原像素,过程中自然会产生小数,这时就采用四舍五入,取与这个点最相近的点。
fengzhizi715
2018/08/24
8590
常用的像素操作算法:Resize、Flip、RotateResizeFlipRotate总结
来聊聊图像插值算法
主要可以分为两类,一类是线性图像插值方法,另一类是非线性图像插值方法,如上图所示。
AI算法修炼营
2020/05/19
1.9K0
DCN V1代码阅读笔记
笔者前几天阅读了MASA 的可变形卷积算法,并写了一篇算法笔记:MASA DCN(可变形卷积) 算法笔记 ,然后里面也说了会放出一个代码解读出来,所以今天的推文就是来干这件事的,希望看完这篇文章你可对DCN的细节可以更加了解。本次解析的代码来自:https://github.com/ChunhuanLin/deform_conv_pytorch 。
BBuf
2020/06/04
1.4K0
最近邻插值、双线性插值、双三次插值
越是简单的模型越适合用来举例子,我们就举个简单的图像:3X3 的256级灰度图,也就是高为3个象素,宽也是3个象素的图像,每个象素的取值可以是 0-255,代表该像素的亮度,255代表最亮,也就是白色,0代表最暗,即黑色。假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source): 234 38 22 67 44 12 89 65 63
全栈程序员站长
2022/07/01
1.5K0
最近邻插值、双线性插值、双三次插值
一文讲解图像插值算法原理!附Python实现
寄语:本文梳理了最近邻插值法、双线性插值法和三次样条插值法的原理,并以图像缩放为例,对原理进行了C++及Python实现。
Datawhale
2020/04/24
6.7K0
一文讲解图像插值算法原理!附Python实现
ISP图像处理之Demosaic算法及相关
图像在将实际的景物转换为图像数据时, 通常是将传感器分别接收红、 绿、 蓝三个分量的信息, 然后将红、 绿、 蓝三个分量的信息合成彩色图像。 该方案需要三块滤镜, 这样价格昂贵,且不好制造, 因为三块滤镜都必须保证每一个像素点都对齐。
AomanHao
2022/01/14
3.2K0
ISP图像处理之Demosaic算法及相关
【GAMES101】Lecture 09 纹理贴图 点查询与范围查询 Mipmap
我们在之前的着色里面说过如何给物体上纹理,就是对于已经光栅化的屏幕点,就是每个像素的中心,去寻找对应纹理的映射位置的纹理颜色,去改变这个反射模型的反射系数kd
叶茂林
2024/01/25
1600
【GAMES101】Lecture 09 纹理贴图 点查询与范围查询 Mipmap
从零开始一起学习SLAM | 用四元数插值来对齐IMU和图像帧
小白:师兄,好久没见到你了啊,我最近在看IMU(Inertial Measurement Unit,惯性导航单元)相关的东西,正好有问题求助啊
用户1150922
2019/08/29
1.2K0
从零开始一起学习SLAM | 用四元数插值来对齐IMU和图像帧
图像处理界双线性插值算法的优化
  在图像处理中,双线性插值算法的使用频率相当高,比如在图像的缩放中,在所有的扭曲算法中,都可以利用该算法改进处理的视觉效果。首先,我们看看该算法的简介。
用户1138785
2019/09/11
1.7K0
图像处理界双线性插值算法的优化
深入理解双线性插值算法
看了好几篇关于双线性插值算法的博文,解释得都不好理解,不过下面这篇博文就解释得很好,以下内容均参考这篇:
全栈程序员站长
2022/09/06
1.5K0
深入理解双线性插值算法
图像插值算法和OpenCV框架
最近邻插值,是指将目标图像中的点,对应到源图像中后,找到最相邻的整数点,作为插值后的输出。
致Great
2020/04/24
1.4K0
图像插值算法和OpenCV框架
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换 OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)数字图像
  图像几何变换又称为图像空间变换,它将一副图像中的坐标位置映射到另一幅图像中的新坐标位置。我们学习几何变换就是确定这种空间映射关系,以及映射过程中的变化参数。图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算:
vv彭
2020/10/27
3.8K0
10、图像的几何变换——平移、镜像、缩放、旋转、仿射变换
    


OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)数字图像
【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值「建议收藏」
2.1 最近邻插值 (Nearest Neighbor Interpolation) —— 零阶插值法
全栈程序员站长
2022/09/06
19.3K0
【图像处理】详解 最近邻插值、线性插值、双线性插值、双三次插值「建议收藏」
用超分辨率扛把子算法 ESRGAN,训练图像增强模型
内容一览:通过硬件或软件方法,提高原有图像的分辨率,让模糊图像秒变清晰,就是超分辨率。随着深度学习技术的发展,图像超分辨率技术在游戏、电影、医疗影像等领域的应用,也愈发广泛。
HyperAI超神经
2021/05/27
1.7K0
用超分辨率扛把子算法 ESRGAN,训练图像增强模型
双线性插值(Bilinear Interpol)原理及应用
在很多神经网络上采样过程中会用到双线性插值,其为基础的图像resize操作。以前一直没时间仔细研究,今天探究并记录一下原理和自己的理解。
全栈程序员站长
2022/09/05
4.1K0
双线性插值(Bilinear Interpol)原理及应用
推荐阅读
相关推荐
常用图像插值方法概述
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文