一种快速图形拉伸算法

162 篇文章 1 订阅
43 篇文章 0 订阅

从数字图像处理的基本理论,我们可以知道:图像的变形变换就是源图像到目标图像的坐标变换。简单的想法就是把源图像的每个点坐标通过变形运算转为目标图像的相应点的新坐标,但是这样会导致一个问题就是目标点的坐标通常不会是整数,而且像放大操作会导致目标图像中没有被源图像的点映射到,这是所谓“向前映射”方法的缺点。所以一般都是采用“逆向映射”法。
但是逆向映射法同样会出现映射到源图像坐标时不是整数的问题。这里就需要“重采样滤波器”。这个术语看起来很专业,其实不过是因为它借用了电子信号处理中的惯用说法(在大多数情况下,它的功能类似于电子信号处理中的带通滤波器),理解起来也不复杂,就是如何确定这个非整数坐标处的点应该是什么颜色的问题。前面说到的三种方法:最近邻域法,线性插值法和三次样条法都是所谓的“重采样滤波器”。
所谓“最近邻域法”就是把这个非整数坐标作一个四舍五入,取最近的整数点坐标处的点的颜色。而“线性插值法”就是根据周围最接近的几个点(对于平面图像来说,共有四点)的颜色作线性插值计算(对于平面图像来说就是二维线性插值)来估计这点的颜色,在大多数情况下,它的准确度要高于最近邻域法,当然效果也要好得多,最明显的就是在放大时,图像边缘的锯齿比最近邻域法小非常多。当然它同时还带业个问题:就是图像会显得比较柔和。这个滤波器用专业术语来说(呵呵,卖弄一下偶的专业^_^)叫做:带阻性能好,但有带通损失,通带曲线的矩形系数不高。至于三次样条法我就不说了,复杂了一点,可自行参考数字图像处理方面的专业书籍,如本文的参考文献。
再来讨论一下坐标变换的算法。简单的空间变换可以用一个变换矩阵来表示:
[x’,y’,w’]=[u,v,w]*T
其中:x’,y’为目标图像坐标,u,v为源图像坐标,w,w’称为齐次坐标,通常设为1,T为一个3X3的变换矩阵。
这种表示方法虽然很数学化,但是用这种形式可以很方便地表示多种不同的变换,如平移,旋转,缩放等。对于缩放来说,相当于:
                         [Su 0 0 ]
[x, y, 1] = [u, v, 1] * | 0 Sv 0 |
                         [0 0 1 ]
            
其中Su,Sv分别是X轴方向和Y轴方向上的缩放率,大于1时放大,大于0小于1时缩小,小于0时反转。
矩阵是不是看上去比较晕?其实把上式按矩阵乘法展开就是:
{ x = u * Su
{ y = v * Sv
就这么简单。^_^
有了上面三个方面的准备,就可以开始编写代码实现了。思路很简单:首先用两重循环遍历目标图像的每个点坐标,通过上面的变换式(注意:因为是用逆向映射,相应的变换式应该是:u = x / Su 和v = y / Sv)取得源坐标。因为源坐标不是整数坐标,需要进行二维线性插值运算:
P = n*b*PA + n * ( 1 – b )*PB + ( 1 – n ) * b * PC + ( 1 – n ) * ( 1 – b ) * PD
其中:n为v(映射后相应点在源图像中的Y轴坐标,一般不是整数)下面最接近的行的Y轴坐标与v的差;同样b也类似,不过它是X轴坐标。PA-PD分别是(u,v)点周围最接近的四个(左上,右上,左下,右下)源图像点的颜色(用TCanvas的Pixels属性)。P为(u,v)点的插值颜色,即(x,y)点的近似颜色。
这段代码我就不写的,因为它的效率实在太低:要对目标图像的每一个点的RGB进行上面那一串复杂的浮点运算。所以一定要进行优化。对于VCL应用来说,有个比较简单的优化方法就是用TBitmap的ScanLine属性,按行进行处理,可以避免Pixels的像素级操作,对性能可以有很大的改善。这已经是算是用VCL进行图像处理的基本优化常识了。不过这个方法并不总是管用的,比如作图像旋转的时候,这时需要更多的技巧。
无论如何,浮点运算的开销都是比整数大很多的,这个也是一定要优化掉的。从上面可以看出,浮点数是在变换时引入的,而变换参数Su,Sv通常就是浮点数,所以就从它下手优化。一般来说,Su,Sv可以表示成分数的形式:
Su = ( double )Dw / Sw; Sv = ( double )Dh / Sh
其中Dw, Dh为目标图像的宽度和高度,Sw, Sh为源图像的宽度和高度(因为都是整数,为求得浮点结果,需要进行类型转换)。
将新的Su, Sv代入前面的变换公式和插值公式,可以导出新的插值公式:
因为:
b = 1 – x * Sw % Dw / ( double )Dw; n = 1 – y * Sh % Dh / ( double )Dh
设:
B = Dw – x * Sw % Dw; N = Dh – y * Sh % Dh
则:
b = B / ( double )Dw; n = N / ( double )Dh
用整数的B,N代替浮点的b, n,转换插值公式:
P = ( B * N * ( PA – PB – PC + PD ) + Dw * N * PB + DH * B * PC + ( Dw * Dh – Dh * B – Dw * N ) * PD ) / ( double )( Dw * Dh )
这里最终结果P是浮点数,对其四舍五入即可得到结果。为完全消除浮点数,可以用这样的方法进行四舍五入:
P = ( B * N … * PD + Dw * Dh / 2 ) / ( Dw * Dh )
这样,P就直接是四舍五入后的整数值,全部的计算都是整数运算了。
简单优化后的代码如下:
int __fastcall TResizeDlg::Stretch_Linear(Graphics::TBitmap * aDest, Graphics::TBitmap * aSrc)
{
    int sw = aSrc->Width - 1, sh = aSrc->Height - 1, dw = aDest->Width - 1, dh = aDest->Height - 1;
    int B, N, x, y;
    int nPixelSize = GetPixelSize( aDest->PixelFormat );
    BYTE * pLinePrev, *pLineNext;
    BYTE * pDest;
    BYTE * pA, *pB, *pC, *pD;
    for ( int i = 0; i <= dh; ++i )
    {
        pDest = ( BYTE * )aDest->ScanLine[i];
        y = i * sh / dh;
        N = dh - i * sh % dh;
        pLinePrev = ( BYTE * )aSrc->ScanLine[y++];
        pLineNext = ( N == dh ) ? pLinePrev : ( BYTE * )aSrc->ScanLine[y];
        for ( int j = 0; j <= dw; ++j )
        {
            x = j * sw / dw * nPixelSize;
            B = dw - j * sw % dw;
            pA = pLinePrev + x;
            pB = pA + nPixelSize;
            pC = pLineNext + x;
            pD = pC + nPixelSize;
            if ( B == dw )
            {
                pB = pA;
                pD = pC;
            }
            for ( int k = 0; k < nPixelSize; ++k )
                *pDest++ = ( BYTE )( int )(
                    ( B * N * ( *pA++ - *pB - *pC + *pD ) + dw * N * *pB++
                    + dh * B * *pC++ + ( dw * dh - dh * B - dw * N ) * *pD++
                    + dw * dh / 2 ) / ( dw * dh )
                );
        }
    }
    return 0;
}
应该说还是比较简洁的。因为宽度高度都是从0开始算,所以要减一,GetPixelSize是根据PixelFormat属性来判断每个像素有多少字节,此代码只支持24或32位色的情况(对于15或16位色需要按位拆开—因为不拆开的话会在计算中出现不期望的进位或借位,导致图像颜色混乱—处理较麻烦;对于8位及8位以下索引色需要查调色板,并且需要重索引,也很麻烦,所以都不支持;但8位灰度图像可以支持)。另外代码中加入一些在图像边缘时防止访问越界的代码。
通过比较,在PIII-733的机器上,目标图像小于1024x768的情况下,基本感觉不出速度比StretchDraw有明显的慢(用浮点时感觉比较明显)。效果也相当令人满意,不论是缩小还是放大,图像质量比StretchDraw方法有明显提高。
不过由于采用了整数运算,有一个问题必须加以重视,那就是溢出的问题:由于式中的分母是dw * dh,而结果应该是一个Byte即8位二进制数,有符号整数最大可表示31位二进制数,所以dw * dh的值不能超过23位二进制数,即按2:1的宽高比计算目标图像分辨率不能超过4096*2048。当然这个也是可以通过用无符号数(可以增加一位)及降低计算精度等方法来实现扩展的,有兴趣的朋友可以自己试试。
 from http://00000000.net.cn/Index.htm

其他文档:

aspjpeg组件高级使用方法介绍 

解密:一张所有XP用户都感到吃惊的图片 

JPEG简易文档 

BMP位图文件结构及VC操作 

一个图形界面的俄罗斯方快c源码 

AutoCad二次开发:ObjectARX技术谈  

<script type="text/javascript"> google_ad_client = "pub-2416224910262877"; google_ad_width = 728; google_ad_height = 90; google_ad_format = "728x90_as"; google_ad_channel = ""; google_color_border = "E1771E"; google_color_bg = "FFFFFF"; google_color_link = "0000FF"; google_color_text = "000000"; google_color_url = "008000"; </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MATLAB上图像畸变矫正算法的实现可以通过下面的步骤进行: 1. 首先,使用MATLAB的相机标定工具箱对相机进行标定。利用标定板拍摄一些已知的图像,然后使用MATLAB提供的函数对这些图像进行标定,得到相机的内参数和畸变系数。 2. 接下来,使用标定结果对输入图像进行畸变校正。可以使用MATLAB的`undistortImage`函数来移除图像中的畸变。该函数需要输入待校正的图像、相机的内参数和畸变系数。函数会返回畸变校正后的图像。 3. 可以选择根据需求进行图像仿射变换。可以使用MATLAB的`imwarp`函数来实现。该函数可以进行平移、旋转、缩放等仿射变换操作。可以通过设置仿射变换矩阵、旋转角度、缩放因子等参数来实现对图像的矫正。 4. 最后,通过显示矫正后的图像来进行可视化。可以使用MATLAB的`imshow`函数来显示图像。将畸变校正和仿射变换后的图像作为函数的输入参数,然后可以使用`figure`和`imshow`函数将图像显示出来。 总结来说,图像畸变矫正算法可以通过相机的标定、畸变校正和仿射变换等步骤来实现。在MATLAB中,可以使用相机标定工具箱、`undistortImage`函数和`imwarp`函数来进行图形矫正的实现。最后,通过`imshow`函数显示矫正后的图像。 ### 回答2: Matlab图形矫正是一种通过算法来纠正图像畸变的方法。图像畸变是由于光学系统、相机等因素引起的图像形状、尺寸或者相对位置的不准确性。 实现图像畸变矫正的算法可以分为以下几个步骤: 1. 畸变模型建立:畸变模型是一种数学函数,描述了图像畸变的特征。常见的畸变模型有径向畸变和切向畸变等。根据具体的畸变情况,选择合适的畸变模型。 2. 畸变参数估计:通过对被拍摄物体进行特定的标定,获取相机的内参和外参,从而推算出畸变参数。内参包括相机的焦距、主点位置等,外参包括相机的旋转矩阵和平移向量。 3. 图像畸变矫正:根据畸变模型和估计的畸变参数,对图像进行畸变矫正。矫正的过程就是根据模型和参数来对图像进行像素的重采样,使得畸变后的像素位置能够在矫正后的图像上正确对应。 在Matlab中,可以利用图像处理函数和几何变换函数进行图形矫正。首先,利用相机标定工具箱对相机进行标定,获取内参和外参。根据畸变模型建立畸变参数,然后使用几何变换函数来进行图像畸变矫正。 以下是基于Matlab的图像畸变矫正算法的示例代码: ```matlab % 读取原始图像 image = imread('original_image.jpg'); % 相机标定,获取内参和外参 % ... % 根据畸变模型建立畸变参数,例如径向畸变 distortionModel = 'radial'; distortionParams = [0.1, 0.2, 0.05]; % 根据实际情况设定畸变参数 % 进行图像畸变矫正 undistortedImage = undistortImage(image, cameraParams, distortionModel, distortionParams); % 显示矫正后的图像 imshow(undistortedImage); ``` 通过以上步骤,我们可以利用Matlab实现图像畸变矫正,根据实际的畸变情况和畸变模型来建立畸变参数,然后使用几何变换函数对图像进行矫正。最后,显示矫正后的图像,以达到修正图像畸变的目的。 ### 回答3: MATLAB图形矫正利用图像畸变矫正算法,对图像进行调整以去除畸变。常用算法包括相机标定和改正畸变。 相机标定是通过对摄像机拍摄的一系列特定图案进行分析来获取相机参数的过程。这些图案包括棋盘格等具有已知结构的模式。通过观察相机捕捉到的图案,可以获取图像的内部参数,如焦距、畸变系数和图像原点。 改正畸变的方法包括径向畸变和切向畸变。径向畸变指的是由于镜头形状不完美导致图像产生的畸变。常见的径向畸变包括径向拉伸和压缩。切向畸变指的是由于镜头和图像平面之间有一定的角度导致的图像扭曲。这些畸变可以通过相机标定的结果来计算并应用矫正。 实现图像畸变矫正的步骤如下: 1. 收集图像:使用相机拍摄一系列棋盘图案或其他具有已知结构的图像。 2. 相机标定:使用MATLAB的相机标定工具箱对收集到的图像进行相机标定。该工具箱会给出图像的内参和外参。 3. 计算畸变矫正参数:根据相机标定得到的内参和外参,计算径向畸变和切向畸变的参数。 4. 应用畸变矫正:将计算得到的畸变矫正参数应用于需要矫正的图像,去除图像中的畸变。 通过这些步骤,我们可以使用MATLAB实现图像畸变矫正。这个过程可以提高图像的质量,使得图像更加逼真和准确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值