求数字图形拉伸算法

Moprheus 2005-04-04 09:58:57
实际是一个播放器

得到的数据如果直接播放大小是352×240(352*288...),想对数据扩展成为 704×576。

使用YUY2格式,2个字节一个点。每行是 352×2字节,共240(288)行

求算法。。
...全文
304 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
thisisll 2005-04-08
  • 打赏
  • 举报
回复
用线性插值吧





从数字图像处理的基本理论,我们可以知道:图像的变形变换就是源图像到目标图像的坐标变换。简单的想法就是把源图像的每个点坐标通过变形运算转为目标图像的相应点的新坐标,但是这样会导致一个问题就是目标点的坐标通常不会是整数,而且像放大操作会导致目标图像中没有被源图像的点映射到,这是所谓“向前映射”方法的缺点。所以一般都是采用“逆向映射”法。

但是逆向映射法同样会出现映射到源图像坐标时不是整数的问题。这里就需要“重采样滤波器”。这个术语看起来很专业,其实不过是因为它借用了电子信号处理中的惯用说法(在大多数情况下,它的功能类似于电子信号处理中的带通滤波器),理解起来也不复杂,就是如何确定这个非整数坐标处的点应该是什么颜色的问题。前面说到的三种方法:最近邻域法,线性插值法和三次样条法都是所谓的“重采样滤波器”。

所谓“最近邻域法”就是把这个非整数坐标作一个四舍五入,取最近的整数点坐标处的点的颜色。而“线性插值法”就是根据周围最接近的几个点(对于平面图像来说,共有四点)的颜色作线性插值计算(对于平面图像来说就是二维线性插值)来估计这点的颜色,在大多数情况下,它的准确度要高于最近邻域法,当然效果也要好得多,最明显的就是在放大时,图像边缘的锯齿比最近邻域法小非常多。当然它同时还带业个问题:就是图像会显得比较柔和。这个滤波器用专业术语来说(呵呵,卖弄一下偶的专业^_^)叫做:带阻性能好,但有带通损失,通带曲线的矩形系数不高。至于三次样条法我就不说了,复杂了一点,可自行参考数字图像处理方面的专业书籍,如本文的参考文献。

再来讨论一下坐标变换的算法。简单的空间变换可以用一个变换矩阵来表示:

[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。当然这个也是可以通过用无符号数(可以增加一位)及降低计算精度等方法来实现扩展的,有兴趣的朋友可以自己试试。

当然这段代码还远没有优化到极致,而且还有很多问题没有深入研究,比如抗混叠(anti-aliasing)等,有兴趣的朋友可以自行参考相关书籍研究,如果你有什么研究成果,非常欢迎你为我的程序编写插件实现。

thisisll 2005-04-08
  • 打赏
  • 举报
回复
我这个算不算最小临近算法
不过缩放效果并不好

//get info
int nWidth , nHeight , nNewWidth , nNewHeight , nNewWidthBit , nWidthBit;
GetInfo( pBmpFile , &nWidth , &nHeight );
nNewHeight = (int) (nHeight * times);
nNewWidth = (int) (nWidth * times);
nNewWidthBit = ( 4 - nNewWidth * 3 % 4 ) % 4 + nNewWidth * 3;
nWidthBit = ( 4 - nWidth * 3 % 4 ) % 4 + nWidth * 3;
//excute new size
int nNewSize = nNewWidthBit * nNewHeight + 54;
//apply memory
BYTE* pNewBmpFile=NULL;
pNewBmpFile = new BYTE [ nNewSize ];
memset( pNewBmpFile , 0 , nNewSize);
//create bmp
pNewBmpFile = CreateBmp( nNewWidth , nNewHeight , pNewBmpFile );

//rewrite bmpdata
BYTE *pBuf = NULL;
BYTE *pNewBuf = NULL;
BYTE *pNewTmp = NULL;
pBuf = &pBmpFile[54];
pNewBuf = &pNewBmpFile[54];
//zoom
for( int i = 0 ; i < nNewHeight ; i++ )
{
pNewTmp = pNewBuf + nNewWidthBit * i;
for( int j = 0 ; j < nNewWidth * 3 ; j += 3 )
{
int offset = i * nNewWidthBit + j;
CPoint point = OffsettoPixel( offset , nNewWidth , nNewHeight );
CPoint oldpoint;
oldpoint.x = (int) (point.x / times);
oldpoint.y = (int) (point.y / times);
int oldoffset = PixeltoOffset( oldpoint , nWidthBit , nHeight );
pNewTmp[j] = pBuf [oldoffset];
pNewTmp[j+1] = pBuf [oldoffset+1];
pNewTmp[j+2] = pBuf [oldoffset+2];
}
}
CloseBmp( pBmpFile );
return pNewBmpFile;
hhoking 2005-04-08
  • 打赏
  • 举报
回复
DS可以调整VideoWindow的大小啊,我记得IVideoWindow里有这样的接口,哪里需要自己处理
数据呢?

好像你需要写自己的filter时,会涉及到color space convert的问题,一般不需要自己做,
太麻烦。

最小临近算法,其实也算不上是算法,只不过根据目标图和源图的比例关系,求出目标图上每
一点是源图的那一点(这个点通常是个浮点值,所以简单取整就行)。
vcmute 2005-04-05
  • 打赏
  • 举报
回复
intel ippi的
ippiResize_8u_C1R
hhoking 2005-04-05
  • 打赏
  • 举报
回复
简单而快速的算法:最小临近算法

求目标图的每一点到源图的对应点(一次线性关系,很简单),取整就完了。注意越界的问题。

具体到你的情况,无非将YUY2转换为YUV或者RGB,完了后再转换回来。
如果直接用YUV的值,因为都是一次线性关系,可能可以合并中间过程,得到一个复合公式。
你自己试一试吧。
Moprheus 2005-04-05
  • 打赏
  • 举报
回复
这个我对图像几乎不了解~
做directshow播放的时候也只需要对原有数据直接输出就可以的了!
但是由于原来数据显示大小的问题~又没有找到其他办法~所以才希望能直接对数据进行处理!

那个 最小临近算法 我网上也找不到!还请详细解说
I_Love_CPP 2005-04-04
  • 打赏
  • 举报
回复
有很多插值的算法。。。。。

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧