Nous utilisons des cookies pour vous garantir la meilleure expérience sur notre site. Si vous continuez à utiliser ce dernier, nous considèrerons que vous acceptez l'utilisation des cookies. J'ai compris ! ou En savoir plus !.
banniere

Le portail francophone de la géomatique


Toujours pas inscrit ? Mot de passe oublié ?
Nom d'utilisateur    Mot de passe              Toujours pas inscrit ?   Mot de passe oublié ?

Annonce

Printemps des cartes 2024

#1 Thu 10 June 2010 21:43

ABombardier
Juste Inscrit !
Date d'inscription: 10 Jun 2010
Messages: 3

Gdal

Bonjour à tous,

Je développe une application qui utilise l'API de GDAL.

L'application lis une image tiff (GDALOpen) extrait les pixels dans buffers pour les trois bandes (GDALRasterIO GF_Read), fait un traitement sur les buffers et sauvegarde les buffers dans le dataSet préalablement ouvert et flush le dataset  pour sauvegarder l'image traitée sur disque.

Je dois parfois créer un dataset vide (GDALDriver->Create(name, width, length,...) afin d'écrire dans le dataset vide les données d'un traitement issue d'une image source. La raison est que la taille de l'output est différente de l'input. Je passe donc par Create pour créer l'image de la taille voulue.

Le problème est que l'image sauvegardée est du garbage. Cela se produit seulement avec Create. Si je load une image avec GDALOpen, fait un traitement sur les buffer des pixels (bandes) et sauvegarde le dataset, tout est beau.

Bref, le problème est seulement lorsque j'utilise Create. Quelqu'un peut il m'expliquer comment faire une sauvegarde depuis un dataset issue de Create.

Merci à l'avance.

Andre Bombardier


Code:

typedef struct
{
    GDALDataset *dataSet;
    GDALDriver *gdalDriver;
 
    GDALRasterBand *redBand;
    GDALRasterBand *blueBand;
    GDALRasterBand *greenBand;
 
    int nXBlocks;
    int nYBlocks;
    int nXBlockSize;
    int nYBlockSize;

    int pixelWidth;
    int pixelHeight;
 
    char imageName[1024];
    int openMode;
 
} IMAGE_ENTRY;

char *papszOptions;
std::vector<IMAGE_ENTRY *> GDAL_ImageList;

int GetBufferByBlockIndex(GDALRasterBand *band, int xIndex, int yIndex, GByte *dataBuffer);
int GetBufferByPixelIndex(GDALRasterBand *band, int xIndex, int yIndex, GByte *dataBuffer);
int GetPixelInBlock(int xIndex, int yIndex, GByte *red, GByte *green, GByte *blue, GByte *redBuffer, GByte *greenBuffer, GByte *blueBuffer);


int GdalWrapperInit()
{
    GDALAllRegister();

    return 1;
}

IMAGE_ENTRY *GetImageEntry(GDALWrapperRGBBufferStruct *imageStruct)
{
    IMAGE_ENTRY *entry = NULL;
    
    std::vector<IMAGE_ENTRY *>::iterator current = GDAL_ImageList.begin();

    while (current != GDAL_ImageList.end())
    {
        if (*current)
        {
            if(strcmp((*current)->imageName, imageStruct->imageName) == 0) return *current;
        }

        current++;
    }

    return NULL;
}


int GdalWrapperOpenImage(GDALWrapperRGBBufferStruct *imageStruct)
{
    IMAGE_ENTRY *imageEntry = GetImageEntry(imageStruct);
    if(imageEntry != NULL) return 0;

    imageEntry = new IMAGE_ENTRY;

    imageEntry->pixelWidth = imageStruct->width;
    imageEntry->pixelHeight = imageStruct->height;
 
    strcpy(imageEntry->imageName, imageStruct->imageName);

    try
    {
        if(imageStruct->wantBlankImage == true)
        {
            imageEntry->gdalDriver = (GDALDriver *) GDALGetDriverByName( "GTiff" );   
            imageEntry->dataSet = imageEntry->gdalDriver->Create(imageEntry->imageName, imageStruct->width, 
                imageStruct->height, 3, GDT_Byte, NULL);
        }
        else
        {
            imageEntry->dataSet = (GDALDataset *) GDALOpen(imageEntry->imageName, GA_Update);
            imageEntry->gdalDriver = imageEntry->dataSet->GetDriver();
        }
    }

    catch(...)
    {
        delete imageEntry;
        return 0;
    }

    int   nXSize = GDALGetRasterBandXSize( imageEntry->dataSet->GetRasterBand(1) );
    int   nYSize = GDALGetRasterBandYSize( imageEntry->dataSet->GetRasterBand(1) );

    imageStruct->width = nXSize;
    imageStruct->height = nYSize;

    try
    {
        imageStruct->redBuffer = (GByte *) CPLMalloc(nXSize * nYSize);
        imageStruct->greenBuffer = (GByte *) CPLMalloc(nXSize * nYSize);
        imageStruct->blueBuffer = (GByte *) CPLMalloc(nXSize * nYSize);
    }

    catch(...)
    {
        if(imageEntry->dataSet != NULL) GDALClose(imageEntry->dataSet);

        if(imageStruct->redBuffer != NULL) CPLFree((void *)imageStruct->redBuffer);
        if(imageStruct->greenBuffer != NULL) CPLFree((void *)imageStruct->greenBuffer);
        if(imageStruct->blueBuffer != NULL) CPLFree((void *)imageStruct->blueBuffer);

        imageStruct->redBuffer = NULL;
        imageStruct->greenBuffer = NULL;
        imageStruct->blueBuffer = NULL;
        
        delete imageEntry;
        return 0;
    }

    // Transfer the Pixels in indidual local buffers
    try
    {
        GDALRasterIO((GDALRasterBandH)(imageEntry->dataSet->GetRasterBand(1)), GF_Read, 
            0, 0, nXSize, nYSize, imageStruct->redBuffer, nXSize , nYSize, GDT_Byte , 0, 0 );

        GDALRasterIO((GDALRasterBandH)imageEntry->dataSet->GetRasterBand(2), GF_Read, 
            0, 0, nXSize, nYSize, imageStruct->greenBuffer, nXSize , nYSize, GDT_Byte , 0, 0 );

        GDALRasterIO((GDALRasterBandH)imageEntry->dataSet->GetRasterBand(3), GF_Read, 
            0, 0, nXSize, nYSize, imageStruct->blueBuffer, nXSize , nYSize, GDT_Byte , 0, 0 );
    }

    catch(...)
    {
        if(imageEntry->dataSet != NULL) GDALClose(imageEntry->dataSet);

        if(imageStruct->redBuffer != NULL) CPLFree((void *)imageStruct->redBuffer);
        if(imageStruct->greenBuffer != NULL) CPLFree((void *)imageStruct->greenBuffer);
        if(imageStruct->blueBuffer != NULL) CPLFree((void *)imageStruct->blueBuffer);

        imageStruct->redBuffer = NULL;
        imageStruct->greenBuffer = NULL;
        imageStruct->blueBuffer = NULL;

        delete imageEntry;
        return 0;
    }

    GDAL_ImageList.push_back(imageEntry);
    
    return 1;
}


int GdalWrapperSaveImage(GDALWrapperRGBBufferStruct *imageStruct)
{
    IMAGE_ENTRY *imageEntry = GetImageEntry(imageStruct);
    if(imageEntry == NULL) return 0;

    GDALDataset *dataSet = imageEntry->dataSet;
    if(dataSet == NULL) return 0;

    int   nXSize = GDALGetRasterBandXSize(dataSet->GetRasterBand(1) );
    int   nYSize = GDALGetRasterBandYSize(dataSet->GetRasterBand(1) );

    GDALRasterIO(dataSet->GetRasterBand(1), GF_Write, 0, 0, nXSize, nYSize, imageStruct->redBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    GDALRasterIO(dataSet->GetRasterBand(2), GF_Write, 0, 0, nXSize, nYSize, imageStruct->greenBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    GDALRasterIO(dataSet->GetRasterBand(3), GF_Write, 0, 0, nXSize, nYSize, imageStruct->blueBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    //if(dataSet->GetRasterBand(1)) dataSet->GetRasterBand(1)->FlushCache();
    //if(dataSet->GetRasterBand(2)) dataSet->GetRasterBand(2)->FlushCache();
    //if(dataSet->GetRasterBand(3)) dataSet->GetRasterBand(3)->FlushCache();

    // Flush cache
    dataSet->FlushCache();

    return 1;
}

int GdalWrapperCloseImage(GDALWrapperRGBBufferStruct *imageStruct)
{
    IMAGE_ENTRY *imageEntry = GetImageEntry(imageStruct);
    if(imageEntry == NULL) return 0;
    
    GDALDataset *dataSet = imageEntry->dataSet;
    if(dataSet == NULL) return 0;

    // Flush cache
    // dataSet->FlushCache();

    IMAGE_ENTRY *entry = NULL;
    
    std::vector<IMAGE_ENTRY *>::iterator current = GDAL_ImageList.begin();

    while (current != GDAL_ImageList.end())
    {
        if (*current)
        {
            if(strcmp((*current)->imageName, imageStruct->imageName) == 0) 
            {
                (*current)->imageName[0] = 0;
                if((*current)->dataSet != NULL) GDALClose((*current)->dataSet);
                GDAL_ImageList.erase(current);

                return 1;
            }
        }

        current++;
    }

    if(imageStruct->redBuffer != NULL) CPLFree((void *)imageStruct->redBuffer);
    if(imageStruct->greenBuffer != NULL) CPLFree((void *)imageStruct->greenBuffer);
    if(imageStruct->blueBuffer != NULL) CPLFree((void *)imageStruct->blueBuffer);


    return 1;

}

Hors ligne

 

#2 Thu 10 June 2010 22:01

rouault
Participant assidu
Date d'inscription: 26 Apr 2009
Messages: 166

Re: Gdal

J'ai parcouru en diagonal ton code seulement, mais visiblement tu n'utilises GDALClose() que dans les cas d'erreurs et pas dans le cas nominal. Hors c'est *obligatoire* sinon tu n'as pas de garantie que toutes les données seront effectivement sauvegardées.

Cf http://gdal.org/classGDALDataset.html#b … 2aa6609e1, en particulier :

Using this method does not prevent use from calling GDALClose() to properly close a dataset and ensure that important data not addressed by FlushCache() is written in the file.

Pourquoi pour le format GTIFF, ça marche dans le cas de GDALOpen(, GA_Update) et pas dans le cas Create() ? En fait dans le cas Create(), certaines structures fondamentales du fichier TIFF (le "directory") ne peuvent être écrites qu'en tout dernier, et donc pas par FlushCache(). Dans le cas Update, si on ne met à jour que l'imagerie, FlushCache() peut effectivement suffire, mais si tu mettais à jour la table de couleurs, les tags de géoréférencement, etc... ça ne suffirait pas.

Morale de l'histoire : toujours utiliser GDALClose().

Cf http://gdal.org/classGDALDriver.html#19 … c711b8f7c4

After you have finished working with the returned dataset, it is required  to close it with GDALClose(). This does not only close the file handle, but also ensures that all the data and metadata has been written to the dataset (GDALFlushCache() is not sufficient for that purpose).

On pourra pas dire que la doc n'est pas explicite ;-) Et pourtant c'est une erreur assez fréquente

Dernière modification par rouault (Thu 10 June 2010 22:02)

Hors ligne

 

#3 Thu 10 June 2010 23:51

ABombardier
Juste Inscrit !
Date d'inscription: 10 Jun 2010
Messages: 3

Re: Gdal

Merci pour ta réponse. J'ai ajouté GDALClose à la fin de ma routine de sauvegarde mais le résultat est le même.  Ce que tu dis aux sujet des métadonnées (table des couleurs, géoréférencement etc...  soulève un point que j'avais soupçonné être la source du problème.

Je me posais la question, si on utilise Create, a-t-ont à spécifier explicitement les métadonnées associées au TIFF ? i.e. table des couleurs par exemple ? Quant au géoréférencement, je ne le spécifie pas car j'utilise j'utilise un TFW, mais a-t-on à le spécifier compte tenu du GeoTiff ?

Je suis sur ce problème depuis deux jours et je ne sais plus quoi essayer. La chose que j'avais pensé faire c'est de créer un nouvelle image (blank) avec CreateCopy de l'image source et faire un resize à la taille désirée et ensuite y verser les pixels de l'image traitée. Intuitivement, étant donné que l'IO fonctionne bien lorsque le dataset est issue d'une image "normalement constituée",  j'ai tendance à croire que ca fonctionnerait  meme si ca ne ferait que contourner le problème. La question serait maintenant, comment faire un resize ?...


Code:

int GdalWrapperSaveImage(GDALWrapperRGBBufferStruct *imageStruct)
{
    IMAGE_ENTRY *imageEntry = GetImageEntry(imageStruct);
    if(imageEntry == NULL) return 0;

    GDALDataset *dataSet = imageEntry->dataSet;
    if(dataSet == NULL) return 0;

    int   nXSize = GDALGetRasterBandXSize(dataSet->GetRasterBand(1) );
    int   nYSize = GDALGetRasterBandYSize(dataSet->GetRasterBand(1) );

    GDALRasterIO(dataSet->GetRasterBand(1), GF_Write, 0, 0, nXSize, nYSize, imageStruct->redBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    GDALRasterIO(dataSet->GetRasterBand(2), GF_Write, 0, 0, nXSize, nYSize, imageStruct->greenBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    GDALRasterIO(dataSet->GetRasterBand(3), GF_Write, 0, 0, nXSize, nYSize, imageStruct->blueBuffer, 
        nXSize , nYSize, GDT_Byte , 0, 0 );

    //if(dataSet->GetRasterBand(1)) dataSet->GetRasterBand(1)->FlushCache();
    //if(dataSet->GetRasterBand(2)) dataSet->GetRasterBand(2)->FlushCache();
    //if(dataSet->GetRasterBand(3)) dataSet->GetRasterBand(3)->FlushCache();

    // Flush cache
    // [i]dataSet->FlushCache();[/i]
    [b]GDALClose(dataSet);[/b]

    return 1;
}

Hors ligne

 

#4 Fri 11 June 2010 00:26

ABombardier
Juste Inscrit !
Date d'inscription: 10 Jun 2010
Messages: 3

Re: Gdal

J'ai oublié de souligner dans ma réponse tantôt que l'appelais effectivement  GDALClose mais dans une fonction spécifique de l'application.

L'ordre des appels étant :

OpenFile
SaveFile
CloseFile

La seule différence si s'en est, une est que maintenant j'appèle GDALClose à la fin de SaveFile et élimine CloseFile de la chaine d'appels.

Hors ligne

 

Pied de page des forums

Powered by FluxBB