00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #ifndef vlDICOM_INCLUDE_ONCE
00033 #define vlDICOM_INCLUDE_ONCE
00034
00035 #include "vlDICOM.hpp"
00036 #include <vlCore/LoadWriterManager.hpp>
00037 #include <vlCore/FileSystem.hpp>
00038 #include <vlCore/GLSLmath.hpp>
00039
00040 #include <gdcmReader.h>
00041 #include <gdcmWriter.h>
00042 #include <gdcmAttribute.h>
00043 #include <gdcmImageReader.h>
00044 #include <gdcmImageWriter.h>
00045 #include <gdcmImage.h>
00046 #include <gdcmPhotometricInterpretation.h>
00047 #include <gdcmImage.h>
00048 #include <gdcmImageWriter.h>
00049
00050 #include <memory>
00051
00052 using namespace vl;
00053
00054
00055 static void to8bits(int bits_used, void* ptr, int px_count, bool reverse)
00056 {
00057 unsigned char* px = (unsigned char*)ptr;
00058 if (bits_used >= 8)
00059 return;
00060 unsigned int max1 = (1<<bits_used)-1;
00061 unsigned int max2 = 0xFF;
00062 for(int i=0; i<px_count; ++i)
00063 if (reverse)
00064 px[i] = 0xFF - (unsigned char)( (float)px[i]/max1*max2 );
00065 else
00066 px[i] = (unsigned char)( (float)px[i]/max1*max2 );
00067 }
00068 static void to16bits(int bits_used, void* ptr, int px_count, bool reverse)
00069 {
00070 unsigned short* px = (unsigned short*)ptr;
00071
00072 if (bits_used >= 16)
00073 return;
00074 unsigned int max1 = (1<<bits_used)-1;
00075 unsigned int max2 = 0xFFFF;
00076 for(int i=0; i<px_count; ++i)
00077 {
00078 if (reverse)
00079 px[i] = 0xFFFF - (unsigned short)( (float)px[i]/max1*max2 );
00080 else
00081 px[i] = (unsigned short)( (float)px[i]/max1*max2 );
00082 }
00083 }
00084 static void to32bits(int bits_used, void* ptr, int px_count, bool reverse)
00085 {
00086 unsigned int* px = (unsigned int*)ptr;
00087 if (bits_used >= 32)
00088 return;
00089 unsigned int max1 = (1<<bits_used)-1;
00090 unsigned int max2 = 0xFFFFFFFF;
00091 for(int i=0; i<px_count; ++i)
00092 if (reverse)
00093 px[i] = 0xFFFFFFFF - (unsigned int)( (float)px[i]/max1*max2 );
00094 else
00095 px[i] = (unsigned int)( (float)px[i]/max1*max2 );
00096 }
00097
00098 ref<Image> vl::loadDICOM(const String& path)
00099 {
00100 ref<VirtualFile> file = defFileSystem()->locateFile(path);
00101 if ( !file )
00102 {
00103 Log::error( Say("File '%s' not found.\n") << path );
00104 return NULL;
00105 }
00106 else
00107 return loadDICOM(file.get());
00108 }
00109
00111 ref<Image> vl::loadDICOM(VirtualFile* vfile)
00112 {
00113 gdcm::ImageReader reader;
00114
00115 if (!vfile->open(OM_ReadOnly))
00116 {
00117 Log::error( Say("loadDICOM: cannot open file '%s'\n") << vfile->path() );
00118 return NULL;
00119 }
00120
00121 const int bufsize = 128*1024;
00122 char buffer[bufsize];
00123 long long count = 0;
00124 std::stringstream strstr;
00125 while( (count=vfile->read(buffer, bufsize)) )
00126 strstr.write(buffer,(int)count);
00127 std::istream* istr = &strstr;
00128 reader.SetStream( *istr );
00129 if( !reader.Read() )
00130 {
00131 std::cerr << "Could not read: " << vfile->path().toStdString() << std::endl;
00132 vfile->close();
00133 return NULL;
00134 }
00135
00136 gdcm::File &file = reader.GetFile();
00137 const gdcm::Image &image = reader.GetImage();
00138
00139 #if 0
00140 printf("GDCM --- --- --- --- ---\n");
00141 image.Print(std::cout);
00142 #endif
00143
00144 unsigned int ndim = image.GetNumberOfDimensions();
00145 const unsigned int *dims = image.GetDimensions();
00146 gdcm::PixelFormat pf = image.GetPixelFormat();
00147 const gdcm::PhotometricInterpretation &pi = image.GetPhotometricInterpretation();
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 ref<KeyValues> tags = new KeyValues;
00160 tags->set("Origin") = Say("%n %n %n") << image.GetOrigin()[0] << image.GetOrigin()[1] << image.GetOrigin()[2];
00161 tags->set("Spacing") = Say("%n %n %n") << image.GetSpacing()[0] << image.GetSpacing()[1] << image.GetSpacing()[2];
00162 tags->set("Intercept") = Say("%n") << image.GetIntercept();
00163 tags->set("Slope") = Say("%n") << image.GetSlope();
00164 tags->set("DirectionCosines") = Say("%n %n %n %n %n %n")
00165 << image.GetDirectionCosines()[0] << image.GetDirectionCosines()[1] << image.GetDirectionCosines()[2]
00166 << image.GetDirectionCosines()[3] << image.GetDirectionCosines()[4] << image.GetDirectionCosines()[5];
00167 tags->set("BitsStored") = Say("%n") << pf.GetBitsStored();
00168
00169 gdcm::DataSet& ds = file.GetDataSet();
00170
00171 {
00172 gdcm::Attribute<0x28,0x1050> win_center;
00173 const gdcm::DataElement& de = ds.GetDataElement( win_center.GetTag() );
00174 if(!de.IsEmpty())
00175 {
00176 win_center.SetFromDataElement( ds.GetDataElement( win_center.GetTag() ) );
00177 tags->set("WindowCenter") = Say("%n") << win_center.GetValue();
00178 }
00179 }
00180
00181 {
00182 gdcm::Attribute<0x28,0x1051> win_width;
00183 const gdcm::DataElement& de = ds.GetDataElement( win_width.GetTag() );
00184 if(!de.IsEmpty())
00185 {
00186 win_width.SetFromDataElement( ds.GetDataElement( win_width.GetTag() ) );
00187 tags->set("WindowWidth") = Say("%n") << win_width.GetValue();
00188 }
00189 }
00190
00191 {
00192 gdcm::Attribute<0x28,0x1052> rescale_intercept;
00193 const gdcm::DataElement& de = ds.GetDataElement( rescale_intercept.GetTag() );
00194 if(!de.IsEmpty())
00195 {
00196 rescale_intercept.SetFromDataElement( ds.GetDataElement( rescale_intercept.GetTag() ) );
00197 tags->set("RescaleIntercept") = Say("%n") << rescale_intercept.GetValue();
00198 }
00199 else
00200 tags->set("RescaleIntercept") = Say("%n") << 0;
00201 }
00202
00203 {
00204 gdcm::Attribute<0x28,0x1053> rescale_slope;
00205 const gdcm::DataElement& de = ds.GetDataElement( rescale_slope.GetTag() );
00206 if(!de.IsEmpty())
00207 {
00208 rescale_slope.SetFromDataElement( ds.GetDataElement( rescale_slope.GetTag() ) );
00209 tags->set("RescaleSlope") = Say("%n") << rescale_slope.GetValue();
00210 }
00211 else
00212 tags->set("RescaleIRescaleSlopentercept") = Say("%n") << 1;
00213 }
00214
00215 #if 0
00216 printf("TAGS --- --- --- --- ---\n");
00217 tags->print();
00218 #endif
00219
00220 int w=1,h=1,d=1;
00221 if (ndim>=1)
00222 w = dims[0];
00223 if (ndim>=2)
00224 h = dims[1];
00225 if (ndim>=3)
00226 d = dims[2];
00227 h = h * d;
00228
00229 ref<Image> img = new Image;
00230 img->setObjectName( vfile->path().toStdString() );
00231 if (pf.GetSamplesPerPixel() == 1 && pi == gdcm::PhotometricInterpretation::PALETTE_COLOR)
00232 {
00233 if (pf.GetBitsStored() <= 8)
00234 {
00235 img->allocate2D( w, h, 1, vl::IF_RGBA, vl::IT_UNSIGNED_BYTE);
00236 image.GetBuffer((char*)img->pixels());
00237
00238 const gdcm::LookupTable& lut = image.GetLUT();
00239 std::auto_ptr<char> rgba( new char[(1<<lut.GetBitSample())*4] );
00240 lut.GetBufferAsRGBA((unsigned char*)rgba.get());
00241 for(int i=w*h; i--; )
00242 {
00243 int idx = img->pixels()[i];
00244 img->pixels()[i*4+0] = rgba.get()[idx*4+0];
00245 img->pixels()[i*4+1] = rgba.get()[idx*4+1];
00246 img->pixels()[i*4+2] = rgba.get()[idx*4+2];
00247 img->pixels()[i*4+3] = rgba.get()[idx*4+3];
00248 }
00249 }
00250 else
00251 if (pf.GetBitsStored() <= 16)
00252 {
00253 img->allocate2D( w, h, 1, vl::IF_RGBA, vl::IT_UNSIGNED_BYTE);
00254 image.GetBuffer((char*)img->pixels());
00255
00256 const gdcm::LookupTable& lut = image.GetLUT();
00257 std::auto_ptr<char> rgba( new char[(1<<lut.GetBitSample())*4] );
00258 lut.GetBufferAsRGBA((unsigned char*)rgba.get());
00259 for(int i=w*h; i--; )
00260 {
00261 int idx = ((unsigned short*)img->pixels())[i];
00262 img->pixels()[i*4+0] = rgba.get()[idx*4+0];
00263 img->pixels()[i*4+1] = rgba.get()[idx*4+1];
00264 img->pixels()[i*4+2] = rgba.get()[idx*4+2];
00265 img->pixels()[i*4+3] = rgba.get()[idx*4+3];
00266 }
00267 }
00268 }
00269 else
00270 if (pf.GetSamplesPerPixel() == 1 && (pi == gdcm::PhotometricInterpretation::MONOCHROME2 || pi == gdcm::PhotometricInterpretation::MONOCHROME1))
00271 {
00272 if (pf.GetBitsStored() <= 8)
00273 {
00274 img->allocate2D( w, h, 1, vl::IF_LUMINANCE, vl::IT_UNSIGNED_BYTE);
00275 image.GetBuffer((char*)img->pixels());
00276 if (pi == gdcm::PhotometricInterpretation::MONOCHROME1)
00277 to8bits(pf.GetBitsStored(), img->pixels(), w*h, true);
00278 else
00279 to8bits(pf.GetBitsStored(), img->pixels(), w*h, false);
00280 }
00281 else
00282 if (pf.GetBitsStored() <= 16)
00283 {
00284 img->allocate2D( w, h, 1, vl::IF_LUMINANCE, vl::IT_UNSIGNED_SHORT);
00285 image.GetBuffer((char*)img->pixels());
00286 if (pi == gdcm::PhotometricInterpretation::MONOCHROME1)
00287 to16bits(pf.GetBitsStored(), img->pixels(), w*h, true);
00288 else
00289 to16bits(pf.GetBitsStored(), img->pixels(), w*h, false);
00290 }
00291 else
00292 if (pf.GetBitsStored() <= 32)
00293 {
00294 img->allocate2D( w, h, 1, vl::IF_LUMINANCE, vl::IT_UNSIGNED_INT);
00295 image.GetBuffer((char*)img->pixels());
00296 if (pi == gdcm::PhotometricInterpretation::MONOCHROME1)
00297 to32bits(pf.GetBitsStored(), img->pixels(), w*h, true);
00298 else
00299 to32bits(pf.GetBitsStored(), img->pixels(), w*h, false);
00300 }
00301 }
00302 else
00303 if (pf.GetSamplesPerPixel() == 3)
00304 {
00305 if (pf.GetBitsStored() <= 8)
00306 {
00307 img->allocate2D( w, h, 1, vl::IF_RGB, vl::IT_UNSIGNED_BYTE);
00308 image.GetBuffer((char*)img->pixels());
00309 if (image.GetPlanarConfiguration())
00310 {
00311 int slice_pix_count = w*h/d;
00312 std::auto_ptr<char> px ( new char[slice_pix_count*3] );
00313 for(int slice=0; slice<d; ++slice)
00314 {
00315 char* red = (char* )img->pixels() + slice*slice_pix_count*3 + 0;
00316 char* green = (char* )img->pixels() + slice*slice_pix_count*3 + slice_pix_count;
00317 char* blue = (char* )img->pixels() + slice*slice_pix_count*3 + slice_pix_count*2;
00318 for(int i=0; i<slice_pix_count*3; i+=3, ++red, ++green, ++blue)
00319 {
00320 px.get()[i+0] = *red;
00321 px.get()[i+1] = *green;
00322 px.get()[i+2] = *blue;
00323 }
00324 memcpy(img->pixels(), px.get(), img->requiredMemory());
00325 }
00326 }
00327 }
00328 else
00329 if (pf.GetBitsStored() <= 16)
00330 {
00331 img->allocate2D( w, h, 1, vl::IF_RGB, vl::IT_UNSIGNED_SHORT);
00332 image.GetBuffer((char*)img->pixels());
00333 if (image.GetPlanarConfiguration())
00334 {
00335 int slice_pix_count = w*h/d;
00336 std::auto_ptr<short> px ( new short[slice_pix_count*3] );
00337 for(int slice=0; slice<d; ++slice)
00338 {
00339 short* red = (short* )img->pixels() + slice*slice_pix_count*3 + 0;
00340 short* green = (short* )img->pixels() + slice*slice_pix_count*3 + slice_pix_count;
00341 short* blue = (short* )img->pixels() + slice*slice_pix_count*3 + slice_pix_count*2;
00342 for(int i=0; i<slice_pix_count*3; i+=3, ++red, ++green, ++blue)
00343 {
00344 px.get()[i+0] = *red;
00345 px.get()[i+1] = *green;
00346 px.get()[i+2] = *blue;
00347 }
00348 memcpy(img->pixels(), px.get(), img->requiredMemory());
00349 }
00350 }
00351 }
00352 else
00353 {
00354 vl::Log::error("DICOM format not supported: SamplesPerPixel = 3, BitsStored > 16.\n\n");
00355 image.Print(std::cout);
00356 vfile->close();
00357 return NULL;
00358 }
00359 }
00360 else
00361 {
00362 vl::Log::error("DICOM format not supported.\n");
00363 image.Print(std::cout);
00364 vfile->close();
00365 return NULL;
00366 }
00367 vfile->close();
00368 img->setHeight(h/d);
00369 img->setDepth(d>1?d:0);
00370 img->setTags(tags.get());
00371 img->flipVertically();
00372 return img;
00373 }
00374
00375 bool vl::isDICOM(VirtualFile* file)
00376 {
00377 file->open(OM_ReadOnly);
00378 file->seekSet(128);
00379 char signature[] = { 'D', 'I', 'C', 'M' };
00380 char buf[] = { 0, 0, 0, 0 };
00381 file->read(buf,4);
00382 bool ok = memcmp(buf,signature,4) == 0;
00383 file->close();
00384 return ok;
00385 }
00386
00387 bool vl::saveDICOM(const Image* src, const String& path)
00388 {
00389 return saveDICOM(src, new DiskFile(path));
00390 }
00391
00392 bool vl::saveDICOM(const Image* src, VirtualFile* fout)
00393 {
00394 if (src->dimension()<1 || src->dimension()>3)
00395 {
00396 vl::Log::error("saveDICOM(): uncompatible image dimension().\n");
00397 return false;
00398 }
00399
00400 ref<Image> img = new Image;
00401 *img = *src;
00402
00403
00404 unsigned short bps = 0;
00405 switch(img->type())
00406 {
00407 case vl::IT_UNSIGNED_BYTE: bps = 1; break;
00408 case vl::IT_UNSIGNED_SHORT: bps = 2; break;
00409 default:
00410 vl::Log::error("saveDICOM(): unsupported image type().\n");
00411 return false;
00412 }
00413
00414
00415 unsigned short spp = 0;
00416 switch(img->format())
00417 {
00418 case vl::IF_ALPHA: spp = 1; break;
00419 case vl::IF_LUMINANCE: spp = 1; break;
00420 case vl::IF_LUMINANCE_ALPHA: spp = 1; break;
00421 case vl::IF_RGB: spp = 3; break;
00422 case vl::IF_BGR: spp = 3; break;
00423 case vl::IF_RGBA: spp = 3; break;
00424 case vl::IF_BGRA: spp = 3; break;
00425 default:
00426 vl::Log::error("saveDICOM(): unsupported image format().\n");
00427 return false;
00428 }
00429
00430 if (img->format() == vl::IF_LUMINANCE_ALPHA)
00431 img = img->convertFormat(vl::IF_LUMINANCE);
00432 if (img->format() == vl::IF_BGR)
00433 img = img->convertFormat(vl::IF_RGB);
00434 if (img->format() == vl::IF_BGRA || img->format() == vl::IF_RGBA)
00435 img = img->convertFormat(vl::IF_RGB);
00436
00437 gdcm::SmartPointer<gdcm::Image> im = new gdcm::Image;
00438 im->SetNumberOfDimensions( img->dimension() );
00439 int dims[] = { img->width(), img->height()?img->height():1, img->depth()?img->depth():1 };
00440 for(int i=0; i<img->dimension(); ++i)
00441 im->SetDimension( i, dims[i] );
00442
00443 im->GetPixelFormat().SetScalarType(bps==8?gdcm::PixelFormat::INT8:gdcm::PixelFormat::INT16);
00444 im->GetPixelFormat().SetBitsAllocated(bps*8);
00445 im->GetPixelFormat().SetBitsStored(bps*8);
00446 im->GetPixelFormat().SetHighBit(bps*8-1);
00447 im->GetPixelFormat().SetSamplesPerPixel(spp);
00448 gdcm::PhotometricInterpretation pi = spp==1?gdcm::PhotometricInterpretation::MONOCHROME2:gdcm::PhotometricInterpretation::RGB;
00449 im->SetPhotometricInterpretation(pi);
00450
00451
00452 unsigned long len = im->GetBufferLength();
00453 unsigned long req = img->requiredMemory();
00454 if( len != req )
00455 {
00456
00457 vl::Log::error("saveDICOM(): buffer size computation error.\n");
00458 VL_TRAP()
00459 return false;
00460 }
00461
00462 img->flipVertically();
00463
00464 gdcm::DataElement pixeldata( gdcm::Tag(0x7fe0,0x0010) );
00465 pixeldata.SetByteValue( (const char*)img->pixels(), len );
00466 im->SetDataElement( pixeldata );
00467
00468 gdcm::ImageWriter w;
00469 w.SetImage( *im );
00470 std::ostringstream ostr;
00471 w.SetStream(ostr);
00472 if( !w.Write() )
00473 {
00474 vl::Log::error("saveDICOM(): error writing stream.\n");
00475 return false;
00476 }
00477 fout->open(OM_WriteOnly);
00478 int bcount = (int)fout->write(ostr.str().c_str(), ostr.str().length());
00479 fout->close();
00480 if( bcount != (int)ostr.str().length() )
00481 {
00482 vl::Log::error("saveDICOM(): error writing file.\n");
00483 return false;
00484 }
00485
00486 return true;
00487 }
00488
00489 #endif