7 #include "vtkGdcmIO/SeriesDBReader.hpp" 9 #include "vtkGdcmIO/helper/GdcmHelper.hpp" 11 #include <fwCore/base.hpp> 13 #include <fwData/Image.hpp> 15 #include <fwDataIO/reader/registry/macros.hpp> 17 #include <fwJobs/IJob.hpp> 18 #include <fwJobs/Observer.hpp> 20 #include <fwMedData/Equipment.hpp> 21 #include <fwMedData/ImageSeries.hpp> 22 #include <fwMedData/Patient.hpp> 23 #include <fwMedData/Series.hpp> 24 #include <fwMedData/SeriesDB.hpp> 25 #include <fwMedData/Study.hpp> 27 #include <fwTools/dateAndTime.hpp> 28 #include <fwTools/fromIsoExtendedString.hpp> 30 #include <fwVtkIO/helper/vtkLambdaCommand.hpp> 31 #include <fwVtkIO/vtk.hpp> 33 #include <boost/algorithm/string/classification.hpp> 34 #include <boost/algorithm/string/split.hpp> 35 #include <boost/filesystem/path.hpp> 37 #include <gdcmAttribute.h> 38 #include <gdcmDataSet.h> 39 #include <gdcmImageHelper.h> 40 #include <gdcmIPPSorter.h> 41 #include <gdcmReader.h> 42 #include <gdcmScanner.h> 43 #include <gdcmSorter.h> 44 #include <vtkGDCMImageReader.h> 45 #include <vtkImageData.h> 46 #include <vtkImageWriter.h> 47 #include <vtkMedicalImageProperties.h> 48 #include <vtkSmartPointer.h> 49 #include <vtkStringArray.h> 61 ::
fwData::location::enableFolder< IObjectReader >(this),
62 ::
fwData::location::enableMultiFiles< IObjectReader >(this),
63 m_job(::
fwJobs::Observer::New(
"SeriesDB reader"))
77 ::fwMedData::SeriesDB::sptr SeriesDBReader::createSeriesDB( const ::boost::filesystem::path& dicomDir )
82 std::vector<std::string> filenames;
83 ::vtkGdcmIO::helper::DicomSearch::searchRecursivelyFiles(dicomDir, filenames);
85 this->addSeries( seriesDB, filenames);
92 bool sortByInstanceNumber(const ::gdcm::DataSet& ds1, const ::gdcm::DataSet& ds2 )
94 ::gdcm::Attribute<0x0020, 0x0013> at1;
96 ::gdcm::Attribute<0x0020, 0x0013> at2;
103 void SeriesDBReader::addSeries( const ::fwMedData::SeriesDB::sptr& seriesDB,
104 const std::vector< std::string >& filenames)
110 ::gdcm::Scanner scanner;
111 const ::gdcm::Tag seriesUIDTag(0x0020, 0x000e);
112 const ::gdcm::Tag seriesDateTag(0x0008, 0x0021);
113 const ::gdcm::Tag seriesTimeTag(0x0008, 0x0031);
114 const ::gdcm::Tag seriesTypeTag(0x0008, 0x0060);
115 const ::gdcm::Tag seriesDescriptionTag(0x0008, 0x103e);
116 const ::gdcm::Tag seriesPhysicianNamesTag(0x0008, 0x1050);
118 const ::gdcm::Tag equipmentInstitutionNameTag(0x0008, 0x0080);
120 const ::gdcm::Tag patientNameTag(0x0010, 0x0010);
121 const ::gdcm::Tag patientIDTag(0x0010, 0x0020);
122 const ::gdcm::Tag patientBirthdateTag(0x0010, 0x0030);
123 const ::gdcm::Tag patientSexTag(0x0010, 0x0040);
124 const ::gdcm::Tag studyUIDTag(0x0020, 0x000d);
125 const ::gdcm::Tag studyDateTag(0x0008, 0x0020);
126 const ::gdcm::Tag studyTimeTag(0x0008, 0x0030);
127 const ::gdcm::Tag studyReferingPhysicianNameTag(0x0008, 0x0090);
128 const ::gdcm::Tag studyDescriptionTag(0x0008, 0x1030);
129 const ::gdcm::Tag studyPatientAgeTag(0x0010, 0x1010);
131 const ::gdcm::Tag imageTypeTag(0x0008, 0x0008);
132 const ::gdcm::Tag acquisitionDateTag(0x0008, 0x0022);
133 const ::gdcm::Tag acquisitionTimeTag(0x0008, 0x0032);
135 scanner.AddTag( seriesUIDTag );
136 scanner.AddTag( seriesDateTag );
137 scanner.AddTag( seriesTimeTag );
138 scanner.AddTag( seriesTypeTag );
139 scanner.AddTag( seriesDescriptionTag );
140 scanner.AddTag( seriesPhysicianNamesTag );
141 scanner.AddTag( equipmentInstitutionNameTag );
142 scanner.AddTag( studyUIDTag );
143 scanner.AddTag( patientNameTag );
144 scanner.AddTag( patientIDTag );
145 scanner.AddTag( patientBirthdateTag );
146 scanner.AddTag( patientSexTag );
147 scanner.AddTag( studyUIDTag );
148 scanner.AddTag( studyDateTag );
149 scanner.AddTag( studyTimeTag );
150 scanner.AddTag( studyReferingPhysicianNameTag );
151 scanner.AddTag( studyDescriptionTag );
152 scanner.AddTag( studyPatientAgeTag );
153 scanner.AddTag( imageTypeTag );
154 scanner.AddTag( acquisitionDateTag );
155 scanner.AddTag( acquisitionTimeTag );
159 const bool isScanned = scanner.Scan( filenames );
165 const ::gdcm::Directory::FilenamesType keys = scanner.GetKeys();
167 typedef std::map< std::string, std::vector< std::string > > MapSeriesType;
168 MapSeriesType mapSeries;
170 for(
const std::string& filename : keys)
172 SLM_ASSERT(
"'"+filename+
"' is not a key in the mapping table", scanner.IsKey(filename.c_str()));
174 const char* seriesUID = scanner.GetValue( filename.c_str(), seriesUIDTag );
175 const char* acqDate = scanner.GetValue( filename.c_str(), acquisitionDateTag );
179 std::string fileSetId = seriesUID;
184 fileSetId += acqDate;
187 const char* imageType = scanner.GetValue(filename.c_str(), imageTypeTag);
191 SLM_TRACE(
"Image Type : " + std::string(imageType));
193 fileSetId += imageType;
195 mapSeries[fileSetId].push_back(filename);
199 SLM_ERROR(
"No series name found in : " + filename );
203 for(
const auto& elt : mapSeries)
205 ::fwMedData::ImageSeries::sptr series = ::fwMedData::ImageSeries::New();
206 ::fwMedData::Patient::sptr patient = series->getPatient();
207 ::fwMedData::Study::sptr study = series->getStudy();
208 ::fwMedData::Equipment::sptr equipment = series->getEquipment();
210 seriesDB->getContainer().push_back(series);
212 SLM_TRACE(
"Processing: '" + elt.first +
"' file set.");
213 const MapSeriesType::mapped_type& files = elt.second;
214 if ( !files.empty() )
216 vtkSmartPointer< vtkStringArray > fileArray = vtkSmartPointer< vtkStringArray >::New();
217 vtkSmartPointer< vtkGDCMImageReader > reader = vtkSmartPointer< vtkGDCMImageReader >::New();
218 reader->FileLowerLeftOn();
220 ::gdcm::IPPSorter ippSorter;
221 ippSorter.SetComputeZSpacing(
true );
222 ippSorter.SetZSpacingTolerance( 1e-3 );
223 bool isSorted = ippSorter.Sort( files );
225 std::vector<std::string> sorted;
226 double zspacing = 0.;
229 sorted = ippSorter.GetFilenames();
230 zspacing = ippSorter.GetZSpacing();
231 OSLM_TRACE(
"Found z-spacing:" << ippSorter.GetZSpacing());
237 SLM_WARN(
"IPP Sorting failed. Falling back to Instance Number sorting.");
238 ::gdcm::Sorter sorter;
239 sorter.SetSortFunction(sortByInstanceNumber);
240 isSorted = sorter.StableSort( filenames);
245 sorted = sorter.GetFilenames();
250 SLM_ERROR(
"Failed to sort '"+elt.first+
"'");
254 fileArray->Initialize();
257 SLM_TRACE(
"Success to sort '" + elt.first+
"'");
258 if (!zspacing && sorted.size() > 1)
263 ::gdcm::Reader localReader1;
264 ::gdcm::Reader localReader2;
265 const std::string& f1 = *(sorted.begin());
266 const std::string& f2 = *(sorted.begin() + 1);
267 SLM_TRACE(
"Search spacing in: '" + f1 +
"'");
268 SLM_TRACE(
"Search spacing in: '" + f2 +
"'");
270 localReader1.SetFileName( f1.c_str() );
271 localReader2.SetFileName( f2.c_str() );
272 const bool canRead = localReader1.Read() && localReader2.Read();
275 const std::vector<double> vOrigin1 =
276 ::gdcm::ImageHelper::GetOriginValue(localReader1.GetFile());
277 const std::vector<double> vOrigin2 =
278 ::gdcm::ImageHelper::GetOriginValue(localReader2.GetFile());
279 zspacing = vOrigin2[2] - vOrigin1[2];
281 "Found z-spacing:" << zspacing <<
" from : << " << vOrigin2[2] <<
" | " <<
284 SLM_ERROR_IF(
"Cannot read: '" + f1 +
"' or: '" + f2 +
"'", !canRead);
289 for(
const std::string& file : sorted)
291 SLM_TRACE(
"Add '" + file +
"' to vtkGdcmReader");
292 fileArray->InsertNextValue(file.c_str());
295 ::fwData::Image::sptr pDataImage = ::fwData::Image::New();
297 if (fileArray->GetNumberOfValues() > 0)
299 reader->SetFileNames( fileArray );
302 SLM_TRACE(
"Read Series: '" + elt.first +
"'");
305 vtkSmartPointer< ::fwVtkIO::helper::vtkLambdaCommand > progressCallback =
306 vtkSmartPointer< ::fwVtkIO::helper::vtkLambdaCommand >::New();
307 progressCallback->SetCallback([
this](vtkObject* caller,
long unsigned int,
void* )
309 auto filter =
static_cast<vtkGDCMImageReader*
>(caller);
310 m_job->doneWork( filter->GetProgress()*100 );
312 reader->AddObserver(vtkCommand::ProgressEvent, progressCallback);
314 m_job->addSimpleCancelHook( [&]()
316 reader->AbortExecuteOn();
320 reader->UpdateInformation();
321 reader->PropagateUpdateExtent();
324 ::fwVtkIO::fromVTKImage(reader->GetOutput(), pDataImage);
327 catch(std::exception& e)
329 OSLM_ERROR(
"VTKImage to fwData::Image failed : "<<e.what());
332 catch (std::exception& e)
334 OSLM_ERROR(
"Error during conversion : " << e.what() );
338 SLM_ERROR(
"Unexpected error during conversion" );
346 SLM_ASSERT(
"No file to read", !files.empty());
349 vtkMedicalImageProperties* medprop = reader->GetMedicalImageProperties();
351 const std::string patientName = medprop->GetPatientName();
352 const std::string patientId = medprop->GetPatientID();
353 const std::string patientBirthdate = medprop->GetPatientBirthDate();
354 const std::string patientSex = medprop->GetPatientSex();
356 const ::gdcm::Scanner::ValuesType gdcmPhysicianNames = scanner.GetValues( seriesPhysicianNamesTag );
358 const char* seriesUIDStr = scanner.GetValue( files[0].c_str(), seriesUIDTag );
359 const char* seriesTimeStr = scanner.GetValue( files[0].c_str(), seriesTimeTag );
360 const char* seriesDateStr = scanner.GetValue( files[0].c_str(), seriesDateTag );
362 const std::string seriesModality = medprop->GetModality();
363 const std::string seriesDescription = medprop->GetSeriesDescription();
364 const std::string seriesDate = ( seriesDateStr ? seriesDateStr :
"" );
365 const std::string seriesTime = ( seriesTimeStr ? seriesTimeStr :
"" );
367 ::fwMedData::DicomValuesType seriesPhysicianNames;
368 for(
const std::string& name : gdcmPhysicianNames)
370 ::fwMedData::DicomValuesType result;
371 ::boost::split( result, name, ::boost::is_any_of(
"\\"));
372 seriesPhysicianNames.reserve(seriesPhysicianNames.size() + result.size());
373 seriesPhysicianNames.insert(seriesPhysicianNames.end(), result.begin(), result.end());
376 const char* studyUIDStr = scanner.GetValue( files[0].c_str(), studyUIDTag );
377 const char* studyReferingPhysicianNameStr =
378 scanner.GetValue( files[0].c_str(), studyReferingPhysicianNameTag );
380 const std::string studyDate = medprop->GetStudyDate();
381 const std::string studyTime = medprop->GetStudyTime();
382 const std::string studyDescription = medprop->GetStudyDescription();
383 const std::string studyPatientAge = medprop->GetPatientAge();
384 const std::string equipementInstitution = medprop->GetInstitutionName();
386 const std::string studyReferingPhysicianName =
387 ( studyReferingPhysicianNameStr ? studyReferingPhysicianNameStr :
"" );
389 const double thickness = medprop->GetSliceThicknessAsDouble();
392 if (medprop->GetNumberOfWindowLevelPresets())
394 medprop->GetNthWindowLevelPreset(0, &width, ¢er);
398 if(pDataImage->getNumberOfDimensions() == 2)
403 pDataImage->setSize(imgSize);
408 pDataImage->setOrigin(imgOrigin);
412 vPixelSpacing.resize(3);
414 vPixelSpacing[2] = zspacing ? zspacing : (thickness ? thickness : 1.);
415 pDataImage->setSpacing(vPixelSpacing);
416 pDataImage->setWindowCenter(center);
417 pDataImage->setWindowWidth(width);
421 series->setInstanceUID(( seriesUIDStr ? seriesUIDStr :
"UNKNOWN-UID" ));
422 series->setModality( seriesModality );
423 series->setDescription( seriesDescription );
424 series->setDate( seriesDate );
425 series->setTime( seriesTime );
426 series->setPerformingPhysiciansName( seriesPhysicianNames );
427 series->setImage(pDataImage);
430 study->setInstanceUID(( studyUIDStr ? studyUIDStr :
"UNKNOWN-UID" ));
431 study->setDate(studyDate);
432 study->setTime(studyTime);
433 study->setDescription(studyDescription);
434 study->setPatientAge(studyPatientAge);
435 study->setReferringPhysicianName(studyReferingPhysicianName);
437 patient->setName(patientName);
438 patient->setPatientId(patientId);
439 patient->setBirthdate(patientBirthdate);
440 patient->setSex(patientSex);
442 equipment->setInstitutionName(equipementInstitution);
447 catch (std::exception& e)
449 OSLM_ERROR(
"Try with another reader or retry with this reader on a specific subfolder : " << e.what() );
450 for(
const auto filename : filenames)
463 std::vector<std::string> filenames;
464 if(::fwData::location::have < ::fwData::location::Folder, ::fwDataIO::reader::IObjectReader > (
this))
466 ::vtkGdcmIO::helper::DicomSearch::searchRecursivelyFiles(this->getFolder(), filenames);
468 else if(::fwData::location::have < ::fwData::location::MultiFiles, ::fwDataIO::reader::IObjectReader > (
this))
470 for(::boost::filesystem::path file : this->
getFiles())
472 filenames.push_back(file.string());
475 this->addSeries( seriesDB, filenames);
FWVTKIO_API void read() override
Reading operator.
#define SLM_TRACE_FUNC()
Trace contextual function signature.
Key class used to restrict access to Object construction. See http://www.drdobbs.com/184402053.
#define OSLM_TRACE(message)
vtkmGdcm reader/writer lib
std::shared_ptr< ::fwJobs::IJob > sptr
Cancel request callback type.
Reads DICOM data from a directory path in order to create a SeriesDB object.
#define SLM_WARN(message)
#define SLM_ERROR(message)
#define OSLM_ERROR(message)
FWVTKIO_API ~SeriesDBReader()
Destructor.
#define SLM_ERROR_IF(message, cond)
#define SLM_ASSERT(message, cond)
work like 'assert' from 'cassert', with in addition a message logged by spylog (with FATAL loglevel) ...
virtual std::shared_ptr< DataType > getConcreteObject()
m_object getter.
std::vector< double > SpacingType
Image spacing type.
#define SLM_TRACE(message)
ILocation::VectPathType getFiles()
Get file system paths.
FWVTKIO_API std::shared_ptr< ::fwJobs::IJob > getJob() const override
Contains the representation of the data objects used in the framework.
This namespace fwJobs provides jobs management.
::fwData::Array::SizeType SizeType
Image size type.
std::vector< double > OriginType
Image origin type.