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
20 #define str2num(str, typeNum) *((typeNum *)(str))
22 /////////////////////////////////////////////////////////////////
25 * \brief Constructor dedicated to writing a new DICOMV3 part10 compliant
26 * file (see SetFileName, SetDcmTag and Write)
27 * Opens (in read only and when possible) an existing file and checks
28 * for DICOM compliance. Returns NULL on failure.
29 * \Note the in-memory representation of all available tags found in
30 * the DICOM header is post-poned to first header information access.
31 * This avoid a double parsing of public part of the header when
32 * one sets an a posteriori shadow dictionary (efficiency can be
33 * seen as a side effect).
35 * @param filename file to be opened for parsing
40 gdcmFile::gdcmFile(string & filename)
41 :gdcmHeader(filename.c_str())
43 SetPixelDataSizeFromHeader();
46 gdcmFile::gdcmFile(const char * filename)
49 SetPixelDataSizeFromHeader();
54 * \brief calcule la longueur (in bytes) A ALLOUER pour recevoir les
56 * ou DES images dans le cas d'un multiframe
57 * ATTENTION : il ne s'agit PAS de la longueur du groupe des Pixels
58 * (dans le cas d'images compressees, elle n'a pas de sens).
60 * @return longueur a allouer
62 void gdcmFile::SetPixelDataSizeFromHeader(void) {
66 str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
67 if (str_nb == "gdcm::Unfound" ) {
70 nb = atoi(str_nb.c_str() );
73 lgrTotale = GetXSize() * GetYSize() * GetZSize() * (nb/8)* GetSamplesPerPixel();
75 string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004);
76 if ( str_PhotometricInterpretation == "PALETTE COLOR "
77 || str_PhotometricInterpretation == "YBR_FULL") { // --> some more to be added !!
81 // remaining to check :
82 // str_PhotometricInterpretation == "YBR_FULL"
83 // str_PhotometricInterpretation == "YBR_FULL_422" (no LUT, no Palette)
84 // -->and some more !!
87 /////////////////////////////////////////////////////////////////
90 * \brief Returns the size (in bytes) of required memory to hold
91 * the pixel data represented in this file.
92 * @return The size of pixel data in bytes.
94 size_t gdcmFile::GetImageDataSize(void) {
99 /////////////////////////////////////////////////////////////////
102 * \brief Parse pixel data from disk and *prints* the result
103 * \ For multi-fragment Jpeg files checking purpose *only*
104 * \ Allows to 'see' if the file *does* conform
105 * \ (some of them do not)
106 * \ with Dicom Part 3, Annex A (PS 3.5-2003, page 58)
109 bool gdcmFile::ParsePixelData(void) {
113 if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
119 IsImplicitVRLittleEndianTransferSyntax() ||
120 IsExplicitVRLittleEndianTransferSyntax() ||
121 IsExplicitVRBigEndianTransferSyntax() ||
122 IsDeflatedExplicitVRLittleEndianTransferSyntax() ) {
124 printf ("gdcmFile::ParsePixelData : non JPEG File\n");
129 string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
130 if (str_nb == "gdcm::Unfound" ) {
133 nb = atoi(str_nb.c_str() );
134 if (nb == 12) nb =16;
138 //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel();
139 int taille = GetXSize() * GetYSize() * GetSamplesPerPixel();
141 printf ("Checking the Dicom-Jpeg/RLE Pixels\n");
143 // ------------------------------- for Parsing : Position on begining of Jpeg Pixels
144 guint16 ItemTagGr,ItemTagEl;
147 char * destination = NULL;
149 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Basic Offset Table Item Tag Gr
150 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Basic Offset Table Item Tag El
152 ItemTagGr=SwapShort(ItemTagGr);
153 ItemTagEl=SwapShort(ItemTagEl);
155 printf ("at %x : ItemTag (should be fffe,e000): %04x,%04x\n",
156 ftellRes,ItemTagGr,ItemTagEl );
160 ln=SwapLong(ln); // Basic Offset Table Item Lentgh
161 printf("at %x : Basic Offset Table Item Lentgh (??) %d x(%08x)\n",
164 // What is it used for ??
165 char * BasicOffsetTableItemValue= (char *)malloc(ln+1);
166 fread(BasicOffsetTableItemValue,ln,1,fp);
168 for (int i=0;i<ln;i+=4){
169 a=str2num(&BasicOffsetTableItemValue[i],guint32);
170 printf(" x(%08x) %d\n",a,a);
175 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
176 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
178 ItemTagGr=SwapShort(ItemTagGr);
179 ItemTagEl=SwapShort(ItemTagEl);
181 printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
182 ftellRes,ItemTagGr,ItemTagEl );
184 while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) { // Parse fragments
189 ln=SwapLong(ln); // length
190 printf(" at %x : fragment length %d x(%08x)\n",
193 destination += taille * nBytes; // location in user's memory
195 printf (" Destination will be x(%x) = %d \n",
196 destination,destination );
198 fseek(fp,ln,SEEK_CUR); // skipping (not reading) fragment pixels
201 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
202 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
204 ItemTagGr=SwapShort(ItemTagGr);
205 ItemTagEl=SwapShort(ItemTagEl);
207 printf ("at %x : ItemTag (should be fffe,e000 or e0dd): %04x,%04x\n",
208 ftellRes,ItemTagGr,ItemTagEl );
215 /////////////////////////////////////////////////////////////////
218 * \brief Read pixel data from disk (optionaly decompressing) into the
219 * caller specified memory location.
220 * @param destination where the pixel data should be stored.
223 bool gdcmFile::ReadPixelData(void* destination) {
228 if ( fseek(fp, GetPixelOffset(), SEEK_SET) == -1 ) {
233 // ------------------------------- Uncompressed File
236 IsImplicitVRLittleEndianTransferSyntax() ||
237 IsExplicitVRLittleEndianTransferSyntax() ||
238 IsExplicitVRBigEndianTransferSyntax() ||
239 IsDeflatedExplicitVRLittleEndianTransferSyntax() ) {
241 size_t ItemRead = fread(destination, lgrTotale, 1, fp);
242 if ( ItemRead != 1 ) {
251 // ----------------------------- JPEG Compressed File .
254 string str_nb=gdcmHeader::GetPubElValByNumber(0x0028,0x0100);
255 if (str_nb == "gdcm::Unfound" ) {
258 nb = atoi(str_nb.c_str() );
259 if (nb == 12) nb =16;
263 //int taille = GetXSize() * GetYSize() * GetZSize() * GetSamplesPerPixel();
264 int taille = GetXSize() * GetYSize() * GetSamplesPerPixel();
267 // ------------------------------- JPEG LossLess : call to Jpeg Libido
269 if (IsJPEGLossless() && GetZSize() == 1) {
271 int ln; // Position on begining of Jpeg Pixels
272 fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : Basic Offset Table Item
275 ln=SwapLong(ln); // Item length
276 fseek(fp,ln,SEEK_CUR); // skipping Basic Offset Table ('ln' bytes)
277 fseek(fp,4,SEEK_CUR); // skipping (fffe,e000) : First fragment Item Tag
278 fread(&ln,4,1,fp); // First fragment length (just to know)
282 ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
283 // (We use the LibIDO one :-(
288 int * dataJpg = jpg->DataImg;
293 unsigned short *dest = (unsigned short *)destination;
294 for (int i=0; i<taille; i++) {
295 *((unsigned char *)dest+i) = *(dataJpg +i);
302 unsigned short *dest = (unsigned short *)destination;
303 for (int i=0; i<taille; i++) {
304 *((unsigned short *)dest+i) = *(dataJpg +i);
309 _IdDcmJpegFree (jpg);
313 // ------------------------------- JPEG Lossy : call to IJG 6b
315 long fragmentBegining; // for ftell, fseek
319 a = gdcmHeader::IsRLELossLessTransferSyntax();
321 bool b = gdcmHeader::IsJPEG2000();
324 guint16 ItemTagGr,ItemTagEl;
325 int ln; // Position on begining of Jpeg Pixels
327 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
328 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
330 ItemTagGr=SwapShort(ItemTagGr);
331 ItemTagEl=SwapShort(ItemTagEl);
335 ln=SwapLong(ln); // Basic Offset Table Item length
338 // What is it used for ?!?
339 char *BasicOffsetTableItemValue = (char *)malloc(ln+1);
340 fread(BasicOffsetTableItemValue,ln,1,fp);
343 // first Fragment initialisation
344 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
345 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
347 ItemTagGr=SwapShort(ItemTagGr);
348 ItemTagEl=SwapShort(ItemTagEl);
351 // parsing fragments until Sequence Delim. Tag found
352 //unsigned short *dest = (unsigned short *)destination;
354 while ( ( ItemTagGr == 0xfffe) && (ItemTagEl != 0xe0dd) ) {
357 ln=SwapLong(ln); // Fragment Item length
359 // FIXME : multi fragments
360 fragmentBegining=ftell(fp);
363 res = (bool)gdcm_read_RLE_file (destination); // Reading Fragment pixels
365 res = (bool)gdcm_read_JPEG2000_file (destination); // Reading Fragment pixels
367 else if (IsJPEGLossless()) { // ------------- call to LibIDO Jpeg for each Frame/fragment
369 // Warning : Works only if there is one fragment per frame
370 // (Or a single fragment for the multiframe file)
371 ClbJpeg* jpg = _IdDcmJpegRead(fp); // TODO : find a 'full' one.
372 // (We use the LibIDO one :-(
377 int * dataJpg = jpg->DataImg;
378 unsigned short *dest = (unsigned short *)destination;
382 for (int i=0; i<taille; i++) {
383 *((unsigned char *)dest+i) = *(dataJpg +i);
390 for (int i=0; i<taille; i++) {
391 *((unsigned short *)dest+i) = *(dataJpg +i);
396 _IdDcmJpegFree (jpg);
398 } // ------------------------------------- endif (IsJPEGLossless())
401 if (GetBitsStored() == 8) {
402 res = (bool)gdcm_read_JPEG_file (destination); // Reading Fragment pixels
404 res = (bool)gdcm_read_JPEG_file12 (destination);// Reading Fragment pixels
409 // FIXME : will work only when each fragment corresponds to a Frame :-(
411 destination = (char *)destination + taille * nBytes; // location in user's memory
412 // for next fragment (if any)
413 // TODO : find a suitable file (multifragment/single Frame Jpeg file) to check
415 fseek(fp,fragmentBegining,SEEK_SET); // To be sure we start
416 fseek(fp,ln,SEEK_CUR); // at the begining of next fragment
418 ItemTagGr = ItemTagEl =0;
419 fread(&ItemTagGr,2,1,fp); // Reading (fffe) : Item Tag Gr
420 fread(&ItemTagEl,2,1,fp); // Reading (e000) : Item Tag El
422 ItemTagGr=SwapShort(ItemTagGr);
423 ItemTagEl=SwapShort(ItemTagEl);
426 //(char *) destination += taille * nBytes;
427 //cout << "destination" << destination << "\n";
435 * \brief Allocates necessary memory, copies the pixel data
436 * (image[s]/volume[s]) to newly allocated zone.
437 * @return Pointer to newly allocated pixel data.
438 * \ NULL if alloc fails
440 void * gdcmFile::GetImageData (void) {
441 PixelData = (void *) malloc(lgrTotale);
443 GetImageDataIntoVector(PixelData, lgrTotale);
449 * \brief Copies at most MaxSize bytes of pixel data to caller's
451 * @param destination Address (in caller's memory space) at which the
452 * pixel data should be copied
453 * @param MaxSize Maximum number of bytes to be copied. When MaxSize
454 * is not sufficient to hold the pixel data the copy is not
455 * executed (i.e. no partial copy).
456 * @return On success, the number of bytes actually copied. Zero on
457 * failure e.g. MaxSize is lower than necessary.
460 size_t gdcmFile::GetImageDataIntoVector (void* destination, size_t MaxSize) {
462 int nb, nbu, highBit, signe;
463 string str_nbFrames, str_nb, str_nbu, str_highBit, str_signe;
465 if ( lgrTotale > MaxSize ) {
466 dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: pixel data bigger"
467 "than caller's expected MaxSize");
471 (void)ReadPixelData(destination);
473 // Nombre de Bits Alloues pour le stockage d'un Pixel
474 str_nb = GetPubElValByNumber(0x0028,0x0100);
475 if (str_nb == "gdcm::Unfound" ) {
478 nb = atoi(str_nb.c_str() );
481 // Nombre de Bits Utilises
482 str_nbu=GetPubElValByNumber(0x0028,0x0101);
483 if (str_nbu == "gdcm::Unfound" ) {
486 nbu = atoi(str_nbu.c_str() );
489 // Position du Bit de Poids Fort
490 str_highBit=GetPubElValByNumber(0x0028,0x0102);
491 if (str_highBit == "gdcm::Unfound" ) {
494 highBit = atoi(str_highBit.c_str() );
498 str_signe=GetPubElValByNumber(0x0028,0x0103);
499 if (str_signe == "gdcm::Unfound" ) {
502 signe = atoi(str_signe.c_str() );
505 // re arange bytes inside the integer
507 SwapZone(destination, GetSwapCode(), lgrTotale, nb);
509 // re arange bits inside the bytes
511 int l = (int)lgrTotale / (nb/8);
513 guint16 mask = 0xffff;
514 mask = mask >> (nb-nbu);
515 guint16 *deb = (guint16 *)destination;
516 for(int i = 0; i<l; i++) {
517 *deb = (*deb >> (nbu-highBit-1)) & mask;
520 } else if (nb == 32 ) {
521 guint32 mask = 0xffffffff;
522 mask = mask >> (nb-nbu);
523 guint32 *deb = (guint32 *)destination;
524 for(int i = 0; i<l; i++) {
525 *deb = (*deb >> (nbu-highBit-1)) & mask;
529 dbg.Verbose(0, "gdcmFile::GetImageDataIntoVector: wierd image");
534 // Try to deal with the color
535 // --------------------------
537 string str_PhotometricInterpretation = gdcmHeader::GetPubElValByNumber(0x0028,0x0004);
539 if ( (str_PhotometricInterpretation == "MONOCHROME1 ")
540 || (str_PhotometricInterpretation == "MONOCHROME2 ")
541 || (str_PhotometricInterpretation == "RGB")) {
545 switch ( GetPlanarConfiguration() ) {
547 // Pixels are already RGB
551 // need to make RGB Pixels from Planes R,G,B
553 int l = lgrTotale/3 ;
555 char * a = (char *)destination;
558 char * newDest = (char*) malloc(lgrTotale);
560 // any trick not to have to allocate temporary buffer is welcome ...
562 for (int j=0;j<l; j++) {
567 memmove(destination,newDest,lgrTotale);
569 // now, it's an RGB image
571 gdcmHeader::SetPubElValByNumber(spp,0x0028,0x0002);
573 gdcmHeader::SetPubElValByNumber(rgb,0x0028,0x0004);
578 // from Lut R + Lut G + Lut B
580 // we no longer use gdcmHeader::GetLUTRGB
581 // since a lot of images have wrong info
582 // in the Lookup Table Descriptors (0028,1101),...
584 unsigned char *lutR =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1201);
585 unsigned char *lutG =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1202);
586 unsigned char *lutB =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1203);
588 if (lutR && lutG && lutB ) { // need to make RGB Pixels
590 // and Lut R,Lut G,Lut B
591 unsigned char * newDest = (unsigned char*) malloc(lgrTotale);
593 memmove(newDest, destination, l);// move Gray pixels to temp area
595 unsigned char * x = newDest;
596 unsigned char * a = (unsigned char *)destination;
598 for (int i=0;i<l; i++) {
599 j=newDest[i]*2; // Who can explain *why* we have to skip bytes
606 // now, it's an RGB image
608 gdcmHeader::SetPubElValByNumber(spp,0x0028,0x0002);
610 gdcmHeader::SetPubElValByNumber(rgb,0x0028,0x0004);
612 } else { // need to make RGB Pixels (?)
613 // from grey Pixels (?!)
614 // and Gray Lut (!?!)
615 unsigned char *lutGray =(unsigned char *)GetPubElValVoidAreaByNumber(0x0028,0x1200);
616 // Well . I'll wait till I find such an image
626 // Je laisse le code integral, au cas ça puisse etre reutilise ailleurs
631 * \brief Swap the bytes, according to swap code.
632 * \warning not end user intended
633 * @param im area to deal with
634 * @param swap swap code
635 * @param lgr Area Length
636 * @param nb Pixels Bit number
639 void gdcmFile::SwapZone(void* im, int swap, int lgr, int nb) {
657 ((unsigned short int*)im)[i]= ((((unsigned short int*)im)[i])>>8)
658 | ((((unsigned short int*)im)[i])<<8);
662 printf("valeur de SWAP (16 bits) not allowed : %d\n", swap);
673 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 4321 */
674 fort =((unsigned long int*)im)[i]>>16;
675 fort= (fort>>8) | (fort<<8);
676 faible=(faible>>8) | (faible<<8);
678 ((unsigned long int*)im)[i]=(s32<<16)|fort;
684 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 2143 */
685 fort=((unsigned long int*)im)[i]>>16;
686 fort= (fort>>8) | (fort<<8);
687 faible=(faible>>8) | (faible<<8);
689 ((unsigned long int*)im)[i]=(s32<<16)|faible;
695 faible= ((unsigned long int*)im)[i]&0x0000ffff; /* 3412 */
696 fort=((unsigned long int*)im)[i]>>16;
698 ((unsigned long int*)im)[i]=(s32<<16)|fort;
703 printf(" SWAP value (32 bits) not allowed : %d\n", swap);
708 /////////////////////////////////////////////////////////////////
712 * \warning doit-etre etre publique ?
713 * TODO : y a-t-il un inconvenient à fusioner ces 2 fonctions
716 * @param ExpectedSize
718 * @return integer acts as a boolean
720 int gdcmFile::SetImageData(void * inData, size_t ExpectedSize) {
721 SetImageDataSize(ExpectedSize);
723 lgrTotale = ExpectedSize;
728 /////////////////////////////////////////////////////////////////
731 * \brief Sets the Pixel Area size in the Header
732 * --> not-for-rats function
734 * \warning WARNING doit-etre etre publique ?
735 * TODO : y aurait il un inconvenient à fusionner ces 2 fonctions
737 * @param ImageDataSize new Pixel Area Size
738 * warning : nothing else is checked
741 void gdcmFile::SetImageDataSize(size_t ImageDataSize) {
744 // Assumes ElValue (0x7fe0, 0x0010) exists ...
745 sprintf(car,"%d",ImageDataSize);
747 gdcmElValue*a = GetElValueByNumber(0x7fe0, 0x0010);
748 a->SetLength(ImageDataSize);
751 sprintf(car,"%d",ImageDataSize);
753 SetPubElValByNumber(content1, 0x7fe0, 0x0000);
757 /////////////////////////////////////////////////////////////////
760 * \brief Ecrit sur disque les pixels d'UNE image
761 * Aucun test n'est fait sur l'"Endiannerie" du processeur.
762 * Ca sera à l'utilisateur d'appeler son Reader correctement
763 * (Equivalent a IdImaWriteRawFile)
769 int gdcmFile::WriteRawData (string fileName) {
771 fp1 = fopen(fileName.c_str(),"wb");
773 printf("Echec ouverture (ecriture) Fichier [%s] \n",fileName.c_str());
776 fwrite (PixelData,lgrTotale, 1, fp1);
781 /////////////////////////////////////////////////////////////////
784 * \brief Ecrit sur disque UNE image Dicom
785 * Aucun test n'est fait sur l'"Endiannerie" du processeur.
786 * Ca fonctionnera correctement (?) sur processeur Intel
787 * (Equivalent a IdDcmWrite)
790 * @return int acts as a boolean
793 int gdcmFile::WriteDcmImplVR (string fileName) {
794 return WriteBase(fileName, ImplicitVR);
797 /////////////////////////////////////////////////////////////////
802 * @return int acts as a boolean
805 int gdcmFile::WriteDcmImplVR (const char* fileName) {
806 return WriteDcmImplVR (string (fileName));
809 /////////////////////////////////////////////////////////////////
814 * @return int acts as a boolean
817 int gdcmFile::WriteDcmExplVR (string fileName) {
818 return WriteBase(fileName, ExplicitVR);
821 /////////////////////////////////////////////////////////////////
824 * \brief Ecrit au format ACR-NEMA sur disque l'entete et les pixels
825 * (a l'attention des logiciels cliniques
826 * qui ne prennent en entrée QUE des images ACR ...
827 * \warning si un header DICOM est fourni en entree,
828 * les groupes < 0x0008 et les groupes impairs sont ignores)
829 * \warning Aucun test n'est fait sur l'"Endiannerie" du processeur.
830 * Ca fonctionnera correctement (?) sur processeur Intel
831 * (Equivalent a IdDcmWrite)
834 * @return int acts as a boolean
837 int gdcmFile::WriteAcr (string fileName) {
838 return WriteBase(fileName, ACR);
841 /////////////////////////////////////////////////////////////////
848 * @return int acts as a boolean
850 int gdcmFile::WriteBase (string FileName, FileType type) {
853 fp1 = fopen(FileName.c_str(),"wb");
855 printf("Echec ouverture (ecriture) Fichier [%s] \n",FileName.c_str());
859 if ( (type == ImplicitVR) || (type == ExplicitVR) ) {
861 // writing Dicom File Preamble
862 filePreamble=(char*)calloc(128,1);
863 fwrite(filePreamble,128,1,fp1);
864 fwrite("DICM",4,1,fp1);
867 // --------------------------------------------------------------
868 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
870 // if recognition code tells us we dealt with a LibIDO image
871 // we reproduce on disk the switch between lineNumber and columnNumber
872 // just before writting ...
874 std::string rows, columns;
875 if ( filetype == ACR_LIBIDO){
876 rows = GetPubElValByNumber(0x0028, 0x0010);
877 columns = GetPubElValByNumber(0x0028, 0x0011);
878 SetPubElValByNumber(columns, 0x0028, 0x0010);
879 SetPubElValByNumber(rows , 0x0028, 0x0011);
881 // ----------------- End of Special Patch ----------------
883 gdcmHeader::Write(fp1, type);
885 // --------------------------------------------------------------
886 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
888 // ...and we restore the Header to be Dicom Compliant again
889 // just after writting
891 if (filetype == ACR_LIBIDO){
892 SetPubElValByNumber(rows , 0x0028, 0x0010);
893 SetPubElValByNumber(columns, 0x0028, 0x0011);
895 // ----------------- End of Special Patch ----------------
897 fwrite(PixelData, lgrTotale, 1, fp1);