7 #include "vtkGdcmIO/SeriesDBLazyReader.hpp" 8 #include "vtkGdcmIO/helper/GdcmHelper.hpp" 9 #include "vtkGdcmIO/helper/ImageDicomStream.hpp" 11 #include <fwCore/base.hpp> 12 #if (SPYLOG_LEVEL >= 4 ) 13 #include <fwCore/HiResTimer.hpp> 16 #include <fwJobs/IJob.hpp> 17 #include <fwJobs/Observer.hpp> 19 #include <fwDataIO/reader/registry/macros.hpp> 21 #include <fwMedData/Equipment.hpp> 22 #include <fwMedData/ImageSeries.hpp> 23 #include <fwMedData/Patient.hpp> 24 #include <fwMedData/Series.hpp> 25 #include <fwMedData/SeriesDB.hpp> 26 #include <fwMedData/Study.hpp> 28 #include <fwVtkIO/helper/vtkLambdaCommand.hpp> 29 #include <fwVtkIO/vtk.hpp> 31 #include <fwMemory/BufferObject.hpp> 33 #include <fwTools/dateAndTime.hpp> 34 #include <fwTools/fromIsoExtendedString.hpp> 37 #include <boost/algorithm/string/classification.hpp> 38 #include <boost/algorithm/string/split.hpp> 39 #include <boost/algorithm/string/trim.hpp> 40 #include <boost/filesystem/path.hpp> 41 #include <boost/iostreams/categories.hpp> 44 #include <gdcmImageHelper.h> 45 #include <gdcmImageReader.h> 46 #include <gdcmIPPSorter.h> 47 #include <gdcmIPPSorter.h> 48 #include <gdcmReader.h> 49 #include <gdcmRescaler.h> 50 #include <gdcmScanner.h> 60 const ::gdcm::Tag seriesUIDTag(0x0020,0x000e);
61 const ::gdcm::Tag seriesDateTag(0x0008,0x0021);
62 const ::gdcm::Tag seriesTimeTag(0x0008,0x0031);
63 const ::gdcm::Tag seriesTypeTag(0x0008,0x0060);
64 const ::gdcm::Tag seriesDescriptionTag(0x0008,0x103e);
65 const ::gdcm::Tag seriesPhysicianNamesTag(0x0008,0x1050);
67 const ::gdcm::Tag equipmentInstitutionNameTag(0x0008,0x0080);
69 const ::gdcm::Tag patientNameTag(0x0010,0x0010);
70 const ::gdcm::Tag patientIDTag(0x0010,0x0020);
71 const ::gdcm::Tag patientBirthdateTag(0x0010,0x0030);
72 const ::gdcm::Tag patientSexTag(0x0010,0x0040);
74 const ::gdcm::Tag studyUIDTag(0x0020,0x000d);
75 const ::gdcm::Tag studyDateTag(0x0008,0x0020);
76 const ::gdcm::Tag studyTimeTag(0x0008,0x0030);
77 const ::gdcm::Tag studyReferingPhysicianNameTag(0x0008,0x0090);
78 const ::gdcm::Tag studyDescriptionTag(0x0008,0x1030);
79 const ::gdcm::Tag studyPatientAgeTag(0x0010,0x1010);
81 const ::gdcm::Tag imageTypeTag(0x0008,0x0008);
82 const ::gdcm::Tag acquisitionDateTag(0x0008,0x0022);
83 const ::gdcm::Tag acquisitionTimeTag(0x0008,0x0032);
85 const ::gdcm::Tag sliceThicknessTag(0x0018,0x0050);
86 const ::gdcm::Tag windowCenterTag(0x0028,0x1050);
87 const ::gdcm::Tag windowWidthTag(0x0028,0x1051);
92 ::
fwData::location::enableFolder< IObjectReader >(this),
93 ::
fwData::location::enableMultiFiles< IObjectReader >(this),
94 m_job(::
fwJobs::Observer::New(
"SeriesDB reader"))
108 ::fwMedData::SeriesDB::sptr SeriesDBLazyReader::createSeriesDB( const ::boost::filesystem::path &dicomDir )
113 std::vector<std::string> filenames;
114 ::vtkGdcmIO::helper::DicomSearch::searchRecursivelyFiles(dicomDir, filenames);
116 this->addSeries( seriesDB, filenames);
122 void SeriesDBLazyReader::scanFiles( ::gdcm::Scanner & scanner,
const std::vector< std::string > & filenames )
124 scanner.AddTag( seriesUIDTag );
125 scanner.AddTag( seriesDateTag );
126 scanner.AddTag( seriesTimeTag );
127 scanner.AddTag( seriesTypeTag );
128 scanner.AddTag( seriesDescriptionTag );
129 scanner.AddTag( seriesPhysicianNamesTag );
131 scanner.AddTag( equipmentInstitutionNameTag );
133 scanner.AddTag( patientNameTag );
134 scanner.AddTag( patientIDTag );
135 scanner.AddTag( patientBirthdateTag );
136 scanner.AddTag( patientSexTag );
138 scanner.AddTag( studyUIDTag );
139 scanner.AddTag( studyUIDTag );
140 scanner.AddTag( studyDateTag );
141 scanner.AddTag( studyTimeTag );
142 scanner.AddTag( studyReferingPhysicianNameTag );
143 scanner.AddTag( studyDescriptionTag );
144 scanner.AddTag( studyPatientAgeTag );
146 scanner.AddTag( imageTypeTag );
148 scanner.AddTag( acquisitionDateTag );
149 scanner.AddTag( acquisitionTimeTag );
151 scanner.AddTag( sliceThicknessTag );
152 scanner.AddTag( windowCenterTag );
153 scanner.AddTag( windowWidthTag );
155 bool scanIsOk = scanner.Scan( filenames );
156 FW_RAISE_IF(
"Dicom scanner failed", !scanIsOk );
161 SeriesDBLazyReader::MapSeriesType buildMapSeriesFromScanner( ::gdcm::Scanner & scanner )
163 ::gdcm::Directory::FilenamesType keys = scanner.GetKeys();
164 ::gdcm::Directory::FilenamesType::const_iterator it = keys.begin();
166 SeriesDBLazyReader::MapSeriesType mapSeries;
168 for(; it != keys.end(); ++it)
170 const char *filename = it->c_str();
171 assert( scanner.IsKey( filename ) );
173 const char *seriesUID = scanner.GetValue( filename, seriesUIDTag );
174 const char *acqDate = scanner.GetValue( filename, acquisitionDateTag );
178 std::string fileSetId = seriesUID;
183 fileSetId += acqDate;
186 const char *imageTypeStr = scanner.GetValue(filename, imageTypeTag);
190 std::string imageType(imageTypeStr);
194 fileSetId += imageTypeStr;
196 mapSeries[fileSetId].push_back(filename);
201 SLM_ERROR (
"Error in vtkGdcmIO : No serie name found in : " + *it );
212 std::string getValue( ::gdcm::Scanner & scanner,
const std::string & dcmFile, const ::gdcm::Tag & tag )
214 const char * tagValue = scanner.GetValue( dcmFile.c_str(), tag );
217 return std::string(tagValue);
221 return std::string(
"");
227 template <
typename T >
228 T getNumericValue( ::gdcm::Scanner & scanner,
const std::string & dcmFile, const ::gdcm::Tag & tag )
230 const char * tagValue = scanner.GetValue( dcmFile.c_str(), tag );
233 std::string val (tagValue);
234 ::boost::algorithm::trim(val);
235 return ::boost::lexical_cast<T>( val );
245 void SeriesDBLazyReader::fillSeries( ::gdcm::Scanner & scanner,
246 const std::string & dcmFile, ::fwMedData::Series::sptr series )
248 const std::string seriesUID = getValue( scanner, dcmFile, seriesUIDTag );
249 const std::string seriesTime = getValue( scanner, dcmFile, seriesTimeTag );
250 const std::string seriesDate = getValue( scanner, dcmFile, seriesDateTag );
251 const std::string seriesModality = getValue( scanner, dcmFile, seriesTypeTag );
252 const std::string seriesDescription = getValue( scanner, dcmFile, seriesDescriptionTag );
254 ::fwMedData::DicomValuesType seriesPhysicianNames;
255 ::gdcm::Scanner::ValuesType gdcmPhysicianNames = scanner.GetValues( seriesPhysicianNamesTag );
256 for(
const std::string &str : gdcmPhysicianNames)
258 ::fwMedData::DicomValuesType result;
259 ::boost::split( result, str, ::boost::is_any_of(
"\\"));
260 seriesPhysicianNames.reserve(seriesPhysicianNames.size() + result.size());
261 seriesPhysicianNames.insert(seriesPhysicianNames.end(), result.begin(), result.end());
264 SLM_ASSERT(
"No series UID", !seriesUID.empty() );
265 series->setInstanceUID( seriesUID );
266 series->setModality( seriesModality );
267 series->setDescription( seriesDescription );
268 series->setDate( seriesDate );
269 series->setTime( seriesTime );
270 series->setPerformingPhysiciansName( seriesPhysicianNames );
275 void SeriesDBLazyReader::fillPatient( ::gdcm::Scanner & scanner,
276 const std::string & dcmFile, ::fwMedData::Patient::sptr patient )
278 const std::string patientName = getValue( scanner, dcmFile, patientNameTag );
279 const std::string patientId = getValue( scanner, dcmFile, patientIDTag );
280 const std::string patientBirthdate = getValue( scanner, dcmFile, patientBirthdateTag );
281 const std::string patientSex = getValue( scanner, dcmFile, patientSexTag );
283 patient->setName(patientName);
284 patient->setPatientId(patientId);
285 patient->setBirthdate(patientBirthdate);
286 patient->setSex(patientSex);
291 void SeriesDBLazyReader::fillStudy( ::gdcm::Scanner & scanner,
292 const std::string & dcmFile, ::fwMedData::Study::sptr study )
294 const std::string studyUID = getValue( scanner, dcmFile, studyUIDTag );
295 const std::string studyReferingPhysicianName = getValue( scanner, dcmFile, studyReferingPhysicianNameTag );
296 const std::string studyDate = getValue( scanner, dcmFile, studyDateTag );
297 const std::string studyTime = getValue( scanner, dcmFile, studyTimeTag );
298 const std::string studyDescription = getValue( scanner, dcmFile, studyDescriptionTag );
299 const std::string studyPatientAge = getValue( scanner, dcmFile, studyPatientAgeTag );
301 SLM_ASSERT(
"No study UID", !studyUID.empty() );
302 study->setInstanceUID(studyUID);
303 study->setDate(studyDate);
304 study->setTime(studyTime);
305 study->setDescription(studyDescription);
306 study->setPatientAge(studyPatientAge);
307 study->setReferringPhysicianName(studyReferingPhysicianName);
312 void SeriesDBLazyReader::fillEquipment( ::gdcm::Scanner & scanner,
313 const std::string & dcmFile, ::fwMedData::Equipment::sptr equipment )
315 const std::string equipementInstitution = getValue( scanner, dcmFile, equipmentInstitutionNameTag );
317 equipment->setInstitutionName(equipementInstitution);
322 void SeriesDBLazyReader::preprocessImage(
323 const ::fwData::Image::sptr & img,
324 const SeriesDBLazyReader::SeriesFilesType & files )
326 ::gdcm::Reader localReader;
327 localReader.SetFileName( files[0].c_str() );
329 ::gdcm::File & gdcmReaderFile = localReader.GetFile();
330 std::vector<double> origin = ::gdcm::ImageHelper::GetOriginValue( gdcmReaderFile );
331 std::vector<unsigned int> dim = ::gdcm::ImageHelper::GetDimensionsValue( gdcmReaderFile );
332 std::vector<double> spacing = ::gdcm::ImageHelper::GetSpacingValue( gdcmReaderFile );
333 ::gdcm::PixelFormat pixelFormat = ::gdcm::ImageHelper::GetPixelFormatValue( gdcmReaderFile );
334 std::vector<double> interceptSlope = ::gdcm::ImageHelper::GetRescaleInterceptSlopeValue( gdcmReaderFile );
339 imgOrigin[0] = origin[0];
340 imgOrigin[1] = origin[1];
341 imgOrigin[2] = origin[2];
342 img->setOrigin( imgOrigin );
346 imgSpacing.resize(3);
347 imgSpacing[0] = spacing[0];
348 imgSpacing[1] = spacing[1];
350 img->setSpacing( imgSpacing );
357 imgSize[2] = files.size();
361 ::gdcm::PixelFormat::ScalarType scalarType = pixelFormat.GetScalarType();
364 r.SetIntercept( interceptSlope[0] );
365 r.SetSlope( interceptSlope[1] );
366 r.SetPixelFormat( scalarType );
367 scalarType = r.ComputeInterceptSlopePixelType();
368 OSLM_TRACE(
"Intercept = " << interceptSlope[0] );
373 case ::gdcm::PixelFormat::UINT8:
374 imgType = ::fwTools::Type::s_UINT8;
376 case ::gdcm::PixelFormat::INT8:
377 imgType = ::fwTools::Type::s_INT8;
379 case ::gdcm::PixelFormat::UINT16:
380 imgType = ::fwTools::Type::s_UINT16;
382 case ::gdcm::PixelFormat::INT16:
383 imgType = ::fwTools::Type::s_INT16;
385 case ::gdcm::PixelFormat::UINT32:
386 imgType = ::fwTools::Type::s_UINT32;
388 case ::gdcm::PixelFormat::INT32:
389 imgType = ::fwTools::Type::s_INT32;
391 case ::gdcm::PixelFormat::FLOAT32:
392 imgType = ::fwTools::Type::s_FLOAT;
394 case ::gdcm::PixelFormat::FLOAT64:
395 imgType = ::fwTools::Type::s_DOUBLE;
403 size_t numberOfComponents = pixelFormat.GetSamplesPerPixel();
405 img->setSize( imgSize );
406 img->setNumberOfComponents( numberOfComponents );
407 img->setType( imgType );
408 img->getDataArray()->resize(imgType, imgSize, numberOfComponents,
false);
413 SeriesDBLazyReader::SeriesFilesType sortImageSeriesFiles(
const SeriesDBLazyReader::SeriesFilesType & seriesFiles )
415 SeriesDBLazyReader::SeriesFilesType sortedSeriesFiles = seriesFiles;
418 s.SetComputeZSpacing(
true );
419 s.SetZSpacingTolerance( 1e-3 );
420 bool success = s.Sort( seriesFiles );
422 SLM_WARN_IF(
"Failed to sort : " + seriesFiles[0], !success );
425 sortedSeriesFiles = s.GetFilenames();
428 return sortedSeriesFiles;
433 double SeriesDBLazyReader::computeZSpacing(
const SeriesDBLazyReader::SeriesFilesType & seriesFiles )
435 SLM_ASSERT(
"This method only work if series files contains at least 2 frames.", seriesFiles.size() > 1);
441 r1.SetFileName( seriesFiles[0].c_str() );
442 r2.SetFileName( seriesFiles[1].c_str() );
444 bool canRead = r1.Read() && r2.Read();
447 std::vector<double> vOrigin1 = ::gdcm::ImageHelper::GetOriginValue(r1.GetFile());
448 std::vector<double> vOrigin2 = ::gdcm::ImageHelper::GetOriginValue(r2.GetFile());
449 zspacing = vOrigin2[2] - vOrigin1[2];
456 void SeriesDBLazyReader::fillImage(
457 ::gdcm::Scanner & scanner,
458 const SeriesDBLazyReader::SeriesFilesType & seriesFiles,
459 const std::string & dcmFile,
460 ::fwData::Image::sptr img )
463 this->preprocessImage(img,seriesFiles);
465 double center = getNumericValue<double>(scanner, dcmFile, windowCenterTag);
466 double width = getNumericValue<double>(scanner, dcmFile, windowWidthTag);
468 img->setWindowCenter(center);
469 img->setWindowWidth(width);
472 imgSpacing.resize(3);
473 double thickness = getNumericValue<double>(scanner, dcmFile, sliceThicknessTag);
474 thickness = thickness ? thickness : 1.;
475 if ( seriesFiles.size() > 1 )
477 double computedZSpacing = computeZSpacing( seriesFiles );
478 imgSpacing[2] = computedZSpacing ? computedZSpacing : thickness;
482 imgSpacing[2] = thickness;
484 img->setSpacing(imgSpacing);
487 ::vtkGdcmIO::helper::ImageDicomInfo::sptr dcmInfo = std::make_shared< ::vtkGdcmIO::helper::ImageDicomInfo >();
488 dcmInfo->m_buffSizeInBytes = img->getSizeInBytes();
489 dcmInfo->m_seriesFiles = seriesFiles;
491 ::fwMemory::BufferObject::sptr buffObj = img->getDataArray()->getBufferObject();
492 buffObj->setIStreamFactory(
493 std::make_shared< ::vtkGdcmIO::helper::ImageDicomStream >( dcmInfo ),
494 img->getSizeInBytes() );
499 void SeriesDBLazyReader::addSeries(
500 const ::fwMedData::SeriesDB::sptr &seriesDB,
501 const std::vector< std::string > & filenames )
510 #if (SPYLOG_LEVEL >= 4 ) // Log level info 516 ::gdcm::Scanner scanner;
519 this->scanFiles( scanner, filenames );
522 MapSeriesType mapSeries = buildMapSeriesFromScanner( scanner );
524 for( MapSeriesType::value_type mapElem : mapSeries )
526 SeriesFilesType seriesFiles = sortImageSeriesFiles( mapElem.second );
527 const std::string & refDcmFile = seriesFiles[0];
531 bool isAnImage =
true;
535 ::fwMedData::ImageSeries::sptr series = ::fwMedData::ImageSeries::New();
536 ::fwData::Image::sptr img = ::fwData::Image::New();
538 series->setImage( img );
541 this->fillSeries( scanner, refDcmFile, series );
542 this->fillPatient( scanner, refDcmFile, series->getPatient() );
543 this->fillStudy( scanner, refDcmFile, series->getStudy() );
544 this->fillEquipment( scanner, refDcmFile, series->getEquipment() );
545 this->fillImage( scanner, seriesFiles, refDcmFile, img );
547 seriesDB->getContainer().push_back(series);
551 #if (SPYLOG_LEVEL >= 4 ) 557 catch (std::exception& e)
559 OSLM_ERROR(
"Try with another reader or retry with this reader on a specific subfolder : " << e.what() );
560 std::vector< std::string >::const_iterator it = filenames.begin();
561 for(; it != filenames.end(); ++it)
574 std::vector<std::string> filenames;
575 if(::fwData::location::have < ::fwData::location::Folder, ::fwDataIO::reader::IObjectReader > (
this))
577 ::vtkGdcmIO::helper::DicomSearch::searchRecursivelyFiles(this->
getFolder(), filenames);
579 else if(::fwData::location::have < ::fwData::location::MultiFiles, ::fwDataIO::reader::IObjectReader > (
this))
581 for(::boost::filesystem::path file : this->
getFiles())
583 filenames.push_back(file.string());
586 this->addSeries( seriesDB, filenames);
This class provide a timer (stopwatch). HiResTimer is able to measure the elapsed time with a few mic...
ILocation::PathType getFolder()
Get folder filesystem path.
#define SLM_TRACE_FUNC()
Trace contextual function signature.
FWCORE_API void stop()
Stop the timer. stop() will not reset the timer.
Key class used to restrict access to Object construction. See http://www.drdobbs.com/184402053.
#define OSLM_INFO(message)
#define OSLM_TRACE(message)
vtkmGdcm reader/writer lib
std::shared_ptr< ::fwJobs::IJob > sptr
Cancel request callback type.
VTKGDCMIO_API ~SeriesDBLazyReader()
Does nothing.
Reads DICOM data from a directory path in order to create a SeriesDB object in lazy mode...
FWCORE_API::fwCore::HiResClock::HiResClockType getElapsedTimeInMilliSec()
#define SLM_ERROR(message)
#define SLM_FATAL(message)
#define OSLM_ERROR(message)
#define SLM_ASSERT(message, cond)
work like 'assert' from 'cassert', with in addition a message logged by spylog (with FATAL loglevel) ...
VTKGDCMIO_API SeriesDBLazyReader(::fwDataIO::reader::IObjectReader::Key key)
Does nothing.
VTKGDCMIO_API std::shared_ptr< ::fwJobs::IJob > getJob() const override
virtual std::shared_ptr< DataType > getConcreteObject()
m_object getter.
VTKGDCMIO_API void read() override
Reads DICOM data from configured path and fills SeriesDB object. Use lazy reading process to read ima...
FWCORE_API void start()
Start the timer.
std::vector< double > SpacingType
Image spacing type.
#define SLM_TRACE(message)
ILocation::VectPathType getFiles()
Get file system paths.
Contains the representation of the data objects used in the framework.
This namespace fwJobs provides jobs management.
::fwData::Array::SizeType SizeType
Image size type.
#define SLM_WARN_IF(message, cond)
std::vector< double > OriginType
Image origin type.