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