Wednesday 20 July 2016

[GDAL API]: can't save image in some formats


edited "32-bit buffer" below means that it contains 32-bit rgba pixels.


I need to save image to file from 32-bit memory buffer. Code below works well for that when I use TIFF format. In this case saved image file is correct. But the same code doesn't work for JPEG and PNG. In these cases files are created, they have valid dimensions, color depth, but all pixels are black. GDAL functions don't signal any errors.


Some strings with error checking were omitted in the code below.


buf variable refers to 32-bit source buffer with ready image pixels.


To save to PNG format, I change driver name strDrv to "PNG" and file extension to .png correspondingly. For jpeg too.


union rgba_t {
struct { unsigned char g, b, r, a; };

unsigned c;
};

int cx = 500, cy = 500;
rgba_t buf = new rgba_t[cx * cy];
memset(buf, 0xff, cx * cy * sizeof(rgba_t));

GDALAllRegister();

GDALDataset *pDs, *pDsMem;

char **opt = NULL;
GDALDriver *pDriver, *pDriverMem;
int nBands = 3;
char *strDrv = "GTiff",
*fpath = "c:\\tmp\\o.tiff";

pDriverMem = GetGDALDriverManager()->GetDriverByName("MEM");
pDsMem = pDriverMem->Create(fpath, cx, cy, nBands, GDT_Byte, opt);

pDriver = GetGDALDriverManager()->GetDriverByName(strDrv);

pDs = pDriver->CreateCopy(fpath, pDsMem, FALSE, opt, NULL, NULL);

GDALRasterBand *pBandR = pDs->GetRasterBand(1),
*pBandG = pDs->GetRasterBand(2),
*pBandB = pDs->GetRasterBand(3);

int szScan = cx * sizeof(rgba_t);

CPLErr err;
err = pBandR->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 2, cx, cy, GDT_Byte, 4, szScan);

err = pBandG->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 1, cx, cy, GDT_Byte, 4, szScan);
err = pBandB->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 0, cx, cy, GDT_Byte, 4, szScan);

GDALClose(pDs);
delete[] buf;

Answer



I've caught my error. May be it will be helpful for somebody.


CreateCopy() makes read-only Dataset for PNG and JPG, so RasterIO() doesn't write anything into dataset's bands. We have to call RasterIO for the memory Dataset. I've made tests for PNG, JPEG and TIFF, it works.


Create() call for "MEM" dataset doesn't allocate memory for the raster. Memory allocation takes place in RasterIO() calls.


We can activate driver and call CreateCopy() for destination format at the end, when image was completely formed in the memory dataset.



The final code can look like...


union rgba_t {
struct { unsigned char g, b, r, a; };
unsigned c;
};

int cx = 500, cy = 500;
rgba_t buf = new rgba_t[cx * cy];
memset(buf, 0xff, cx * cy * sizeof(rgba_t));


GDALAllRegister();

GDALDataset *pDs, *pDsMem;
char **opt = NULL;
GDALDriver *pDriver, *pDriverMem;
int nBands = 3;
char *strDrv = "GTiff",
*fpath = "c:\\tmp\\o.tiff";

pDriverMem = GetGDALDriverManager()->GetDriverByName("MEM");

pDsMem = pDriverMem->Create("", cx, cy, nBands, GDT_Byte, opt);

GDALRasterBand *pBandR = pDsMem->GetRasterBand(1),
*pBandG = pDsMem->GetRasterBand(2),
*pBandB = pDsMem->GetRasterBand(3);

int szScan = cx * sizeof(rgba_t);

CPLErr err;
err = pBandR->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 2, cx, cy, GDT_Byte, 4, szScan);

err = pBandG->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 1, cx, cy, GDT_Byte, 4, szScan);
err = pBandB->RasterIO(GF_Write, 0, 0, cx, cy, (BYTE *)buf + 0, cx, cy, GDT_Byte, 4, szScan);

pDriver = GetGDALDriverManager()->GetDriverByName(strDrv);
pDs = pDriver->CreateCopy(fpath, pDsMem, FALSE, opt, NULL, NULL);

GDALClose(pDsMem);
GDALClose(pDs);

delete[] buf;

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...