问题背景
之前给公司的建模师做过一个切图的小工具,主要功能就是将一张大图切成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);
}
后续可以使用它来做地图的切片啦~