Преглед на файлове

Refactor ctkDICOMIndexer to support individual file indexing

Before, directory tree traversal and dicom file indexing
were in the same method, now the two are independent.

Also added some signals so code can monitor what's going one
(e.g. for progress dialogs).

Also switched to use logger.
Steve Pieper преди 13 години
родител
ревизия
274486f8ae
променени са 2 файла, в които са добавени 335 реда и са изтрити 284 реда
  1. 317 284
      Libs/DICOM/Core/ctkDICOMIndexer.cpp
  2. 18 0
      Libs/DICOM/Core/ctkDICOMIndexer.h

+ 317 - 284
Libs/DICOM/Core/ctkDICOMIndexer.cpp

@@ -33,6 +33,7 @@
 
 
 // ctkDICOM includes
+#include "ctkLogger.h"
 #include "ctkDICOMIndexer.h"
 #include "ctkDICOMAbstractThumbnailGenerator.h"
 
@@ -49,8 +50,9 @@
 #include <dcmtk/dcmimage/diregist.h>  /* include support for color images */
 
 
-#define MITK_ERROR std::cout
-#define MITK_INFO std::cout
+//------------------------------------------------------------------------------
+static ctkLogger logger("org.commontk.dicom.DICOMIndexer" );
+//------------------------------------------------------------------------------
 
 //------------------------------------------------------------------------------
 class ctkDICOMIndexerPrivate
@@ -60,6 +62,15 @@ public:
   ~ctkDICOMIndexerPrivate();
 
   ctkDICOMAbstractThumbnailGenerator* thumbnailGenerator;
+
+  /// 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;
 };
 
 //------------------------------------------------------------------------------
@@ -69,6 +80,13 @@ public:
 ctkDICOMIndexerPrivate::ctkDICOMIndexerPrivate()
 {
     this->thumbnailGenerator = NULL;
+
+    this->lastPatientID = "";
+    this->lastPatientsName = "";
+    this->lastPatientsBirthDate = "";
+    this->lastStudyInstanceUID = "";
+    this->lastSeriesInstanceUID = "";
+    this->lastPatientUID = -1;
 }
 
 //------------------------------------------------------------------------------
@@ -93,364 +111,374 @@ ctkDICOMIndexer::~ctkDICOMIndexer()
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& database, 
-                                   const QString& directoryName,
+void ctkDICOMIndexer::addFile(ctkDICOMDatabase& ctkDICOMDatabase, 
+                                   const QString& filePath,
                                    const QString& destinationDirectoryName,
                                    bool createHierarchy,
                                    bool createThumbnails)
 {
   Q_D(ctkDICOMIndexer);
 
-  QSqlDatabase db = database.database();
-  const std::string src_directory(directoryName.toStdString());
-
-  OFList<OFString> originalDcmtkFileNames;
-  OFList<OFString> dcmtkFileNames;
-  OFStandard::searchDirectoryRecursively( QDir::toNativeSeparators(src_directory.c_str()).toAscii().data(), originalDcmtkFileNames, "", "");
-
-  // 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 );
-  }
+  logger.setDebug();
 
   DcmFileFormat fileformat;
   DcmDataset *dataset;
 
-  OFListIterator(OFString) iter = dcmtkFileNames.begin();
-  OFListIterator(OFString) last = dcmtkFileNames.end();
+  QSqlQuery query(ctkDICOMDatabase.database());
 
-  if(iter == last) return;
+  std::string filename = filePath.toStdString();
 
-  QSqlQuery query(database.database());
+  emit indexingFilePath(filePath);
 
+  /// first we check if the file is already in the database
+  QSqlQuery fileExists(ctkDICOMDatabase.database());
+  fileExists.prepare("SELECT InsertTimestamp FROM Images WHERE Filename == ?");
+  fileExists.bindValue(0,filePath);
+  fileExists.exec();
+  if (
+    fileExists.next() &&
+    QFileInfo(filePath).lastModified() < QDateTime::fromString(fileExists.value(0).toString(),Qt::ISODate)
+    )
+    {
+    logger.debug( "File " + filePath + " already added.");
+    return;
+    }
 
-  /// 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.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;
-      }
+  logger.debug( "Processing " + filePath ); 
+  OFCondition status = fileformat.loadFile(filename.c_str());
 
-    MITK_INFO << filename << "\n";
-    OFCondition status = fileformat.loadFile(filename.c_str());
-    ++iter;
+  dataset = fileformat.getDataset();
 
-    dataset = fileformat.getDataset();
+  if (!status.good())
+  {
+    logger.error( "Could not load " + filePath );
+    logger.error( "DCMTK says: " + QString(status.text()) );
+    return;
+  }
 
-    if (!status.good())
-    {
-      MITK_ERROR << "Could not load " << filename << "\nDCMTK says: " << status.text();
-      continue;
-    }
+  OFString patientsName, patientID, patientsBirthDate, patientsBirthTime, patientsSex,
+    patientComments, patientsAge;
 
-    OFString patientsName, patientID, patientsBirthDate, patientsBirthTime, patientsSex,
-      patientComments, patientsAge;
+  OFString studyInstanceUID, studyID, studyDate, studyTime,
+    accessionNumber, modalitiesInStudy, institutionName, performingPhysiciansName, referringPhysician, studyDescription;
 
-    OFString studyInstanceUID, studyID, studyDate, studyTime,
-      accessionNumber, modalitiesInStudy, institutionName, performingPhysiciansName, referringPhysician, studyDescription;
+  OFString seriesInstanceUID, seriesDate, seriesTime,
+    seriesDescription, bodyPartExamined, frameOfReferenceUID,
+    contrastAgent, scanningSequence;
+  OFString instanceNumber, sopInstanceUID ;
 
-    OFString seriesInstanceUID, seriesDate, seriesTime,
-      seriesDescription, bodyPartExamined, frameOfReferenceUID,
-      contrastAgent, scanningSequence;
-    OFString instanceNumber, sopInstanceUID ;
+  Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
 
-    Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
+  //The patient UID is a unique number within the database, generated by the sqlite autoincrement
+  //Thus, this is _not_ the DICOM Patient ID.
+  int patientUID = -1;
 
-    //The patient UID is a unique number within the database, generated by the sqlite autoincrement
-    //Thus, this is _not_ the DICOM Patient ID.
-    int patientUID = -1;
+  //If the following fields can not be evaluated, cancel evaluation of the DICOM file
+  if (!dataset->findAndGetOFString(DCM_PatientName, patientsName).good())
+  {
+    logger.error( "Could not read DCM_PatientName from " + filePath );
+    return;
+  }
 
-    //If the following fields can not be evaluated, cancel evaluation of the DICOM file
-    if (!dataset->findAndGetOFString(DCM_PatientName, patientsName).good())
-    {
-      MITK_ERROR << "Could not read DCM_PatientName from " << filename;
-      continue;
-    }
+  if (!dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())
+  {
+    logger.error( "Could not read DCM_StudyInstanceUID from " + filePath );
+    return;
+  }
 
-    if (!dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())
-    {
-      MITK_ERROR << "Could not read DCM_StudyInstanceUID from " << filename;
-      continue;
-    }
+  if (!dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())
+  {
+    logger.error( "Could not read DCM_SeriesInstanceUID from " + filePath );
+    return;
+  }
 
-    if (!dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())
-    {
-      MITK_ERROR << "Could not read DCM_SeriesInstanceUID from " << filename;
-      continue;
-    }
+  if (!dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID).good())
+  {
+    logger.error( "Could not read DCM_SOPInstanceUID from " + filePath );
+    return;
+  }
+  if (!dataset->findAndGetOFString(DCM_InstanceNumber, instanceNumber).good())
+  {
+    logger.error( "Could not read DCM_InstanceNumber from " + filePath );
+    return;
+  }
 
-    if (!dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID).good())
-    {
-      MITK_ERROR << "Could not read DCM_SOPInstanceUID from " << filename;
-      continue;
-    }
-    if (!dataset->findAndGetOFString(DCM_InstanceNumber, instanceNumber).good())
-    {
-      MITK_ERROR << "Could not read DCM_InstanceNumber from " << filename;
-      continue;
-    }
 
+  dataset->findAndGetOFString(DCM_PatientID, patientID);
+  dataset->findAndGetOFString(DCM_PatientBirthDate, patientsBirthDate);
+  dataset->findAndGetOFString(DCM_PatientBirthTime, patientsBirthTime);
+  dataset->findAndGetOFString(DCM_PatientSex, patientsSex);
+  dataset->findAndGetOFString(DCM_PatientAge, patientsAge);
+  dataset->findAndGetOFString(DCM_PatientComments, patientComments);
+  dataset->findAndGetOFString(DCM_StudyID, studyID);
+  dataset->findAndGetOFString(DCM_StudyDate, studyDate);
+  dataset->findAndGetOFString(DCM_StudyTime, studyTime);
+  dataset->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
+  dataset->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
+  dataset->findAndGetOFString(DCM_InstitutionName, institutionName);
+  dataset->findAndGetOFString(DCM_PerformingPhysicianName, performingPhysiciansName);
+  dataset->findAndGetOFString(DCM_ReferringPhysicianName, referringPhysician);
+  dataset->findAndGetOFString(DCM_StudyDescription, studyDescription);
+
+  dataset->findAndGetOFString(DCM_SeriesDate, seriesDate);
+  dataset->findAndGetOFString(DCM_SeriesTime, seriesTime);
+  dataset->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
+  dataset->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
+  dataset->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
+  dataset->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
+  dataset->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
+
+  dataset->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
+  dataset->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
+  dataset->findAndGetSint32(DCM_EchoNumbers, echoNumber);
+  dataset->findAndGetSint32(DCM_TemporalPositionIdentifier, temporalPosition);
+
+  logger.debug( "Adding new items to database:" );
+  logger.debug( "studyID: " + QString(studyID.c_str()) );
+  logger.debug( "seriesInstanceUID: " + QString(seriesInstanceUID.c_str()) );
+  logger.debug( "Patient's Name: " + QString(patientsName.c_str()) );
+
+  //-----------------------
+  //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(d->lastPatientID.compare(patientID) || d->lastPatientsBirthDate.compare(patientsBirthDate) || d->lastPatientsName.compare(patientsName))
+  {
+    //Check if patient is already present in the db
+    QSqlQuery check_exists_query(ctkDICOMDatabase.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());
 
-    dataset->findAndGetOFString(DCM_PatientID, patientID);
-    dataset->findAndGetOFString(DCM_PatientBirthDate, patientsBirthDate);
-    dataset->findAndGetOFString(DCM_PatientBirthTime, patientsBirthTime);
-    dataset->findAndGetOFString(DCM_PatientSex, patientsSex);
-    dataset->findAndGetOFString(DCM_PatientAge, patientsAge);
-    dataset->findAndGetOFString(DCM_PatientComments, patientComments);
-    dataset->findAndGetOFString(DCM_StudyID, studyID);
-    dataset->findAndGetOFString(DCM_StudyDate, studyDate);
-    dataset->findAndGetOFString(DCM_StudyTime, studyTime);
-    dataset->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
-    dataset->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
-    dataset->findAndGetOFString(DCM_InstitutionName, institutionName);
-    dataset->findAndGetOFString(DCM_PerformingPhysicianName, performingPhysiciansName);
-    dataset->findAndGetOFString(DCM_ReferringPhysicianName, referringPhysician);
-    dataset->findAndGetOFString(DCM_StudyDescription, studyDescription);
-
-    dataset->findAndGetOFString(DCM_SeriesDate, seriesDate);
-    dataset->findAndGetOFString(DCM_SeriesTime, seriesTime);
-    dataset->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
-    dataset->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
-    dataset->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
-    dataset->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
-    dataset->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
-
-    dataset->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
-    dataset->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
-    dataset->findAndGetSint32(DCM_EchoNumbers, echoNumber);
-    dataset->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))
+    /// 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())
     {
-      //Check if patient is already present in the db
-      QSqlQuery check_exists_query(database.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()
+         )
       {
-        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();
-          patientExists = true;
-          break;
-        }
+        /// found it
+        patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
+        patientExists = true;
+        break;
       }
+    }
 
-      if(!patientExists)
-      {
+    if(!patientExists)
+    {
 
-        std::stringstream query_string;
+      std::stringstream query_string;
 
-        query_string << "INSERT INTO Patients VALUES( NULL,'"
-        << patientsName << "','"
-        << patientID << "','"
-        << patientsBirthDate << "','"
-        << patientsBirthTime << "','"
-        << patientsSex << "','"
-        << patientsAge << "','"
-        << patientComments << "')";
+      query_string << "INSERT INTO Patients VALUES( NULL,'"
+      << patientsName << "','"
+      << patientID << "','"
+      << patientsBirthDate << "','"
+      << patientsBirthTime << "','"
+      << patientsSex << "','"
+      << patientsAge << "','"
+      << patientComments << "')";
 
-        query.exec(query_string.str().c_str());
+      query.exec(query_string.str().c_str());
 
-        patientUID = query.lastInsertId().toInt();
-        MITK_INFO << "New patient inserted: " << patientUID << "\n";
-      }
+      patientUID = query.lastInsertId().toInt();
+      QString patientUIDQString;
+      patientUIDQString.setNum(patientUID);
+      logger.debug( "New patient inserted: " + patientUIDQString );
+    }
+  }
+  else
+    {
+    patientUID = d->lastPatientUID;
     }
-    else
-      {
-      patientUID = lastPatientUID;
-      }
 
-    /// keep this for the next image
-    lastPatientUID = patientUID;
-    lastPatientID = patientID;
-    lastPatientsBirthDate = patientsBirthDate;
-    lastPatientsName = patientsName;
+  /// keep this for the next image
+  d->lastPatientUID = patientUID;
+  d->lastPatientID = patientID;
+  d->lastPatientsBirthDate = patientsBirthDate;
+  d->lastPatientsName = patientsName;
 
-    //---------------------
-    //Add Study to Database
-    //---------------------
+  //---------------------
+  //Add Study to Database
+  //---------------------
 
-    if(lastStudyInstanceUID.compare(studyInstanceUID))
+  if(d->lastStudyInstanceUID.compare(studyInstanceUID))
+  {
+    QSqlQuery check_exists_query(ctkDICOMDatabase.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())
     {
-      QSqlQuery check_exists_query(database.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;
 
-        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());
-      }
+      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());
     }
+  }
+
+  d->lastStudyInstanceUID = studyInstanceUID;
 
-    lastStudyInstanceUID = studyInstanceUID;
+  //----------------------
+  //Add Series to Database
+  //----------------------
 
-    //----------------------
-    //Add Series to Database
-    //----------------------
+  if(d->lastSeriesInstanceUID.compare(seriesInstanceUID))
+  {
 
-    if(lastSeriesInstanceUID.compare(seriesInstanceUID))
+    QSqlQuery check_exists_query(ctkDICOMDatabase.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())
     {
 
-      QSqlQuery check_exists_query(database.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());
+      std::stringstream query_string;
 
-      if(!check_exists_query.next())
-      {
+      query_string << "INSERT INTO Series VALUES('"
+        << seriesInstanceUID << "','"
+        << studyInstanceUID << "','"
+        << static_cast<int>(seriesNumber) << "','"
+        << QDate::fromString(seriesDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
+        << seriesTime << "','"
+        << seriesDescription << "','"
+        << bodyPartExamined << "','"
+        << frameOfReferenceUID << "','"
+        << static_cast<int>(acquisitionNumber) << "','"
+        << contrastAgent << "','"
+        << scanningSequence << "','"
+        << static_cast<int>(echoNumber) << "','"
+        << static_cast<int>(temporalPosition) << "')";
 
-        std::stringstream query_string;
-
-        query_string << "INSERT INTO Series VALUES('"
-          << seriesInstanceUID << "','"
-          << studyInstanceUID << "','"
-          << static_cast<int>(seriesNumber) << "','"
-          << QDate::fromString(seriesDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
-          << seriesTime << "','"
-          << seriesDescription << "','"
-          << bodyPartExamined << "','"
-          << frameOfReferenceUID << "','"
-          << static_cast<int>(acquisitionNumber) << "','"
-          << contrastAgent << "','"
-          << scanningSequence << "','"
-          << static_cast<int>(echoNumber) << "','"
-          << static_cast<int>(temporalPosition) << "')";
-
-        query.exec(query_string.str().c_str());
-      }
+      query.exec(query_string.str().c_str());
     }
+  }
 
-    lastSeriesInstanceUID = seriesInstanceUID;
+  d->lastSeriesInstanceUID = seriesInstanceUID;
 
-    QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
+  QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
 
-    //----------------------------------
-    //Move file to destination directory
-    //----------------------------------
+  //----------------------------------
+  //Move file to destination directory
+  //----------------------------------
 
-    if (!destinationDirectoryName.isEmpty())
-      {
-      QFile currentFile( qfilename );
-      QDir destinationDir(destinationDirectoryName + "/dicom");
-      QString destFileName = sopInstanceUID.c_str();
-      if (createHierarchy)
-      {
-        destinationDir.mkpath(studySeriesDirectory);
-        destFileName.prepend( destinationDir.absolutePath() + "/"  + studySeriesDirectory + "/" );
-      }
-      currentFile.copy(destFileName);
-      qfilename = destFileName;
+  QString finalFilePath(filePath);
+  if (!destinationDirectoryName.isEmpty())
+  {
+    QFile currentFile( filePath );
+    QDir destinationDir(destinationDirectoryName + "/dicom");
+    QString finalFilePath = sopInstanceUID.c_str();
+    if (createHierarchy)
+    {
+      destinationDir.mkpath(studySeriesDirectory);
+      finalFilePath.prepend( destinationDir.absolutePath() + "/"  + studySeriesDirectory + "/" );
     }
+    currentFile.copy(finalFilePath);
+  }
 
-    if (createThumbnails)
+  if (createThumbnails)
+  {
+    if(d->thumbnailGenerator)
     {
-        if(d->thumbnailGenerator){
-          QString thumbnailBaseDir =  database.databaseDirectory() + "/thumbs/";
-          QString thumbnailFilename = thumbnailBaseDir + "/" + database.pathForDataset(dataset) + ".png";
-          QFileInfo thumbnailInfo(thumbnailFilename);
-          if ( ! ( thumbnailInfo.exists() && thumbnailInfo.lastModified() < QFileInfo(qfilename).lastModified() ) )
-          {
-            QDir(thumbnailBaseDir).mkpath(studySeriesDirectory);
-            DicomImage dcmtkImage(QDir::toNativeSeparators(qfilename).toStdString().c_str());
-            d->thumbnailGenerator->generateThumbnail(&dcmtkImage, thumbnailFilename);
-          }
-        }
+      QString thumbnailBaseDir =  ctkDICOMDatabase.databaseDirectory() + "/thumbs/";
+      QString thumbnailFilename = thumbnailBaseDir + "/" + ctkDICOMDatabase.pathForDataset(dataset) + ".png";
+      QFileInfo thumbnailInfo(thumbnailFilename);
+      if ( ! ( thumbnailInfo.exists() && thumbnailInfo.lastModified() < QFileInfo(finalFilePath).lastModified() ) )
+      {
+        QDir(thumbnailBaseDir).mkpath(studySeriesDirectory);
+        DicomImage dcmtkImage(QDir::toNativeSeparators(finalFilePath).toStdString().c_str());
+        d->thumbnailGenerator->generateThumbnail(&dcmtkImage, thumbnailFilename);
+      }
     }
-    //------------------------
-    //Add Filename to Database
-    //------------------------
+  }
+
+  //------------------------
+  //Add Filename to Database
+  //------------------------
 
 //    std::stringstream relativeFilePath;
 //    relativeFilePath << seriesInstanceUID.c_str() << "/" << currentFilePath.getFileName();
 
-    QSqlQuery check_exists_query(database.database());
-    std::stringstream check_exists_query_string;
+  QSqlQuery check_exists_query(ctkDICOMDatabase.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 SOPInstanceUID = '" << sopInstanceUID << "'";
-    check_exists_query.exec(check_exists_query_string.str().c_str());
+  check_exists_query_string << "SELECT * FROM Images WHERE SOPInstanceUID = '" << sopInstanceUID << "'";
+  check_exists_query.exec(check_exists_query_string.str().c_str());
 
-    if(!check_exists_query.next())
-    {
-      std::stringstream query_string;
+  if(!check_exists_query.next())
+  {
+    std::stringstream query_string;
 
-      //To save absolute path: destDirectoryPath.str()
-      query_string << "INSERT INTO Images VALUES('"
-        << sopInstanceUID << "','" << qfilename.toStdString() << "','" << seriesInstanceUID << "','" << QDateTime::currentDateTime().toString(Qt::ISODate).toStdString() << "')";
+    //To save absolute path: destDirectoryPath.str()
+    query_string << "INSERT INTO Images VALUES('"
+      << sopInstanceUID << "','" << finalFilePath.toStdString() << "','" << seriesInstanceUID << "','" << QDateTime::currentDateTime().toString(Qt::ISODate).toStdString() << "')";
 
-      query.exec(query_string.str().c_str());
-    }
+    query.exec(query_string.str().c_str());
   }
 
-  // db.commit();
-  // db.close();
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMIndexer::addDirectory(ctkDICOMDatabase& ctkDICOMDatabase, 
+                                   const QString& directoryName,
+                                   const QString& destinationDirectoryName,
+                                   bool createHierarchy,
+                                   bool createThumbnails)
+{
+  const std::string src_directory(directoryName.toStdString());
+
+  OFList<OFString> originalDcmtkFileNames;
+  OFList<OFString> dcmtkFileNames;
+  OFStandard::searchDirectoryRecursively( QDir::toNativeSeparators(src_directory.c_str()).toAscii().data(), originalDcmtkFileNames, "", "");
 
+  // 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 );
+  }
+
+  OFListIterator(OFString) iter = dcmtkFileNames.begin();
+  OFListIterator(OFString) last = dcmtkFileNames.end();
+
+  if(iter == last) return;
+
+  emit foundFilesToIndex(dcmtkFileNames.size());
+
+  /* iterate over all input filenames */
+  int fileNumber = 0;
+  while (iter != last)
+  {
+    emit indexingFileNumber(++fileNumber);
+    QString filePath((*iter).c_str());
+    this->addFile(ctkDICOMDatabase, filePath, destinationDirectoryName, createHierarchy, createThumbnails);
+    ++iter;
+  }
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& database, const QString& directoryName)
+void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& ctkDICOMDatabase, const QString& directoryName)
 {
   /// get all filenames from the database
-  QSqlQuery allFilesQuery(database.database());
+  QSqlQuery allFilesQuery(ctkDICOMDatabase.database());
   QStringList databaseFileNames;
   QStringList filesToRemove;
   allFilesQuery.exec("SELECT Filename from Images;");
@@ -471,6 +499,11 @@ void ctkDICOMIndexer::refreshDatabase(ctkDICOMDatabase& database, const QString&
     {
     filesytemFiles.insert(dirIt.next());
     }
+  
+  // TODO: it looks like this function was never finished...
+  // 
+  // I guess the next step is to remove all filesToRemove from the database
+  // and also to add filesystemFiles into the database tables
 }
 
 //------------------------------------------------------------------------------

+ 18 - 0
Libs/DICOM/Core/ctkDICOMIndexer.h

@@ -51,6 +51,18 @@ public:
   Q_INVOKABLE void addDirectory(ctkDICOMDatabase& database, const QString& directoryName,
                     const QString& destinationDirectoryName = "",
                     bool createHierarchy = true, bool createThumbnails = true);
+
+  /**
+      \brief Adds a file to database and optionally copies the file to
+      destinationDirectory.
+ 
+      Scan the file using Dcmtk and populate the database with all the
+      DICOM fields accordingly.
+  */
+  Q_INVOKABLE void addFile(ctkDICOMDatabase& database, const QString& filePath,
+                    const QString& destinationDirectoryName = "",
+                    bool createHierarchy = true, bool createThumbnails = true);
+
   Q_INVOKABLE void refreshDatabase(ctkDICOMDatabase& database, const QString& directoryName);
 
   ///
@@ -59,6 +71,12 @@ public:
   ///
   /// get thumbnail genrator object
   ctkDICOMAbstractThumbnailGenerator* thumbnailGenerator();
+
+signals:
+  void foundFilesToIndex(int);
+  void indexingFileNumber(int);
+  void indexingFilePath(QString);
+
 protected:
   QScopedPointer<ctkDICOMIndexerPrivate> d_ptr;