3

I'm a rookie in GDAL and C++. I tried to copy the red band of the RGB tif file with Create() and GetRasterBand(1)->RasterIO(). I got the right size of my new tif file, but all the value of the new image is 128. I tried to check the value while it run, and I'm sure they have the same value in the red band of the RGB TIFF file. I guessed there is something wrong in the GetRasterBand(1)->RasterIO(). However, I still have no idea after checking the document.

This project need to run with the Visual Studio 2008 on Win10, so I used the version 2.2.3 of GDAL.

#include "stdafx.h"
#include "stdio.h"
#include "gdal.h"
#include "gdal_priv.h"
#include "cpl_conv.h" //for CPLMalloc()
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[]) {
//Register GDAL
 GDALAllRegister();

//image file input_file
const char* input = "C:\\Users\\GOLiF\\Documents\\Visual Studio 2008\\Projects\\GDAL_223\\GDAL_223\\94212060_20190924_109EMAP.tif";
const char* output = "C:\\Users\\GOLiF\\Documents\\Visual Studio 2008\\Projects\\GDAL_223\\GDAL_223\\test.tif";

GDALDataset *pOldDS, *pNewDS ;
GDALDriver *pDriverTiff ;
int nRows, nCols ;
double transform[6] ;
double noData ;

pOldDS = (GDALDataset*) GDALOpen( input, GA_ReadOnly ) ;

nRows = pOldDS ->GetRasterBand(1) ->GetYSize() ;
nCols = pOldDS ->GetRasterBand(1) ->GetXSize() ;
noData = pOldDS ->GetRasterBand(1) ->GetNoDataValue() ;
pOldDS ->GetGeoTransform(transform) ;

pDriverTiff = GetGDALDriverManager()->GetDriverByName("GTiff") ;

pNewDS =  pDriverTiff->Create( output, nCols, nRows, 1, GDT_Int16, NULL) ;
pNewDS->SetGeoTransform(transform) ;
pNewDS->GetRasterBand(1)->SetNoDataValue(noData) ;

short *oldRow = (short *) CPLMalloc(sizeof(short)*nCols) ;
short *newRow = (short *) CPLMalloc(sizeof(short)*nCols) ;

for (int i = 0 ; i < nRows ; i++){
    pOldDS->GetRasterBand(1)->RasterIO(GF_Read, 0, i, nCols, 1, oldRow, nCols, 1, GDT_Int16, 0, 0) ;
    for (int j = 0 ; j < nCols ; j++){
        if (oldRow[j] == noData ){
            newRow[j] = noData ;
            //cout << "nodata : newrow[" << j << "] = " << newrow[j] << endl;
            //system("pause");
        }
        else{
            newRow[j] = oldRow[j] ;
            //cout << "oldrow[" << j << " ]= " << oldRow[j] << endl;
            //cout << "newrow[" << j << " ]= " << newRow[j] << endl;
            //system("pause");
        }
    }
    pNewDS->GetRasterBand(1)->RasterIO(GF_Write, 0, i, nCols, 1, newRow, nCols, 1, GDT_Int16, 0, 0) ;
    //cout << "newrow= " << &newRow << endl;
}

GDALClose(pOldDS);
GDALClose(pNewDS);

system("pause");

}
3
  • I can't see any obvious flaws with your code but are you sure your input raster is 16 bit? For this type of operation I've used __int16 as 'short' is up to the compiler but guaranteed to be at least 16 bits. A hint: your duplication of oldRow and newRow is wasteful as too are your nested loops, as the array is the same size you could do pNewDS->GetRasterBand(1)->RasterIO(GF_Write, 0, i, nCols, 1, oldRow. I would also suggest casting your band (source and destination) to a GDALRasterBand* object, if only to make less typing. Commented Sep 28, 2022 at 7:05
  • Actually, my input raster is byte ( if I call ` GDALGetDataTypeName(poBand->GetRasterDataType()) ` I'll get the "Byte" ) and the value of my raw tiff is 50-233, but if I read the band with 'GDT_Byte', I can't get the right value. I think I misunderstand something, but I directually use the 'GDT_Int16'. And back to your suggestion, I check my input raster is 16 bit(if I call ` sizeof(short) ` I'll get the "2"). However, I agree using '__int16' is better, so I changed it, Of cause, all the value of the new image is 128.
    – Blue
    Commented Sep 29, 2022 at 4:28
  • Also, thank your hint, but I tried to calculate the value in three bands and failed. So I tried to just read and write and still failed. That's why I posted my code to see if I miss somthing.
    – Blue
    Commented Sep 29, 2022 at 4:31

1 Answer 1

3

I just remembered that I had this problem also many years ago, it's in your close statements:

GDALClose(pOldDS);
GDALClose(pNewDS);

From the help document

Equivalent of the C callable GDALClose(). Except that GDALClose() first decrements the reference count, and then closes only if it has dropped to zero. For Windows users, it is not recommended to use the delete operator on the dataset object because of known issues when allocating and freeing memory across module boundaries. Calling GDALClose() is then a better option.

Which means delete pOldDS; isn't a safe option so the best way is with GDALClose. However the C callable GDALClose (GDALDatasetH hDS) expects a parameter of type GDALDatasetH, which is different to GDALDataset*, so when I changed my close statements to cast the pointer to a handle e.g. GDALClose((GDALDatasetH)FirstClass_DS); the dataset was closed properly and flushed to disc. Can you try changing your close statements to:

pNewDS->FlushCache(); // only need to flush the dataset that has been written to
GDALClose((GDALDatasetH)pOldDS);
GDALClose((GDALDatasetH)pNewDS);
GDALDestroyDriverManager(); // this should remove any remaining locks but no more GDAL functions from here will work

and see if that fixes the problem.

5
  • Thank for your answer. It's quite clear. I read the document and I just notice that I shouldn't use 'delete pOldDS;'. I changed my close statements to: GDALClose((GDALDatasetH)pOldDS); GDALClose((GDALDatasetH)pNewDS); However, all the value of the new image is still 128.
    – Blue
    Commented Sep 29, 2022 at 4:44
  • Perhaps there's still a reference remaining, try flush the writes and GDALDestroyDriverManager(); on the very last line, I'll edit that into the code to show where I put them. Commented Sep 29, 2022 at 5:20
  • Thank you so much. Unfortunately, the way didn't work. All the value of the new image is still 128. Do you have other idea of that situation?
    – Blue
    Commented Sep 29, 2022 at 8:39
  • The only thing I can see different is I allocate my memory using _aligned_malloc learn.microsoft.com/en-us/cpp/c-runtime-library/reference/… to ensure multibyte types align with the word boundary. As your input raster is of type GDT_Byte perhaps it would be a good time to change the data types to match the input. In the meantime I'll test your code on an 8 bit 3 band raster I have locally. Commented Sep 30, 2022 at 2:25
  • I tested your code exactly as written (different raster paths though) with the flush to close and it worked for me. The 16/8 bit data type was not a problem. I'm using 64 bit GDAL 1.11.1, released 2014/09/24 with Visual Studio 2012, version 11.0.61219.00 update 5 which is a bit newer than your version of studio, perhaps you might need to grab a much older dev version of GDAL from the archives of GISInternals gisinternals.com/archive.php. Commented Sep 30, 2022 at 4:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.