#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: 168
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