歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> GDAL關於讀寫圖像的簡明總結

GDAL關於讀寫圖像的簡明總結

日期:2017/3/1 9:13:16   编辑:Linux編程

讀寫影像可以說是圖像處理最基礎的一步。關於使用GDAL讀寫影像,平時也在網上查了很多資料,就想結合自己的使用心得,做做簡單的總結。

在這裡寫一個例子:裁剪lena圖像的某部分內容,將其放入到新創建的.tif文。以此來說明GDAL讀寫影像的具體實現。

1.打開圖像

用GDAL打開lena.bmp,實現如下。注意這裡打開圖像,指的是獲取圖像的頭文件,以此得到圖像的一些信息,沒有涉及到讀取像素操作。

GDALAllRegister();          //GDAL所有操作都需要先注冊格式
const char* imgPath = "E:\\Data\\lena.bmp";
GDALDataset* img = (GDALDataset *)GDALOpen(imgPath, GA_ReadOnly);
if (img == nullptr)
{
    cout << "Can't Open Image!" << endl;
    return 1;
}

圖像需要關注的信息很多,可以重點關注以下四個值。圖像寬、高總所周知了,而波段數就是通道,如RGB圖像的波段數為3。深度標識的就是圖像的存儲單位,比如一般圖像就是8位,用無字節字符型unsigned char來表達0~255的像素值;而除以8標識1個字節,方便讀取像素buf。

int imgWidth = img->GetRasterXSize();   //圖像寬度
int imgHeight = img->GetRasterYSize();  //圖像高度
int bandNum = img->GetRasterCount();    //波段數
int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8;    //圖像深度

如果已經讀取完畢或者不需要這張圖像的相關操作了,最後要關閉打開的文件,否則會內存洩漏。

GDALClose(img);

2.創建圖像

用GDAL創建一個新的圖像,例如這裡創建了一個256X256大小,被讀取圖像波段,深度8位的tif。

GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("GTIFF"); //圖像驅動
char** ppszOptions = NULL;
ppszOptions = CSLSetNameValue(ppszOptions, "BIGTIFF", "IF_NEEDED"); //配置圖像信息
const char* dstPath = "E:\\Data\\dst.tif";
int bufWidth = 256;
int bufHeight = 256;
GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, bandNum, GDT_Byte, ppszOptions);
if (dst == nullptr)
{
    printf("Can't Write Image!");
    return false;
}

需要注意的是創建圖像可能需要一些特別的設置信息,是需要到GDAL對應格式的文檔中去查看的,也可以什麼都不設置用默認值。我這裡設置的是如果需要的話,就創建支持大小超過4G的bigtiff。

如果已經寫入完畢或者不需要這張圖像的相關操作了,最後一定要注意關閉關閉打開的文件,之前只會內存洩漏,而這裡還會可能創建失敗。

GDALClose(dst);

如果創建後什麼都不做,關閉後GDAL會自動寫入0像素值,打開後就是純黑色圖像。

3.圖像讀寫

GDAL讀寫圖像是通過RasterIO()這個函數實現的,這個函數提供了非常強大的功能,目前筆者也只總結了這以下方面的內容。

3.1.一般情況下讀寫

GDAL讀取圖像是以左上角為起點的,讀取起點位置開始的256X256的內容,寫入dst.tif中的實現如下:

//申請buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];   
//讀取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//寫入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//釋放
delete[] imgBuf;
imgBuf = nullptr;

逐個說明RasterIO()參數的含義:

  • 參數1:讀寫標記。如果為GF_Read,則是將影像內容寫入內存,如果為GF_Write,則是將內存中內容寫入文件。
  • 參數2、3:讀寫開始位置。相對於圖像左上角頂點(從零開始)的行列偏移量。
  • 參數4、5:要讀寫的塊在x方向的象素個數和y方向的象素列數。
  • 參數6:指向目標緩沖區的指針,由用戶分配。
  • 參數7、8:目標塊在x方向上和y方向上的大小。
  • 參數9:目標緩沖區的數據類型,原類型會自動轉換為目標類型。
  • 參數10:要處理的波段數。
  • 參數11:記錄要操作的波段的索引(波段索引從1開始)的數組,若為空則數組中存放的是前nBandCount個波段的索引。
  • 參數12:X方向上兩個相鄰象素之間的字節偏移,默認為0,則列間的實際字節偏移由目標數據類型eBufType確定。
  • 參數13:y方向上相鄰兩行之間的字節偏移, 默認為0,則行間的實際字節偏移為eBufType * nBufXSize。
  • 參數14:相鄰兩波段之間的字節偏移,默認為0,則意味著波段是順序結構的,其間字節偏移為nLineSpace * nBufYSize。

有的參數推薦使用上面的標准寫法而不是采用默認值0,可以更好地理解圖像buf的存放排布。最後得到的dst.tif如下:

3.2.16位影像讀寫

上述RasterIO()的寫法可以兼容16為圖像的讀寫,只不過要注意的是buf中是用2個Gbyte來表達1個16像素值的。當然為了更方便圖像處理,也可以采用16位整型來讀取buf:

//申請buf
size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum;
GUInt16 *imgBuf = new GUInt16[imgBufNum];
//讀取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_UInt16, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//寫入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_UInt16, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//釋放
delete[] imgBuf;
imgBuf = nullptr;

可以發現,除了要更改buf的容量和RasterIO()的第九個參數GDT_UInt16,其余什麼都不需要更改。注意創建16位圖像時參數也需要更改成16位:

GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, bandNum, GDT_UInt16, ppszOptions);

3.3.讀取特定波段

某些情況下需要讀取特定波段,或者需要重組波段順序。例如VC中顯示圖像往往需要將buf按照BGR傳遞給BITMAP,再顯示BITMAP。這時只需要修改第11個參數就行了:

//波段索引
int panBandMap[3] = { 3,2,1 };
//申請buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];   
//讀取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_Byte, bandNum, panBandMap, bandNum*depth, bufWidth*bandNum*depth, depth);
//寫入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//釋放
delete[] imgBuf;
imgBuf = nullptr; 

這時得到的dst.tif為:

3.4.左下角起點讀寫

默認情況RasterIO()是以左上角起點讀寫的,不過也是可以以左下角為起點讀寫,只需要重新設置排布buf的位置。這裡讀寫lena圖像上同一塊位置:

//申請buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
size_t imgBufOffset = (size_t) bufWidth * (bufHeight-1) * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];
//讀取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//寫入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//釋放
delete[] imgBuf;
imgBuf = nullptr; 

注意這裡Y方向起點位置,也就是第三個參數仍然要用左上角起算,但是buf已經是左下角起點了。

3.5.重采樣讀寫

RasterIO()另外一個用法是可以自動縮放,重采樣讀寫影像,例如這裡將512X512大小的lena圖像重采樣成256X256大小:

//申請buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
size_t imgBufOffset = (size_t) bufWidth * (bufHeight-1) * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];   
//讀取
img->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//寫入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
    GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//釋放
delete[] imgBuf;
imgBuf = nullptr; 

可以看到重采樣讀寫只需要修改參數4,參數5就行了。查閱網上資料得知,RasterIO()重采樣方式默認是最臨近的方法,只有建立金字塔時可以設置重采樣方式,但也僅限於縮小。最後得到的dst.tif結果:

GDAL功能非常豐富,本文僅僅做了一點關於圖像讀寫的總結,自認為算的上“簡明”了。當然也希望大家批評指正。

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm

《OpenGL超級寶典》學習筆記 http://www.linuxidc.com/Linux/2013-10/91414.htm

Copyright © Linux教程網 All Rights Reserved