ctkDICOMIndexer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QSqlQuery>
  16. #include <QSqlRecord>
  17. #include <QSqlError>
  18. #include <QVariant>
  19. #include <QDate>
  20. #include <QStringList>
  21. #include <QSet>
  22. #include <QFile>
  23. #include <QDirIterator>
  24. #include <QFileInfo>
  25. #include <QDebug>
  26. #include <QPixmap>
  27. #include <QtConcurrentRun>
  28. // ctkDICOM includes
  29. #include "ctkLogger.h"
  30. #include "ctkDICOMIndexer.h"
  31. #include "ctkDICOMIndexer_p.h"
  32. #include "ctkDICOMDatabase.h"
  33. // DCMTK includes
  34. #include <dcmtk/dcmdata/dcfilefo.h>
  35. #include <dcmtk/dcmdata/dcfilefo.h>
  36. #include <dcmtk/dcmdata/dcdeftag.h>
  37. #include <dcmtk/dcmdata/dcdatset.h>
  38. #include <dcmtk/ofstd/ofcond.h>
  39. #include <dcmtk/ofstd/ofstring.h>
  40. #include <dcmtk/ofstd/ofstd.h> /* for class OFStandard */
  41. #include <dcmtk/dcmdata/dcddirif.h> /* for class DicomDirInterface */
  42. #include <dcmtk/dcmimgle/dcmimage.h> /* for class DicomImage */
  43. #include <dcmtk/dcmimage/diregist.h> /* include support for color images */
  44. class AddFileFunctor
  45. {
  46. public:
  47. AddFileFunctor(ctkDICOMIndexer* indexer, ctkDICOMDatabase& database,
  48. const QString& destinationDirectoryName = "")
  49. : Indexer(indexer), Database(database), DestinationDirectoryName(destinationDirectoryName) { }
  50. bool operator()(const QString &filePath)
  51. {
  52. Indexer->addFile(Database,filePath,DestinationDirectoryName);
  53. return false; // make sure it is removed;
  54. }
  55. ctkDICOMIndexer* Indexer;
  56. ctkDICOMDatabase& Database;
  57. QString DestinationDirectoryName;
  58. };
  59. //------------------------------------------------------------------------------
  60. static ctkLogger logger("org.commontk.dicom.DICOMIndexer" );
  61. //------------------------------------------------------------------------------
  62. //------------------------------------------------------------------------------
  63. // ctkDICOMIndexerPrivate methods
  64. //------------------------------------------------------------------------------
  65. ctkDICOMIndexerPrivate::ctkDICOMIndexerPrivate(ctkDICOMIndexer& o) : q_ptr(&o), Canceled(false), CurrentPercentageProgress(-1)
  66. {
  67. Q_Q(ctkDICOMIndexer);
  68. connect(&DirectoryImportWatcher,SIGNAL(progressValueChanged(int)),this,SLOT(OnProgress(int)));
  69. connect(&DirectoryImportWatcher,SIGNAL(finished()),q,SIGNAL(indexingComplete()));
  70. connect(&DirectoryImportWatcher,SIGNAL(canceled()),q,SIGNAL(indexingComplete()));
  71. }
  72. //------------------------------------------------------------------------------
  73. ctkDICOMIndexerPrivate::~ctkDICOMIndexerPrivate()
  74. {
  75. DirectoryImportWatcher.cancel();
  76. DirectoryImportWatcher.waitForFinished();
  77. }
  78. void ctkDICOMIndexerPrivate::OnProgress(int)
  79. {
  80. Q_Q(ctkDICOMIndexer);
  81. int newPercentageProgress = ( 100 * DirectoryImportFuture.progressValue() ) / DirectoryImportFuture.progressMaximum();
  82. if (newPercentageProgress != CurrentPercentageProgress)
  83. {
  84. CurrentPercentageProgress = newPercentageProgress;
  85. emit q->progress(newPercentageProgress);
  86. }
  87. }
  88. //------------------------------------------------------------------------------
  89. //------------------------------------------------------------------------------
  90. // ctkDICOMIndexer methods
  91. //------------------------------------------------------------------------------
  92. ctkDICOMIndexer::ctkDICOMIndexer(QObject *parent):d_ptr(new ctkDICOMIndexerPrivate(*this))
  93. {
  94. Q_UNUSED(parent);
  95. }
  96. //------------------------------------------------------------------------------
  97. ctkDICOMIndexer::~ctkDICOMIndexer()
  98. {
  99. }
  100. //------------------------------------------------------------------------------
  101. void ctkDICOMIndexer::addFile(ctkDICOMDatabase& database,
  102. const QString filePath,
  103. const QString& destinationDirectoryName)
  104. {
  105. std::cout << filePath.toStdString();
  106. if (!destinationDirectoryName.isEmpty())
  107. {
  108. logger.warn("Ignoring destinationDirectoryName parameter, just taking it as indication we should copy!");
  109. }
  110. emit indexingFilePath(filePath);
  111. database.insert(filePath, !destinationDirectoryName.isEmpty(), true);
  112. }
  113. //------------------------------------------------------------------------------
  114. void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& ctkDICOMDatabase,
  115. const QString& directoryName,
  116. const QString& destinationDirectoryName)
  117. {
  118. Q_D(ctkDICOMIndexer);
  119. QStringList listOfFiles;
  120. QDir directory(directoryName);
  121. if(directory.exists("DICOMDIR"))
  122. {
  123. addDicomdir(ctkDICOMDatabase,directoryName,destinationDirectoryName);
  124. }
  125. else
  126. {
  127. QDirIterator it(directoryName,QDir::Files,QDirIterator::Subdirectories);
  128. while(it.hasNext())
  129. {
  130. listOfFiles << it.next();
  131. }
  132. emit foundFilesToIndex(listOfFiles.count());
  133. addListOfFiles(ctkDICOMDatabase,listOfFiles,destinationDirectoryName);
  134. }
  135. }
  136. //------------------------------------------------------------------------------
  137. void ctkDICOMIndexer::addListOfFiles(ctkDICOMDatabase& ctkDICOMDatabase,
  138. const QStringList& listOfFiles,
  139. const QString& destinationDirectoryName)
  140. {
  141. Q_D(ctkDICOMIndexer);
  142. if(!listOfFiles.isEmpty())
  143. {
  144. if(d->DirectoryImportWatcher.isRunning())
  145. {
  146. d->DirectoryImportWatcher.cancel();
  147. d->DirectoryImportWatcher.waitForFinished();
  148. }
  149. d->FilesToIndex.append(listOfFiles);
  150. d->DirectoryImportFuture = QtConcurrent::filter(d->FilesToIndex,AddFileFunctor(this,ctkDICOMDatabase,destinationDirectoryName));
  151. d->DirectoryImportWatcher.setFuture(d->DirectoryImportFuture);
  152. }
  153. }
  154. //------------------------------------------------------------------------------
  155. void ctkDICOMIndexer::addDicomdir(ctkDICOMDatabase& ctkDICOMDatabase,
  156. const QString& directoryName,
  157. const QString& destinationDirectoryName
  158. )
  159. {
  160. Q_D(ctkDICOMIndexer);
  161. //Initialize dicomdir with directory path
  162. QString dcmFilePath = directoryName;
  163. dcmFilePath.append("/DICOMDIR");
  164. DcmDicomDir* dicomDir = new DcmDicomDir(dcmFilePath.toStdString().c_str());
  165. //Values to store records data at the moment only uid needed
  166. OFString patientsName, studyInstanceUID, seriesInstanceUID, sopInstanceUID, referencedFileName ;
  167. //Variables for progress operations
  168. QString instanceFilePath;
  169. QStringList listOfInstances;
  170. DcmDirectoryRecord* rootRecord = &(dicomDir->getRootRecord());
  171. DcmDirectoryRecord* patientRecord = NULL;
  172. DcmDirectoryRecord* studyRecord = NULL;
  173. DcmDirectoryRecord* seriesRecord = NULL;
  174. DcmDirectoryRecord* fileRecord = NULL;
  175. /*Iterate over all records in dicomdir and setup path to the dataset of the filerecord
  176. then insert. the filerecord into the database.
  177. If any UID is missing the record and all of it's subelements won't be added to the database*/
  178. if(rootRecord != NULL)
  179. {
  180. while (((patientRecord = rootRecord->nextSub(patientRecord)) != NULL)
  181. &&(patientRecord->findAndGetOFString(DCM_PatientName, patientsName).good()))
  182. {
  183. logger.debug( "Reading new Patients:" );
  184. logger.debug( "Patient's Name: " + QString(patientsName.c_str()) );
  185. while (((studyRecord = patientRecord->nextSub(studyRecord)) != NULL)
  186. && (studyRecord->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good()))
  187. {
  188. logger.debug( "Reading new Studys:" );
  189. logger.debug( "Studies Name: " + QString(studyInstanceUID.c_str()) );
  190. while (((seriesRecord = studyRecord->nextSub(seriesRecord)) != NULL)
  191. &&(seriesRecord->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good()))
  192. {
  193. logger.debug( "Reading new Series:" );
  194. logger.debug( "Series Instance Name: " + QString(seriesInstanceUID.c_str()) );
  195. while (((fileRecord = seriesRecord->nextSub(fileRecord)) != NULL)
  196. &&(fileRecord->findAndGetOFStringArray(DCM_ReferencedSOPInstanceUIDInFile, sopInstanceUID).good())
  197. &&(fileRecord->findAndGetOFStringArray(DCM_ReferencedFileID,referencedFileName).good()))
  198. {
  199. //Get the filepath of the instance and insert it into a list
  200. instanceFilePath = directoryName;
  201. instanceFilePath.append("/");
  202. instanceFilePath.append(QString( referencedFileName.c_str() ));
  203. instanceFilePath.replace("\\","/");
  204. listOfInstances << instanceFilePath;
  205. }
  206. }
  207. }
  208. }
  209. emit foundFilesToIndex(listOfInstances.count());
  210. addListOfFiles(ctkDICOMDatabase,listOfInstances,destinationDirectoryName);
  211. }
  212. }
  213. //------------------------------------------------------------------------------
  214. void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& dicomDatabase, const QString& directoryName)
  215. {
  216. Q_UNUSED(dicomDatabase);
  217. Q_UNUSED(directoryName);
  218. /*
  219. * Probably this should go to the database class as well
  220. * Or we have to extend the interface to make possible what we do here
  221. * without using SQL directly
  222. /// get all filenames from the database
  223. QSqlQuery allFilesQuery(dicomDatabase.database());
  224. QStringList databaseFileNames;
  225. QStringList filesToRemove;
  226. this->loggedExec(allFilesQuery, "SELECT Filename from Images;");
  227. while (allFilesQuery.next())
  228. {
  229. QString fileName = allFilesQuery.value(0).toString();
  230. databaseFileNames.append(fileName);
  231. if (! QFile::exists(fileName) )
  232. {
  233. filesToRemove.append(fileName);
  234. }
  235. }
  236. QSet<QString> filesytemFiles;
  237. QDirIterator dirIt(directoryName);
  238. while (dirIt.hasNext())
  239. {
  240. filesytemFiles.insert(dirIt.next());
  241. }
  242. // TODO: it looks like this function was never finished...
  243. //
  244. // I guess the next step is to remove all filesToRemove from the database
  245. // and also to add filesystemFiles into the database tables
  246. */
  247. }
  248. //----------------------------------------------------------------------------
  249. void ctkDICOMIndexer::waitForImportFinished()
  250. {
  251. Q_D(ctkDICOMIndexer);
  252. d->DirectoryImportWatcher.waitForFinished();
  253. }
  254. //----------------------------------------------------------------------------
  255. void ctkDICOMIndexer::cancel()
  256. {
  257. Q_D(ctkDICOMIndexer);
  258. d->DirectoryImportWatcher.cancel();
  259. }