圖像縮放算法及速度優化——(一)最近鄰插值


第0節 簡介

 

  圖像縮放算法是數字圖像處理算法中經常遇到的問題。我們經常會將某種尺寸的圖像轉換為其他尺寸的圖像,如放大或者縮小圖像。OpenCV中的Resize() 函數非常方便而且效率非常高。下面是OPENCV提供的cvResize函數原型。

/****************************************************************************************************/
圖像大小變換
void cvResize( const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR );
src
輸入圖像.
dst
輸出圖像.
interpolation
插值方法:
CV_INTER_NN - 最近鄰插值,
CV_INTER_LINEAR - 雙線性插值 (缺省使用)
CV_INTER_AREA - 使用象素關系重采樣。當圖像縮小時候,該方法可以避免波紋出現。當圖像放大時,類似於 CV_INTER_NN 方法..
CV_INTER_CUBIC - 立方插值.
函數 cvResize 將圖像 src 改變尺寸得到與 dst 同樣大小。若設定 ROI,函數將按常規支持 ROI.
/****************************************************************************************************/

  相信使用過Opencv的朋友都知道如何使用此函數。下面根據我自己的理解,用VC++ 來實現圖像縮放算法 ,希望大家能從中理解圖像縮放算法的原理。

 


第1節 最近鄰插值

 

  最簡單的圖像縮放算法就是最近鄰插值。顧名思義,就是將目標圖像各點的像素值設為源圖像中與其最近的點。假設源圖像的寬度和高度分別為w0和h0, 縮放后的目標圖像的寬度和高度分別為w1和h1, 那么比例就是float fw = float(w0)/w1; float fh = float(h0)/h1; 對於目標圖像中的(x,y)點坐標對應着源圖像中的(x0, y0)點。其中:x0 = int(x*fw), y0 = int(y*fh)。

  示例1:現在將一張670*503的BMP圖像縮放到200*160,代碼和效果如下。

void ResizeNear01(CImage &src, CImage &dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();

int w1 = dst.GetWidth();
int h1 = dst.GetHeight();

float fw = float(w0) / w1;
float fh = float(h0) / h1;

int x0, y0;
for(int y=0; y<h1; y++)
{
y0 = int(y * fh);
for(int x=0; x<w1; x++)
{
x0 = int(x * fw);
dst.SetPixel(x, y, src.GetPixel(x0, y0));
}
}
}

分析:對於此程序,我們將執行此ResizeNear01函數的語句加上一個for循環,使其執行100次,看它的速度怎么樣。

#include <time.h>
void CResizeDemoDlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
CImage src, dst;
src.Load(L"d:\\1.bmp");
dst.Create(200, 160, 24);

clock_t start = clock();
for(int i=0; i<100; i++)
{
ResizeNear01(src, dst);
}
float end = float(clock() - start)/CLOCKS_PER_SEC;
CString str;
str.Format(L"%6.2f", end);
MessageBox(str);


dst.Save(L"d:\\rs.jpg");
}

  顯示程序執行所用的時間為20.59秒,平均一次需要0.2秒,看起來速度還可以,是因為時間復雜度較低。目標圖像的尺寸大小是200*160。


  示例2:示例1中的算法可以改進速度的地方有兩個。第一,因為最近鄰插值算法求源圖像中的坐標是固定的,可以把每個目標每個x和每個y對應的值通過一次循環先求出來,再利進入雙重循環。第二,使用指針效率更高,如果使用CImage提供的GetPixel和SetPixel是費時間的。

  

//優化后的最近鄰插值算法
void ResizeNear02(CImage &src, CImage &dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();
int pitch0 = src.GetPitch();

int w1 = dst.GetWidth();
int h1 = dst.GetHeight();
int pitch1 = dst.GetPitch();

float fw = float(w0) / w1;
float fh = float(h0) / h1;

int *arr_x = new int[w1];
int *arr_y = new int[h1];
for(int y=0; y<h1; y++)
{
arr_y[y] = int(y*fh);
}
for(int x=0; x<w1; x++)
{
arr_x[x] = int(x*fw);
}

BYTE* pSrc = (BYTE*)src.GetBits();
BYTE* pDst = (BYTE*)dst.GetBits();
BYTE* p0, *p1;
for(int y=0; y<h1; y++)
{
p0 = pSrc + pitch0 * arr_y[y];
p1 = pDst + pitch1 * y;
for(int x=0; x<w1; x++)
{
//dst.SetPixel(x, y, src.GetPixel(arr_x[x], arr_y[y]));
memcpy(p1 + 3*x, p0 + arr_x[x]*3, 3);
}
}

delete []arr_x;
delete []arr_y;
}

  同樣執行示例1中的測試程序,讓ResizeNear02也循環一百次,在我機器上測試得到的結果是0.05秒,速度提高了400倍,這是一件多么讓人興奮的事情啊。
本人接觸圖像處理雖然也快一年了,但還是新手,望大家多多指正。

 

 





 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM