Skip to content

Commit

Permalink
Add method to convert a QImage to a gdal memory dataset
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 1, 2019
1 parent a90d1b3 commit 27e5eb1
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/core/qgsgdalutils.cpp
Expand Up @@ -95,6 +95,65 @@ gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandTiffDataset( QString file
return hDstDS;
}

gdal::dataset_unique_ptr QgsGdalUtils::imageToMemoryDataset( const QImage &image, QgsRectangle extent, const QgsCoordinateReferenceSystem &crs )
{
if ( image.isNull() )
return nullptr;
QByteArray red, green, blue, alpha;

const QRgb *rgb = reinterpret_cast<const QRgb *>( image.constBits() );

// potentially an overflow here -- that's on Qt!
const qgssize count = static_cast< qgssize >( image.width() ) * image.height();
red.resize( sizeof( float ) * count );
green.resize( sizeof( float ) * count );
blue.resize( sizeof( float ) * count );
alpha.resize( sizeof( float ) * count );

float *rData = reinterpret_cast<float *>( red.data() );
float *gData = reinterpret_cast<float *>( green.data() );
float *bData = reinterpret_cast<float *>( blue.data() );
float *aData = reinterpret_cast<float *>( alpha.data() );

gdal::dataset_unique_ptr hSrcDS( QgsGdalUtils::createMultiBandMemoryDataset( GDT_Float32, 4, extent, image.width(), image.height(), crs ) );
for ( qgssize i = 0; i < count; ++i )
{
const QRgb c = *rgb++;
*rData++ = qRed( c );
*gData++ = qGreen( c );
*bData++ = qBlue( c );
*aData++ = qAlpha( c );
}


CPLErr err = GDALRasterIO( GDALGetRasterBand( hSrcDS.get(), 1 ), GF_Write, 0, 0, image.width(), image.height(), red.data(), image.width(), image.height(), GDT_Float32, 0, 0 );
if ( err != CE_None )
{
QgsDebugMsg( QStringLiteral( "failed to write red data to GDAL dataset" ) );
return nullptr;
}
err = GDALRasterIO( GDALGetRasterBand( hSrcDS.get(), 2 ), GF_Write, 0, 0, image.width(), image.height(), green.data(), image.width(), image.height(), GDT_Float32, 0, 0 );
if ( err != CE_None )
{
QgsDebugMsg( QStringLiteral( "failed to write green data to GDAL dataset" ) );
return nullptr;
}
err = GDALRasterIO( GDALGetRasterBand( hSrcDS.get(), 3 ), GF_Write, 0, 0, image.width(), image.height(), blue.data(), image.width(), image.height(), GDT_Float32, 0, 0 );
if ( err != CE_None )
{
QgsDebugMsg( QStringLiteral( "failed to write blue data to GDAL dataset" ) );
return nullptr;
}
err = GDALRasterIO( GDALGetRasterBand( hSrcDS.get(), 4 ), GF_Write, 0, 0, image.width(), image.height(), alpha.data(), image.width(), image.height(), GDT_Float32, 0, 0 );
if ( err != CE_None )
{
QgsDebugMsg( QStringLiteral( "failed to write alpha data to GDAL dataset" ) );
return nullptr;
}

return hSrcDS;
}

void QgsGdalUtils::resampleSingleBandRaster( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg )
{
gdal::warp_options_unique_ptr psWarpOptions( GDALCreateWarpOptions() );
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsgdalutils.h
Expand Up @@ -60,6 +60,12 @@ class CORE_EXPORT QgsGdalUtils
*/
static gdal::dataset_unique_ptr createSingleBandTiffDataset( QString filename, GDALDataType dataType, QgsRectangle extent, int width, int height, const QgsCoordinateReferenceSystem &crs );

/**
* Converts an \a image to a GDAL memory dataset.
*
* \since QGIS 3.12
*/
static gdal::dataset_unique_ptr imageToMemoryDataset( const QImage &image, QgsRectangle extent, const QgsCoordinateReferenceSystem &crs );

/**
* Resamples a single band raster to the destination dataset with different resolution (and possibly with different CRS).
Expand Down
72 changes: 72 additions & 0 deletions tests/src/core/testqgsgdalutils.cpp
Expand Up @@ -39,8 +39,11 @@ class TestQgsGdalUtils: public QObject
void testCreateMultiBandMemoryDataset();
void testCreateSingleBandTiffDataset();
void testResampleSingleBandRaster();
void testImageToDataset();

private:

double identify( GDALDatasetH dataset, int band, int px, int py );
};

void TestQgsGdalUtils::initTestCase()
Expand Down Expand Up @@ -91,6 +94,8 @@ void TestQgsGdalUtils::supportsRasterCreate()
"AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433," \
"AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]"
#endif
#define EPSG_3857_WKT \
"PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"

void TestQgsGdalUtils::testCreateSingleBandMemoryDataset()
{
Expand Down Expand Up @@ -191,5 +196,72 @@ void TestQgsGdalUtils::testResampleSingleBandRaster()
QFile::remove( outputFilename );
}

void TestQgsGdalUtils::testImageToDataset()
{
QString inputFilename = QString( TEST_DATA_DIR ) + "/rgb256x256.png";
QImage src = QImage( inputFilename );
src = src.convertToFormat( QImage::Format_ARGB32 );
QVERIFY( !src.isNull() );

gdal::dataset_unique_ptr dstDS = QgsGdalUtils::imageToMemoryDataset( QImage(), QgsRectangle( 0, 0, 256, 256 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) );
QVERIFY( !dstDS );

dstDS = QgsGdalUtils::imageToMemoryDataset( src, QgsRectangle( 0, 0, 256, 256 ), QgsCoordinateReferenceSystem( "EPSG:3857" ) );
QVERIFY( dstDS );

QCOMPARE( GDALGetRasterCount( dstDS.get() ), 4 );
QCOMPARE( GDALGetRasterXSize( dstDS.get() ), 256 );
QCOMPARE( GDALGetRasterYSize( dstDS.get() ), 256 );

QCOMPARE( GDALGetProjectionRef( dstDS.get() ), EPSG_3857_WKT );
double geoTransform[6];
double geoTransformExpected[] = { 0, 1, 0, 256, 0, -1 };
QCOMPARE( GDALGetGeoTransform( dstDS.get(), geoTransform ), CE_None );
QGSCOMPARENEAR( geoTransform[0], geoTransformExpected[0], 0.0001 );
QGSCOMPARENEAR( geoTransform[1], geoTransformExpected[1], 0.0001 );
QGSCOMPARENEAR( geoTransform[2], geoTransformExpected[2], 0.0001 );
QGSCOMPARENEAR( geoTransform[3], geoTransformExpected[3], 0.0001 );
QGSCOMPARENEAR( geoTransform[4], geoTransformExpected[4], 0.0001 );
QGSCOMPARENEAR( geoTransform[5], geoTransformExpected[5], 0.0001 );

QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 1 ) ), GDT_Float32 );
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 2 ) ), GDT_Float32 );
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 3 ) ), GDT_Float32 );
QCOMPARE( GDALGetRasterDataType( GDALGetRasterBand( dstDS.get(), 4 ) ), GDT_Float32 );

QCOMPARE( identify( dstDS.get(), 1, 50, 50 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 1, 200, 50 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 1, 50, 200 ), 0.0 );
QCOMPARE( identify( dstDS.get(), 1, 200, 200 ), 0.0 );

QCOMPARE( identify( dstDS.get(), 2, 50, 50 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 2, 200, 50 ), 0.0 );
QCOMPARE( identify( dstDS.get(), 2, 50, 200 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 2, 200, 200 ), 0.0 );

QCOMPARE( identify( dstDS.get(), 3, 50, 50 ), 0.0 );
QCOMPARE( identify( dstDS.get(), 3, 200, 50 ), 0.0 );
QCOMPARE( identify( dstDS.get(), 3, 50, 200 ), 0.0 );
QCOMPARE( identify( dstDS.get(), 3, 200, 200 ), 255.0 );

QCOMPARE( identify( dstDS.get(), 4, 50, 50 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 4, 200, 50 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 4, 50, 200 ), 255.0 );
QCOMPARE( identify( dstDS.get(), 4, 200, 200 ), 255.0 );
}

double TestQgsGdalUtils::identify( GDALDatasetH dataset, int band, int px, int py )
{
GDALRasterBandH hBand = GDALGetRasterBand( dataset, band );

float *pafScanline = ( float * ) CPLMalloc( sizeof( float ) );
CPLErr err = GDALRasterIO( hBand, GF_Read, px, py, 1, 1,
pafScanline, 1, 1, GDT_Float32, 0, 0 );
double value = err == CE_None ? pafScanline[0] : std::numeric_limits<double>::quiet_NaN();
CPLFree( pafScanline );

return value;
}

QGSTEST_MAIN( TestQgsGdalUtils )
#include "testqgsgdalutils.moc"

0 comments on commit 27e5eb1

Please sign in to comment.