3 //This is needed when compiling in debug mode
5 // 'type' : forcing value to bool 'true' or 'false' (performance warning)
6 //#pragma warning ( disable : 4800 )
7 // 'identifier' : class 'type' needs to have dll-interface to be used by
8 // clients of class 'type2'
9 #pragma warning ( disable : 4251 )
10 // 'identifier' : identifier was truncated to 'number' characters in the
12 #pragma warning ( disable : 4786 )
17 #include "iddcmjpeg.h" // for the 'LibIDO' Jpeg LossLess
19 /////////////////////////////////////////////////////////////////
22 * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
23 * file (see SetFileName, SetDcmTag and Write)
24 * Opens (in read only and when possible) an existing file and checks
25 * for DICOM compliance. Returns NULL on failure.
26 * \Note the in-memory representation of all available tags found in
27 * the DICOM header is post-poned to first header information access.
28 * This avoid a double parsing of public part of the header when
29 * one sets an a posteriori shadow dictionary (efficiency can be
30 * seen as a side effect).
32 * @param filename file to be opened for parsing
37 gdcmFile::gdcmFile(std::string & filename)
38 :gdcmHeader(filename.c_str())
40 SetPixelDataSizeFromHeader();
43 gdcmFile::gdcmFile(const char * filename)
46 SetPixelDataSizeFromHeader();
51 * \brief calcule la longueur (in bytes) A ALLOUER pour recevoir les
53 * ou DES images dans le cas d'un multiframe
54 * ATTENTION : il ne s'agit PAS de la longueur du groupe des Pixels
55 * (dans le cas d'images compressees, elle n'a pas de sens).
57 * @return longueur a allouer
59 void gdcmFile::SetPixelDataSizeFromHeader(void) {
63 str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
64 if (str_nb == GDCM_UNFOUND ) {
67 nb = atoi(str_nb.c_str() );
70 lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8)* GetSamplesPerPixel();
72 std::string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004);
73 if ( str_PhotometricInterpretation == "PALETTE COLOR "
74 || str_PhotometricInterpretation == "YBR_FULL") { // --> some more to be added !!
78 // see PS 3.3-2003 : C.7.6.3.2.1
88 // YBR_FULL_422 (no LUT, no Palette)
94 // ex : gdcm-US-ALOKA-16.dcm
95 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
96 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
97 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
99 // ex : US-PAL-8-10x-echo.dcm, 8BitsRunLengthColor.dcm
100 // 0028|1201 [OW] [Red Palette Color Lookup Table Data]
101 // 0028|1202 [OW] [Green Palette Color Lookup Table Data]
102 // 0028|1203 [OW] [Blue Palette Color Lookup Table Data]
104 // ex : OT-PAL-8-face.dcm
105 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
106 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
107 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
111 /////////////////////////////////////////////////////////////////
114 * \brief Returns the size (in bytes) of required memory to hold
115 * the pixel data represented in this file.
116 * @return The size of pixel data in bytes.
119 size_t gdcmFile::GetImageDataSize(void) {
124 /////////////////////////////////////////////////////////////////
127 * \brief Read pixel data from disk (optionaly decompressing) into the
128 * caller specified memory location.
129 * @param destination where the pixel data should be stored.
132 bool gdcmFile::ReadPixelData(void* destination) {
137 if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
142 // ------------------------- Uncompressed File
145 IsImplicitVRLittleEndianTransferSyntax() ||
146 IsExplicitVRLittleEndianTransferSyntax() ||
147 IsExplicitVRBigEndianTransferSyntax() ||
148 IsDeflatedExplicitVRLittleEndianTransferSyntax() ) {
150 size_t ItemRead = fread(destination, lgrTotale, 1, fp);
151 if ( ItemRead != 1 ) {
160 // ------------------------ Compressed File .
163 std::string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
164 if (str_nb == GDCM_UNFOUND ) {
167 nb = atoi(str_nb.c_str() );
168 if (nb == 12) nb =16;
172 int taille = GetXSize() * GetYSize() * GetSamplesPerPixel();
175 // ------------------------------- JPEG LossLess : call to Jpeg Libido
177 if (IsJPEGLossless() && GetZSize() == 1) {
179 int ln; // Position on begining of Jpeg Pixels
180 fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : Basic Offset Table Item
183 ln=SwapLong(ln); // Item length
184 fseek(fp,ln,SEEK_CUR); // skipping Basic Offset Table ('ln' bytes)
185 fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : First fragment Item Tag
186 fread(&ln,4,1,fp); // First fragment length (just to know)
190 ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
191 // (We use the LibIDO one :-(
196 int * dataJpg = jpg->DataImg;
201 unsigned short *dest = (unsigned short *)destination;
202 for (int i=0; i<taille; i++) {
203 *((unsigned char *)dest+i) = *(dataJpg +i);
210 unsigned short *dest = (unsigned short *)destination;
211 for (int i=0; i<taille; i++) {
212 *((unsigned short *)dest+i) = *(dataJpg +i);
217 _IdDcmJpegFree (jpg);
221 // ------------------------------- RLE
223 if (gdcmHeader::IsRLELossLessTransferSyntax()) {
224 int res = (bool)gdcm_read_RLE_file (destination);
228 // ------------------------------- JPEG Lossy : call to IJG 6b
230 long fragmentBegining; // for ftell, fseek
231 bool b = gdcmHeader::IsJPEG2000();
234 guint16 ItemTagGr,ItemTagEl;
235 int ln; // Position on begining of Jpeg Pixels
237 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
238 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
240 ItemTagGr=SwapShort(ItemTagGr);
241 ItemTagEl=SwapShort(ItemTagEl);
245 ln=SwapLong(ln); // Basic Offset Table Item length
248 // What is it used for ?!?
249 char *BasicOffsetTableItemValue = (char *)malloc(ln+1);
250 fread(BasicOffsetTableItemValue,ln,1,fp);
253 // first Fragment initialisation
254 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
255 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
257 ItemTagGr=SwapShort(ItemTagGr);
258 ItemTagEl=SwapShort(ItemTagEl);
261 // parsing fragments until Sequence Delim. Tag found
262 //unsigned short *dest = (unsigned short *)destination;
264 while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) {
267 ln=SwapLong(ln); // Fragment Item length
269 // FIXME : multi fragments
270 fragmentBegining=ftell(fp);
274 res = (bool)gdcm_read_JPEG2000_file (destination); // Reading Fragment pixels
276 else if (IsJPEGLossless()) {
277 // ------------- call to LibIDO Jpeg for each Frame/fragment
279 // Warning : Works only if there is one fragment per frame
280 // (Or a single fragment for the multiframe file)
281 ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
282 // (We use the LibIDO one :-(
287 int * dataJpg = jpg->DataImg;
288 unsigned short *dest = (unsigned short *)destination;
292 for (int i=0; i<taille; i++) {
293 *((unsigned char *)dest+i) = *(dataJpg +i);
300 for (int i=0; i<taille; i++) {
301 *((unsigned short *)dest+i) = *(dataJpg +i);
306 _IdDcmJpegFree (jpg);
308 } // ------------------------------------- endif (IsJPEGLossless())
311 if (GetBitsStored() == 8) {
312 res = (bool)gdcm_read_JPEG_file (destination); // Reading Fragment pixels
314 res = (bool)gdcm_read_JPEG_file12 (destination);// Reading Fragment pixels
319 // FIXME : will work only when each fragment corresponds to a Frame :-(
321 destination = (char *)destination + taille * nBytes; // location in user's memory
322 // for next fragment (if any)
323 // TODO : find a suitable file (multifragment/single Frame Jpeg file) to check
325 fseek(fp,fragmentBegining,SEEK_SET); // To be sure we start
326 fseek(fp,ln,SEEK_CUR); // at the begining of next fragment
328 ItemTagGr = ItemTagEl =0;
329 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
330 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
332 ItemTagGr=SwapShort(ItemTagGr);
333 ItemTagEl=SwapShort(ItemTagEl);
336 //(char *) destination += taille * nBytes;
337 //std::cout << "destination" << destination << std::endl;
345 * \brief Allocates necessary memory, copies the pixel data
346 * (image[s]/volume[s]) to newly allocated zone.
347 * @return Pointer to newly allocated pixel data.
348 * \ NULL if alloc fails
350 void * gdcmFile::GetImageData (void) {
351 PixelData = (void *) malloc(lgrTotale);
353 GetImageDataIntoVector(PixelData, lgrTotale);
359 * \brief Copies at most MaxSize bytes of pixel data to caller's
361 * @param destination Address (in caller's memory space) at which the
362 * pixel data should be copied
363 * @param MaxSize Maximum number of bytes to be copied. When MaxSize
364 * is not sufficient to hold the pixel data the copy is not
365 * executed (i.e. no partial copy).
366 * @return On success, the number of bytes actually copied. Zero on
367 * failure e.g. MaxSize is lower than necessary.
370 size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) {
372 int nb, nbu, highBit, signe;
373 std::string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe;
375 if ( lgrTotale > MaxSize ) {
376 dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
377 "than caller's expected MaxSize");
381 (void)ReadPixelData(destination);
383 // Nombre de Bits Alloues pour le stockage d'un Pixel
384 str_nb = GetPubElValByNumber(0x0028,0x0100);
385 if (str_nb == GDCM_UNFOUND ) {
388 nb = atoi(str_nb.c_str() );
391 // Nombre de Bits Utilises
392 str_nbu=GetPubElValByNumber(0x0028,0x0101);
393 if (str_nbu == GDCM_UNFOUND ) {
396 nbu = atoi(str_nbu.c_str() );
399 // Position du Bit de Poids Fort
400 str_highBit=GetPubElValByNumber(0x0028,0x0102);
401 if (str_highBit == GDCM_UNFOUND ) {
404 highBit = atoi(str_highBit.c_str() );
408 str_signe=GetPubElValByNumber(0x0028,0x0103);
409 if (str_signe == GDCM_UNFOUND ) {
412 signe = atoi(str_signe.c_str() );
415 // re arange bytes inside the integer
417 SwapZone(destination, GetSwapCode(), lgrTotale, nb);
419 // re arange bits inside the bytes
421 int l = (int)lgrTotale / (nb/8);
423 guint16 mask = 0xffff;
424 mask = mask >> (nb-nbu);
425 guint16 *deb = (guint16 *)destination;
426 for(int i = 0; i<l; i++) {
427 *deb = (*deb >> (nbu-highBit-1)) & mask;
430 } else if (nb == 32 ) {
431 guint32 mask = 0xffffffff;
432 mask = mask >> (nb-nbu);
433 guint32 *deb = (guint32 *)destination;
434 for(int i = 0; i<l; i++) {
435 *deb = (*deb >> (nbu-highBit-1)) & mask;
439 dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: wierd image");
444 // *Try* to deal with the color
445 // --------------------------
447 std::string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004);
449 if ( (str_PhotometricInterpretation == "MONOCHROME1 ")
450 || (str_PhotometricInterpretation == "MONOCHROME2 ")
451 || (str_PhotometricInterpretation == "RGB")) {
454 int planConf=GetPlanarConfiguration();
456 if( str_PhotometricInterpretation!="PALETTE COLOR" && planConf == 0)
457 planConf=2; // Sorry, this is an heuristic
461 // Pixels are already RGB
465 // need to make RGB Pixels from Planes R,G,B
467 int l = lgrTotale/3 ;
469 char * a = (char *)destination;
472 char * newDest = (char*) malloc(lgrTotale);
474 // any trick not to have to allocate temporary buffer is welcome ...
476 for (int j=0;j<l; j++) {
481 memmove(destination,newDest,lgrTotale);
483 // now, it's an RGB image
484 std::string spp = "3";
485 gdcmHeader::SetPubElValByNumber(spp,0x0028,0x0002);
486 std::string rgb="RGB";
487 gdcmHeader::SetPubElValByNumber(rgb,0x0028,0x0004);
492 // from Lut R + Lut G + Lut B
494 // we no longer use gdcmHeader::GetLUTRGB
495 // since a lot of images have wrong info
496 // in the Lookup Table Descriptors (0028,1101),...
498 unsigned char *lutR =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1201);
499 unsigned char *lutG =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1202);
500 unsigned char *lutB =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1203);
502 if (lutR && lutG && lutB ) { // need to make RGB Pixels
504 // and Lut R,Lut G,Lut B
505 unsigned char * newDest = (unsigned char*) malloc(lgrTotale);
507 memmove(newDest, destination, l);// move Gray pixels to temp area
509 unsigned char * x = newDest;
510 unsigned char * a = (unsigned char *)destination;
512 for (int i=0;i<l; i++) {
513 j=newDest[i]*2; // Who can explain *why* we have to skip bytes
520 // now, it's an RGB image
521 std::string spp = "3";
522 gdcmHeader::SetPubElValByNumber(spp,0x0028,0x0002);
523 std::string rgb="RGB";
524 gdcmHeader::SetPubElValByNumber(rgb,0x0028,0x0004);
526 } else { // need to make RGB Pixels (?)
527 // from grey Pixels (?!)
528 // and Gray Lut (!?!)
529 unsigned char *lutGray =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1200);
530 // Well . I'll wait till I find such an image
540 // Je laisse le code integral, au cas ça puisse etre reutilise ailleurs
545 * \brief Swap the bytes, according to swap code.
546 * \warning not end user intended
547 * @param im area to deal with
548 * @param swap swap code
549 * @param lgr Area Length
550 * @param nb Pixels Bit number
553 void gdcmFile::SwapZone(void* im, int swap, int lgr, int nb) {
571 ((unsigned short int*)im)[i]= ((((unsigned short int*)im)[i])>>8)
572 | ((((unsigned short int*)im)[i])<<8);
576 printf("valeur de SWAP (16 bits) not allowed : %d\n", swap);
587 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 4321 */
588 fort =((unsigned long int*)im)[i]>>16;
589 fort= (fort>>8) | (fort<<8);
590 faible=(faible>>8) | (faible<<8);
592 ((unsigned long int*)im)[i]=(s32<<16)|fort;
598 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 2143 */
599 fort=((unsigned long int*)im)[i]>>16;
600 fort= (fort>>8) | (fort<<8);
601 faible=(faible>>8) | (faible<<8);
603 ((unsigned long int*)im)[i]=(s32<<16)|faible;
609 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 3412 */
610 fort=((unsigned long int*)im)[i]>>16;
612 ((unsigned long int*)im)[i]=(s32<<16)|fort;
617 printf(" SWAP value (32 bits) not allowed : %d\n", swap);
622 /////////////////////////////////////////////////////////////////
626 * \warning doit-etre etre publique ?
627 * TODO : y a-t-il un inconvenient à fusioner ces 2 fonctions
630 * @param ExpectedSize
632 * @return integer acts as a boolean
634 int gdcmFile::SetImageData(void * inData, size_t ExpectedSize) {
635 SetImageDataSize(ExpectedSize);
637 lgrTotale = ExpectedSize;
642 /////////////////////////////////////////////////////////////////
645 * \brief Sets the Pixel Area size in the Header
646 * --> not-for-rats function
648 * \warning WARNING doit-etre etre publique ?
649 * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions
651 * @param ImageDataSize new Pixel Area Size
652 * warning : nothing else is checked
655 void gdcmFile::SetImageDataSize(size_t ImageDataSize) {
656 std::string content1;
658 // Assumes ElValue (0x7fe0, 0x0010) exists ...
659 sprintf(car,"%d",ImageDataSize);
661 gdcmElValue*a = GetElValueByNumber(0x7fe0, 0x0010);
662 a->SetLength(ImageDataSize);
665 sprintf(car,"%d",ImageDataSize);
667 SetPubElValByNumber(content1, 0x7fe0, 0x0000);
671 /////////////////////////////////////////////////////////////////
674 * \brief Ecrit sur disque les pixels d'UNE image
675 * Aucun test n'est fait sur l'"Endiannerie" du processeur.
676 * Ca sera à l'utilisateur d'appeler son Reader correctement
677 * (Equivalent a IdImaWriteRawFile)
683 int gdcmFile::WriteRawData (std::string fileName) {
685 fp1 = fopen(fileName.c_str(),"wb");
687 printf("Echec ouverture (ecriture) Fichier [%s] \n",fileName.c_str());
690 fwrite (PixelData,lgrTotale, 1, fp1);
695 /////////////////////////////////////////////////////////////////
698 * \brief Ecrit sur disque UNE image Dicom
699 * Aucun test n'est fait sur l'"Endiannerie" du processeur.
700 * Ca fonctionnera correctement (?) sur processeur Intel
701 * (Equivalent a IdDcmWrite)
704 * @return int acts as a boolean
707 int gdcmFile::WriteDcmImplVR (std::string fileName) {
708 return WriteBase(fileName, ImplicitVR);
711 /////////////////////////////////////////////////////////////////
716 * @return int acts as a boolean
719 int gdcmFile::WriteDcmImplVR (const char* fileName) {
720 return WriteDcmImplVR (std::string (fileName));
723 /////////////////////////////////////////////////////////////////
728 * @return int acts as a boolean
731 int gdcmFile::WriteDcmExplVR (std::string fileName) {
732 return WriteBase(fileName, ExplicitVR);
735 /////////////////////////////////////////////////////////////////
738 * \brief Ecrit au format ACR-NEMA sur disque l'entete et les pixels
739 * (a l'attention des logiciels cliniques
740 * qui ne prennent en entrée QUE des images ACR ...
741 * \warning si un header DICOM est fourni en entree,
742 * les groupes < 0x0008 et les groupes impairs sont ignores)
743 * \warning Aucun test n'est fait sur l'"Endiannerie" du processeur.
744 * Ca fonctionnera correctement (?) sur processeur Intel
745 * (Equivalent a IdDcmWrite)
748 * @return int acts as a boolean
751 int gdcmFile::WriteAcr (std::string fileName) {
752 return WriteBase(fileName, ACR);
755 /////////////////////////////////////////////////////////////////
762 * @return int acts as a boolean
764 int gdcmFile::WriteBase (std::string FileName, FileType type) {
767 fp1 = fopen(FileName.c_str(),"wb");
769 printf("Echec ouverture (ecriture) Fichier [%s] \n",FileName.c_str());
773 if ( (type == ImplicitVR) || (type == ExplicitVR) ) {
775 // writing Dicom File Preamble
776 filePreamble=(char*)calloc(128,1);
777 fwrite(filePreamble,128,1,fp1);
778 fwrite("DICM",4,1,fp1);
781 // --------------------------------------------------------------
782 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
784 // if recognition code tells us we dealt with a LibIDO image
785 // we reproduce on disk the switch between lineNumber and columnNumber
786 // just before writting ...
788 std::string rows, columns;
789 if ( filetype == ACR_LIBIDO){
790 rows = GetPubElValByNumber(0x0028, 0x0010);
791 columns = GetPubElValByNumber(0x0028, 0x0011);
792 SetPubElValByNumber(columns, 0x0028, 0x0010);
793 SetPubElValByNumber(rows , 0x0028, 0x0011);
795 // ----------------- End of Special Patch ----------------
797 gdcmHeader::Write(fp1, type);
799 // --------------------------------------------------------------
800 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
802 // ...and we restore the Header to be Dicom Compliant again
803 // just after writting
805 if (filetype == ACR_LIBIDO){
806 SetPubElValByNumber(rows , 0x0028, 0x0010);
807 SetPubElValByNumber(columns, 0x0028, 0x0011);
809 // ----------------- End of Special Patch ----------------
811 fwrite(PixelData, lgrTotale, 1, fp1);