|
@@ -0,0 +1,332 @@
|
|
|
|
+#include "qCTKDCMTKIndexer.h"
|
|
|
|
+
|
|
|
|
+///
|
|
|
|
+/// DCMTK includes
|
|
|
|
+#ifndef WIN32
|
|
|
|
+ #define HAVE_CONFIG_H
|
|
|
|
+#endif
|
|
|
|
+#include "dcmtk/dcmdata/dcfilefo.h"
|
|
|
|
+#include "dcmtk/dcmdata/dcdeftag.h"
|
|
|
|
+#include "dcmtk/dcmdata/dcdatset.h"
|
|
|
|
+#include "dcmtk/ofstd/ofcond.h"
|
|
|
|
+#include "dcmtk/ofstd/ofstring.h"
|
|
|
|
+
|
|
|
|
+#include "dcmtk/ofstd/ofstd.h" /* for class OFStandard */
|
|
|
|
+#include "dcmtk/dcmdata/dcddirif.h" /* for class DicomDirInterface */
|
|
|
|
+
|
|
|
|
+#include <QSqlQuery>
|
|
|
|
+#include <QSqlRecord>
|
|
|
|
+#include <QVariant>
|
|
|
|
+#include <QDate>
|
|
|
|
+
|
|
|
|
+#define MITK_ERROR std::cout
|
|
|
|
+#define MITK_INFO std::cout
|
|
|
|
+
|
|
|
|
+class qCTKDCMTKIndexerPrivate: public qCTKPrivate<qCTKDCMTKIndexer>
|
|
|
|
+{
|
|
|
|
+public:
|
|
|
|
+ qCTKDCMTKIndexerPrivate();
|
|
|
|
+ ~qCTKDCMTKIndexerPrivate();
|
|
|
|
+
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+qCTKDCMTKIndexerPrivate::qCTKDCMTKIndexerPrivate()
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+qCTKDCMTKIndexerPrivate::~qCTKDCMTKIndexerPrivate()
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+qCTKDCMTKIndexer::qCTKDCMTKIndexer()
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+qCTKDCMTKIndexer::~qCTKDCMTKIndexer()
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void qCTKDCMTKIndexer::AddDirectory(QSqlDatabase database, const QString& directoryName)
|
|
|
|
+{
|
|
|
|
+ QSqlDatabase db = database;
|
|
|
|
+ const std::string src_directory(directoryName.toStdString());
|
|
|
|
+ // db.transaction();
|
|
|
|
+
|
|
|
|
+ OFList<OFString> originalDcmtkFileNames;
|
|
|
|
+ OFList<OFString> dcmtkFileNames;
|
|
|
|
+ OFStandard::searchDirectoryRecursively(src_directory.c_str(), originalDcmtkFileNames, NULL, NULL);
|
|
|
|
+
|
|
|
|
+ // hack to reverse list of filenames (not neccessary when image loading works correctly)
|
|
|
|
+ for ( OFListIterator(OFString) iter = originalDcmtkFileNames.begin(); iter != originalDcmtkFileNames.end(); ++iter )
|
|
|
|
+ {
|
|
|
|
+ dcmtkFileNames.push_front( *iter );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DcmFileFormat fileformat;
|
|
|
|
+
|
|
|
|
+ OFListIterator(OFString) iter = dcmtkFileNames.begin();
|
|
|
|
+ OFListIterator(OFString) last = dcmtkFileNames.end();
|
|
|
|
+
|
|
|
|
+ if(iter == last) return;
|
|
|
|
+
|
|
|
|
+ QSqlQuery query(database);
|
|
|
|
+
|
|
|
|
+ /* iterate over all input filenames */
|
|
|
|
+ OFString lastPatientID = "", lastPatientsName = "", lastPatientsBirthDate = "", lastStudyInstanceUID = "", lastSeriesInstanceUID = "";
|
|
|
|
+ int lastPatientUID = -1;
|
|
|
|
+
|
|
|
|
+ while (iter != last)
|
|
|
|
+ {
|
|
|
|
+ std::string filename((*iter).c_str());
|
|
|
|
+ MITK_INFO << filename << "\n";
|
|
|
|
+ OFCondition status = fileformat.loadFile(filename.c_str());
|
|
|
|
+ ++iter;
|
|
|
|
+
|
|
|
|
+ if (!status.good())
|
|
|
|
+ {
|
|
|
|
+ MITK_ERROR << "Could not load " << filename << "\nDCMTK says: " << status.text();
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ OFString patientsName = "", patientID = "", patientsBirthDate = "", patientsBirthTime = "", patientsSex = "",
|
|
|
|
+ patientComments = "", patientsAge = "";
|
|
|
|
+
|
|
|
|
+ OFString studyInstanceUID = "", studyID = "", studyDate = "", studyTime = "",
|
|
|
|
+ accessionNumber = "", modalitiesInStudy = "", performingPhysiciansName = "", referringPhysician = "", studyDescription = "";
|
|
|
|
+
|
|
|
|
+ OFString seriesInstanceUID = "", seriesDate = "", seriesTime = "",
|
|
|
|
+ seriesDescription = "", bodyPartExamined = "", frameOfReferenceUID = "",
|
|
|
|
+ contrastAgent = "", scanningSequence = "";
|
|
|
|
+
|
|
|
|
+ Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
|
|
|
|
+
|
|
|
|
+ //The patient UID is a unique number within the database, generated by the sqlite autoincrement
|
|
|
|
+ int patientUID = -1;
|
|
|
|
+
|
|
|
|
+ //If the following fields can not be evaluated, cancel evaluation of the DICOM file
|
|
|
|
+ if (!fileformat.getDataset()->findAndGetOFString(DCM_PatientsName, patientsName).good())
|
|
|
|
+ {
|
|
|
|
+ MITK_ERROR << "Could not read DCM_PatientsName from " << filename;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!fileformat.getDataset()->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())
|
|
|
|
+ {
|
|
|
|
+ MITK_ERROR << "Could not read DCM_StudyInstanceUID from " << filename;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!fileformat.getDataset()->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())
|
|
|
|
+ {
|
|
|
|
+ MITK_ERROR << "Could not read DCM_SeriesInstanceUID from " << filename;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientID, patientID);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientsSex, patientsSex);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientsAge, patientsAge);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PatientComments, patientComments);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_StudyID, studyID);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_StudyDate, studyDate);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_StudyTime, studyTime);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_PerformingPhysiciansName, performingPhysiciansName);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_ReferringPhysiciansName, referringPhysician);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_StudyDescription, studyDescription);
|
|
|
|
+
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_SeriesDate, seriesDate);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_SeriesTime, seriesTime);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
|
|
|
|
+ fileformat.getDataset()->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
|
|
|
|
+
|
|
|
|
+ fileformat.getDataset()->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
|
|
|
|
+ fileformat.getDataset()->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
|
|
|
|
+ fileformat.getDataset()->findAndGetSint32(DCM_EchoNumbers, echoNumber);
|
|
|
|
+ fileformat.getDataset()->findAndGetSint32(DCM_TemporalPositionIdentifier, temporalPosition);
|
|
|
|
+
|
|
|
|
+ MITK_INFO << "Adding new items to database:";
|
|
|
|
+ MITK_INFO << "studyID: " << studyID;
|
|
|
|
+ MITK_INFO << "seriesInstanceUID: " << seriesInstanceUID;
|
|
|
|
+ MITK_INFO << "Patient's Name: " << patientsName;
|
|
|
|
+
|
|
|
|
+ //-----------------------
|
|
|
|
+ //Add Patient to Database
|
|
|
|
+ //-----------------------
|
|
|
|
+
|
|
|
|
+ //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
|
|
|
|
+ if(lastPatientID.compare(patientID) || lastPatientsBirthDate.compare(patientsBirthDate) || lastPatientsName.compare(patientsName))
|
|
|
|
+ {
|
|
|
|
+ //Check if patient is already present in the db
|
|
|
|
+ QSqlQuery check_exists_query(database);
|
|
|
|
+ std::stringstream check_exists_query_string;
|
|
|
|
+ check_exists_query_string << "SELECT * FROM Patients WHERE PatientID = '" << patientID << "'";
|
|
|
|
+ check_exists_query.exec(check_exists_query_string.str().c_str());
|
|
|
|
+ //check_exists_query_string.flush();
|
|
|
|
+
|
|
|
|
+ bool patientExists = false;
|
|
|
|
+
|
|
|
|
+ while (check_exists_query.next())
|
|
|
|
+ {
|
|
|
|
+ patientExists = true;
|
|
|
|
+ QString checkPatientsName = check_exists_query.value(check_exists_query.record().indexOf("PatientsName")).toString();
|
|
|
|
+ if(checkPatientsName.toStdString().compare(patientsName.c_str())) patientExists = false;
|
|
|
|
+
|
|
|
|
+ QString checkPatientsBirthDate = check_exists_query.value(check_exists_query.record().indexOf("PatientsBirthDate")).toString();
|
|
|
|
+ if(checkPatientsBirthDate.toStdString().compare(patientsBirthDate.c_str())) patientExists = false;
|
|
|
|
+
|
|
|
|
+ if(patientExists)
|
|
|
|
+ {
|
|
|
|
+ patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(!patientExists)
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ std::stringstream query_string;
|
|
|
|
+
|
|
|
|
+ query_string << "INSERT INTO Patients VALUES( NULL,'" << patientsName << "','" << patientID << "','" << patientsBirthDate << "','"
|
|
|
|
+ << patientsBirthTime << "','" << patientsSex << "','" << patientsAge << "','" << patientComments << "')";
|
|
|
|
+
|
|
|
|
+ query.exec(query_string.str().c_str());
|
|
|
|
+
|
|
|
|
+ patientUID = query.lastInsertId().toInt();
|
|
|
|
+ MITK_INFO << "Query result: " << patientUID << "\n";
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else patientUID = lastPatientUID;
|
|
|
|
+
|
|
|
|
+ lastPatientUID = patientUID;
|
|
|
|
+ lastPatientID = patientID;
|
|
|
|
+ lastPatientsBirthDate = patientsBirthDate;
|
|
|
|
+ lastPatientsName = patientsName;
|
|
|
|
+
|
|
|
|
+ //---------------------
|
|
|
|
+ //Add Study to Database
|
|
|
|
+ //---------------------
|
|
|
|
+
|
|
|
|
+ if(lastStudyInstanceUID.compare(studyInstanceUID))
|
|
|
|
+ {
|
|
|
|
+ QSqlQuery check_exists_query(database);
|
|
|
|
+ std::stringstream check_exists_query_string;
|
|
|
|
+ check_exists_query_string << "SELECT * FROM Studies WHERE StudyInstanceUID = '" << studyInstanceUID << "'";
|
|
|
|
+ check_exists_query.exec(check_exists_query_string.str().c_str());
|
|
|
|
+
|
|
|
|
+ if(!check_exists_query.next())
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ std::stringstream query_string;
|
|
|
|
+
|
|
|
|
+ query_string << "INSERT INTO Studies VALUES('"
|
|
|
|
+ << studyInstanceUID << "','" << patientUID << "','" << studyID << "','"
|
|
|
|
+ << QDate::fromString(studyDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
|
|
|
|
+ << studyTime << "','" << accessionNumber << "','" << modalitiesInStudy << "','" << referringPhysician << "','" << performingPhysiciansName << "','" << studyDescription << "')";
|
|
|
|
+
|
|
|
|
+ query.exec(query_string.str().c_str());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lastStudyInstanceUID = studyInstanceUID;
|
|
|
|
+
|
|
|
|
+ //----------------------
|
|
|
|
+ //Add Series to Database
|
|
|
|
+ //----------------------
|
|
|
|
+
|
|
|
|
+ if(lastSeriesInstanceUID.compare(seriesInstanceUID))
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ QSqlQuery check_exists_query(database);
|
|
|
|
+ std::stringstream check_exists_query_string;
|
|
|
|
+ check_exists_query_string << "SELECT * FROM Series WHERE SeriesInstanceUID = '" << seriesInstanceUID << "'";
|
|
|
|
+ check_exists_query.exec(check_exists_query_string.str().c_str());
|
|
|
|
+
|
|
|
|
+ if(!check_exists_query.next())
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ std::stringstream query_string;
|
|
|
|
+
|
|
|
|
+ query_string << "INSERT INTO Series VALUES('"
|
|
|
|
+ << seriesInstanceUID << "','" << studyInstanceUID << "','" << (int) seriesNumber << "','"
|
|
|
|
+ << QDate::fromString(seriesDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
|
|
|
|
+ << seriesTime << "','" << seriesDescription << "','" << bodyPartExamined << "','"
|
|
|
|
+ << frameOfReferenceUID << "','" << (int) acquisitionNumber << "','" << contrastAgent << "','"
|
|
|
|
+ << scanningSequence << "','" << (int) echoNumber << "','" << (int) temporalPosition << "')";
|
|
|
|
+
|
|
|
|
+ query.exec(query_string.str().c_str());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ lastSeriesInstanceUID = seriesInstanceUID;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ //----------------------------------
|
|
|
|
+ //Move file to destination directory
|
|
|
|
+ //----------------------------------
|
|
|
|
+ /*
|
|
|
|
+ // This depends on Poco and should be converted to Qt code
|
|
|
|
+
|
|
|
|
+ Poco::File currentFile(filename);
|
|
|
|
+ Poco::Path currentFilePath(filename);
|
|
|
|
+ MITK_INFO << "currentFilePath.getFileName(): " << currentFilePath.getFileName() << "\n";
|
|
|
|
+
|
|
|
|
+ if(moveFiles)
|
|
|
|
+ {
|
|
|
|
+ std::stringstream destDirectoryPath;
|
|
|
|
+ if((dest_directory[dest_directory.length()-1] != '/') && (dest_directory[dest_directory.length()-1] != '\\'))
|
|
|
|
+ destDirectoryPath << dest_directory << Poco::Path::separator() << seriesInstanceUID.c_str();
|
|
|
|
+ else
|
|
|
|
+ destDirectoryPath << dest_directory << seriesInstanceUID.c_str();
|
|
|
|
+
|
|
|
|
+ MITK_INFO << "last symbol: " << dest_directory[dest_directory.length()-1]
|
|
|
|
+ << "\ndestDirectoryPath: " << destDirectoryPath.str() << "\n";
|
|
|
|
+
|
|
|
|
+ Poco::File directory(destDirectoryPath.str());
|
|
|
|
+
|
|
|
|
+ if (!directory.exists()) directory.createDirectory();
|
|
|
|
+
|
|
|
|
+ destDirectoryPath << Poco::Path::separator() << currentFilePath.getFileName();
|
|
|
|
+
|
|
|
|
+ currentFile.moveTo(destDirectoryPath.str());
|
|
|
|
+ //for testing only: copy file instead of moving
|
|
|
|
+ //currentFile.copyTo(destDirectoryPath.str());
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+ //------------------------
|
|
|
|
+ //Add Filename to Database
|
|
|
|
+ //------------------------
|
|
|
|
+
|
|
|
|
+// std::stringstream relativeFilePath;
|
|
|
|
+// relativeFilePath << seriesInstanceUID.c_str() << "/" << currentFilePath.getFileName();
|
|
|
|
+
|
|
|
|
+ QSqlQuery check_exists_query(database);
|
|
|
|
+ std::stringstream check_exists_query_string;
|
|
|
|
+// check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << relativeFilePath.str() << "'";
|
|
|
|
+ check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << filename << "'";
|
|
|
|
+ check_exists_query.exec(check_exists_query_string.str().c_str());
|
|
|
|
+
|
|
|
|
+ if(!check_exists_query.next())
|
|
|
|
+ {
|
|
|
|
+ std::stringstream query_string;
|
|
|
|
+
|
|
|
|
+ //To save absolute path: destDirectoryPath.str()
|
|
|
|
+ query_string << "INSERT INTO Images VALUES('"
|
|
|
|
+ << /*relativeFilePath.str()*/ filename << "','" << seriesInstanceUID << "')";
|
|
|
|
+
|
|
|
|
+ query.exec(query_string.str().c_str());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // db.commit();
|
|
|
|
+ // db.close();
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|