使用GDAL對超過2G的大型圖片進行分割

問題背景

  之前給公司的建模師做過一個切圖的小工具,主要功能就是將一張大圖切成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);
}

後續可以使用它來做地圖的切片啦~

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