Pārlūkot izejas kodu

Working DICOM Query!

Created ctkDICOMIndexerBase (more accurately, *debugged* ctkDICOMIndexerBase) to abstract the process of inserting new DICOM files into the SQLLite database.  ctkDICOMQuery is now a subclass of ctkDICOMIndexerBase.  Next step is to migrate ctkDICOMIndexer to be a subclass as well.
Daniel Blezek 15 gadi atpakaļ
vecāks
revīzija
145560c290

+ 12 - 5
Applications/ctkDICOMQuery/ctkDICOMQuery.cpp

@@ -49,11 +49,6 @@ int main(int argc, char** argv)
   ctkLogger logger ( "org.commontk.core.Logger" );
   logger.setDebug();
 
-  if ( logger.isDebugEnabled() )
-    {
-    std::cout << "Debugging" << std::endl;
-    }
-
   if (argc < 5)
   {
     print_usage();
@@ -64,7 +59,19 @@ int main(int argc, char** argv)
   QTextStream out(stdout);
 
   ctkDICOM myCTK;
+  logger.debug ( "Opening database " + QString ( argv[1] ) );
   myCTK.openDatabase ( argv[1] );
+  logger.debug ( "Last error: " + myCTK.GetLastError() );
+  if ( myCTK.database().isOpen() ) 
+    {
+    logger.debug ( "Database is open" );
+    }
+  else
+    {
+    logger.debug ( "Database is not open" );
+    }
+
+  
 
   ctkDICOMQuery query;
   query.setCallingAETitle ( QString ( argv[2] ) );

+ 1 - 0
Libs/DICOM/Core/ctkDICOM.cpp

@@ -118,6 +118,7 @@ bool ctkDICOMPrivate::executeScript(const QString& script) {
   {
     if (! (*it).startsWith("--") )
       {
+      qDebug() << *it << "\n";
       query.exec(*it);
       if (query.lastError().type())
         {

+ 103 - 82
Libs/DICOM/Core/ctkDICOMIndexerBase.cpp

@@ -21,6 +21,7 @@
 // Qt includes
 #include <QSqlQuery>
 #include <QSqlRecord>
+#include <QSqlError>
 #include <QVariant>
 #include <QDate>
 #include <QStringList>
@@ -37,7 +38,7 @@
 
 // DCMTK includes
 #ifndef WIN32
-  #define HAVE_CONFIG_H 
+#define HAVE_CONFIG_H 
 #endif
 #include <dcmtk/dcmdata/dcfilefo.h>
 #include <dcmtk/dcmdata/dcfilefo.h>
@@ -91,6 +92,11 @@ void ctkDICOMIndexerBase::setDatabase ( QSqlDatabase database ) {
   d->db = database;
 }
 
+const QSqlDatabase& ctkDICOMIndexerBase::database () const {
+  CTK_D(const ctkDICOMIndexerBase);
+  return d->db;
+}
+
 void ctkDICOMIndexerBase::insert ( DcmDataset *dataset ) {
   this->insert ( dataset, QString() );
 }
@@ -109,62 +115,64 @@ void ctkDICOMIndexerBase::insert ( DcmDataset *dataset, QString filename ) {
     return;
     }
 
-    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;
-
-    //If the following fields can not be evaluated, cancel evaluation of the DICOM file
-    dataset->findAndGetOFString(DCM_PatientsName, patientsName);
-    dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID);
-    dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID);
-    dataset->findAndGetOFString(DCM_PatientID, patientID);
-
-    dataset->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
-    dataset->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
-    dataset->findAndGetOFString(DCM_PatientsSex, patientsSex);
-    dataset->findAndGetOFString(DCM_PatientsAge, 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_PerformingPhysiciansName, performingPhysiciansName);
-    dataset->findAndGetOFString(DCM_ReferringPhysiciansName, 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);
-
+  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;
+
+  //If the following fields can not be evaluated, cancel evaluation of the DICOM file
+  dataset->findAndGetOFString(DCM_PatientsName, patientsName);
+  dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID);
+  dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID);
+  dataset->findAndGetOFString(DCM_PatientID, patientID);
+
+  dataset->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
+  dataset->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
+  dataset->findAndGetOFString(DCM_PatientsSex, patientsSex);
+  dataset->findAndGetOFString(DCM_PatientsAge, 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_PerformingPhysiciansName, performingPhysiciansName);
+  dataset->findAndGetOFString(DCM_ReferringPhysiciansName, 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);
+
+  QSqlQuery check_exists_query(d->db);
+  //The patient UID is a unique number within the database, generated by the sqlite autoincrement
+  int patientUID = -1;
+  if ( patientID != "" && patientsName != "" )
+    {
     //Check if patient is already present in the db
-    QSqlQuery check_exists_query(d->db);
     check_exists_query.prepare ( "SELECT * FROM Patients WHERE PatientID = ? AND PatientsName = ?" );
     check_exists_query.bindValue ( 0, QString ( patientID.c_str() ) );
     check_exists_query.bindValue ( 1, QString ( patientsName.c_str() ) );
     check_exists_query.exec();
-
-    //The patient UID is a unique number within the database, generated by the sqlite autoincrement
-    int patientUID = -1;
+    
     if (check_exists_query.next())
       {
       patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
@@ -185,36 +193,45 @@ void ctkDICOMIndexerBase::insert ( DcmDataset *dataset, QString filename ) {
       patientUID = statement.lastInsertId().toInt();
       logger.debug ( "New patient inserted: " + QString().setNum ( patientUID ) );
       }
+    }
 
+  if ( studyInstanceUID != "" )
+    {
     check_exists_query.prepare ( "SELECT * FROM Studies WHERE StudyInstanceUID = ?" );
     check_exists_query.bindValue ( 0, QString ( studyInstanceUID.c_str() ) );
     check_exists_query.exec();
     if(!check_exists_query.next())
       {
-        QSqlQuery statement ( d->db );
-        statement.prepare ( "INSERT INTO Studies ( 'StudyInstanceUID', 'PatientsUID', 'StudyID', 'StudyDate', 'StudyTime', 'AccessionNumber', 'ModalitiesInStudy', 'InstitutionName', 'ReferringPhysician', 'PerformingPhysiciansName', 'StudyDescription' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
-        statement.bindValue ( 0, QString ( studyInstanceUID.c_str() ) );
-        statement.bindValue ( 1, patientUID );
-        statement.bindValue ( 2, QString ( studyID.c_str() ) );
-        statement.bindValue ( 3, QDate::fromString ( studyDate.c_str(), "yyyyMMdd" ) );
-        statement.bindValue ( 4, QString ( studyTime.c_str() ) );
-        statement.bindValue ( 5, QString ( accessionNumber.c_str() ) );
-        statement.bindValue ( 6, QString ( modalitiesInStudy.c_str() ) );
-        statement.bindValue ( 7, QString ( institutionName.c_str() ) );
-        statement.bindValue ( 8, QString ( referringPhysician.c_str() ) );
-        statement.bindValue ( 9, QString ( performingPhysiciansName.c_str() ) );
-        statement.bindValue ( 10, QString ( studyDescription.c_str() ) );
-        statement.exec();
+      QSqlQuery statement ( d->db );
+      statement.prepare ( "INSERT INTO Studies ( 'StudyInstanceUID', 'PatientsUID', 'StudyID', 'StudyDate', 'StudyTime', 'AccessionNumber', 'ModalitiesInStudy', 'InstitutionName', 'ReferringPhysician', 'PerformingPhysiciansName', 'StudyDescription' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
+      statement.bindValue ( 0, QString ( studyInstanceUID.c_str() ) );
+      statement.bindValue ( 1, patientUID );
+      statement.bindValue ( 2, QString ( studyID.c_str() ) );
+      statement.bindValue ( 3, QDate::fromString ( studyDate.c_str(), "yyyyMMdd" ) );
+      statement.bindValue ( 4, QString ( studyTime.c_str() ) );
+      statement.bindValue ( 5, QString ( accessionNumber.c_str() ) );
+      statement.bindValue ( 6, QString ( modalitiesInStudy.c_str() ) );
+      statement.bindValue ( 7, QString ( institutionName.c_str() ) );
+      statement.bindValue ( 8, QString ( referringPhysician.c_str() ) );
+      statement.bindValue ( 9, QString ( performingPhysiciansName.c_str() ) );
+      statement.bindValue ( 10, QString ( studyDescription.c_str() ) );
+      if ( !statement.exec() )
+        {
+        logger.error ( "Error executing statament: " + statement.lastQuery() + " Error: " + statement.lastError().text() );
+        }
       }
+    }
 
-
+  if ( seriesInstanceUID != "" )
+    {
     check_exists_query.prepare ( "SELECT * FROM Series WHERE SeriesInstanceUID = ?" );
     check_exists_query.bindValue ( 0, QString ( seriesInstanceUID.c_str() ) );
+    logger.warn ( "Statement: " + check_exists_query.lastQuery() );
     check_exists_query.exec();
     if(!check_exists_query.next())
       {
       QSqlQuery statement ( d->db );
-      statement.prepare ( "INSERT INTO Studies ( 'SeriesInstanceUID', 'StudyInstanceUID', 'SeriesNumber', 'SeriesDate', 'SeriesTime', 'SeriesDescription', 'BodyPartExamined', 'FrameOfReferenceUID', 'AcquisitionNumber', 'ContrastAgent', 'ScanningSequence', 'EchoNumber', 'TemporalPosition' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
+      statement.prepare ( "INSERT INTO Series ( 'SeriesInstanceUID', 'StudyInstanceUID', 'SeriesNumber', 'SeriesDate', 'SeriesTime', 'SeriesDescription', 'BodyPartExamined', 'FrameOfReferenceUID', 'AcquisitionNumber', 'ContrastAgent', 'ScanningSequence', 'EchoNumber', 'TemporalPosition' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
       statement.bindValue ( 0, QString ( seriesInstanceUID.c_str() ) );
       statement.bindValue ( 1, QString ( studyInstanceUID.c_str() ) );
       statement.bindValue ( 2, seriesNumber );
@@ -228,23 +245,27 @@ void ctkDICOMIndexerBase::insert ( DcmDataset *dataset, QString filename ) {
       statement.bindValue ( 10, QString ( scanningSequence.c_str() ) );
       statement.bindValue ( 11, echoNumber );
       statement.bindValue ( 12, temporalPosition );
-      statement.exec();
-      }
-    if ( !filename.isEmpty() )
-      {
-      check_exists_query.prepare ( "SELECT * FROM Images WHERE Filename = ?" );
-      check_exists_query.bindValue ( 0, filename );
-      check_exists_query.exec();
-      if(!check_exists_query.next())
+      if ( !statement.exec() )
         {
-        QSqlQuery statement ( d->db );
-        statement.prepare ( "INSERT INTO Images ( 'Filename', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ? )" );
-        statement.bindValue ( 0, filename );
-        statement.bindValue ( 1, QString ( seriesInstanceUID.c_str() ) );
-        statement.bindValue ( 2, QDateTime::currentDateTime() );
-        statement.exec();
+        logger.error ( "Error executing statament: " + statement.lastQuery() + " Error: " + statement.lastError().text() );
         }
       }
+    }
+  if ( !filename.isEmpty() )
+    {
+    check_exists_query.prepare ( "SELECT * FROM Images WHERE Filename = ?" );
+    check_exists_query.bindValue ( 0, filename );
+    check_exists_query.exec();
+    if(!check_exists_query.next())
+      {
+      QSqlQuery statement ( d->db );
+      statement.prepare ( "INSERT INTO Images ( 'Filename', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ? )" );
+      statement.bindValue ( 0, filename );
+      statement.bindValue ( 1, QString ( seriesInstanceUID.c_str() ) );
+      statement.bindValue ( 2, QDateTime::currentDateTime() );
+      statement.exec();
+      }
+    }
 }
 
 

+ 2 - 0
Libs/DICOM/Core/ctkDICOMIndexerBase.h

@@ -40,6 +40,8 @@ public:
   explicit ctkDICOMIndexerBase();
   virtual ~ctkDICOMIndexerBase();
   void setDatabase ( QSqlDatabase database );
+  const QSqlDatabase& database() const;
+
   /**
    * Will create an entry in the appropriate tables for this dataset.
    */

+ 39 - 6
Libs/DICOM/Core/ctkDICOMQuery.cpp

@@ -65,8 +65,8 @@ public:
   QString Host;
   int Port;
   DcmSCU SCU;
-  QSqlDatabase db;
   DcmDataset* query;
+  QStringList StudyInstanceUIDList;
 
 };
 
@@ -93,14 +93,16 @@ static void QueryCallback (void *callbackData,
                            T_DIMSE_C_FindRSP* /*rsp*/, 
                            DcmDataset *dataset) {
   ctkDICOMQuery* query = (ctkDICOMQuery*) callbackData;
-  OFString StudyDescription, PatientID, PatientsName;
+  OFString StudyDescription, PatientID, PatientsName, SeriesDescription, StudyInstanceUID;
   dataset->findAndGetOFString ( DCM_StudyDescription, StudyDescription );
+  dataset->findAndGetOFString ( DCM_StudyInstanceUID, StudyInstanceUID );
   dataset->findAndGetOFString ( DCM_PatientID, PatientID );
   dataset->findAndGetOFString ( DCM_PatientsName, PatientsName );
   logger.debug ( "Found study description: " + QString ( StudyDescription.c_str() ) 
                  + " for Patient: " + QString ( PatientsName.c_str() )
                  + "/" + QString ( PatientID.c_str() ) );
   query->insert ( dataset );
+  query->addStudyInstanceUID ( QString ( StudyInstanceUID.c_str() ) );
 }
 
 //------------------------------------------------------------------------------
@@ -116,6 +118,12 @@ ctkDICOMQuery::~ctkDICOMQuery()
 {
 }
 
+void ctkDICOMQuery::addStudyInstanceUID ( QString s )
+{
+  CTK_D(ctkDICOMQuery);
+  d->StudyInstanceUIDList.append ( s );
+}
+
 /// Set methods for connectivity
 void ctkDICOMQuery::setCallingAETitle ( QString callingAETitle )
 {
@@ -163,13 +171,17 @@ int ctkDICOMQuery::port()
 //------------------------------------------------------------------------------
 void ctkDICOMQuery::query(QSqlDatabase database )
 {
+  ctkDICOMIndexerBase::setDatabase ( database );
   CTK_D(ctkDICOMQuery);
-  d->db = database;
-  
-  if ( logger.isDebugEnabled() )
+  if ( this->database().isOpen() )
+    {
+    logger.debug ( "DB open in Query" );
+    }
+  else
     {
-    std::cout << "Debugging ctkDICOMQuery" << std::endl;
+    logger.debug ( "DB not open in Query" );
     }
+  d->StudyInstanceUIDList.clear();
   d->SCU.setAETitle ( this->callingAETitle().toStdString() );
   d->SCU.setPeerAETitle ( this->calledAETitle().toStdString() );
   d->SCU.setPeerHostName ( this->host().toStdString() );
@@ -243,6 +255,27 @@ void ctkDICOMQuery::query(QSqlDatabase database )
     {
     logger.error ( "Find failed" );
     }
+
+  // Now search each Study
+  d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
+  foreach ( QString StudyInstanceUID, d->StudyInstanceUIDList )
+    {
+    logger.debug ( "Starting Series C-FIND for Series: " + StudyInstanceUID );
+    d->query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() );
+    status = d->SCU.sendFINDRequest ( 0, d->query, QueryCallback, (void*)this );
+    if ( status.good() )
+      {
+      logger.debug ( "Find succeded for Series: " + StudyInstanceUID );
+      }
+    else
+      {
+      logger.error ( "Find failed for Series: " + StudyInstanceUID );
+      }
+    }
+    
+    
+
+
   d->SCU.closeAssociation ( DUL_PEERREQUESTEDRELEASE );
 }
 

+ 3 - 0
Libs/DICOM/Core/ctkDICOMQuery.h

@@ -55,6 +55,9 @@ public:
   /// Query a remote DICOM Image Store SCP
   void query(QSqlDatabase database);
 
+  // Add a StudyInstanceUID to be queried
+  void addStudyInstanceUID ( QString StudyInstanceUID );
+
 private:
   CTK_DECLARE_PRIVATE(ctkDICOMQuery);
 };