使用GDAL对超过2G的大型图片进行分割

本文分享了使用GDAL库解决大图切割问题的经验,详细介绍了如何通过GDAL处理超过2GB的大图,并提供了具体的代码实现,包括图像读取、切割及保存过程。

问题背景

  之前给公司的建模师做过一个切图的小工具,主要功能就是将一张大图切成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);
}

后续可以使用它来做地图的切片啦~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鬼马行天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值