| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 | // Qt includes#include <QSqlQuery>#include <QSqlRecord>#include <QVariant>#include <QDate>#include <QStringList>#include <QSet>#include <QFile>#include <QDirIterator>#include <QFileInfo>#include <QDebug>// CTKDCMTK includes#include "qCTKDCMTKIndexer.h"// DCMTK includes#ifndef WIN32  #define HAVE_CONFIG_H #endif#include <dcmtk/dcmdata/dcfilefo.h>#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 */#define MITK_ERROR std::cout#define MITK_INFO std::cout//------------------------------------------------------------------------------class qCTKDCMTKIndexerPrivate: public qCTKPrivate<qCTKDCMTKIndexer>{public:  qCTKDCMTKIndexerPrivate();  ~qCTKDCMTKIndexerPrivate();};//------------------------------------------------------------------------------// qCTKDCMTKIndexerPrivate methods//------------------------------------------------------------------------------qCTKDCMTKIndexerPrivate::qCTKDCMTKIndexerPrivate(){}//------------------------------------------------------------------------------qCTKDCMTKIndexerPrivate::~qCTKDCMTKIndexerPrivate(){}//------------------------------------------------------------------------------// qCTKDCMTKIndexer methods//------------------------------------------------------------------------------qCTKDCMTKIndexer::qCTKDCMTKIndexer(){}//------------------------------------------------------------------------------qCTKDCMTKIndexer::~qCTKDCMTKIndexer(){}//------------------------------------------------------------------------------void qCTKDCMTKIndexer::addDirectory(QSqlDatabase database, const QString& directoryName,const QString& destinationDirectoryName)  {  QSqlDatabase db = database;  const std::string src_directory(directoryName.toStdString());  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);  /// these are for optimizing the import of image sequences  /// since most information are identical for all slices  OFString lastPatientID = "";  OFString lastPatientsName = "";  OFString lastPatientsBirthDate = "";  OFString lastStudyInstanceUID = "";  OFString lastSeriesInstanceUID = "";  int lastPatientUID = -1;  /* iterate over all input filenames */  while (iter != last)  {    std::string filename((*iter).c_str());    QString qfilename(filename.c_str());     /// first we check if the file is already in the database    QSqlQuery fileExists(database);    fileExists.prepare("SELECT InsertTimestamp FROM Images WHERE Filename == ?");     fileExists.bindValue(0,qfilename);    fileExists.exec();    if (      fileExists.next() &&       QFileInfo(qfilename).lastModified() < QDateTime::fromString(fileExists.value(0).toString(),Qt::ISODate)            )      {      MITK_INFO << "File " << filename << " already added.";      continue;      }    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, institutionName, performingPhysiciansName, referringPhysician, studyDescription;    OFString seriesInstanceUID, seriesDate, seriesTime,      seriesDescription, bodyPartExamined, frameOfReferenceUID,      contrastAgent, scanningSequence;    OFString instanceNumber;    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;    }    if (!fileformat.getDataset()->findAndGetOFString(DCM_InstanceNumber, instanceNumber).good())    {      MITK_ERROR << "Could not read DCM_InstanceNumber 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_InstitutionName, institutionName);    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    bool patientExists = false;    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());      /// we check only patients with the same PatientID      /// PatientID is not unique in DICOM, so we also compare Name and BirthDate      /// and assume this is sufficient      while (check_exists_query.next())      {        if (            check_exists_query.record().value("PatientsName").toString() == patientsName.c_str() &&            check_exists_query.record().value("PatientsBirthDate").toString() == patientsBirthDate.c_str()           )        {          /// found it          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 << "New patient inserted: " << patientUID << "\n";      }    }    else       {      patientUID = lastPatientUID;      }             /// keep this for the next image    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 << "','"           << institutionName << "','"           << 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    //----------------------------------    if (!destinationDirectoryName.isEmpty())      {      QFile currentFile( qfilename );      QDir destinationDir(destinationDirectoryName);      QString uniqueDirName = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();      qDebug() << "MKPath: " << uniqueDirName;      destinationDir.mkpath(uniqueDirName);      QString destFileName = destinationDir.absolutePath().append("/").append(instanceNumber.c_str());      qDebug() << "Copy: " << qfilename << " -> " << destFileName;      currentFile.copy(destFileName);      //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 << "','" << QDateTime::currentDateTime().toString(Qt::ISODate).toStdString() << "')";      query.exec(query_string.str().c_str());    }  }  // db.commit();  // db.close();}//------------------------------------------------------------------------------void qCTKDCMTKIndexer::refreshDatabase(QSqlDatabase database, const QString& directoryName){  /// get all filenames from the database  QSqlQuery allFilesQuery(database);  QStringList databaseFileNames;  QStringList filesToRemove;  allFilesQuery.exec("SELECT Filename from Images;");  while (allFilesQuery.next())    {    QString fileName = allFilesQuery.value(0).toString();    databaseFileNames.append(fileName);    if (! QFile::exists(fileName) )       {      filesToRemove.append(fileName);      }    }  QSet<QString> filesytemFiles;  QDirIterator dirIt(directoryName);  while (dirIt.hasNext())     {    filesytemFiles.insert(dirIt.next());    }}
 |