ctkDICOMIndexer.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00020 
00021 // Qt includes
00022 #include <QSqlQuery>
00023 #include <QSqlRecord>
00024 #include <QVariant>
00025 #include <QDate>
00026 #include <QStringList>
00027 #include <QSet>
00028 #include <QFile>
00029 #include <QDirIterator>
00030 #include <QFileInfo>
00031 #include <QDebug>
00032 
00033 // ctkDICOM includes
00034 #include "ctkDICOMIndexer.h"
00035 
00036 // DCMTK includes
00037 #ifndef WIN32
00038   #define HAVE_CONFIG_H 
00039 #endif
00040 #include <dcmtk/dcmdata/dcfilefo.h>
00041 #include <dcmtk/dcmdata/dcfilefo.h>
00042 #include <dcmtk/dcmdata/dcdeftag.h>
00043 #include <dcmtk/dcmdata/dcdatset.h>
00044 #include <dcmtk/ofstd/ofcond.h>
00045 #include <dcmtk/ofstd/ofstring.h>
00046 #include <dcmtk/ofstd/ofstd.h>        /* for class OFStandard */
00047 #include <dcmtk/dcmdata/dcddirif.h>   /* for class DicomDirInterface */
00048 
00049 #define MITK_ERROR std::cout
00050 #define MITK_INFO std::cout
00051 
00052 //------------------------------------------------------------------------------
00053 class ctkDICOMIndexerPrivate: public ctkPrivate<ctkDICOMIndexer>
00054 {
00055 public:
00056   ctkDICOMIndexerPrivate();
00057   ~ctkDICOMIndexerPrivate();
00058 
00059 };
00060 
00061 //------------------------------------------------------------------------------
00062 // ctkDICOMIndexerPrivate methods
00063 
00064 //------------------------------------------------------------------------------
00065 ctkDICOMIndexerPrivate::ctkDICOMIndexerPrivate()
00066 {
00067 }
00068 
00069 //------------------------------------------------------------------------------
00070 ctkDICOMIndexerPrivate::~ctkDICOMIndexerPrivate()
00071 {
00072 }
00073 
00074 //------------------------------------------------------------------------------
00075 // ctkDICOMIndexer methods
00076 
00077 //------------------------------------------------------------------------------
00078 ctkDICOMIndexer::ctkDICOMIndexer()
00079 {
00080 }
00081 
00082 //------------------------------------------------------------------------------
00083 ctkDICOMIndexer::~ctkDICOMIndexer()
00084 {
00085 }
00086 
00087 //------------------------------------------------------------------------------
00088 void ctkDICOMIndexer::addDirectory(QSqlDatabase database, const QString& directoryName,const QString& destinationDirectoryName)
00089 {
00090   QSqlDatabase db = database;
00091   const std::string src_directory(directoryName.toStdString());
00092 
00093   OFList<OFString> originalDcmtkFileNames;
00094   OFList<OFString> dcmtkFileNames;
00095   OFStandard::searchDirectoryRecursively( src_directory.c_str(), originalDcmtkFileNames, "", "");
00096 
00097   // hack to reverse list of filenames (not neccessary when image loading works correctly)
00098   for ( OFListIterator(OFString) iter = originalDcmtkFileNames.begin(); iter != originalDcmtkFileNames.end(); ++iter )
00099   {
00100     dcmtkFileNames.push_front( *iter );
00101   }
00102 
00103   DcmFileFormat fileformat;
00104 
00105   OFListIterator(OFString) iter = dcmtkFileNames.begin();
00106   OFListIterator(OFString) last = dcmtkFileNames.end();
00107 
00108   if(iter == last) return;
00109 
00110   QSqlQuery query(database);
00111 
00112 
00115   OFString lastPatientID = "";
00116   OFString lastPatientsName = "";
00117   OFString lastPatientsBirthDate = "";
00118   OFString lastStudyInstanceUID = "";
00119   OFString lastSeriesInstanceUID = "";
00120   int lastPatientUID = -1;
00121 
00122   /* iterate over all input filenames */
00123   while (iter != last)
00124   {
00125     std::string filename((*iter).c_str());
00126     QString qfilename(filename.c_str()); 
00128     QSqlQuery fileExists(database);
00129     fileExists.prepare("SELECT InsertTimestamp FROM Images WHERE Filename == ?"); 
00130     fileExists.bindValue(0,qfilename);
00131     fileExists.exec();
00132     if (
00133       fileExists.next() && 
00134       QFileInfo(qfilename).lastModified() < QDateTime::fromString(fileExists.value(0).toString(),Qt::ISODate)      
00135       )
00136       {
00137       MITK_INFO << "File " << filename << " already added.";
00138       continue;
00139       }
00140 
00141     MITK_INFO << filename << "\n";
00142     OFCondition status = fileformat.loadFile(filename.c_str());
00143     ++iter;
00144 
00145     if (!status.good())
00146     {
00147       MITK_ERROR << "Could not load " << filename << "\nDCMTK says: " << status.text();
00148       continue;
00149     }
00150 
00151     OFString patientsName, patientID, patientsBirthDate, patientsBirthTime, patientsSex,
00152       patientComments, patientsAge;
00153 
00154     OFString studyInstanceUID, studyID, studyDate, studyTime,
00155       accessionNumber, modalitiesInStudy, institutionName, performingPhysiciansName, referringPhysician, studyDescription;
00156 
00157     OFString seriesInstanceUID, seriesDate, seriesTime,
00158       seriesDescription, bodyPartExamined, frameOfReferenceUID,
00159       contrastAgent, scanningSequence;
00160     OFString instanceNumber;
00161 
00162     Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
00163 
00164     //The patient UID is a unique number within the database, generated by the sqlite autoincrement
00165     int patientUID = -1;
00166 
00167     //If the following fields can not be evaluated, cancel evaluation of the DICOM file
00168     if (!fileformat.getDataset()->findAndGetOFString(DCM_PatientsName, patientsName).good())
00169     {
00170       MITK_ERROR << "Could not read DCM_PatientsName from " << filename;
00171       continue;
00172     }
00173 
00174     if (!fileformat.getDataset()->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID).good())
00175     {
00176       MITK_ERROR << "Could not read DCM_StudyInstanceUID from " << filename;
00177       continue;
00178     }
00179 
00180     if (!fileformat.getDataset()->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID).good())
00181     {
00182       MITK_ERROR << "Could not read DCM_SeriesInstanceUID from " << filename;
00183       continue;
00184     }
00185     if (!fileformat.getDataset()->findAndGetOFString(DCM_InstanceNumber, instanceNumber).good())
00186     {
00187       MITK_ERROR << "Could not read DCM_InstanceNumber from " << filename;
00188       continue;
00189     }
00190 
00191 
00192     fileformat.getDataset()->findAndGetOFString(DCM_PatientID, patientID);
00193     fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
00194     fileformat.getDataset()->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
00195     fileformat.getDataset()->findAndGetOFString(DCM_PatientsSex, patientsSex);
00196     fileformat.getDataset()->findAndGetOFString(DCM_PatientsAge, patientsAge);
00197     fileformat.getDataset()->findAndGetOFString(DCM_PatientComments, patientComments);
00198     fileformat.getDataset()->findAndGetOFString(DCM_StudyID, studyID);
00199     fileformat.getDataset()->findAndGetOFString(DCM_StudyDate, studyDate);
00200     fileformat.getDataset()->findAndGetOFString(DCM_StudyTime, studyTime);
00201     fileformat.getDataset()->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
00202     fileformat.getDataset()->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
00203     fileformat.getDataset()->findAndGetOFString(DCM_InstitutionName, institutionName);
00204     fileformat.getDataset()->findAndGetOFString(DCM_PerformingPhysiciansName, performingPhysiciansName);
00205     fileformat.getDataset()->findAndGetOFString(DCM_ReferringPhysiciansName, referringPhysician);
00206     fileformat.getDataset()->findAndGetOFString(DCM_StudyDescription, studyDescription);
00207 
00208     fileformat.getDataset()->findAndGetOFString(DCM_SeriesDate, seriesDate);
00209     fileformat.getDataset()->findAndGetOFString(DCM_SeriesTime, seriesTime);
00210     fileformat.getDataset()->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
00211     fileformat.getDataset()->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
00212     fileformat.getDataset()->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
00213     fileformat.getDataset()->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
00214     fileformat.getDataset()->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
00215 
00216     fileformat.getDataset()->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
00217     fileformat.getDataset()->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
00218     fileformat.getDataset()->findAndGetSint32(DCM_EchoNumbers, echoNumber);
00219     fileformat.getDataset()->findAndGetSint32(DCM_TemporalPositionIdentifier, temporalPosition);
00220 
00221     MITK_INFO << "Adding new items to database:";
00222     MITK_INFO << "studyID: " << studyID;
00223     MITK_INFO << "seriesInstanceUID: " << seriesInstanceUID;
00224     MITK_INFO << "Patient's Name: " << patientsName;
00225 
00226     //-----------------------
00227     //Add Patient to Database
00228     //-----------------------
00229 
00230     //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
00231     bool patientExists = false;
00232     if(lastPatientID.compare(patientID) || lastPatientsBirthDate.compare(patientsBirthDate) || lastPatientsName.compare(patientsName))
00233     {
00234       //Check if patient is already present in the db
00235       QSqlQuery check_exists_query(database);
00236       std::stringstream check_exists_query_string;
00237       check_exists_query_string << "SELECT * FROM Patients WHERE PatientID = '" << patientID << "'";
00238       check_exists_query.exec(check_exists_query_string.str().c_str());
00239 
00243       while (check_exists_query.next())
00244       {
00245         if (
00246             check_exists_query.record().value("PatientsName").toString() == patientsName.c_str() &&
00247             check_exists_query.record().value("PatientsBirthDate").toString() == patientsBirthDate.c_str()
00248            )
00249         {
00251           patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
00252           break;
00253         }
00254       }
00255 
00256       if(!patientExists)
00257       {
00258 
00259         std::stringstream query_string;
00260 
00261         query_string << "INSERT INTO Patients VALUES( NULL,'" 
00262         << patientsName << "','" 
00263         << patientID << "','" 
00264         << patientsBirthDate << "','"
00265         << patientsBirthTime << "','" 
00266         << patientsSex << "','" 
00267         << patientsAge << "','" 
00268         << patientComments << "')";
00269 
00270         query.exec(query_string.str().c_str());
00271 
00272         patientUID = query.lastInsertId().toInt();
00273         MITK_INFO << "New patient inserted: " << patientUID << "\n";
00274       }
00275     }
00276     else 
00277       {
00278       patientUID = lastPatientUID;
00279       }     
00280     
00282     lastPatientUID = patientUID;
00283     lastPatientID = patientID;
00284     lastPatientsBirthDate = patientsBirthDate;
00285     lastPatientsName = patientsName;
00286 
00287     //---------------------
00288     //Add Study to Database
00289     //---------------------
00290 
00291     if(lastStudyInstanceUID.compare(studyInstanceUID))
00292     {
00293       QSqlQuery check_exists_query(database);
00294       std::stringstream check_exists_query_string;
00295       check_exists_query_string << "SELECT * FROM Studies WHERE StudyInstanceUID = '" << studyInstanceUID << "'";
00296       check_exists_query.exec(check_exists_query_string.str().c_str());
00297 
00298       if(!check_exists_query.next())
00299       {
00300 
00301         std::stringstream query_string;
00302 
00303         query_string << "INSERT INTO Studies VALUES('"
00304           << studyInstanceUID << "','" 
00305           << patientUID << "','" 
00306           << studyID << "','"
00307           << QDate::fromString(studyDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
00308           << studyTime << "','" 
00309           << accessionNumber << "','" 
00310           << modalitiesInStudy << "','" 
00311           << institutionName << "','" 
00312           << referringPhysician << "','" 
00313           << performingPhysiciansName << "','" 
00314           << studyDescription << "')";
00315 
00316         query.exec(query_string.str().c_str());
00317       }
00318     }
00319 
00320     lastStudyInstanceUID = studyInstanceUID;
00321 
00322     //----------------------
00323     //Add Series to Database
00324     //----------------------
00325 
00326     if(lastSeriesInstanceUID.compare(seriesInstanceUID))
00327     {
00328 
00329       QSqlQuery check_exists_query(database);
00330       std::stringstream check_exists_query_string;
00331       check_exists_query_string << "SELECT * FROM Series WHERE SeriesInstanceUID = '" << seriesInstanceUID << "'";
00332       check_exists_query.exec(check_exists_query_string.str().c_str());
00333 
00334       if(!check_exists_query.next())
00335       {
00336 
00337         std::stringstream query_string;
00338 
00339         query_string << "INSERT INTO Series VALUES('"
00340           << seriesInstanceUID << "','" << studyInstanceUID << "','" << (int) seriesNumber << "','"
00341           << QDate::fromString(seriesDate.c_str(), "yyyyMMdd").toString("yyyy-MM-dd").toStdString() << "','"
00342           << seriesTime << "','" << seriesDescription << "','" << bodyPartExamined << "','"
00343           << frameOfReferenceUID << "','" << (int) acquisitionNumber << "','" << contrastAgent << "','"
00344           << scanningSequence << "','" << (int) echoNumber << "','" << (int) temporalPosition << "')";
00345 
00346         query.exec(query_string.str().c_str());
00347       }
00348     }
00349 
00350     lastSeriesInstanceUID = seriesInstanceUID;
00351 
00352 
00353     //----------------------------------
00354     //Move file to destination directory
00355     //----------------------------------
00356 
00357     if (!destinationDirectoryName.isEmpty())
00358       {
00359       QFile currentFile( qfilename );
00360       QDir destinationDir(destinationDirectoryName);
00361 
00362       QString uniqueDirName = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
00363       qDebug() << "MKPath: " << uniqueDirName;
00364       destinationDir.mkpath(uniqueDirName);
00365       QString destFileName = destinationDir.absolutePath().append("/").append(instanceNumber.c_str());
00366       qDebug() << "Copy: " << qfilename << " -> " << destFileName;
00367       currentFile.copy(destFileName);
00368       //for testing only: copy file instead of moving
00369       //currentFile.copyTo(destDirectoryPath.str());
00370     }
00371     // */
00372     //------------------------
00373     //Add Filename to Database
00374     //------------------------
00375 
00376 //    std::stringstream relativeFilePath;
00377 //    relativeFilePath << seriesInstanceUID.c_str() << "/" << currentFilePath.getFileName();
00378 
00379     QSqlQuery check_exists_query(database);
00380     std::stringstream check_exists_query_string;
00381 //    check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << relativeFilePath.str() << "'";
00382     check_exists_query_string << "SELECT * FROM Images WHERE Filename = '" << filename << "'";
00383     check_exists_query.exec(check_exists_query_string.str().c_str());
00384 
00385     if(!check_exists_query.next())
00386     {
00387       std::stringstream query_string;
00388 
00389       //To save absolute path: destDirectoryPath.str()
00390       query_string << "INSERT INTO Images VALUES('"
00391         << /*relativeFilePath.str()*/ filename << "','" << seriesInstanceUID << "','" << QDateTime::currentDateTime().toString(Qt::ISODate).toStdString() << "')";
00392 
00393       query.exec(query_string.str().c_str());
00394     }
00395   }
00396 
00397   // db.commit();
00398   // db.close();
00399 
00400 }
00401 
00402 //------------------------------------------------------------------------------
00403 void ctkDICOMIndexer::refreshDatabase(QSqlDatabase database, const QString& directoryName)
00404 {
00406   QSqlQuery allFilesQuery(database);
00407   QStringList databaseFileNames;
00408   QStringList filesToRemove;
00409   allFilesQuery.exec("SELECT Filename from Images;");
00410 
00411   while (allFilesQuery.next())
00412     {
00413     QString fileName = allFilesQuery.value(0).toString();
00414     databaseFileNames.append(fileName);
00415     if (! QFile::exists(fileName) ) 
00416       {
00417       filesToRemove.append(fileName);
00418       }
00419     }
00420 
00421   QSet<QString> filesytemFiles;
00422   QDirIterator dirIt(directoryName);
00423   while (dirIt.hasNext()) 
00424     {
00425     filesytemFiles.insert(dirIt.next());
00426     }
00427 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1