소스 검색

Merge branch 'instanceTag'

Steve Pieper 13 년 전
부모
커밋
5ea83f7d36

+ 2 - 0
Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt

@@ -3,6 +3,7 @@ set(KIT ${PROJECT_NAME})
 create_test_sourcelist(Tests ${KIT}CppTests.cpp
   ctkDICOMCoreTest1.cpp
   ctkDICOMDatabaseTest1.cpp
+  ctkDICOMDatabaseTest2.cpp
   ctkDICOMDatasetTest1.cpp
   ctkDICOMIndexerTest1.cpp
   ctkDICOMModelTest1.cpp
@@ -29,6 +30,7 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME})
 
 # ctkDICOMDatabase
 SIMPLE_TEST(ctkDICOMDatabaseTest1)
+SIMPLE_TEST(ctkDICOMDatabaseTest2 ${CTKData_DIR}/Data/DICOM/MRHEAD/000055.IMA)
 SIMPLE_TEST(ctkDICOMDatasetTest1)
 SIMPLE_TEST(ctkDICOMIndexerTest1 )
 

+ 131 - 0
Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest2.cpp

@@ -0,0 +1,131 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QCoreApplication>
+#include <QDir>
+#include <QTimer>
+
+// ctkDICOMCore includes
+#include "ctkDICOMDatabase.h"
+
+// STD includes
+#include <iostream>
+#include <cstdlib>
+
+
+int ctkDICOMDatabaseTest2( int argc, char * argv [] )
+{
+  QCoreApplication app(argc, argv);
+
+  if (argc < 2)
+    {
+    std::cerr << "ctkDICOMDatabaseTest2: missing dicom filePath argument";
+    std::cerr << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  QString dicomFilePath(argv[1]);
+
+  ctkDICOMDatabase database;
+  QDir databaseDirectory = QDir::temp();
+  QFileInfo databaseFile(databaseDirectory, QString("database.test"));
+  database.openDatabase(databaseFile.absoluteFilePath());
+
+  if (!database.lastError().isEmpty())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << qPrintable(database.lastError()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if (!database.database().isValid())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << "invalid sql database" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if (database.isInMemory())
+    {
+    std::cerr << "ctkDICOMDatabase::openDatabase() failed: "
+              << "database should not be in memory" << std::endl;
+    return EXIT_FAILURE;    
+    }
+
+  bool res = database.initializeDatabase();
+  
+  if (!res)
+    {
+    std::cerr << "ctkDICOMDatabase::initializeDatabase() failed." << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  //
+  // Test that the tag interface works to parse ascii
+  //
+  QString tag("0008,103e");
+  unsigned short group, element;
+  if ( !database.tagToGroupElement(tag, group, element) )
+    {
+    std::cerr << "ctkDICOMDatabase: could not parse tag" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if ( group != 0x8 || element != 0x103e )
+    {
+    std::cerr << "ctkDICOMDatabase: expected: " << "0008,103e" << std::endl;
+    std::cerr << "ctkDICOMDatabase: got: " << group << " " << element << std::endl;
+    std::cerr << "ctkDICOMDatabase: parsed tag does not match group/element" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  //
+  // Basic test:
+  // - insert the file specified on the command line
+  // - ask for tag values and compare to known results
+  //
+  database.insert(dicomFilePath, false, false);
+  QString instanceUID("1.2.840.113619.2.135.3596.6358736.4843.1115808177.83");
+
+  QString foundFile = database.fileForInstance(instanceUID);
+
+  if (foundFile != dicomFilePath)
+    {
+    std::cerr << "ctkDICOMDatabase: didn't get back the original file path" << std::endl;
+    return EXIT_FAILURE;
+    }
+  
+
+  QString knownSeriesDescription("3D Cor T1 FAST IR-prepped GRE");
+
+  QString foundSeriesDescription = database.instanceValue(instanceUID, tag);
+
+  if (foundSeriesDescription != knownSeriesDescription)
+    {
+    std::cerr << "ctkDICOMDatabase: invalid element value returned" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  database.closeDatabase();
+  database.initializeDatabase();
+
+  return EXIT_SUCCESS;
+}

+ 77 - 5
Libs/DICOM/Core/ctkDICOMDatabase.cpp

@@ -382,6 +382,22 @@ QStringList ctkDICOMDatabase::filesForSeries(QString seriesUID)
 }
 
 //------------------------------------------------------------------------------
+QString ctkDICOMDatabase::fileForInstance(QString sopInstanceUID)
+{
+  Q_D(ctkDICOMDatabase);
+  QSqlQuery query(d->Database);
+  query.prepare ( "SELECT Filename FROM Images WHERE SOPInstanceUID=?");
+  query.bindValue ( 0, sopInstanceUID );
+  query.exec();
+  QString result;
+  if (query.next()) 
+    {
+    result = query.value(0).toString();
+    }
+  return( result );
+}
+
+//------------------------------------------------------------------------------
 void ctkDICOMDatabase::loadInstanceHeader (QString sopInstanceUID)
 {
   Q_D(ctkDICOMDatabase);
@@ -437,11 +453,68 @@ QString ctkDICOMDatabase::headerValue (QString key)
 }
 
 //------------------------------------------------------------------------------
-/*
-void ctkDICOMDatabase::insert ( DcmDataset *dataset ) {
-  this->insert ( dataset, QString() );
+QString ctkDICOMDatabase::instanceValue(QString sopInstanceUID, QString tag)
+{
+  unsigned short group, element;
+  this->tagToGroupElement(tag, group, element);
+  return( this->instanceValue(sopInstanceUID, group, element) );
+}
+
+//------------------------------------------------------------------------------
+QString ctkDICOMDatabase::instanceValue(const QString sopInstanceUID, const unsigned short group, const unsigned short element)
+{
+  QString filePath = this->fileForInstance(sopInstanceUID);
+  if (filePath != "" )
+    {
+    return( this->fileValue(filePath, group, element) );
+    }
+  else
+    {
+    return ("");
+    }
+}
+
+
+//------------------------------------------------------------------------------
+QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag)
+{
+  unsigned short group, element;
+  this->tagToGroupElement(tag, group, element);
+  return( this->fileValue(fileName, group, element) );
+}
+
+//------------------------------------------------------------------------------
+QString ctkDICOMDatabase::fileValue(const QString fileName, const unsigned short group, const unsigned short element)
+{
+  // here is where the real lookup happens
+  // - for now we create a ctkDICOMDataset and extract the value from there
+  // - then we convert to the appropriate type of string
+  //
+  //As an optimization we could consider
+  // - check if we are currently looking at the dataset for this fileName
+  // - if so, are we looking for a group/element that is past the last one
+  //   accessed
+  //   -- if so, keep looking for the requested group/element
+  //   -- if not, start again from the begining
+
+  ctkDICOMDataset dataset;
+  dataset.InitializeFromFile(fileName);
+  
+  DcmTagKey tagKey(group, element);
+
+  return( dataset.GetAllElementValuesAsString(tagKey) );
+}
+
+//------------------------------------------------------------------------------
+bool ctkDICOMDatabase::tagToGroupElement(const QString tag, unsigned short& group, unsigned short& element)
+{
+  QStringList groupElement = tag.split(",");
+  bool groupOK, elementOK;
+  group = groupElement[0].toUInt(&groupOK, 16);
+  element = groupElement[1].toUInt(&elementOK, 16);
+
+  return( groupOK && elementOK );
 }
-*/
 
 //------------------------------------------------------------------------------
 void ctkDICOMDatabase::insert( DcmDataset *dataset, bool storeFile, bool generateThumbnail)
@@ -970,4 +1043,3 @@ bool ctkDICOMDatabase::removePatient(const QString& patientID)
   d->lastPatientUID = -1;
   return result;
 }
-

+ 28 - 7
Libs/DICOM/Core/ctkDICOMDatabase.h

@@ -113,16 +113,21 @@ public:
   ///
   /// \brief database accessors
   Q_INVOKABLE QStringList patients ();
-  Q_INVOKABLE QStringList studiesForPatient (QString patientUID);
-  Q_INVOKABLE QStringList seriesForStudy (QString studyUID);
-  Q_INVOKABLE QStringList filesForSeries (QString seriesUID);
+  Q_INVOKABLE QStringList studiesForPatient (const QString patientUID);
+  Q_INVOKABLE QStringList seriesForStudy (const QString studyUID);
+  Q_INVOKABLE QStringList filesForSeries (const QString seriesUID);
+  Q_INVOKABLE QString fileForInstance (const QString sopInstanceUID);
 
   ///
   /// \brief load the header from a file and allow access to elements
-  Q_INVOKABLE void loadInstanceHeader (QString sopInstanceUID);
-  Q_INVOKABLE void loadFileHeader (QString fileName);
+  /// @param sopInstanceUID A string with the uid for a given instance
+  ///                       (corresponding file will be found via database)
+  /// @param fileName Full path to a dicom file to load.
+  /// @param key A group,element tag in zero-filled hex
+  Q_INVOKABLE void loadInstanceHeader (const QString sopInstanceUID);
+  Q_INVOKABLE void loadFileHeader (const QString fileName);
   Q_INVOKABLE QStringList headerKeys ();
-  Q_INVOKABLE QString headerValue (QString key);
+  Q_INVOKABLE QString headerValue (const QString key);
 
   /// Insert into the database if not already exsting.
   /// @param dataset The dataset to store into the database. Usually, this is
@@ -139,7 +144,7 @@ public:
   ///                  does only make sense if a full object is received.
   /// @param @generateThumbnail If true, a thumbnail is generated.
   ///
-  void insert( const ctkDICOMDataset& ctkDataset, bool storeFile, bool generateThumbnail);
+  Q_INVOKABLE void insert( const ctkDICOMDataset& ctkDataset, bool storeFile, bool generateThumbnail);
   void insert ( DcmDataset *dataset, bool storeFile = true, bool generateThumbnail = true);
   Q_INVOKABLE void insert ( const QString& filePath, bool storeFile = true, bool generateThumbnail = true, bool createHierarchy = true, const QString& destinationDirectoryName = QString() );
   
@@ -153,6 +158,22 @@ public:
   Q_INVOKABLE bool removePatient(const QString& patientID);
   bool cleanup();
 
+  ///
+  /// \brief access element values for given instance
+  /// @param sopInstanceUID A string with the uid for a given instance
+  ///                       (corresponding file will be found via database)
+  /// @param fileName Full path to a dicom file to load.
+  /// @param key A group,element tag in zero-filled hex
+  /// @param group The group portion of the tag as an integer
+  /// @param element The element portion of the tag as an integer
+  /// @Returns empty string is element is missing
+  Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const QString tag);
+  Q_INVOKABLE QString instanceValue (const QString sopInstanceUID, const unsigned short group, const unsigned short element);
+  Q_INVOKABLE QString fileValue (const QString fileName, const QString tag);
+  Q_INVOKABLE QString fileValue (const QString fileName, const unsigned short group, const unsigned short element);
+  bool tagToGroupElement (const QString tag, unsigned short& group, unsigned short& element);
+
+
 Q_SIGNALS:
   void databaseChanged();