Pārlūkot izejas kodu

Implement on-the-fly thumbnail generation to DICOM/Widgets and free ctkDICOMDatabase from using ctkDICOMImage

nherlambang 14 gadi atpakaļ
vecāks
revīzija
2854dce837

+ 2 - 4
Libs/DICOM/Core/Testing/Cpp/ctkDICOMDatabaseTest1.cpp

@@ -85,10 +85,8 @@ int ctkDICOMDatabaseTest1( int argc, char * argv [] )
 
   // check if it doesn't crash
   database.pathForDataset(0);
-  database.insert(0, true, true);
-  database.insert(0, true, false);
-  database.insert(0, false, false);
-  database.insert(0, false, true);
+  database.insert(0, true);
+  database.insert(0, false);
 
   database.closeDatabase();
   database.initializeDatabase();

+ 1 - 19
Libs/DICOM/Core/ctkDICOMDatabase.cpp

@@ -36,7 +36,6 @@
 
 // ctkDICOM includes
 #include "ctkDICOMDatabase.h"
-#include "ctkDICOMImage.h"
 
 #include "ctkLogger.h"
 
@@ -362,7 +361,7 @@ void ctkDICOMDatabase::insert ( DcmDataset *dataset ) {
 */
 
 //------------------------------------------------------------------------------
-void ctkDICOMDatabase::insert ( DcmDataset *dataset, bool storeFile, bool createThumbnail )
+void ctkDICOMDatabase::insert ( DcmDataset *dataset, bool storeFile)
 {
   Q_D(ctkDICOMDatabase);
 
@@ -562,23 +561,6 @@ void ctkDICOMDatabase::insert ( DcmDataset *dataset, bool storeFile, bool create
       }
     }
 
-  if (createThumbnail)
-  {
-    QString thumbnailBaseDir = databaseDirectory() + "/thumbs/";
-    QString thumbnailFilename = thumbnailBaseDir + "/" + pathForDataset(dataset) + ".png";
-    QFileInfo thumbnailInfo(thumbnailFilename);
-    if ( ! ( thumbnailInfo.exists() && thumbnailInfo.lastModified() < QFileInfo(filename).lastModified() ) )
-    {
-      QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str();
-      QDir(thumbnailBaseDir).mkpath(studySeriesDirectory);
-      // TODO: reuse dataset
-      DicomImage dcmtkImage(filename.toAscii());
-      ctkDICOMImage ctkImage(&dcmtkImage);
-      QImage image( ctkImage.frame(0) );
-      image.scaled(128,128,Qt::KeepAspectRatio).save(thumbnailFilename,"PNG");
-    }
-  }
-
   if (d->DatabaseFileName == ":memory:")
     {
       emit databaseChanged();

+ 1 - 1
Libs/DICOM/Core/ctkDICOMDatabase.h

@@ -81,7 +81,7 @@ public:
   /**
    * Insert into the database if not already exsting.
    */
-  void insert ( DcmDataset *dataset, bool storeFile = true, bool createThumbnail = true );
+  void insert ( DcmDataset *dataset, bool storeFile = true);
   /***
     * Helper method: get the path that should be used to store this  image.
     */

+ 2 - 2
Libs/DICOM/Core/ctkDICOMQuery.cpp

@@ -362,7 +362,7 @@ bool ctkDICOMQuery::query(ctkDICOMDatabase& database )
     DcmDataset *dataset = (*it)->m_dataset;
     if ( dataset != NULL )
       {
-      database.insert ( dataset, false, false );
+      database.insert ( dataset, false);
       OFString StudyInstanceUID;
       dataset->findAndGetOFString ( DCM_StudyInstanceUID, StudyInstanceUID );
       d->addStudyInstanceUID ( QString ( StudyInstanceUID.c_str() ) );
@@ -390,7 +390,7 @@ bool ctkDICOMQuery::query(ctkDICOMDatabase& database )
         DcmDataset *dataset = (*it)->m_dataset;
         if ( dataset != NULL )
           {
-          database.insert ( dataset, false, false );
+          database.insert ( dataset, false);
           }
         }
       logger.debug ( "Find succeded for Series: " + StudyInstanceUID );

+ 1 - 0
Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp

@@ -227,6 +227,7 @@ void ctkDICOMAppWidget::onThumbnailDoubleClicked(const ctkDICOMThumbnailWidget&
 {
     Q_D(ctkDICOMAppWidget);
 
+    logger.debug("double clicked");
     QModelIndex index = widget.sourceIndex();
 
     ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));

+ 7 - 7
Libs/DICOM/Widgets/ctkDICOMDatasetView.cpp

@@ -105,19 +105,19 @@ void ctkDICOMDatasetViewPrivate::setImage(const QModelIndex &imageIndex){
         QModelIndex seriesIndex = imageIndex.parent();
         QModelIndex studyIndex = seriesIndex.parent();
 
-        QString thumbnailPath = this->databaseDirectory;
-        thumbnailPath.append("/dicom/").append(model->data(studyIndex ,ctkDICOMModel::UIDRole).toString());
-        thumbnailPath.append("/").append(model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString());
-        thumbnailPath.append("/").append(model->data(imageIndex ,ctkDICOMModel::UIDRole).toString());
+        QString dicomPath = this->databaseDirectory;
+        dicomPath.append("/dicom/").append(model->data(studyIndex ,ctkDICOMModel::UIDRole).toString());
+        dicomPath.append("/").append(model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString());
+        dicomPath.append("/").append(model->data(imageIndex ,ctkDICOMModel::UIDRole).toString());
 
-        if (QFile(thumbnailPath).exists()){
-            DicomImage dcmImage( thumbnailPath.toStdString().c_str() );
+        if (QFile(dicomPath).exists()){
+            DicomImage dcmImage( dicomPath.toStdString().c_str() );
 
             q->clearImages();
             q->addImage( dcmImage );
             this->currentImageIndex = imageIndex;
         }else{
-          q->clearImages();
+            q->clearImages();
         }
     }
 }

+ 111 - 58
Libs/DICOM/Widgets/ctkDICOMThumbnailListWidget.cpp

@@ -4,7 +4,10 @@
 #include <QResizeEvent>
 #include <QPushButton>
 #include <QPixmap>
+#include <QDir>
 #include <QFile>
+#include <QFileInfo>
+#include <QDateTime>
 
 // ctk includes
 #include "ctkLogger.h"
@@ -24,6 +27,9 @@
 // STD includes
 #include <iostream>
 
+// DCMTK includes
+#include <dcmimage.h>
+
 static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMThumbnailListWidget");
 
 //----------------------------------------------------------------------------
@@ -38,7 +44,9 @@ public:
 
   QModelIndex currentSelectedModel;
 
+  QString loadImageThumbnail(const QModelIndex &imageIndex);
   void clearAllThumbnails();
+  void addThumbnailWidget(const QModelIndex &imageIndex, const QModelIndex& sourceIndex, const QString& text);
 
   void onPatientModelSelected(const QModelIndex &index);
   void onStudyModelSelected(const QModelIndex &index);
@@ -67,24 +75,10 @@ void ctkDICOMThumbnailListWidgetPrivate::onPatientModelSelected(const QModelInde
             QModelIndex seriesIndex = studyIndex.child(0, 0);
             QModelIndex imageIndex = seriesIndex.child(0, 0);
 
-            QString thumbnail = this->databaseDirectory +
-                                    "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-
-            if (QFile(thumbnail).exists()){
-                ctkDICOMThumbnailWidget* widget = new ctkDICOMThumbnailWidget(this->scrollAreaContentWidget);
-                widget->setText( model->data(studyIndex, Qt::DisplayRole).toString() );
-                QPixmap pix(thumbnail);
-                logger.debug("Setting pixmap to " + thumbnail);
-                widget->setPixmap(pix);
-                widget->setSourceIndex(studyIndex);
-                this->scrollAreaContentWidget->layout()->addWidget(widget);
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SLOT(onThumbnailSelected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SIGNAL(selected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)), q, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)));
-            }else{
-                logger.error("No thumbnail file " + thumbnail);
+            QString thumbnailPath = this->loadImageThumbnail(imageIndex);
+
+            if(thumbnailPath != QString("") && QFile(thumbnailPath).exists()){
+                this->addThumbnailWidget(imageIndex, studyIndex, model->data(studyIndex, Qt::DisplayRole).toString());
             }
         }
     }
@@ -105,24 +99,10 @@ void ctkDICOMThumbnailListWidgetPrivate::onStudyModelSelected(const QModelIndex
             QModelIndex seriesIndex = studyIndex.child(i, 0);
             QModelIndex imageIndex = seriesIndex.child(0, 0);
 
-            QString thumbnail = this->databaseDirectory +
-                                    "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                    model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-
-            if (QFile(thumbnail).exists()){
-                ctkDICOMThumbnailWidget* widget = new ctkDICOMThumbnailWidget(this->scrollAreaContentWidget);
-                widget->setText( model->data(seriesIndex, Qt::DisplayRole).toString() );
-                QPixmap pix(thumbnail);
-                logger.debug("Setting pixmap to " + thumbnail);
-                widget->setPixmap(pix);
-                widget->setSourceIndex(seriesIndex);
-                this->scrollAreaContentWidget->layout()->addWidget(widget);
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SLOT(onThumbnailSelected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SIGNAL(selected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)), q, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)));
-            }else{
-                logger.error("No thumbnail file " + thumbnail);
+            QString thumbnailPath = this->loadImageThumbnail(imageIndex);
+
+            if (thumbnailPath != QString("") && QFile(thumbnailPath).exists()){
+                this->addThumbnailWidget(imageIndex, seriesIndex, model->data(seriesIndex, Qt::DisplayRole).toString());
             }
         }
     }
@@ -139,33 +119,16 @@ void ctkDICOMThumbnailListWidgetPrivate::onSeriesModelSelected(const QModelIndex
     if(model){
         model->fetchMore(seriesIndex);
 
-        QString thumbnailPath = this->databaseDirectory +
-                                "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
-                                model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/";
-
         int imageCount = model->rowCount(seriesIndex);
         logger.debug(QString("Thumbs: %1").arg(imageCount));
         for (int i = 0 ; i < imageCount ; i++ )
         {
             QModelIndex imageIndex = seriesIndex.child(i,0);
-            QString thumbnail = thumbnailPath + model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
-            if (QFile(thumbnail).exists())
-            {
-                ctkDICOMThumbnailWidget* widget = new ctkDICOMThumbnailWidget(this->scrollAreaContentWidget);
-                QString widgetLabel = QString("Image %1").arg(i);
-                widget->setText( widgetLabel );
-                QPixmap pix(thumbnail);
-                logger.debug("Setting pixmap to " + thumbnail);
-                widget->setPixmap(pix);
-                widget->setSourceIndex(imageIndex);
-                this->scrollAreaContentWidget->layout()->addWidget(widget);
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SLOT(onThumbnailSelected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SIGNAL(selected(ctkDICOMThumbnailWidget)));
-                q->connect(widget, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)), q, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)));
-            }
-            else
-            {
-                logger.error("No thumbnail file " + thumbnail);
+
+            QString thumbnailPath = this->loadImageThumbnail(imageIndex);
+
+            if(thumbnailPath != QString("") && QFile(thumbnailPath).exists()){
+                this->addThumbnailWidget(imageIndex, imageIndex, QString("Image %1").arg(i));
             }
         }
     }
@@ -187,6 +150,96 @@ void ctkDICOMThumbnailListWidgetPrivate::clearAllThumbnails(){
     }
 }
 
+QString ctkDICOMThumbnailListWidgetPrivate::loadImageThumbnail(const QModelIndex &imageIndex){
+    Q_Q(ctkDICOMThumbnailListWidget);
+
+    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(imageIndex.model()));
+
+    if(model){
+        QModelIndex seriesIndex = imageIndex.parent();
+        QModelIndex studyIndex = seriesIndex.parent();
+        QModelIndex patientIndex = studyIndex.parent();
+
+        QString thumbnailPath = this->databaseDirectory +
+                                "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                                model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                                model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
+
+        QString dicomPath = this->databaseDirectory +
+                            "/dicom/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                            model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                            model->data(imageIndex ,ctkDICOMModel::UIDRole).toString();
+
+        QFileInfo thumbnailInfo(thumbnailPath);
+        QFileInfo dicomInfo(dicomPath);
+
+        if(!(QFile(thumbnailPath).exists() && (thumbnailInfo.lastModified() > dicomInfo.lastModified()))){
+            QString studySeriesDirectory = model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" + model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString();
+
+            //Create thumbnail here
+            QDir(this->databaseDirectory + "/thumbs/").mkpath(studySeriesDirectory);
+            // TODO: reuse dataset
+            DicomImage dcmImage(dicomPath.toAscii());
+            QImage image;
+            if ((dcmImage.getStatus() == EIS_Normal)){
+                dcmImage.setWindow(0);
+                /* get image extension */
+                const unsigned long width = dcmImage.getWidth();
+                const unsigned long height = dcmImage.getHeight();
+                QString header = QString("P5 %1 %2 255\n").arg(width).arg(height);
+                const unsigned long offset = header.length();
+                const unsigned long length = width * height + offset;
+                /* create output buffer for DicomImage class */
+                QByteArray buffer;
+                buffer.append(header);
+                buffer.resize(length);
+
+                /* copy PGM header to buffer */
+
+                if (dcmImage.getOutputData(static_cast<void *>(buffer.data() + offset), length - offset, 8, 0)){
+                    if (!image.loadFromData( buffer )){
+                        logger.error("QImage couldn't created");
+                        return QString("");
+                    }
+                }
+            }
+            image.scaled(128,128,Qt::KeepAspectRatio).save(thumbnailPath,"PNG");
+        }
+
+        return thumbnailPath;
+    }
+
+    return QString("");
+}
+
+void ctkDICOMThumbnailListWidgetPrivate::addThumbnailWidget(const QModelIndex& imageIndex, const QModelIndex& sourceIndex, const QString &text){
+    Q_Q(ctkDICOMThumbnailListWidget);
+
+    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(imageIndex.model()));
+
+    if(model){
+        QModelIndex seriesIndex = imageIndex.parent();
+        QModelIndex studyIndex = seriesIndex.parent();
+
+        QString thumbnailPath = this->databaseDirectory +
+                                "/thumbs/" + model->data(studyIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                                model->data(seriesIndex ,ctkDICOMModel::UIDRole).toString() + "/" +
+                                model->data(imageIndex, ctkDICOMModel::UIDRole).toString() + ".png";
+
+        ctkDICOMThumbnailWidget* widget = new ctkDICOMThumbnailWidget(this->scrollAreaContentWidget);
+        QString widgetLabel = text;
+        widget->setText( widgetLabel );
+        QPixmap pix(thumbnailPath);
+        logger.debug("Setting pixmap to " + thumbnailPath);
+        widget->setPixmap(pix);
+        widget->setSourceIndex(sourceIndex);
+        this->scrollAreaContentWidget->layout()->addWidget(widget);
+        q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SLOT(onThumbnailSelected(ctkDICOMThumbnailWidget)));
+        q->connect(widget, SIGNAL(selected(ctkDICOMThumbnailWidget)), q, SIGNAL(selected(ctkDICOMThumbnailWidget)));
+        q->connect(widget, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)), q, SIGNAL(doubleClicked(ctkDICOMThumbnailWidget)));
+    }
+}
+
 //----------------------------------------------------------------------------
 // ctkDICOMThumbnailListWidget methods