qCTKDCMTKIndexer.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. #include "qCTKDCMTKIndexer.h"
  2. ///
  3. /// DCMTK includes
  4. #ifndef WIN32
  5. #define HAVE_CONFIG_H
  6. #endif
  7. #include "dcmtk/dcmdata/dcfilefo.h"
  8. #include "dcmtk/dcmdata/dcdeftag.h"
  9. #include "dcmtk/dcmdata/dcdatset.h"
  10. #include "dcmtk/ofstd/ofcond.h"
  11. #include "dcmtk/ofstd/ofstring.h"
  12. #include "dcmtk/ofstd/ofstd.h" /* for class OFStandard */
  13. #include "dcmtk/dcmdata/dcddirif.h" /* for class DicomDirInterface */
  14. #include <QSqlQuery>
  15. #include <QSqlRecord>
  16. #include <QVariant>
  17. #include <QDate>
  18. #include <QStringList>
  19. #include <QSet>
  20. #include <QFile>
  21. #include <QDirIterator>
  22. #define MITK_ERROR std::cout
  23. #define MITK_INFO std::cout
  24. class qCTKDCMTKIndexerPrivate: public qCTKPrivate<qCTKDCMTKIndexer>
  25. {
  26. public:
  27. qCTKDCMTKIndexerPrivate();
  28. ~qCTKDCMTKIndexerPrivate();
  29. };
  30. qCTKDCMTKIndexerPrivate::qCTKDCMTKIndexerPrivate()
  31. {
  32. }
  33. qCTKDCMTKIndexerPrivate::~qCTKDCMTKIndexerPrivate()
  34. {
  35. }
  36. qCTKDCMTKIndexer::qCTKDCMTKIndexer()
  37. {
  38. }
  39. qCTKDCMTKIndexer::~qCTKDCMTKIndexer()
  40. {
  41. }
  42. void qCTKDCMTKIndexer::AddDirectory(QSqlDatabase database, const QString& directoryName)
  43. {
  44. QSqlDatabase db = database;
  45. const std::string src_directory(directoryName.toStdString());
  46. // db.transaction();
  47. OFList<OFString> originalDcmtkFileNames;
  48. OFList<OFString> dcmtkFileNames;
  49. OFStandard::searchDirectoryRecursively(src_directory.c_str(), originalDcmtkFileNames, NULL, NULL);
  50. // hack to reverse list of filenames (not neccessary when image loading works correctly)
  51. for ( OFListIterator(OFString) iter = originalDcmtkFileNames.begin(); iter != originalDcmtkFileNames.end(); ++iter )
  52. {
  53. dcmtkFileNames.push_front( *iter );
  54. }
  55. DcmFileFormat fileformat;
  56. OFListIterator(OFString) iter = dcmtkFileNames.begin();
  57. OFListIterator(OFString) last = dcmtkFileNames.end();
  58. if(iter == last) return;
  59. QSqlQuery query(database);
  60. /// these are for optimizing the import of image sequences
  61. /// since most information are identical for all slices
  62. OFString lastPatientID = "";
  63. OFString lastPatientsName = "";
  64. OFString lastPatientsBirthDate = "";
  65. OFString lastStudyInstanceUID = "";
  66. OFString lastSeriesInstanceUID = "";
  67. int lastPatientUID = -1;
  68. /* iterate over all input filenames */
  69. while (iter != last)
  70. {
  71. /// first we
  72. std::string filename((*iter).c_str());
  73. MITK_INFO << filename << "\n";
  74. OFCondition status = fileformat.loadFile(filename.c_str());
  75. ++iter;
  76. if (!status.good())
  77. {
  78. MITK_ERROR << "Could not load " << filename << "\nDCMTK says: " << status.text();
  79. continue;
  80. }
  81. OFString patientsName = "", patientID = "", patientsBirthDate = "", patientsBirthTime = "", patientsSex = "",
  82. patientComments = "", patientsAge = "";
  83. OFString studyInstanceUID = "", studyID = "", studyDate = "", studyTime = "",
  84. accessionNumber = "", modalitiesInStudy = "", performingPhysiciansName = "", referringPhysician = "", studyDescription = "";
  85. OFString seriesInstanceUID = "", seriesDate = "", seriesTime = "",
  86. seriesDescription = "", bodyPartExamined = "", frameOfReferenceUID = "",
  87. contrastAgent = "", scanningSequence = "";
  88. Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
  89. //The patient UID is a unique number within the database, generated by the sqlite autoincrement
  90. int patientUID = -1;
  91. //If the following fields can not be evaluated, cancel evaluation of the DICOM file
  92. if (!fileformat.getDataset()->findAndGetOFString(DCM_PatientsName, patientsName).good())
  93. {
  94. MITK_ERROR << "Could not read DCM_PatientsName from " << filename;
  95. continue;
  96. }
  97. if (!fileformat.getDataset()->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())
  98. {
  99. MITK_ERROR << "Could not read DCM_StudyInstanceUID from " << filename;
  100. continue;
  101. }
  102. if (!fileformat.getDataset()->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())
  103. {
  104. MITK_ERROR << "Could not read DCM_SeriesInstanceUID from " << filename;
  105. continue;
  106. }
  107. fileformat.getDataset()->findAndGetOFString(DCM_PatientID, patientID);
  108. fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
  109. fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
  110. fileformat.getDataset()->findAndGetOFString(DCM_PatientsSex, patientsSex);
  111. fileformat.getDataset()->findAndGetOFString(DCM_PatientsAge, patientsAge);
  112. fileformat.getDataset()->findAndGetOFString(DCM_PatientComments, patientComments);
  113. fileformat.getDataset()->findAndGetOFString(DCM_StudyID, studyID);
  114. fileformat.getDataset()->findAndGetOFString(DCM_StudyDate, studyDate);
  115. fileformat.getDataset()->findAndGetOFString(DCM_StudyTime, studyTime);
  116. fileformat.getDataset()->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
  117. fileformat.getDataset()->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
  118. fileformat.getDataset()->findAndGetOFString(DCM_PerformingPhysiciansName, performingPhysiciansName);
  119. fileformat.getDataset()->findAndGetOFString(DCM_ReferringPhysiciansName, referringPhysician);
  120. fileformat.getDataset()->findAndGetOFString(DCM_StudyDescription, studyDescription);
  121. fileformat.getDataset()->findAndGetOFString(DCM_SeriesDate, seriesDate);
  122. fileformat.getDataset()->findAndGetOFString(DCM_SeriesTime, seriesTime);
  123. fileformat.getDataset()->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
  124. fileformat.getDataset()->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
  125. fileformat.getDataset()->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
  126. fileformat.getDataset()->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
  127. fileformat.getDataset()->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
  128. fileformat.getDataset()->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
  129. fileformat.getDataset()->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
  130. fileformat.getDataset()->findAndGetSint32(DCM_EchoNumbers, echoNumber);
  131. fileformat.getDataset()->findAndGetSint32(DCM_TemporalPositionIdentifier, temporalPosition);
  132. MITK_INFO << "Adding new items to database:";
  133. MITK_INFO << "studyID: " << studyID;
  134. MITK_INFO << "seriesInstanceUID: " << seriesInstanceUID;
  135. MITK_INFO << "Patient's Name: " << patientsName;
  136. //-----------------------
  137. //Add Patient to Database
  138. //-----------------------
  139. //Speed up: Check if patient is the same as in last file; very probable, as all images belonging to a study have the same patient
  140. bool patientExists = false;
  141. if(lastPatientID.compare(patientID) || lastPatientsBirthDate.compare(patientsBirthDate) || lastPatientsName.compare(patientsName))
  142. {
  143. //Check if patient is already present in the db
  144. QSqlQuery check_exists_query(database);
  145. std::stringstream check_exists_query_string;
  146. check_exists_query_string << "SELECT * FROM Patients WHERE PatientID = '" << patientID << "'";
  147. check_exists_query.exec(check_exists_query_string.str().c_str());
  148. /// we check only patients with the same PatientID
  149. /// PatientID is not unique in DICOM, so we also compare Name and BirthDate
  150. /// and assume this is sufficient
  151. while (check_exists_query.next())
  152. {
  153. if (
  154. check_exists_query.record().value("PatientsName").toString() == patientsName.c_str() &&
  155. check_exists_query.record().value("PatientsBirthDate").toString() == patientsBirthDate.c_str()
  156. )
  157. {
  158. /// found it
  159. patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
  160. break;
  161. }
  162. }
  163. if(!patientExists)
  164. {
  165. std::stringstream query_string;
  166. query_string << "INSERT INTO Patients VALUES( NULL,'"
  167. << patientsName << "','"
  168. << patientID << "','"
  169. << patientsBirthDate << "','"
  170. << patientsBirthTime << "','"
  171. << patientsSex << "','"
  172. << patientsAge << "','"
  173. << patientComments << "')";
  174. query.exec(query_string.str().c_str());
  175. patientUID = query.lastInsertId().toInt();
  176. MITK_INFO << "New patient inserted: " << patientUID << "\n";
  177. }
  178. }
  179. else
  180. {
  181. patientUID = lastPatientUID;
  182. }
  183. /// keep this for the next image
  184. lastPatientUID = patientUID;
  185. lastPatientID = patientID;
  186. lastPatientsBirthDate = patientsBirthDate;
  187. lastPatientsName = patientsName;
  188. //---------------------
  189. //Add Study to Database
  190. //---------------------
  191. if(lastStudyInstanceUID.compare(studyInstanceUID))
  192. {
  193. QSqlQuery check_exists_query(database);
  194. std::stringstream check_exists_query_string;
  195. check_exists_query_string << "SELECT * FROM Studies WHERE StudyInstanceUID = '" << studyInstanceUID << "'";
  196. check_exists_query.exec(check_exists_query_string.str().c_str());
  197. if(!check_exists_query.next())
  198. {
  199. std::stringstream query_string;
  200. query_string << "INSERT INTO Studies VALUES('"
  201. << studyInstanceUID << "','" << patientUID << "','" << studyID << "','"
  202. << QDate::fromString(studyDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
  203. << studyTime << "','" << accessionNumber << "','" << modalitiesInStudy << "','" << referringPhysician << "','" << performingPhysiciansName << "','" << studyDescription << "')";
  204. query.exec(query_string.str().c_str());
  205. }
  206. }
  207. lastStudyInstanceUID = studyInstanceUID;
  208. //----------------------
  209. //Add Series to Database
  210. //----------------------
  211. if(lastSeriesInstanceUID.compare(seriesInstanceUID))
  212. {
  213. QSqlQuery check_exists_query(database);
  214. std::stringstream check_exists_query_string;
  215. check_exists_query_string << "SELECT * FROM Series WHERE SeriesInstanceUID = '" << seriesInstanceUID << "'";
  216. check_exists_query.exec(check_exists_query_string.str().c_str());
  217. if(!check_exists_query.next())
  218. {
  219. std::stringstream query_string;
  220. query_string << "INSERT INTO Series VALUES('"
  221. << seriesInstanceUID << "','" << studyInstanceUID << "','" << (int) seriesNumber << "','"
  222. << QDate::fromString(seriesDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
  223. << seriesTime << "','" << seriesDescription << "','" << bodyPartExamined << "','"
  224. << frameOfReferenceUID << "','" << (int) acquisitionNumber << "','" << contrastAgent << "','"
  225. << scanningSequence << "','" << (int) echoNumber << "','" << (int) temporalPosition << "')";
  226. query.exec(query_string.str().c_str());
  227. }
  228. }
  229. lastSeriesInstanceUID = seriesInstanceUID;
  230. //----------------------------------
  231. //Move file to destination directory
  232. //----------------------------------
  233. /*
  234. // This depends on Poco and should be converted to Qt code
  235. Poco::File currentFile(filename);
  236. Poco::Path currentFilePath(filename);
  237. MITK_INFO << "currentFilePath.getFileName(): " << currentFilePath.getFileName() << "\n";
  238. if(moveFiles)
  239. {
  240. std::stringstream destDirectoryPath;
  241. if((dest_directory[dest_directory.length()-1] != '/') && (dest_directory[dest_directory.length()-1] != '\\'))
  242. destDirectoryPath << dest_directory << Poco::Path::separator() << seriesInstanceUID.c_str();
  243. else
  244. destDirectoryPath << dest_directory << seriesInstanceUID.c_str();
  245. MITK_INFO << "last symbol: " << dest_directory[dest_directory.length()-1]
  246. << "\ndestDirectoryPath: " << destDirectoryPath.str() << "\n";
  247. Poco::File directory(destDirectoryPath.str());
  248. if (!directory.exists()) directory.createDirectory();
  249. destDirectoryPath << Poco::Path::separator() << currentFilePath.getFileName();
  250. currentFile.moveTo(destDirectoryPath.str());
  251. //for testing only: copy file instead of moving
  252. //currentFile.copyTo(destDirectoryPath.str());
  253. }
  254. */
  255. //------------------------
  256. //Add Filename to Database
  257. //------------------------
  258. // std::stringstream relativeFilePath;
  259. // relativeFilePath << seriesInstanceUID.c_str() << "/" << currentFilePath.getFileName();
  260. QSqlQuery check_exists_query(database);
  261. std::stringstream check_exists_query_string;
  262. // check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << relativeFilePath.str() << "'";
  263. check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << filename << "'";
  264. check_exists_query.exec(check_exists_query_string.str().c_str());
  265. if(!check_exists_query.next())
  266. {
  267. std::stringstream query_string;
  268. //To save absolute path: destDirectoryPath.str()
  269. query_string << "INSERT INTO Images VALUES('"
  270. << /*relativeFilePath.str()*/ filename << "','" << seriesInstanceUID << "','" << QDateTime::currentDateTime().toString(Qt::ISODate).toStdString() << "')";
  271. query.exec(query_string.str().c_str());
  272. }
  273. }
  274. // db.commit();
  275. // db.close();
  276. }
  277. void qCTKDCMTKIndexer::refreshDatabase(QSqlDatabase database, const QString& directoryName)
  278. {
  279. /// get all filenames from the database
  280. QSqlQuery allFilesQuery(database);
  281. QStringList databaseFileNames;
  282. QStringList filesToRemove;
  283. allFilesQuery.exec("SELECT Filename from Images;");
  284. while (allFilesQuery.next())
  285. {
  286. QString fileName = allFilesQuery.value(0).toString();
  287. databaseFileNames.append(fileName);
  288. if (! QFile::exists(fileName) )
  289. {
  290. filesToRemove.append(fileName);
  291. }
  292. }
  293. QSet<QString> filesytemFiles;
  294. QDirIterator dirIt(directoryName);
  295. while (dirIt.hasNext())
  296. {
  297. filesytemFiles.insert(dirIt.next());
  298. }
  299. }