問題背景
之前給公司的建模師做過一個切圖的小工具,主要功能就是將一張大圖切成nn份,每份的像素大小爲wh的圖像,剛開始是簡單地用Qt的QPixmap做的,基本上一兩行代碼就可以實現。但是後面建模師在切一張3個G大的圖片的時候,這個工具就沒用了,定位了下問題,QPixmap無法加載超過2G的圖片,遂卒。
解決方案
後面就找到了GDAL庫,過程中參考了兩篇博客GDAL關於讀寫圖像的簡明總結和使用GDAL打開和保存常見格式圖像(代碼)。不多說了,直接上代碼吧,這樣好解釋一點,中間有些與界面交互以及防止界面卡死的代碼已經刪掉了,僅貼出了分割相關的代碼:
//切割圖片並在源圖片位置生成切割好的圖片
void Widget::splitWithGDAL(QString filepath,int xNum, int yNum, int scaleWidth, int scaleHeight)
{
GDALAllRegister();
const char* pszFilename = filepath.toStdString ().c_str ();
GDALDataset *srcDataset;
srcDataset = (GDALDataset*)GDALOpen(pszFilename,GA_ReadOnly);
if(srcDataset == NULL)
return;
QFileInfo fileinfo;
fileinfo = QFileInfo(filepath);
QString filebasepath=fileinfo.absolutePath () + QString("/");
QString filebasename=fileinfo.baseName ();
QString filebasetype=QString(".") + fileinfo.suffix ();
int index=0;
int srcWidth = srcDataset->GetRasterXSize ();//源圖片寬度
int srcHeight = srcDataset->GetRasterYSize ();//源圖片高度
int bandCount = srcDataset->GetRasterCount ();//源圖片波段數(通道數,彩色一般爲3,可簡單理解爲RGB三通道)
int depth = GDALGetDataTypeSize(srcDataset->GetRasterBand (1)->GetRasterDataType ());//源圖片位數
int srcBufWidth = srcWidth / xNum;//算出每份所佔源圖的寬度
int srcBufHeight = srcHeight /yNum;//算出每份所佔源圖的高度
GDALDriver* poDriver;
poDriver = srcDataset->GetDriver ();
char** papszMetadata;
papszMetadata = poDriver->GetMetadata ();
if(CSLFetchBoolean(papszMetadata, GDAL_DCAP_CREATE, FALSE ))//判斷該圖像格式的驅動是否支持使用Create方法
{
//驅動支持Create()函數的情況
for (int y = 0; y< yNum; y++)
{
for (int x = 0; x < xNum; x++)
{
index++;
QString target_name= filebasepath + filebasename + QString::number (index) + filebasetype;
//GDAL處理圖像
int startX = x * srcWidth / xNum;
int startY = y * srcHeight / yNum;
GDALDataset* poDstDS;
poDstDS = poDriver->Create (target_name.toStdString ().c_str (),scaleWidth,scaleHeight,bandCount,GDT_Byte,NULL);
size_t imgBufNum = (size_t) scaleWidth * scaleHeight * bandCount;
GByte *imgBuf = new GByte[imgBufNum];
//讀取
srcDataset->RasterIO(GF_Read, startX, startY, srcBufWidth, srcBufHeight, imgBuf, scaleWidth, scaleHeight,
GDT_Byte, bandCount, nullptr, 0,0,0);//使用默認的讀取方式,最後面的三個參數具體含義,參見https://www.cnblogs.com/charlee44/p/5723213.html
poDstDS->RasterIO(GF_Write, 0, 0, scaleWidth, scaleHeight, imgBuf, scaleWidth, scaleHeight,
GDT_Byte, bandCount, nullptr, 0,0,0);
//釋放資源
delete[] imgBuf;
imgBuf = nullptr;
if(poDstDS!=NULL)
GDALClose((GDALDatasetH)poDstDS);
}
}
}
else
{
//驅動不支持Create()函數的情況
if(CSLFetchBoolean( papszMetadata, GDAL_DCAP_CREATECOPY, FALSE ))
{
GDALDriver *pDriverMEM = GetGDALDriverManager()->GetDriverByName("MEM");//使用MEM格式對圖片進行轉存
for (int y = 0; y< yNum; y++)
{
for (int x = 0; x < xNum; x++)
{
index++;
QString target_name= filebasepath + filebasename + QString::number (index) + filebasetype;
//GDAL處理圖像
int startX = x * srcWidth / xNum;
int startY = y * srcHeight / yNum;
GDALDataset *dstMEMDataset = pDriverMEM->Create("", scaleWidth,scaleHeight,bandCount,GDT_Byte,NULL);
size_t imgBufNum = (size_t) scaleWidth * scaleHeight * bandCount;
GByte *imgBuf = new GByte[imgBufNum];
//讀取
srcDataset->RasterIO(GF_Read, startX, startY, srcBufWidth, srcBufHeight, imgBuf, scaleWidth, scaleHeight,
GDT_Byte, bandCount, nullptr, 0,0,0);
qDebug()<<"Split: "<<index<<"read successfully";
dstMEMDataset->RasterIO(GF_Write, 0, 0, scaleWidth, scaleHeight, imgBuf, scaleWidth, scaleHeight,
GDT_Byte, bandCount, nullptr, 0,0,0);
poDriver->CreateCopy(target_name.toStdString ().c_str (),dstMEMDataset,FALSE,0,0,0);
//釋放資源
delete[] imgBuf;
imgBuf = nullptr;
if(dstMEMDataset!=NULL)
GDALClose((GDALDatasetH)dstMEMDataset);
}
}
}
else
{
GDALClose((GDALDatasetH)srcDataset);
return;
}
}
GDALClose((GDALDatasetH)srcDataset);
}
後續可以使用它來做地圖的切片啦~