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

数字图像放大算法

作者头像
小白学视觉
发布2019-10-24 14:35:04
1.5K0
发布2019-10-24 14:35:04
举报

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

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
复制
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
复制
//双线性插值法
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 删除。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档