幾個圖像縮放算法的比較
前段時間由于項目的需求,需要實現圖像的縮放功能,期間查找了不少關于圖像縮放算法的資料,現把自己的心得整理一下。
由于研究生期間沒有選修過圖像處理方面的課程,所以對圖像縮放的原理可謂一竅不通,當時開始編寫代碼的時候簡直就是一頭霧水。而且網上雖然介紹圖像處理的代碼很多,但涉及圖像縮放的代碼卻很少,因為很多軟件都直接使用了windows的GDI函數庫的API函數:StretchBlt,或者VCL中TCanvas類的StretchDraw。無奈這兩個函數都是直接對BMP圖像進行縮放,而且StretchBlt是在CDC里面調用的,結果只是在顯示的時候對圖像進行縮放,不能夠進行縮放的存儲。那些天在GDI和GDIPLUS摸索了半天,都找不到合適的函數,某天卻迸出個想法來:圖像放大不就是把每個象素點再多弄幾個出來,而縮小不就是去掉里面一些象素點。所以就按照自己的想法寫了一個比較粗糙的放大函數:
BYTE *src,*dst,*ptr,*buffer,*next;
for(int i=0,n=0; i < this->Height(); i++,n=n+rate)
{
src = this->GetLinePtr(i);
dst = tempdib->GetLinePtr(n);
ptr = dst;
for(int j=0; j < this->Width(); j++,ptr=ptr+3*rate)
{
memcpy(ptr,src+j*3,3);
for(int m=1;m
memcpy(ptr+m*3,ptr,3);
}
for(int m=n+1;m
{
buffer = dst;
next = tempdib->GetLinePtr(m);
ptr = next;
memcpy(ptr,buffer,dstwidth*3);
}
}
這段代碼的效果比較粗糙,但處理的辦法比較有意思。首先是讀取一行的圖像數據,然后在每一行循環讀取一個象素的RGB值并復制到新圖像的內存空間,然后根據放大的比例再作一次循環,把這個RGB值按照比例復制進內存空間。當進行完一行的處理后,在新圖像的內存空間進行一次循環處理,把這行數據按照比例復制給下面幾行。這樣就通過象素點的復制實現了圖片的放大。不過放大的效果不是特別好,圖像列方向上會出現很多的毛刺,放大4倍的話圖像就很模糊了。
所以還是重新去查找資料,結果在網上搜到一篇不錯的文章——用線性插值算法實現圖像縮放。看了文章,才發現我原先的辦法還真不是一般的原始,不過思路還跟GDI里面的StretchBlt差不多。StretchBlt采用的方法在圖像處理領域稱為最近鄰域法,其基本原理就是先取出原圖的相鄰四個點,然后把新位置的點跟這四個點的位置做比較,把最近一個點的RGB值賦給新位置的點。所以在放大的時候,幾乎就是像我那樣把前一個點的象素賦給新位置的點。這樣處理的結果就是導致圖像不夠平滑,因為點與點之間是一個過渡的過程,不是簡單的復制,稍微好點的辦法就是把新點附近幾個點的顏色值取平均再賦給這個點。這種方法在數值計算方法叫做線性插值。但那篇文章提供了一個更好的方法,叫做二維線性插值,其原理也是對附近的點取平均,但它對各個點的顏色值加上不同的權數,這個權數就是各個點距離這個點的位置。其計算方法如下:
P = n*b*PA + n * ( 1 – b )*PB + ( 1 – n ) * b * PC + ( 1 – n ) * ( 1 – b ) * PD
其中:n為v(映射后相應點在源圖像中的Y軸坐標,一般不是整數)下面最接
|
|