]> Creatis software - gdcm.git/blob - Testing/TestAllReadCompareDicom.cxx
Avoid some warnings
[gdcm.git] / Testing / TestAllReadCompareDicom.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: TestAllReadCompareDicom.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/11/03 14:07:12 $
7   Version:   $Revision: 1.51 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18 #include "gdcmDirList.h"
19 #include "gdcmFile.h"
20 #include "gdcmFileHelper.h"
21 #include "gdcmGlobal.h"
22 #include "gdcmTS.h"
23 #include "gdcmDebug.h"
24
25 #include <iostream>
26
27 //Generated file:
28 #include "gdcmDataImages.h"
29
30 //-->
31 //--> WARNING :
32 //-->          The .tst files *must* be generated on a Little Endian based computer.
33 //-->
34 /**
35  * /brief   File Read/Writer specific for the TestAllReadCompareDicom test
36  * /remarks The Test file format is (only in little endian) :
37  *  - 4 bytes : 'gdcm'
38  *  - 4 bytes : size X
39  *  - 4 bytes : size Y
40  *  - 4 bytes : size Z
41  *  - 2 bytes : scalar size (8,16,32) --> ?!? 1 or 2 only in DICOM V3 !
42  *  - 2 bytes : number of components per pixel (1,2,3) ---> 1 or 3 only in DICOMV3 !
43  *  - n bytes : data
44  */
45 class TestFile
46 {
47 public:
48    TestFile();
49    ~TestFile();
50
51    bool IsReadable() {return readable;}
52    
53    int GetXSize() {return SizeX;}
54    int GetYSize() {return SizeY;}
55    int GetZSize() {return SizeZ;}
56    
57    void SetXSize(int size) {SizeX = size;}   
58    void SetYSize(int size) {SizeY = size;}
59    void SetZSize(int size) {SizeZ = size;}
60    
61    int GetScalarSize()                  {return ScalarSize;}
62    void SetScalarSize(int size)         {ScalarSize = size;}
63    
64    int GetNumberOfComponents()          {return Components;}
65    void SetNumberOfComponents(int size) {Components = size;}
66    int GetSwapCode() {return SwapCode;}
67
68    unsigned long GetDataSize() {return GetLineSize()*SizeY*SizeZ;}
69    uint8_t *GetData() {return Data;}
70    void SetData(const uint8_t *newData);
71
72    void Load(const std::string &filename);
73    void Write(const std::string &filename);
74
75 private:
76    unsigned long GetLineSize() {return SizeX*ScalarSize*Components;}
77    int ComputeSwapCode(uint32_t tag);
78
79    void NewData();
80    void DeleteData();
81
82    void ReadFile();
83    bool ReadFileHeader(std::ifstream *fp);
84    bool ReadFileData(std::ifstream *fp);
85    void WriteFile();
86    bool WriteFileHeader(std::ofstream *fp);
87    bool WriteFileData(std::ofstream *fp);
88
89    uint8_t  ReadInt8 (std::ifstream *fp)
90 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
91  throw( std::ios::failure );
92 #else
93  ;
94 #endif
95    uint16_t ReadInt16(std::ifstream *fp)
96 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
97   throw( std::ios::failure );
98 #else
99  ;
100 #endif
101    uint32_t ReadInt32(std::ifstream *fp)
102 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
103   throw( std::ios::failure );
104 #else
105  ;
106 #endif
107    void WriteInt8 (std::ofstream *fp,uint8_t  value);
108    void WriteInt16(std::ofstream *fp,uint16_t value);
109    void WriteInt32(std::ofstream *fp,uint32_t value);
110
111    std::string fileName;
112    bool readable;
113
114    int SizeX;
115    int SizeY;
116    int SizeZ;
117    uint16_t ScalarSize;
118    uint16_t Components;
119    uint8_t *Data;
120    int SwapCode;
121
122    static const unsigned int HEADER_SIZE;
123 };
124
125 const unsigned int MAX_NUMBER_OF_DIFFERENCE = 10;
126 const unsigned int TestFile::HEADER_SIZE = 20;
127
128 TestFile::TestFile()
129 {
130    fileName = "";
131    readable=false;
132
133    SizeX = 0;
134    SizeY = 0;
135    SizeZ = 0;
136    ScalarSize = 0;
137    Components = 0;
138    Data = NULL;
139
140    SwapCode = 1234;
141 }
142
143 TestFile::~TestFile()
144 {
145    DeleteData();
146 }
147
148 void TestFile::SetData(const uint8_t *newData)
149 {
150    DeleteData();
151    NewData();
152    if( Data )
153       memcpy(Data,newData,GetDataSize());
154 }
155
156 void TestFile::Load(const std::string &filename)
157 {
158    fileName = filename;
159    ReadFile();
160 }
161
162 void TestFile::Write(const std::string &filename)
163 {
164    fileName = filename;
165    WriteFile();
166 }
167
168 int TestFile::ComputeSwapCode(uint32_t tag)
169 {
170    int swap = 0;
171    for(int i=0;i<4;i++)
172    {
173       switch(tag&0x000000FF)
174       {
175          case 'g':
176             swap += (i+1)*1000;
177             break;
178          case 'd':
179             swap += (i+1)*100;
180             break;
181          case 'c':
182             swap += (i+1)*10;
183             break;
184          case 'm':
185             swap += (i+1);
186             break;
187          default:
188             return 0;
189       }
190       tag >>= 8;
191    }
192    return swap;
193 }
194
195 void TestFile::NewData()
196 {
197    DeleteData();
198    if( GetDataSize() == 0 )
199       return;
200    Data = new uint8_t[GetDataSize()];
201 }
202
203 void TestFile::DeleteData()
204 {
205    if( Data )
206       delete[] Data;
207    Data = NULL;
208 }
209
210 void TestFile::ReadFile()
211 {
212    readable=true;
213    std::ifstream fp(fileName.c_str(),std::ios::in | std::ios::binary);
214
215    if(!fp)
216    {
217       readable=false;
218       return;
219    }
220
221    try
222    {
223       readable=ReadFileHeader(&fp);
224       if(!readable)
225       {
226          std::cout << "Problems when reading Header part" << std::endl;
227          fp.close();
228          return;
229       }
230
231       readable=ReadFileData(&fp);
232       if(!readable)
233       {
234          std::cout << "Problems when reading data" << std::endl;
235          fp.close();
236          return;
237       }
238    }
239    catch(...)
240    {
241       readable=false;
242       fp.close();
243       return;
244    }
245
246    fp.close();
247 }
248
249 bool TestFile::ReadFileHeader(std::ifstream *fp)
250 {
251    uint32_t tag = ReadInt32(fp);
252    SwapCode = ComputeSwapCode(tag);
253    if( SwapCode == 0 )
254    {
255       std::cout << "TestFile: Bad tag - Must be 'gdcm'" << std::endl;
256       return(false);
257    }
258
259    SizeX = ReadInt32(fp); // Size X
260    SizeY = ReadInt32(fp); // Size Y
261    SizeZ = ReadInt32(fp); // Size Z
262    ScalarSize = ReadInt16(fp)/8; // bits per scalar
263    Components = ReadInt16(fp);   // Number of components
264
265    return(true);
266 }
267
268 bool TestFile::ReadFileData(std::ifstream *fp)
269 {
270    DeleteData();
271
272    // Allocate data
273    NewData();
274    if( !Data )
275       return(false);
276
277    // Read data
278    fp->read((char *)Data,GetDataSize());
279       
280    if (GetScalarSize() == 1 || GetSwapCode() == 1234)
281    {
282       return true;
283    }
284    // We *know* the .tst files are written in 'Little Endian' format.
285    // We *know* DataSize may be 1 or 2 !  
286    uint16_t g;   
287    for (unsigned int i=0; i<GetDataSize()/2; i++)
288    {
289       g = ((uint16_t *)Data)[i];
290       g = ( g << 8 |  g >> 8  );
291       ((uint16_t *)Data)[i] = g;   
292    }
293    
294    
295
296    return(true);
297 }
298
299 void TestFile::WriteFile()
300 {
301    std::ofstream fp(fileName.c_str(),std::ios::out | std::ios::binary);
302
303    if(!fp)
304    {
305       readable=false;
306       return;
307    }
308
309    WriteFileHeader(&fp);
310    WriteFileData(&fp);
311
312    fp.close();
313 }
314
315 bool TestFile::WriteFileHeader(std::ofstream *fp)
316 {
317    WriteInt8(fp,'g'); // Bitmap tag - must be 'g'
318    WriteInt8(fp,'d'); // Bitmap tag - must be 'd'
319    WriteInt8(fp,'c'); // Bitmap tag - must be 'c'
320    WriteInt8(fp,'m'); // Bitmap tag - must be 'm'
321    WriteInt32(fp,SizeX); // Size X
322    WriteInt32(fp,SizeY); // Size Y
323    WriteInt32(fp,SizeZ); // Size Z
324    WriteInt16(fp,ScalarSize*8); // bits per scalar
325    WriteInt16(fp,Components);   // number of components
326
327    return(true);
328 }
329
330 bool TestFile::WriteFileData(std::ofstream *fp)
331 {
332    fp->write((char *)Data,GetDataSize());
333
334    return(true);
335 }
336
337 uint8_t  TestFile::ReadInt8 (std::ifstream *fp)
338 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
339    throw( std::ios::failure )
340 #endif
341 {
342    uint8_t g;
343    fp->read ((char*)&g, (size_t)1);
344 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
345    if ( fp->fail() )
346       throw std::ios::failure( "TestFile::ReadInt8() - file error." );
347    if( fp->eof() )
348       throw std::ios::failure( "TestFile::ReadInt8() - EOF." );
349 #endif
350    return g;
351 }
352
353 uint16_t TestFile::ReadInt16(std::ifstream *fp)
354 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
355    throw( std::ios::failure )
356 #endif
357 {
358    uint16_t g;
359    fp->read ((char*)&g, (size_t)2);
360 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
361    if ( fp->fail() )
362       throw std::ios::failure( "TestFile::ReadInt16() - file error." );
363    if( fp->eof() )
364       throw std::ios::failure( "TestFile::ReadInt16() - EOF." );
365 #endif
366
367 #if defined(GDCM_WORDS_BIGENDIAN)
368    g = ( g << 8 |  g >> 8  );
369 #endif
370    return g;
371 }
372
373 uint32_t TestFile::ReadInt32(std::ifstream *fp)
374 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
375    throw( std::ios::failure )
376 #endif
377 {
378    uint32_t g;
379    fp->read ((char*)&g, (size_t)4);
380 #if !(__GNUC__==2  && __GNUC_MINOR__<=96)
381    if ( fp->fail() )
382       throw std::ios::failure( "TestFile::ReadInt32() - file error." );
383    if( fp->eof() )
384       throw std::ios::failure( "TestFile::ReadInt32() - EOF." );
385 #endif
386
387 #if defined(GDCM_WORDS_BIGENDIAN)
388    g = (  (g<<24)               | ((g<<8)  & 0x00ff0000) | 
389        (  (g>>8)  & 0x0000ff00) |  (g>>24)               );
390 #endif
391    return g;
392 }
393
394 void TestFile::WriteInt8 (std::ofstream *fp,uint8_t value)
395 {
396    fp->write((char*)&value, (size_t)1);
397 }
398
399 void TestFile::WriteInt16(std::ofstream *fp,uint16_t value)
400 {
401 #if defined(GDCM_WORDS_BIGENDIAN)
402    value = ( value << 8 |  value >> 8  );
403 #endif
404    fp->write((char*)&value, (size_t)2);
405 }
406
407 void TestFile::WriteInt32(std::ofstream *fp,uint32_t value)
408 {
409 #if defined(GDCM_WORDS_BIGENDIAN)
410    value = (  (value<<24)               | ((value<<8)  & 0x00ff0000) | 
411            (  (value>>8)  & 0x0000ff00) |  (value>>24)               );
412 #endif
413    fp->write((char*)&value, (size_t)4);
414 }
415
416 int InternalTest(std::string const &filename, 
417                  std::string const &referenceFileName )
418 {
419       std::cout << "   Testing: " << filename << std::endl;
420       std::cout << "      ";
421
422       ////// Step 1:
423       std::cout << "1...";
424
425        // new style 
426       gdcm::File *f = gdcm::File::New();
427       f->SetLoadMode ( gdcm::LD_ALL ); // Load everything
428       f->SetFileName( filename );
429       f->Load();
430  
431       if( !f->IsReadable() )
432       {
433         std::cout << " Failed" << std::endl
434                    << "      Image not gdcm compatible:"
435                   << filename << std::endl;
436         f->Delete();
437         return 1;
438       }
439       gdcm::FileHelper *tested = gdcm::FileHelper::New( f );
440      
441       ////// Step 2:
442       ////// Check for existence of reference baseline dicom file:
443       std::cout << "2...";
444
445       TestFile *reference = new TestFile();
446       std::ifstream refFile(referenceFileName.c_str(),
447                             std::ios::binary|std::ios::in);
448       if(!refFile)
449       {
450          std::cout << " Failed" << std::endl
451                    << "      Image not found:"
452                    << referenceFileName << std::endl;
453          reference->SetXSize(tested->GetFile()->GetXSize());
454          reference->SetYSize(tested->GetFile()->GetYSize());
455          reference->SetZSize(tested->GetFile()->GetZSize());
456          reference->SetScalarSize(tested->GetFile()->GetPixelSize());
457          reference->SetNumberOfComponents(tested->GetFile()->GetNumberOfScalarComponents());
458          reference->SetData(tested->GetImageData());
459          reference->Write(referenceFileName);
460       }
461       else
462          refFile.close();
463
464       reference->Load(referenceFileName);
465       if(!reference->IsReadable())
466       {
467         std::cout << " Failed" << std::endl
468                    << "      Image not Testing compatible:"
469                   << filename << std::endl;
470          delete reference;
471          tested->Delete();
472          f->Delete();
473          return 1;
474       }
475
476       ////// Step 3:
477       std::string PixelType = tested->GetFile()->GetPixelType();
478       std::cout << "3...";
479       int testedDataSize    = tested->GetImageDataSize();
480       uint8_t *testedImageData = tested->GetImageData();
481     
482       int    referenceDataSize = reference->GetDataSize();
483       uint8_t *referenceImageData = reference->GetData();
484
485       // Test the image size
486       if (tested->GetFile()->GetXSize() != reference->GetXSize() ||
487           tested->GetFile()->GetYSize() != reference->GetYSize() ||
488           tested->GetFile()->GetZSize() != reference->GetZSize())
489       {
490          std::cout << "Failed" << std::endl
491                    << "        Size differs: "
492                    << "X: " << tested->GetFile()->GetXSize() << " # " 
493                    << reference->GetXSize() << " | "
494                    << "Y: " << tested->GetFile()->GetYSize() << " # " 
495                    << reference->GetYSize() << " | "
496                    << "Z: " << tested->GetFile()->GetZSize() << " # " 
497                    << reference->GetZSize() << std::endl;
498          delete reference;
499          tested->Delete();
500          f->Delete();
501          return 1;
502       }
503
504       // Test the pixel size
505       if (tested->GetFile()->GetPixelSize() != reference->GetScalarSize() ||
506           tested->GetFile()->GetNumberOfScalarComponents() != reference->GetNumberOfComponents())
507       {
508          std::cout << "Failed" << std::endl
509                    << "        Pixel size differs: " << std::endl
510                    << "        Scalar size: " << tested->GetFile()->GetPixelSize() << " # " 
511                    << reference->GetScalarSize() << std::endl
512                    << "        Number of scalar: " << tested->GetFile()->GetNumberOfScalarComponents() << " # " 
513                    << reference->GetNumberOfComponents() << std::endl
514                    << "        Pixel type: " << tested->GetFile()->GetPixelType() << std::endl;
515          delete reference;
516          tested->Delete();
517          f->Delete();
518          return 1;
519       }
520
521       // Test the data size
522       if (testedDataSize != referenceDataSize)
523       {
524          std::cout << " Failed" << std::endl
525                    << "        pixel ("
526                    << PixelType
527                    <<") areas lengths differ: "
528                    << testedDataSize << " # " << referenceDataSize
529                    << std::endl
530                    << "        Image size: ("
531                    << tested->GetFile()->GetXSize() << ","
532                    << tested->GetFile()->GetYSize() << ","
533                    << tested->GetFile()->GetZSize() << ")"
534                    << std::endl;
535          tested->Delete();
536          delete reference;
537          f->Delete();
538          return 1;
539       }
540
541       // Test the data content
542       if ( memcmp(testedImageData, referenceImageData,
543                            testedDataSize) != 0 )
544       {
545          std::string ts  = tested->GetFile()->GetTransferSyntax();
546
547          std::cout << " Failed" << std::endl
548                    << "        pixel (" 
549                    << PixelType
550                    << ") differ (as expanded in memory)."
551                    << std::endl
552                    << "        compression : " 
553                    << gdcm::Global::GetTS()->GetValue(ts) << std::endl;
554
555          std::cout << "        list of the first " << MAX_NUMBER_OF_DIFFERENCE
556                    << " pixels differing (pos : test - ref) :" 
557                    << std::endl;
558          int i;
559          unsigned int j;
560          for(i=0, j=0;i<testedDataSize && j<MAX_NUMBER_OF_DIFFERENCE;i++)
561          {
562             if(testedImageData[i]!=referenceImageData[i])
563               {
564                std::cout << std::hex << "(" << i << " : " 
565                          << std::hex << (int)(testedImageData[i]) << " - "
566                          << std::hex << (int)(referenceImageData[i]) << ") "
567                          << std::dec;
568                ++j;
569               }
570          }
571          std::cout << std::endl;
572
573          tested->Delete();
574          delete reference;
575          f->Delete();
576          return 1;
577       }
578
579       //////////////// Clean up:
580       tested->Delete();
581       delete reference;
582       f->Delete();
583
584       std::cout << "OK." << std::endl;
585       
586       return 0;
587 }
588
589 int TestAllReadCompareDicom(int argc, char *argv[]) 
590 {
591    if (argc == 4)
592       gdcm::Debug::DebugOn();
593
594    if ( argc >= 3 )
595    {
596       // The test is specified a specific filename, use it instead of looping
597       // over all images
598       const std::string input = argv[1];
599       const std::string reference = argv[2];
600       return InternalTest( input, reference );
601    }
602    else if ( argc > 4 || argc == 2 )
603    {
604       std::cerr << "   Usage: " << argv[0]
605                 << " (no arguments needed)." << std::endl;
606       std::cerr << "or   Usage: " << argv[0]
607                 << " filename.dcm reference.dcm" << std::endl;
608       return 1;
609    }
610    // else other cases:
611    
612    std::cout << "   Description (Test::TestAllReadCompareDicom): "
613              << std::endl;
614    std::cout << "   For all images in gdcmData (and not blacklisted in "
615                 "Test/CMakeLists.txt)"
616              << std::endl;
617    std::cout << "   apply the following to each filename.xxx: "
618              << std::endl;
619    std::cout << "   step 1: parse the image (as gdcmFile) and call"
620              << " IsReadable(). "
621              << std::endl;
622    std::cout << "   step 2: find in GDCM_DATA_ROOT/BaselineDicom/filename.tst"
623              << std::endl
624              << "           special internal file format containing the"
625              << std::endl
626              << "           caracteristic of the image and the pixel data "
627              << "(uncompressed). This file is written if it's not found."
628              << std::endl;
629    std::cout << "   step 3: compare the DICOM image with the reference image"
630              << std::endl
631              << "           (.tst file). The test is made on the caracteristics"
632              << std::endl
633              << "           of the image and the pixel data"
634              << std::endl << std::endl;
635
636    int i = 0;
637    int result = 0;
638    while( gdcmDataImages[i] != 0 )
639    {
640       ////// Check for existence of reference baseline directory
641
642       std::string baseLineDir = GDCM_DATA_ROOT;
643       baseLineDir += "/BaselineDicom";
644
645       if( !gdcm::DirList::IsDirectory(baseLineDir) )
646       {
647          std::cerr << "   The reference baseline directory " << std::endl
648                    << "      "
649                    << baseLineDir << std::endl
650                    << "   couldn't be opened."
651                    << std::endl;
652          return 1;
653       }
654
655       ////// Step 1 (see above description):
656       std::string filename = GDCM_DATA_ROOT;
657       filename += "/";
658       filename += gdcmDataImages[i];
659       
660       baseLineDir += '/';
661       std::string referenceFileName = baseLineDir + gdcmDataImages[i++];
662       std::string::size_type slash_pos = referenceFileName.rfind( "." );
663       if( slash_pos != std::string::npos )
664       {
665          referenceFileName.replace( slash_pos + 1, 3, "tst" );
666       }
667
668       if( InternalTest( filename, referenceFileName ) != 0 )
669       {
670          result++;
671       }
672    }
673
674    return result;
675 }