]> Creatis software - gdcm.git/blob - src/gdcmFile.cxx
* src/gdcmElValSet.cxx, gdcmFile.cxx: JPR bug fix, removal of
[gdcm.git] / src / gdcmFile.cxx
1 // gdcmFile.cxx
2
3 #include "gdcmFile.h"
4 #include "gdcmUtil.h"
5 #include "iddcmjpeg.h"
6 using namespace std;
7
8 /////////////////////////////////////////////////////////////////
9 /**
10  * \ingroup   gdcmFile
11  * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
12  *        file (see SetFileName, SetDcmTag and Write)
13  *        Opens (in read only and when possible) an existing file and checks
14  *        for DICOM compliance. Returns NULL on failure.
15  * \Note  the in-memory representation of all available tags found in
16  *        the DICOM header is post-poned to first header information access.
17  *        This avoid a double parsing of public part of the header when
18  *        one sets an a posteriori shadow dictionary (efficiency can be
19  *        seen as a side effect).   
20  *
21  * @param filename file to be opened for parsing
22  *
23  * @return      
24  */
25  
26 gdcmFile::gdcmFile(string & filename) 
27         :gdcmHeader(filename.c_str())   
28 {
29    SetPixelDataSizeFromHeader();
30 }
31
32 gdcmFile::gdcmFile(const char * filename) 
33         :gdcmHeader(filename)   
34 {
35    SetPixelDataSizeFromHeader();
36 }
37
38 /**
39  * \ingroup   gdcmFile
40  * \brief     calcule la longueur (in bytes) A ALLOUER pour recevoir les
41  *              pixels de l'image
42  *              ou DES images dans le cas d'un multiframe
43  *              ATTENTION : il ne s'agit PAS de la longueur du groupe des Pixels        
44  *              (dans le cas d'images compressees, elle n'a pas de sens).
45  *
46  * @return      longueur a allouer 
47  */
48 void gdcmFile::SetPixelDataSizeFromHeader(void) {
49    int nb;
50    string str_nb;
51
52    str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
53    if (str_nb == "gdcm::Unfound" ) {
54       nb = 16;
55    } else {
56       nb = atoi(str_nb.c_str() );
57       if (nb == 12) nb =16;
58    }
59    lgrTotale =  GetXSize() *  GetYSize() *  GetZSize() * (nb/8)* GetSamplesPerPixel();;
60 }
61
62 /////////////////////////////////////////////////////////////////
63 /**
64  * \ingroup   gdcmFile
65  * \brief     Returns the size (in bytes) of required memory to hold
66  *            the pixel data represented in this file.
67  * @return    The size of pixel data in bytes.
68  */
69 size_t gdcmFile::GetImageDataSize(void) {
70    return (lgrTotale);
71 }
72
73
74 /////////////////////////////////////////////////////////////////
75 /**
76  * \ingroup gdcmFile
77  * \brief   Read pixel data from disk (optionaly decompressing) into the
78  *          caller specified memory location.
79  * @param   destination where the pixel data should be stored.
80  *
81  */
82 bool gdcmFile::ReadPixelData(void* destination) {
83    if ( !OpenFile())
84       return false;
85       
86     if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
87       CloseFile();
88       return false;
89    }     
90     
91    if ( !IsDicomV3()                             ||
92         IsImplicitVRLittleEndianTransferSyntax() ||
93         IsExplicitVRLittleEndianTransferSyntax() ||
94         IsExplicitVRBigEndianTransferSyntax()    ||
95         IsDeflatedExplicitVRLittleEndianTransferSyntax() ) { 
96                     
97       size_t ItemRead = fread(destination, lgrTotale, 1, fp);
98       if ( ItemRead != 1 ) {
99          CloseFile();
100          return false;
101       } else {
102          CloseFile();
103          return true;
104       }
105    } 
106      
107   // ------------------------------- Position on begining of Jpeg Pixels
108   
109       int ln;
110       fseek(fp,4,SEEK_CUR);
111       fread(&ln,4,1,fp); 
112       if(GetSwapCode()) 
113          ln=SwapLong(ln);
114       fseek(fp,ln,SEEK_CUR);
115       fseek(fp,4,SEEK_CUR);
116       fread(&ln,4,1,fp); 
117       if(GetSwapCode()) 
118          ln=SwapLong(ln);
119           
120   // ------------------------------- JPEG LossLess : call to Jpeg Libido
121    
122    if (IsJPEGLossless()) {
123  
124       ClbJpeg* jpg = _IdDcmJpegRead(fp);
125       if(jpg == NULL) {
126          CloseFile();
127          return false;
128       } 
129
130       int nb;
131       string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
132       if (str_nb == "gdcm::Unfound" ) {
133          nb = 16;
134       } else {
135          nb = atoi(str_nb.c_str() );
136          if (nb == 12) nb =16;
137       }
138       int nBytes= nb/8;
139       int * dataJpg = jpg->DataImg;
140       int taille = GetXSize() *  GetYSize() *  GetZSize() * GetSamplesPerPixel();
141           
142       switch (nBytes) {   
143          case 1:
144          {
145             unsigned short *dest = (unsigned short *)destination;
146             for (int i=0; i<taille; i++) {
147                *((unsigned char *)dest+i) = *(dataJpg +i);    
148             }
149          }
150          break;        
151          
152          case 2:
153          {
154             unsigned short *dest = (unsigned short *)destination;
155             for (int i=0; i<taille; i++) {           
156                *((unsigned short *)dest+i) = *(dataJpg +i);    
157             }
158          }
159          break;
160             
161          case 4:
162          {
163             unsigned int *dest=(unsigned int *)destination;
164             for (int i=0;i<taille; i++) {
165                *((unsigned int *)dest+i) = *(dataJpg +i);    
166             }
167           }
168          break;          
169      }
170       
171       _IdDcmJpegFree (jpg);
172       return true;
173    }  
174  
175   // ------------------------------- JPEG Lossy : call to IJG 6b
176   
177   // TODO : faire qq chose d'intelligent pour limiter 
178   // la duplication du code JPEG <bits=8 / bits=12>
179   // TODO : eplucher icelui pour traiter *egalement* bits=16
180   
181       int nBS;        
182       if  ((nBS = GetBitsStored()) != 12) {
183          printf("Sorry, Bits Stored = %d not yet taken into account\n",nBS);
184          return false;
185       }
186       
187       bool res = (bool)gdcm_read_JPEG_file (destination);
188       return res;
189 }   
190
191 /**
192  * \ingroup gdcmFile
193  * \brief   Allocates necessary memory, copies the pixel data
194  *          (image[s]/volume[s]) to newly allocated zone.
195  * @return  Pointer to newly allocated pixel data. 
196  */
197 void * gdcmFile::GetImageData (void) {
198    PixelData = (void *) malloc(lgrTotale);
199    GetImageDataIntoVector(PixelData, lgrTotale);
200    return(PixelData);
201 }
202
203 /**
204  * \ingroup gdcmFile
205  * \brief   Copies at most MaxSize bytes of pixel data to caller's
206  *          memory space.
207  * @param   destination Address (in caller's memory space) at which the
208  *          pixel data should be copied
209  * @param   MaxSize Maximum number of bytes to be copied. When MaxSize
210  *          is not sufficient to hold the pixel data the copy is not
211  *          executed (i.e. no partial copy).
212  * @return  On success, the number of bytes actually copied. Zero on
213  *          failure e.g. MaxSize is lower than necessary.
214  */
215
216 size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) {
217
218    int nb, nbu, highBit, signe;
219    string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe;
220  
221    if ( lgrTotale > MaxSize ) {
222       dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
223                      "than caller's expected MaxSize");
224       return (size_t)0; 
225    }
226         
227         (void)ReadPixelData(destination);
228                         
229         // Nombre de Bits Alloues pour le stockage d'un Pixel
230         str_nb = GetPubElValByNumber(0x0028,0x0100);
231         if (str_nb == "gdcm::Unfound" ) {
232                 nb = 16;
233         } else {
234                 nb = atoi(str_nb.c_str() );
235         }
236         
237         // Nombre de Bits Utilises
238         str_nbu=GetPubElValByNumber(0x0028,0x0101);
239         if (str_nbu == "gdcm::Unfound" ) {
240                 nbu = nb;
241         } else {
242                 nbu = atoi(str_nbu.c_str() );
243         }       
244         
245         // Position du Bit de Poids Fort
246         str_highBit=GetPubElValByNumber(0x0028,0x0102);
247         if (str_highBit == "gdcm::Unfound" ) {
248                 highBit = nb - 1;
249         } else {
250                 highBit = atoi(str_highBit.c_str() );
251         }
252                 
253         // Signe des Pixels 
254         str_signe=GetPubElValByNumber(0x0028,0x0103);
255         if (str_signe == "gdcm::Unfound" ) {
256                 signe = 1;
257         } else {
258                 signe = atoi(str_signe.c_str() );
259         }
260
261    // On remet les Octets dans le bon ordre si besoin est
262    if (nb != 8)
263      SwapZone(destination, GetSwapCode(), lgrTotale, nb);
264  
265    // On remet les Bits des Octets dans le bon ordre si besoin est
266    if (nbu != nb){
267       int l = (int)lgrTotale / (nb/8);
268       if (nb == 16) {
269          guint16 mask = 0xffff;
270          mask = mask >> (nb-nbu);
271          guint16 *deb = (guint16 *)destination;
272          for(int i = 0; i<l; i++) {
273             *deb = (*deb >> (nbu-highBit-1)) & mask;
274             deb ++;
275          }
276       } else if (nb == 32 ) {
277          guint32 mask = 0xffffffff;
278          mask = mask >> (nb-nbu);
279          guint32 *deb = (guint32 *)destination;
280          for(int i = 0; i<l; i++) {
281             *deb = (*deb >> (nbu-highBit-1)) & mask;
282             deb ++;
283          }
284       } else {
285          dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: wierd image");
286          return (size_t)0; 
287       }
288    }
289    
290  // ---
291    string str_PlanarConfiguration = GetPubElValByNumber(0x0028,0x0006);
292    int PlanarConfiguration;
293    if (str_PlanarConfiguration == "gdcm::Unfound" ) {
294       PlanarConfiguration = 0;
295    } else {
296       PlanarConfiguration = atoi(str_PlanarConfiguration.c_str() );
297    }   
298  // ---
299  
300    // TODO : replace by                  
301    // if (GetPlanarConfiguration() == 1) {
302    // after unfreeze
303    
304    if (PlanarConfiguration == 1) { // need to make RGB Pixels
305       int l = lgrTotale/3 ;
306
307       char * a = (char *)destination;
308       char * b = a + l;
309       char * c = b + l;
310       char * newDest = (char*) malloc(lgrTotale);
311       // TODO :
312       // any trick not to have to allocate temporary buffer is welcome ...
313       char *x = newDest;
314       for (int j=0;j<l; j++) {
315          *(x++) = *(a++);
316          *(x++) = *(b++);
317          *(x++) = *(c++);  
318       }
319       a = (char *)destination;
320       x = newDest;
321       for (int i=0;i<lgrTotale; i++) {
322          *(a++) = *(x++);
323       }
324       free(newDest);
325    }
326    return lgrTotale; 
327 }
328
329
330 //
331 // Je laisse le code integral, au cas ça puisse etre reutilise ailleurs
332 //
333
334 void gdcmFile::SwapZone(void* im, int swap, int lgr, int nb) {
335 guint32 s32;
336 guint16 fort,faible;
337 int i;
338
339 if(nb == 16)  
340    switch(swap) {
341       case 0:
342       case 12:
343       case 1234:
344          break;
345                 
346       case 21:
347       case 3412:
348       case 2143:
349       case 4321:
350
351          for(i=0;i<lgr;i++)
352             ((unsigned short int*)im)[i]= ((((unsigned short int*)im)[i])>>8)
353                                         | ((((unsigned short int*)im)[i])<<8);
354          break;
355                         
356       default:
357          printf("valeur de SWAP (16 bits) non autorisee : %d\n", swap);
358    } 
359  
360 if( nb == 32 )
361    switch (swap) {
362       case 0:
363       case 1234:
364          break;
365
366       case 4321:
367          for(i=0;i<lgr;i++) {
368             faible=  ((unsigned long int*)im)[i]&0x0000ffff;    /* 4321 */
369             fort  =((unsigned long int*)im)[i]>>16;
370             fort=  (fort>>8)   | (fort<<8);
371             faible=(faible>>8) | (faible<<8);
372             s32=faible;
373             ((unsigned long int*)im)[i]=(s32<<16)|fort;
374          }
375          break;
376
377       case 2143:
378          for(i=0;i<lgr;i++) {
379             faible=  ((unsigned long int*)im)[i]&0x0000ffff;    /* 2143 */
380             fort=((unsigned long int*)im)[i]>>16;
381             fort=  (fort>>8)   | (fort<<8);
382             faible=(faible>>8) | (faible<<8);
383             s32=fort; 
384             ((unsigned long int*)im)[i]=(s32<<16)|faible;
385          }
386          break;
387   
388       case 3412:
389          for(i=0;i<lgr;i++) {
390             faible=  ((unsigned long int*)im)[i]&0x0000ffff;    /* 3412 */
391             fort=((unsigned long int*)im)[i]>>16;                  
392             s32=faible; 
393             ((unsigned long int*)im)[i]=(s32<<16)|fort;
394          }                 
395          break; 
396                                 
397       default:
398          printf("valeur de SWAP (32 bits) non autorisee : %d\n", swap);
399    } 
400 return;
401 }
402
403 /////////////////////////////////////////////////////////////////
404 /**
405  * \ingroup   gdcmFile
406  * \brief TODO JPR
407  * \warning doit-etre etre publique ?  FIXME JPR
408  * TODO : y a-t-il un inconvenient à fusioner ces 2 fonctions
409  *
410  * @param inData TODO JPR
411  * @param ExpectedSize TODO JPR
412  *
413  * @return TODO JPR     
414  */
415 int gdcmFile::SetImageData(void * inData, size_t ExpectedSize) {
416    SetImageDataSize(ExpectedSize);
417    PixelData = inData;
418    lgrTotale = ExpectedSize;
419    return(1);
420 }
421
422
423 /////////////////////////////////////////////////////////////////
424 /**
425  * \ingroup   gdcmFile
426  * \brief TODO JPR
427  * \
428  * \warning WARNING doit-etre etre publique ? FIXME JPR
429  * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions
430  *
431  * @param ImageDataSize TODO JPR
432  *
433  */
434
435 void gdcmFile::SetImageDataSize(size_t ImageDataSize) {
436
437    string content1;
438    char car[20];
439         
440    // suppose que le ElValue (0x7fe0, 0x0010) existe ...
441         
442    sprintf(car,"%d",ImageDataSize);
443  
444    gdcmElValue*a = GetElValueByNumber(0x7fe0, 0x0010);
445    a->SetLength(ImageDataSize);
446                 
447    ImageDataSize+=8;
448    sprintf(car,"%d",ImageDataSize);
449    content1=car;        
450    SetPubElValByNumber(content1, 0x7fe0, 0x0000);
451 }
452
453
454 /////////////////////////////////////////////////////////////////
455 /**
456  * \ingroup   gdcmFile
457  * \brief Ecrit sur disque les pixels d'UNE image
458  *        Aucun test n'est fait sur l'"Endiannerie" du processeur.
459  *        Ca sera à l'utilisateur d'appeler son Reader correctement
460  *        (Equivalent a IdImaWriteRawFile) FIXME JPR
461  *
462  * @param nomFichier TODO JPR
463  *
464  * @return TODO JPR     
465  */
466
467 int gdcmFile::WriteRawData (string nomFichier) {
468
469    FILE * fp1;
470    fp1 = fopen(nomFichier.c_str(),"wb");
471    if (fp1 == NULL) {
472       printf("Echec ouverture (ecriture) Fichier [%s] \n",nomFichier.c_str());
473       return (0);
474    } 
475         
476    fwrite (PixelData,lgrTotale, 1, fp1);
477    fclose (fp1);
478    return(1);
479 }
480
481
482
483 /////////////////////////////////////////////////////////////////
484 /**
485  * \ingroup   gdcmFile
486  * \brief Ecrit sur disque UNE image Dicom
487  *        Aucun test n'est fait sur l'"Endiannerie" du processeur.
488  *         Ca fonctionnera correctement (?) sur processeur Intel
489  *         (Equivalent a IdDcmWrite) FIXME JPR 
490  *
491  * @param nomFichier TODO JPR
492  *
493  * @return      TODO JPR
494  */
495
496 int gdcmFile::WriteDcmImplVR (string nomFichier) {
497    return WriteBase(nomFichier, ImplicitVR);
498 }
499
500 /////////////////////////////////////////////////////////////////
501 /**
502  * \ingroup   gdcmFile
503  *
504  * @param  nomFichier TODO JPR
505  *
506  * @return TODO JPR
507  */
508  
509 int gdcmFile::WriteDcmImplVR (const char* nomFichier) {
510    return WriteDcmImplVR (string (nomFichier));
511 }
512         
513 /////////////////////////////////////////////////////////////////
514 /**
515  * \ingroup   gdcmFile
516  *
517  * @param  nomFichier TODO JPR
518  *
519  * @return TODO JPR
520  */
521
522 int gdcmFile::WriteDcmExplVR (string nomFichier) {
523    return WriteBase(nomFichier, ExplicitVR);
524 }
525         
526 /////////////////////////////////////////////////////////////////
527 /**
528  * \ingroup   gdcmFile
529  * \brief  Ecrit au format ACR-NEMA sur disque l'entete et les pixels
530  *        (a l'attention des logiciels cliniques 
531  *        qui ne prennent en entrée QUE des images ACR ...
532  * \warning si un header DICOM est fourni en entree,
533  *        les groupes < 0x0008 et les groupes impairs sont ignores)
534  * \warning Aucun test n'est fait sur l'"Endiannerie" du processeur.
535  *        Ca fonctionnera correctement (?) sur processeur Intel
536  *        (Equivalent a IdDcmWrite) 
537  *
538  * @param nomFichier TODO JPR
539  *
540  * @return TODO JPR     
541  */
542
543 int gdcmFile::WriteAcr (string nomFichier) {
544    return WriteBase(nomFichier, ACR);
545 }
546
547 /////////////////////////////////////////////////////////////////
548 /**
549  * \ingroup   gdcmFile
550  *
551  * @param  FileName TODO JPR
552  * @param  type TODO JPR
553  *
554  * @return TODO JPR
555  */
556 int gdcmFile::WriteBase (string FileName, FileType type) {
557
558    FILE * fp1;
559    fp1 = fopen(FileName.c_str(),"wb");
560    if (fp1 == NULL) {
561       printf("Echec ouverture (ecriture) Fichier [%s] \n",FileName.c_str());
562       return (0);
563    }
564
565    if ( (type == ImplicitVR) || (type == ExplicitVR) ) {
566       char * filePreamble;
567       // Ecriture Dicom File Preamble
568       filePreamble=(char*)calloc(128,1);
569       fwrite(filePreamble,128,1,fp1);
570       fwrite("DICM",4,1,fp1);
571    }
572
573    gdcmHeader::Write(fp1, type);
574    fwrite(PixelData, lgrTotale, 1, fp1);
575    fclose (fp1);
576    return(1);
577 }