雙線性插值的圖像縮放算法的研究與實現

一、引言

數字圖像處理的對象因其涉及到社會的各個領域,倍受到越來越多的關注,而圖像縮放作爲數字圖像處理中的基本操作尤爲重要,在社會的很多領域都需要對圖像進行放大和縮小。利用VC++的MFC類庫中的StretchBlt函數可以很容易的實現圖像放大和縮小,但是當放大或縮小的比率比較大時就容易出現失真現象,因此必須進行改進。本文提出了一種雙線性插值算法,用以改進圖像的縮放質量。

二、空間變換

    圖像的空間變換,也稱幾何變換或幾何運算,包括圖像的平移、旋轉、鏡像變換、轉置、縮放等。幾何運算可改變圖像中各物體之間的空間關係,這種運算可以被看成是將各物體在圖像內移動。

   空間變換可如下表示:設(u,v)爲源圖像上的點,(x,y)爲目標圖像上的點,則空間變換就是將源圖像上(u,v)處的顏色值與目標圖像上(x,y)處的顏色對應起來。

(u,v) ß----------------à (x,y)

並具有以下關係:

x=X(u,v),y=Y(u,v)   (即由(u,v)計算對應點(x,y)  )    (1.1)

u=U(x,y),v=V(x,y)  (即由(x,y)反求對應點(u,v)  )(1.2)其中X(u,v)、Y(u,v)、U(x,y)、V(x,y)均爲變換。由(1.1)對應的變換稱作向前映射法也叫像素移交法,而由(1.2)對應的變換稱作向後映射法也叫像素填充法,向後映射法是向前映射法的逆。

對於向前映射法來說,由於許多輸入像素可能映射到輸出圖像的邊界之外,所以,向前映射法有些浪費,而且每個輸出像素的灰度值可能要由許多輸入像素的灰度值來決定,因此要涉及多次運算。如果空間變換中包括縮小處理,則會有四個以上的輸入像素來決定輸出像素的灰度值;如果含有放大處理,則一些輸出像素可能被漏掉。而向後映射算法是逐像素、逐行地產生輸出圖像。每個像素的灰度級由最多四個像素參與的插值所惟一確定,雖然向後映射法比向前映射法要複雜,但是向後映射法對於一般的應用卻具有更爲現實的意義。本文就是採取了向後映射法來實現圖像縮放的。

三.雙線性插值

   最簡單的插值算法是最鄰近插值,也稱爲零階插值。它輸出的像素灰度值就等於距離它映射到的位置最近的輸入像素的灰度值,最鄰近插值算法簡單,在許多情況下都能得到令人滿意的結果,但是當圖像中包含像素之間灰度級有變化的細微結構時,最鄰近算法會在圖像中產生人爲加工的痕跡。雙線性插值算法計算量比零階插值大,但縮放後圖像質量高,不會出現像素值不連續的情況,這樣就可以獲得一個令人滿意的結果。

   雙線性插值是利用了需要處理的原始圖像像素點周圍的四個像素點的相關性,通過雙線性算法計算得出的。對於一個目的座標,通過向後映射法得到其在原始圖像的對應的浮點座標(i+u,j+v),其中i,j均爲非負整數,u,v爲[0,1]區間的浮點數,則這個像素的值 f(i+u,j+v) 可由原圖像中座標爲 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應的周圍四個像素的值決定,即:f(i+u,j+v) =(1-u)×(1-v) ×f(i,j)+(1-u) ×v×f(i,j+1)+u×(1-v) ×f(i+1,j)+u×v×f(i+1,j+1),其中f(i,j)表示源圖像(i,j)處的像素值,以此類推,這就是雙線性內插值法。

如圖1所示,已知(0,0)、(0,1)、(1,0)、(1,1)四點的灰度,可以由相鄰像素的灰度值f(0,0)和f(1,0) 在X方向上線性插值求出(x,0)的灰度f(x,0),由另外兩個相鄰像素f(0,1)和f(1,1)在X方向上線性插值可求出(x,1)的灰度f(x,1),最後由f(x,0)、f(x,1)在Y方向上進行線性插值就可以得到(x,y)的灰度f(x,y)。

四.算法

 

1.算法

 

假設原始圖像大小爲size=m×n,其中m與n分別是原始圖像的行數與列<數。若圖像的縮放因子是t(t>0),則目標圖像的大小size=t×m×t×n。對於目標圖像的某個像素點P(x,y)通過P*1/t可得到對應的原始圖像座標P’( x1,y1),其中x1=x/t,y1=y/t,由於x1,y1都不是整數所以並不存在這樣的點,這樣可以找出與它相鄰的四個點的灰度f1、f2、f3、f4,使用雙線性插值算法就可以得到這個像素點P’(x1,y1)的灰度,也就是像素點P(x,y)的灰度。

一個完整的雙線性插值算法可描述如下:

(1)       通過原始圖像和比例因子得到新圖像的大小,並創建新圖像。

(2)       由新圖像的某個像素(x,y)映射到原始圖像(x’,y’)處。

(3)       對x’,y’取整得到(xx,yy)並得到(xx,yy)、(xx+1,yy)、(xx,yy+1)和(xx+1,yy+1)的值。

(4)       利用雙線性插值得到像素點(x,y)的值並寫回新圖像。

(5)       重複步驟(2)直到新圖像的所有像素寫完。

2.核心代碼

//函數名Bilinear

//參數float k

//返回值無

//作用利用雙線性插值來實現圖像縮放

void CChildView::Bilinear(float k)

{  

    int nBpp=m_imPicture .GetBPP ();   

    int widthNew,heightNew;//新圖像的寬度和高度

    float widthScale=(float)(1.0/k),heightScale=(float)(1.0/k);

    float xx,yy;

    int a,b;

    int rr,gg,bb;//保存R、G、B分量

    //得到新圖像的寬度和高度

    widthNew=(int)(m_imPicture .GetWidth ()*k);

    heightNew =(int)(m_imPicture .GetHeight ()*k);

    //利用新圖像的寬度和高度來創建新圖像

    m_imNewPicture .Destroy ();

    m_imNewPicture .Create (widthNew ,heightNew ,nBpp);

    //得到新、老圖像的每行的字節數

    int nPitch=m_imPicture .GetPitch ();

    int nPitchNew=m_imNewPicture .GetPitch ();

    //得到新、老圖像的數據指針

    LPBYTE pBitsNew=(LPBYTE)m_imNewPicture .GetBits ();

    LPBYTE pBits=(LPBYTE)m_imPicture .GetBits ();  

    if(m_imPicture.GetBPP ()!=24){

        MessageBox ("必須是24位圖像或8位圖像");

        m_imNewPicture .Destroy ();

        Invalidate();

        return ;

    }  

    for(int x=(int)k;x<widthNew -k;x++){

        for(int y=(int)k;y<heightNew -k;y++){

            xx=x*widthScale ;

            yy=y*heightScale ;

            if(xx<=1e-8){

                xx=0;

            }

            if(xx>m_imPicture .GetWidth ()-2)

                xx=(float)(m_imPicture .GetWidth ()-2);

            if(yy<=1e-8)

                yy=0;

            if(yy>m_imPicture .GetHeight ()-2)

                yy=(float)(m_imPicture .GetHeight ()-2);

            a=(int)xx;

            b=(int)yy;         

            //分別得到對應像素的R、G、B值並用雙線性插值得到新像素的R、G、B值

            int r11,r12,r21,r22;

            r11=*(pBits+b*nPitch+3*a+2);

            r12=*(pBits+b*nPitch+3*(a+1)+2);

            r21=*(pBits+(b+1)*nPitch+3*a+2);

            r22=*(pBits+(b+1)*nPitch+3*(a+1)+2);

            rr=(int)(r11*(a+1-xx)*(b+1-yy)+r12*(a+1-xx)*(yy-b)

                +r21*(xx-a)*(b+1-yy)+r22*(xx-a)*(yy-b));

 

            int g11,g12,g21,g22;

            g11=*(pBits+b*nPitch+3*a+1);

            g12=*(pBits+b*nPitch+3*(a+1)+1);

            g21=*(pBits+(b+1)*nPitch+3*a+1);

            g22=*(pBits+(b+1)*nPitch+3*(a+1)+1);

            gg=(int)(g11*(a+1-xx)*(b+1-yy)+g12*(a+1-xx)*(yy-b)

                +g21*(xx-a)*(b+1-yy)+g22*(xx-a)*(yy-b));

 

            int b11,b12,b21,b22;

            b11=*(pBits+b*nPitch+3*a);

            b12=*(pBits+b*nPitch+3*(a+1));

            b21=*(pBits+(b+1)*nPitch+3*a);

            b22=*(pBits+(b+1)*nPitch+3*(a+1));

            bb=(int)(b11*(a+1-xx)*(b+1-yy)+b12*(a+1-xx)*(yy-b)

                +b21*(xx-a)*(b+1-yy)+b22*(xx-a)*(yy-b));

            //將得到的新R、G、B值寫到新圖像中          

            *(pBitsNew +y*nPitchNew +x*3)=min(255,bb);

            *(pBitsNew +y*nPitchNew +x*3+1)=min(255,gg);

            *(pBitsNew +y*nPitchNew +x*3+2)=min(255,rr);

        }      

    }      

    m_imPicture .Destroy ();

    Invalidate ();

}

五.結語

   本文介紹了一種利用雙線性插值來實現圖像縮放的算法,通過圖2可以看到這種算法和傳統的利用StretchBlt來實現圖像縮放相比具有很大的改善。StretchBlt實現的圖像具有很大的失真,並且隨着縮小的比率越大失真也越嚴重;而雙線性插值算法則很好地解決了這個問題,可以得到很高的清晰度,這種方法可以廣泛應用在圖像變形、計算機動畫、計算機輔助設計等領域。所附源代碼在VC++.NET 2003下編譯通過。

圖2

注:左圖是利用本文介紹的雙線性插值算法縮小一倍得到圖像,右圖是利用StretchBlt縮小一倍得到圖像。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章