ctkDICOMDatabase.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. #include <stdexcept>
  15. // Qt includes
  16. #include <QSqlQuery>
  17. #include <QSqlRecord>
  18. #include <QSqlError>
  19. #include <QVariant>
  20. #include <QDate>
  21. #include <QStringList>
  22. #include <QSet>
  23. #include <QFile>
  24. #include <QDirIterator>
  25. #include <QFileInfo>
  26. #include <QDebug>
  27. #include <QFileSystemWatcher>
  28. // ctkDICOM includes
  29. #include "ctkDICOMDatabase.h"
  30. #include "ctkDICOMImage.h"
  31. #include "ctkLogger.h"
  32. // DCMTK includes
  33. #ifndef WIN32
  34. #define HAVE_CONFIG_H
  35. #endif
  36. #include <dcmtk/dcmdata/dcfilefo.h>
  37. #include <dcmtk/dcmdata/dcfilefo.h>
  38. #include <dcmtk/dcmdata/dcdeftag.h>
  39. #include <dcmtk/dcmdata/dcdatset.h>
  40. #include <dcmtk/ofstd/ofcond.h>
  41. #include <dcmtk/ofstd/ofstring.h>
  42. #include <dcmtk/ofstd/ofstd.h> /* for class OFStandard */
  43. #include <dcmtk/dcmdata/dcddirif.h> /* for class DicomDirInterface */
  44. #include <dcmimage.h>
  45. #include <dcmtk/dcmjpeg/djdecode.h> /* for dcmjpeg decoders */
  46. #include <dcmtk/dcmjpeg/djencode.h> /* for dcmjpeg encoders */
  47. #include <dcmtk/dcmdata/dcrledrg.h> /* for DcmRLEDecoderRegistration */
  48. #include <dcmtk/dcmdata/dcrleerg.h> /* for DcmRLEEncoderRegistration */
  49. //------------------------------------------------------------------------------
  50. static ctkLogger logger("org.commontk.dicom.DICOMDatabase" );
  51. //------------------------------------------------------------------------------
  52. //------------------------------------------------------------------------------
  53. class ctkDICOMDatabasePrivate
  54. {
  55. Q_DECLARE_PUBLIC(ctkDICOMDatabase);
  56. protected:
  57. ctkDICOMDatabase* const q_ptr;
  58. public:
  59. ctkDICOMDatabasePrivate(ctkDICOMDatabase&);
  60. ~ctkDICOMDatabasePrivate();
  61. void init(QString databaseFile);
  62. void registerCompressionLibraries();
  63. bool executeScript(const QString script);
  64. QString DatabaseFileName;
  65. QString LastError;
  66. QSqlDatabase Database;
  67. QMap<QString, QString> LoadedHeader;
  68. };
  69. //------------------------------------------------------------------------------
  70. // ctkDICOMDatabasePrivate methods
  71. //------------------------------------------------------------------------------
  72. ctkDICOMDatabasePrivate::ctkDICOMDatabasePrivate(ctkDICOMDatabase& o): q_ptr(&o)
  73. {
  74. }
  75. //------------------------------------------------------------------------------
  76. void ctkDICOMDatabasePrivate::init(QString databaseFilename)
  77. {
  78. Q_Q(ctkDICOMDatabase);
  79. q->openDatabase(databaseFilename);
  80. }
  81. //------------------------------------------------------------------------------
  82. void ctkDICOMDatabasePrivate::registerCompressionLibraries(){
  83. logger.debug("Register compression libraries");
  84. // Register the JPEG libraries in case we need them
  85. // (registration only happens once, so it's okay to call repeatedly)
  86. // register global JPEG decompression codecs
  87. DJDecoderRegistration::registerCodecs();
  88. // register global JPEG compression codecs
  89. DJEncoderRegistration::registerCodecs();
  90. // register RLE compression codec
  91. DcmRLEEncoderRegistration::registerCodecs();
  92. // register RLE decompression codec
  93. DcmRLEDecoderRegistration::registerCodecs();
  94. }
  95. //------------------------------------------------------------------------------
  96. ctkDICOMDatabasePrivate::~ctkDICOMDatabasePrivate()
  97. {
  98. }
  99. //------------------------------------------------------------------------------
  100. void ctkDICOMDatabase::openDatabase(const QString databaseFile, const QString& connectionName )
  101. {
  102. Q_D(ctkDICOMDatabase);
  103. d->DatabaseFileName = databaseFile;
  104. d->Database = QSqlDatabase::addDatabase("QSQLITE",connectionName);
  105. d->Database.setDatabaseName(databaseFile);
  106. if ( ! (d->Database.open()) )
  107. {
  108. d->LastError = d->Database.lastError().text();
  109. throw std::runtime_error(qPrintable(d->LastError));
  110. }
  111. if ( d->Database.tables().empty() )
  112. {
  113. initializeDatabase();
  114. }
  115. if (databaseFile != ":memory")
  116. {
  117. QFileSystemWatcher* watcher = new QFileSystemWatcher(QStringList(databaseFile),this);
  118. connect(watcher, SIGNAL( fileChanged(const QString&)),this, SIGNAL ( databaseChanged() ) );
  119. }
  120. }
  121. //------------------------------------------------------------------------------
  122. // ctkDICOMDatabase methods
  123. //------------------------------------------------------------------------------
  124. ctkDICOMDatabase::ctkDICOMDatabase(QString databaseFile)
  125. : d_ptr(new ctkDICOMDatabasePrivate(*this))
  126. {
  127. Q_D(ctkDICOMDatabase);
  128. d->registerCompressionLibraries();
  129. d->init(databaseFile);
  130. }
  131. ctkDICOMDatabase::ctkDICOMDatabase(QObject* parent)
  132. : d_ptr(new ctkDICOMDatabasePrivate(*this))
  133. {
  134. Q_D(ctkDICOMDatabase);
  135. d->registerCompressionLibraries();
  136. }
  137. //------------------------------------------------------------------------------
  138. ctkDICOMDatabase::~ctkDICOMDatabase()
  139. {
  140. }
  141. //----------------------------------------------------------------------------
  142. //------------------------------------------------------------------------------
  143. const QString ctkDICOMDatabase::lastError() const {
  144. Q_D(const ctkDICOMDatabase);
  145. return d->LastError;
  146. }
  147. //------------------------------------------------------------------------------
  148. const QString ctkDICOMDatabase::databaseFilename() const {
  149. Q_D(const ctkDICOMDatabase);
  150. return d->DatabaseFileName;
  151. }
  152. //------------------------------------------------------------------------------
  153. const QString ctkDICOMDatabase::databaseDirectory() const {
  154. QString databaseFile = databaseFilename();
  155. if (!QFileInfo(databaseFile).isAbsolute())
  156. {
  157. databaseFile.prepend(QDir::currentPath() + "/");
  158. }
  159. return QFileInfo ( databaseFile ).absoluteDir().path();
  160. }
  161. //------------------------------------------------------------------------------
  162. const QSqlDatabase& ctkDICOMDatabase::database() const {
  163. Q_D(const ctkDICOMDatabase);
  164. return d->Database;
  165. }
  166. //------------------------------------------------------------------------------
  167. bool ctkDICOMDatabasePrivate::executeScript(const QString script) {
  168. QFile scriptFile(script);
  169. scriptFile.open(QIODevice::ReadOnly);
  170. if ( !scriptFile.isOpen() )
  171. {
  172. qDebug() << "Script file " << script << " could not be opened!\n";
  173. return false;
  174. }
  175. QString sqlCommands( QTextStream(&scriptFile).readAll() );
  176. sqlCommands.replace( '\n', ' ' );
  177. sqlCommands.replace("; ", ";\n");
  178. QStringList sqlCommandsLines = sqlCommands.split('\n');
  179. QSqlQuery query(Database);
  180. for (QStringList::iterator it = sqlCommandsLines.begin(); it != sqlCommandsLines.end()-1; ++it)
  181. {
  182. if (! (*it).startsWith("--") )
  183. {
  184. qDebug() << *it << "\n";
  185. query.exec(*it);
  186. if (query.lastError().type())
  187. {
  188. qDebug() << "There was an error during execution of the statement: " << (*it);
  189. qDebug() << "Error message: " << query.lastError().text();
  190. return false;
  191. }
  192. }
  193. }
  194. return true;
  195. }
  196. //------------------------------------------------------------------------------
  197. bool ctkDICOMDatabase::initializeDatabase(const char* sqlFileName)
  198. {
  199. Q_D(ctkDICOMDatabase);
  200. return d->executeScript(sqlFileName);
  201. }
  202. //------------------------------------------------------------------------------
  203. void ctkDICOMDatabase::closeDatabase()
  204. {
  205. Q_D(ctkDICOMDatabase);
  206. d->Database.close();
  207. }
  208. //------------------------------------------------------------------------------
  209. QStringList ctkDICOMDatabase::studiesForPatient(QString patientUID)
  210. {
  211. Q_D(ctkDICOMDatabase);
  212. QSqlQuery query(d->Database);
  213. query.prepare ( "SELECT StudyInstanceUID FROM Studies WHERE PatientsUID = ?" );
  214. query.bindValue ( 0, patientUID );
  215. query.exec();
  216. QStringList result;
  217. while (query.next())
  218. {
  219. result << query.value(0).toString();
  220. }
  221. return( result );
  222. }
  223. //------------------------------------------------------------------------------
  224. QStringList ctkDICOMDatabase::seriesForStudy(QString studyUID)
  225. {
  226. Q_D(ctkDICOMDatabase);
  227. QSqlQuery query(d->Database);
  228. query.prepare ( "SELECT SeriesInstanceUID FROM Series WHERE StudyInstanceUID=?");
  229. query.bindValue ( 0, studyUID );
  230. query.exec();
  231. QStringList result;
  232. while (query.next())
  233. {
  234. result << query.value(0).toString();
  235. }
  236. return( result );
  237. }
  238. //------------------------------------------------------------------------------
  239. QStringList ctkDICOMDatabase::filesForSeries(QString seriesUID)
  240. {
  241. Q_D(ctkDICOMDatabase);
  242. QSqlQuery query(d->Database);
  243. query.prepare ( "SELECT Filename FROM Images WHERE SeriesInstanceUID=?");
  244. query.bindValue ( 0, seriesUID );
  245. query.exec();
  246. QStringList result;
  247. while (query.next())
  248. {
  249. result << query.value(0).toString();
  250. }
  251. return( result );
  252. }
  253. //------------------------------------------------------------------------------
  254. void ctkDICOMDatabase::loadInstanceHeader (QString sopInstanceUID)
  255. {
  256. Q_D(ctkDICOMDatabase);
  257. QSqlQuery query(d->Database);
  258. query.prepare ( "SELECT Filename FROM Images WHERE SOPInstanceUID=?");
  259. query.bindValue ( 0, sopInstanceUID );
  260. query.exec();
  261. d->LoadedHeader.clear();
  262. if (query.next())
  263. {
  264. QString fileName = query.value(0).toString();
  265. this->loadFileHeader(fileName);
  266. }
  267. return;
  268. }
  269. //------------------------------------------------------------------------------
  270. void ctkDICOMDatabase::loadFileHeader (QString fileName)
  271. {
  272. Q_D(ctkDICOMDatabase);
  273. DcmFileFormat fileFormat;
  274. OFCondition status = fileFormat.loadFile(fileName.toLatin1().data());
  275. if (status.good())
  276. {
  277. DcmDataset *dataset = fileFormat.getDataset();
  278. DcmStack stack;
  279. while (dataset->nextObject(stack, true) == EC_Normal)
  280. {
  281. DcmObject *dO = stack.top();
  282. if (dO->isaString())
  283. {
  284. QString tag = QString("%1,%2").arg(
  285. dO->getGTag(),4,16,QLatin1Char('0')).arg(
  286. dO->getETag(),4,16,QLatin1Char('0'));
  287. std::ostringstream s;
  288. dO->print(s);
  289. d->LoadedHeader[tag] = QString(s.str().c_str());
  290. }
  291. }
  292. }
  293. return;
  294. }
  295. //------------------------------------------------------------------------------
  296. QStringList ctkDICOMDatabase::headerKeys ()
  297. {
  298. Q_D(ctkDICOMDatabase);
  299. return (d->LoadedHeader.keys());
  300. }
  301. //------------------------------------------------------------------------------
  302. QString ctkDICOMDatabase::headerValue (QString key)
  303. {
  304. Q_D(ctkDICOMDatabase);
  305. return (d->LoadedHeader[key]);
  306. }
  307. //------------------------------------------------------------------------------
  308. /*
  309. void ctkDICOMDatabase::insert ( DcmDataset *dataset ) {
  310. this->insert ( dataset, QString() );
  311. }
  312. */
  313. //------------------------------------------------------------------------------
  314. void ctkDICOMDatabase::insert ( DcmDataset *dataset, bool storeFile, bool createThumbnail )
  315. {
  316. Q_D(ctkDICOMDatabase);
  317. if (!dataset)
  318. {
  319. return;
  320. }
  321. // Check to see if the file has already been loaded
  322. OFString sopInstanceUID ;
  323. dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID);
  324. QSqlQuery fileExists ( d->Database );
  325. fileExists.prepare("SELECT InsertTimestamp,Filename FROM Images WHERE SOPInstanceUID == ?");
  326. fileExists.bindValue(0,QString(sopInstanceUID.c_str()));
  327. fileExists.exec();
  328. if ( fileExists.next() && QFileInfo(fileExists.value(1).toString()).lastModified() < QDateTime::fromString(fileExists.value(0).toString(),Qt::ISODate) )
  329. {
  330. logger.debug ( "File " + fileExists.value(1).toString() + " already added" );
  331. return;
  332. }
  333. OFString patientsName, patientID, patientsBirthDate, patientsBirthTime, patientsSex,
  334. patientComments, patientsAge;
  335. OFString studyInstanceUID, studyID, studyDate, studyTime,
  336. accessionNumber, modalitiesInStudy, institutionName, performingPhysiciansName, referringPhysician, studyDescription;
  337. OFString seriesInstanceUID, seriesDate, seriesTime,
  338. seriesDescription, bodyPartExamined, frameOfReferenceUID,
  339. contrastAgent, scanningSequence;
  340. OFString instanceNumber;
  341. Sint32 seriesNumber = 0, acquisitionNumber = 0, echoNumber = 0, temporalPosition = 0;
  342. //If the following fields can not be evaluated, cancel evaluation of the DICOM file
  343. dataset->findAndGetOFString(DCM_PatientsName, patientsName);
  344. dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID);
  345. dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID);
  346. dataset->findAndGetOFString(DCM_PatientID, patientID);
  347. dataset->findAndGetOFString(DCM_PatientsBirthDate, patientsBirthDate);
  348. dataset->findAndGetOFString(DCM_PatientsBirthTime, patientsBirthTime);
  349. dataset->findAndGetOFString(DCM_PatientsSex, patientsSex);
  350. dataset->findAndGetOFString(DCM_PatientsAge, patientsAge);
  351. dataset->findAndGetOFString(DCM_PatientComments, patientComments);
  352. dataset->findAndGetOFString(DCM_StudyID, studyID);
  353. dataset->findAndGetOFString(DCM_StudyDate, studyDate);
  354. dataset->findAndGetOFString(DCM_StudyTime, studyTime);
  355. dataset->findAndGetOFString(DCM_AccessionNumber, accessionNumber);
  356. dataset->findAndGetOFString(DCM_ModalitiesInStudy, modalitiesInStudy);
  357. dataset->findAndGetOFString(DCM_InstitutionName, institutionName);
  358. dataset->findAndGetOFString(DCM_PerformingPhysiciansName, performingPhysiciansName);
  359. dataset->findAndGetOFString(DCM_ReferringPhysiciansName, referringPhysician);
  360. dataset->findAndGetOFString(DCM_StudyDescription, studyDescription);
  361. dataset->findAndGetOFString(DCM_SeriesDate, seriesDate);
  362. dataset->findAndGetOFString(DCM_SeriesTime, seriesTime);
  363. dataset->findAndGetOFString(DCM_SeriesDescription, seriesDescription);
  364. dataset->findAndGetOFString(DCM_BodyPartExamined, bodyPartExamined);
  365. dataset->findAndGetOFString(DCM_FrameOfReferenceUID, frameOfReferenceUID);
  366. dataset->findAndGetOFString(DCM_ContrastBolusAgent, contrastAgent);
  367. dataset->findAndGetOFString(DCM_ScanningSequence, scanningSequence);
  368. dataset->findAndGetSint32(DCM_SeriesNumber, seriesNumber);
  369. dataset->findAndGetSint32(DCM_AcquisitionNumber, acquisitionNumber);
  370. dataset->findAndGetSint32(DCM_EchoNumbers, echoNumber);
  371. dataset->findAndGetSint32(DCM_TemporalPositionIdentifier, temporalPosition);
  372. // store the file if the database is not in memomry
  373. QString filename;
  374. if ( storeFile && !this->isInMemory() )
  375. {
  376. DcmFileFormat* fileformat = new DcmFileFormat ( dataset );
  377. QString destinationDirectoryName = databaseDirectory() + "/dicom/";
  378. QDir destinationDir(destinationDirectoryName);
  379. QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
  380. destinationDir.mkpath(studySeriesDirectory);
  381. filename = databaseDirectory() + "/dicom/" + pathForDataset(dataset);
  382. logger.debug ( "Saving file: " + filename );
  383. OFCondition status = fileformat->saveFile ( filename.toAscii() );
  384. if ( !status.good() )
  385. {
  386. logger.error ( "Error saving file: " + filename + "\nError is " + status.text() );
  387. delete fileformat;
  388. return;
  389. }
  390. delete fileformat;
  391. }
  392. QSqlQuery check_exists_query(d->Database);
  393. //The patient UID is a unique number within the database, generated by the sqlite autoincrement
  394. int patientUID = -1;
  395. if ( patientID != "" && patientsName != "" )
  396. {
  397. //Check if patient is already present in the db
  398. check_exists_query.prepare ( "SELECT * FROM Patients WHERE PatientID = ? AND PatientsName = ?" );
  399. check_exists_query.bindValue ( 0, QString ( patientID.c_str() ) );
  400. check_exists_query.bindValue ( 1, QString ( patientsName.c_str() ) );
  401. check_exists_query.exec();
  402. if (check_exists_query.next())
  403. {
  404. patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt();
  405. }
  406. else
  407. {
  408. // Insert it
  409. QSqlQuery statement ( d->Database );
  410. statement.prepare ( "INSERT INTO Patients ('UID', 'PatientsName', 'PatientID', 'PatientsBirthDate', 'PatientsBirthTime', 'PatientsSex', 'PatientsAge', 'PatientsComments' ) values ( NULL, ?, ?, ?, ?, ?, ?, ? )" );
  411. statement.bindValue ( 0, QString ( patientsName.c_str() ) );
  412. statement.bindValue ( 1, QString ( patientID.c_str() ) );
  413. statement.bindValue ( 2, QString ( patientsBirthDate.c_str() ) );
  414. statement.bindValue ( 3, QString ( patientsBirthTime.c_str() ) );
  415. statement.bindValue ( 4, QString ( patientsSex.c_str() ) );
  416. statement.bindValue ( 5, QString ( patientsAge.c_str() ) );
  417. statement.bindValue ( 6, QString ( patientComments.c_str() ) );
  418. statement.exec ();
  419. patientUID = statement.lastInsertId().toInt();
  420. logger.debug ( "New patient inserted: " + QString().setNum ( patientUID ) );
  421. }
  422. }
  423. if ( studyInstanceUID != "" )
  424. {
  425. check_exists_query.prepare ( "SELECT * FROM Studies WHERE StudyInstanceUID = ?" );
  426. check_exists_query.bindValue ( 0, QString ( studyInstanceUID.c_str() ) );
  427. check_exists_query.exec();
  428. if(!check_exists_query.next())
  429. {
  430. QSqlQuery statement ( d->Database );
  431. statement.prepare ( "INSERT INTO Studies ( 'StudyInstanceUID', 'PatientsUID', 'StudyID', 'StudyDate', 'StudyTime', 'AccessionNumber', 'ModalitiesInStudy', 'InstitutionName', 'ReferringPhysician', 'PerformingPhysiciansName', 'StudyDescription' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
  432. statement.bindValue ( 0, QString ( studyInstanceUID.c_str() ) );
  433. statement.bindValue ( 1, patientUID );
  434. statement.bindValue ( 2, QString ( studyID.c_str() ) );
  435. statement.bindValue ( 3, QDate::fromString ( studyDate.c_str(), "yyyyMMdd" ) );
  436. statement.bindValue ( 4, QString ( studyTime.c_str() ) );
  437. statement.bindValue ( 5, QString ( accessionNumber.c_str() ) );
  438. statement.bindValue ( 6, QString ( modalitiesInStudy.c_str() ) );
  439. statement.bindValue ( 7, QString ( institutionName.c_str() ) );
  440. statement.bindValue ( 8, QString ( referringPhysician.c_str() ) );
  441. statement.bindValue ( 9, QString ( performingPhysiciansName.c_str() ) );
  442. statement.bindValue ( 10, QString ( studyDescription.c_str() ) );
  443. if ( !statement.exec() )
  444. {
  445. logger.error ( "Error executing statament: " + statement.lastQuery() + " Error: " + statement.lastError().text() );
  446. }
  447. }
  448. }
  449. if ( seriesInstanceUID != "" )
  450. {
  451. check_exists_query.prepare ( "SELECT * FROM Series WHERE SeriesInstanceUID = ?" );
  452. check_exists_query.bindValue ( 0, QString ( seriesInstanceUID.c_str() ) );
  453. logger.warn ( "Statement: " + check_exists_query.lastQuery() );
  454. check_exists_query.exec();
  455. if(!check_exists_query.next())
  456. {
  457. QSqlQuery statement ( d->Database );
  458. statement.prepare ( "INSERT INTO Series ( 'SeriesInstanceUID', 'StudyInstanceUID', 'SeriesNumber', 'SeriesDate', 'SeriesTime', 'SeriesDescription', 'BodyPartExamined', 'FrameOfReferenceUID', 'AcquisitionNumber', 'ContrastAgent', 'ScanningSequence', 'EchoNumber', 'TemporalPosition' ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )" );
  459. statement.bindValue ( 0, QString ( seriesInstanceUID.c_str() ) );
  460. statement.bindValue ( 1, QString ( studyInstanceUID.c_str() ) );
  461. statement.bindValue ( 2, static_cast<int>(seriesNumber) );
  462. statement.bindValue ( 3, QString ( seriesDate.c_str() ) );
  463. statement.bindValue ( 4, QDate::fromString ( seriesTime.c_str(), "yyyyMMdd" ) );
  464. statement.bindValue ( 5, QString ( seriesDescription.c_str() ) );
  465. statement.bindValue ( 6, QString ( bodyPartExamined.c_str() ) );
  466. statement.bindValue ( 7, QString ( frameOfReferenceUID.c_str() ) );
  467. statement.bindValue ( 8, static_cast<int>(acquisitionNumber) );
  468. statement.bindValue ( 9, QString ( contrastAgent.c_str() ) );
  469. statement.bindValue ( 10, QString ( scanningSequence.c_str() ) );
  470. statement.bindValue ( 11, static_cast<int>(echoNumber) );
  471. statement.bindValue ( 12, static_cast<int>(temporalPosition) );
  472. if ( !statement.exec() )
  473. {
  474. logger.error ( "Error executing statament: " + statement.lastQuery() + " Error: " + statement.lastError().text() );
  475. }
  476. }
  477. }
  478. if ( !filename.isEmpty() )
  479. {
  480. check_exists_query.prepare ( "SELECT * FROM Images WHERE Filename = ?" );
  481. check_exists_query.bindValue ( 0, filename );
  482. check_exists_query.exec();
  483. if(!check_exists_query.next())
  484. {
  485. QSqlQuery statement ( d->Database );
  486. statement.prepare ( "INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ? )" );
  487. statement.bindValue ( 0, QString ( sopInstanceUID.c_str() ) );
  488. statement.bindValue ( 1, filename );
  489. statement.bindValue ( 2, QString ( seriesInstanceUID.c_str() ) );
  490. statement.bindValue ( 3, QDateTime::currentDateTime() );
  491. statement.exec();
  492. }
  493. }
  494. if (createThumbnail)
  495. {
  496. QString thumbnailBaseDir = databaseDirectory() + "/thumbs/";
  497. QString thumbnailFilename = thumbnailBaseDir + "/" + pathForDataset(dataset) + ".png";
  498. QFileInfo thumbnailInfo(thumbnailFilename);
  499. if ( ! ( thumbnailInfo.exists() && thumbnailInfo.lastModified() < QFileInfo(filename).lastModified() ) )
  500. {
  501. QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
  502. QDir(thumbnailBaseDir).mkpath(studySeriesDirectory);
  503. // TODO: reuse dataset
  504. DicomImage dcmtkImage(filename.toAscii());
  505. ctkDICOMImage ctkImage(&dcmtkImage);
  506. QImage image( ctkImage.frame(0) );
  507. image.scaled(128,128,Qt::KeepAspectRatio).save(thumbnailFilename,"PNG");
  508. }
  509. }
  510. if (d->DatabaseFileName == ":memory:")
  511. {
  512. emit databaseChanged();
  513. }
  514. }
  515. bool ctkDICOMDatabase::isInMemory() const
  516. {
  517. Q_D(const ctkDICOMDatabase);
  518. return d->DatabaseFileName == ":memory:";
  519. }
  520. QString ctkDICOMDatabase::pathForDataset( DcmDataset *dataset)
  521. {
  522. if (!dataset)
  523. {
  524. return QString();
  525. }
  526. OFString studyInstanceUID, seriesInstanceUID, sopInstanceUID;
  527. dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID);
  528. dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID);
  529. dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID);
  530. return QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str() + "/" + sopInstanceUID.c_str();
  531. }