//------------------------------------------------------------------------------ void ctkDICOMDatabase::insert ( const QString& filePath, bool storeFile, bool generateThumbnail, bool createHierarchy, const QString& destinationDirectoryName) { Q_D(ctkDICOMDatabase); Q_UNUSED(createHierarchy); Q_UNUSED(destinationDirectoryName); /// first we check if the file is already in the database if (fileExistsAndUpToDate(filePath)) { logger.debug( "File " + filePath + " already added."); return; } logger.debug( "Processing " + filePath ); std::string filename = filePath.toStdString(); DcmFileFormat fileformat; ctkDICOMDataset ctkDataset; ctkDataset.InitializeFromFile(filePath); if ( ctkDataset.IsInitialized() ) { d->insert( ctkDataset, filePath, storeFile, generateThumbnail ); } else { logger.warn(QString("Could not read DICOM file:") + filePath); } }
//----------------------------------------------------------------------------- void ctkVTKHistogram::setRange(qreal minRange, qreal maxRange) { Q_D(const ctkVTKHistogram); if (d->DataArray.GetPointer() == 0) { //Q_ASSERT(d->DataArray.GetPointer()); logger.warn("no data array. range will be reset when setting array."); minRange = 1.; // set incorrect values maxRange = 0.; return; } if (minRange >= maxRange) { //Q_ASSERT(d->DataArray.GetPointer()); logger.warn("minRange >= maxRange"); qreal pivot = minRange; minRange = maxRange; maxRange = pivot; } int numberOfBinsBefore = d->computeNumberOfBins(); d->Range[0] = minRange; d->Range[1] = maxRange; if (d->computeNumberOfBins() != numberOfBinsBefore) { this->build(); } }
//------------------------------------------------------------------------------ bool ctkDICOMRetrievePrivate::initializeSCU( const QString& studyInstanceUID, const QString& seriesInstanceUID, const RetrieveType retrieveType, DcmDataset *retrieveParameters) { if ( !this->Database ) { logger.error ( "No Database for retrieve transaction" ); return false; } // If we like to query another server than before, be sure to disconnect first if (this->SCU.isConnected() && this->ConnectionParamsChanged) { this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); } // Connect to server if not already connected if (!this->SCU.isConnected()) { // Check and initialize networking parameters in DCMTK if ( !this->SCU.initNetwork().good() ) { logger.error ( "Error initializing the network" ); return false; } // Negotiate (i.e. start the) association logger.debug ( "Negotiating Association" ); if ( !this->SCU.negotiateAssociation().good() ) { logger.error ( "Error negotiating association" ); return false;; } } this->ConnectionParamsChanged = false; // Setup query about what to be received from the PACS logger.debug ( "Setting Retrieve Parameters" ); if ( retrieveType == RetrieveSeries ) { retrieveParameters->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" ); retrieveParameters->putAndInsertString ( DCM_SeriesInstanceUID, seriesInstanceUID.toStdString().c_str() ); // Always required to send all highler level unique keys, so add study here (we are in Study Root) retrieveParameters->putAndInsertString ( DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str() ); //TODO } else { retrieveParameters->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" ); retrieveParameters->putAndInsertString ( DCM_StudyInstanceUID, studyInstanceUID.toStdString().c_str() ); } return true; }
//------------------------------------------------------------------------------ bool ctkDICOMRetrieve::getStudy(const QString& studyInstanceUID) { if (studyInstanceUID.isEmpty()) { logger.error("Cannot receive series: Study Instance UID empty."); return false; } Q_D(ctkDICOMRetrieve); logger.info ( "Starting getStudy" ); return d->get ( studyInstanceUID, "", ctkDICOMRetrievePrivate::RetrieveStudy ); }
//------------------------------------------------------------------------------ bool ctkDICOMRetrieve::getSeries(const QString& studyInstanceUID, const QString& seriesInstanceUID) { if (studyInstanceUID.isEmpty() || seriesInstanceUID.isEmpty()) { logger.error("Cannot receive series: Either Study or Series Instance UID empty."); return false; } Q_D(ctkDICOMRetrieve); logger.info ( "Starting getSeries" ); return d->get ( studyInstanceUID, seriesInstanceUID, ctkDICOMRetrievePrivate::RetrieveSeries ); }
//----------------------------------------------------------------------------- void ctkVTKHistogram::resetRange() { Q_D(ctkVTKHistogram); if (d->DataArray.GetPointer() == 0) { //Q_ASSERT(d->DataArray.GetPointer()); logger.warn("no dataArray"); d->Range[0] = 1.; // set incorrect values d->Range[1] = 0.; return; } if (d->DataArray->GetDataType() == VTK_CHAR || d->DataArray->GetDataType() == VTK_SIGNED_CHAR || d->DataArray->GetDataType() == VTK_UNSIGNED_CHAR) { d->Range[0] = d->DataArray->GetDataTypeMin(); d->Range[1] = d->DataArray->GetDataTypeMax(); } else { d->DataArray->GetRange(d->Range, d->Component); if (d->DataArray->GetDataType() == VTK_FLOAT || d->DataArray->GetDataType() == VTK_DOUBLE) { d->Range[1] += 0.01; } //else // { // this->Range[1] += 1; // } } }
// -------------------------------------------------------------------------- void ctkWorkflowStep::validate(const QString& desiredBranchId) { Q_D(ctkWorkflowStep); logger.info(QString("validate - validating the input from %1").arg(d->Name)); this->validationComplete(true, desiredBranchId); }
//----------------------------------------------------------------------------- QVariant ctkVTKColorTransferFunction::maxValue()const { Q_D(const ctkVTKColorTransferFunction); if (d->ColorTransferFunction.GetPointer() == 0) { //Q_ASSERT(d->ColorTransferFunction.GetPointer()); logger.warn("no ColorTransferFunction"); return -1; } double rgb[3]; QColor minValue = QColor::fromRgbF(0.,0.,0.); for (int i = 0; i < this->count(); ++i) { d->ColorTransferFunction->GetColor(i, rgb); Q_ASSERT(rgb[0] >= 0. && rgb[0] <= 1. && rgb[1] >= 0. && rgb[1] <= 1. && rgb[2] >= 0. && rgb[2] <= 1.); QColor color = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]); if ( qGray(color.red(), color.green(), color.blue()) > qGray(minValue.red(), minValue.green(), minValue.blue())) { minValue = color; } } return minValue; }
//---------------------------------------------------------------------------- void ctkVTKSliceView::forceRender() { Q_D(ctkVTKSliceView); if (!d->RenderEnabled || !this->isVisible()) { return; } logger.trace("forceRender"); d->RenderWindow->Render(); d->RenderPending = false; }
// -------------------------------------------------------------------------- void ctkWorkflowStep::setId(const QString& newId) { Q_D(ctkWorkflowStep); if (d->Workflow && d->Workflow->hasStep(newId) && !this->id().isEmpty()) { logger.error(QString("ctkWorkflowStep - Failed to change id from '%1' to '%2' - " "Step already added to a workflow !").arg(this->id()).arg(newId)); return; } d->Id = newId; }
// -------------------------------------------------------------------------- void ctkWorkflowGroupBox::updateGroupBox(ctkWorkflowStep* currentStep) { Q_D(ctkWorkflowGroupBox); d->StepShownPreviously = d->StepShownCurrently; d->StepShownCurrently = currentStep; if (currentStep) { this->setTitle(currentStep->name()); this->setSubTitle(currentStep->description()); this->setErrorText(currentStep->statusText()); // don't show textual elements if they are empty d->SubTitleTextBrowser->setVisible(!this->subTitle().isEmpty()); d->PreTextBrowser->setVisible(!this->preText().isEmpty()); d->PostTextBrowser->setVisible(!this->postText().isEmpty()); d->ErrorTextBrowser->setVisible(!this->errorText().isEmpty()); } // disable/hide the previously shown step if (ctkWorkflowWidgetStep* prevStep = dynamic_cast<ctkWorkflowWidgetStep*>(d->StepShownPreviously)) { logger.debug(QString("updateClientArea - hiding %1").arg(prevStep->name())); if (QWidget* stepArea = prevStep->stepArea()) { stepArea->setEnabled(false); if (d->HideWidgetsOfNonCurrentSteps) { stepArea->hide(); } } } ctkWorkflowWidgetStep* currentWidgetStep = dynamic_cast<ctkWorkflowWidgetStep*>(currentStep); // show/enable the current step if (currentWidgetStep) { currentWidgetStep->showUserInterface(); if (QWidget* stepArea = currentWidgetStep->stepArea()) { // add the step's client area to the widget if we haven't before if (!this->isAncestorOf(stepArea)) { d->ClientAreaLayout->addWidget(stepArea); } stepArea->setEnabled(true); stepArea->show(); } } }
//------------------------------------------------------------------------------ void ctkDICOMDatabasePrivate::registerCompressionLibraries(){ logger.debug("Register compression libraries"); // Register the JPEG libraries in case we need them // (registration only happens once, so it's okay to call repeatedly) // register global JPEG decompression codecs DJDecoderRegistration::registerCodecs(); // register global JPEG compression codecs DJEncoderRegistration::registerCodecs(); // register RLE compression codec DcmRLEEncoderRegistration::registerCodecs(); // register RLE decompression codec DcmRLEDecoderRegistration::registerCodecs(); }
//------------------------------------------------------------------------------ void ctkDICOMIndexer::addFile(ctkDICOMDatabase& database, const QString filePath, const QString& destinationDirectoryName) { std::cout << filePath.toStdString(); if (!destinationDirectoryName.isEmpty()) { logger.warn("Ignoring destinationDirectoryName parameter, just taking it as indication we should copy!"); } emit indexingFilePath(filePath); database.insert(filePath, !destinationDirectoryName.isEmpty(), true); }
//----------------------------------------------------------------------------- void ctkVTKHistogram::range(qreal& minRange, qreal& maxRange)const { Q_D(const ctkVTKHistogram); if (d->DataArray.GetPointer() == 0) { //Q_ASSERT(d->DataArray.GetPointer()); logger.warn("no dataArray"); minRange = 1.; // set incorrect values maxRange = 0.; return; } minRange = d->Range[0]; maxRange = d->Range[1]; }
//------------------------------------------------------------------------------ bool ctkDICOMDatabasePrivate::loggedExec(QSqlQuery& query, const QString& queryString) { bool success; if (queryString.compare("")) { success = query.exec(queryString); } else { success = query.exec(); } if (!success) { QSqlError sqlError = query.lastError(); logger.debug( "SQL failed\n Bad SQL: " + query.lastQuery()); logger.debug( "Error text: " + sqlError.text()); } else { logger.debug( "SQL worked!\n SQL: " + query.lastQuery()); } return (success); }
// -------------------------------------------------------------------------- void ctkWorkflowWidget::setWorkflow(ctkWorkflow* newWorkflow) { Q_D(ctkWorkflowWidget); if (!newWorkflow) { logger.error(QString("setWorkflow - cannot set workflow to NULL")); return; } if (d->Workflow) { QObject::disconnect(d->Workflow, SIGNAL(currentStepChanged(ctkWorkflowStep*)), this, SLOT(onCurrentStepChanged(ctkWorkflowStep))); }
//---------------------------------------------------------------------------- void ctkVTKSliceView::scheduleRender() { Q_D(ctkVTKSliceView); logger.trace("scheduleRender"); if (!d->RenderEnabled) { return; } if (!d->RenderPending) { d->RenderPending = true; QTimer::singleShot(0, this, SLOT(forceRender())); } }
//----------------------------------------------------------------------------- void ctkVTKColorTransferFunction::range(qreal& minRange, qreal& maxRange)const { Q_D(const ctkVTKColorTransferFunction); if (d->ColorTransferFunction.GetPointer() == 0) { //Q_ASSERT(d->ColorTransferFunction.GetPointer()); logger.warn("no ColorTransferFunction"); minRange = 1.; maxRange = 0.; return; } double rangeValues[2]; d->ColorTransferFunction->GetRange(rangeValues); minRange = rangeValues[0]; maxRange = rangeValues[1]; }
// -------------------------------------------------------------------------- void ctkWorkflowAbstractPagedWidget::associateStepWithPage(ctkWorkflowStep* step, int index) { Q_D(ctkWorkflowAbstractPagedWidget); if (index < 0) { logger.error(QString("Cannot associate step with a page of index -1")); return; } if (step) { d->StepToIndexMap[step] = index; if (!d->IndexToGroupBoxMap.contains(index)) { d->IndexToGroupBoxMap[index] = 0; } } }
// ------------------------------------------------------------------------- void ctkDICOMDatasetView::displayImage(int imageIndex){ Q_D(ctkDICOMDatasetView); if(d->CurrentImageIndex.isValid()) { QModelIndex seriesIndex = d->CurrentImageIndex.parent(); ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(seriesIndex.model())); if(model) { if(imageIndex >= 0 && imageIndex < model->rowCount(seriesIndex)) { this->onModelSelected(model->index(imageIndex, 0, seriesIndex)); } else { logger.debug("out of index"); } } } }
//---------------------------------------------------------------------------- void ctkDICOMThumbnailListWidgetPrivate ::addThumbnailWidget(const QModelIndex& imageIndex, const QModelIndex& sourceIndex, const QString &text) { ctkDICOMModel* model = const_cast<ctkDICOMModel*>( qobject_cast<const ctkDICOMModel*>(imageIndex.model())); if(!model) { return; } 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"; if(!QFileInfo(thumbnailPath).exists()) { return; } ctkThumbnailLabel* widget = new ctkThumbnailLabel(this->ScrollAreaContentWidget); QString widgetLabel = text; widget->setText( widgetLabel ); QPixmap pix(thumbnailPath); logger.debug("Setting pixmap to " + thumbnailPath); if(this->ThumbnailSize.isValid()) { widget->setFixedSize(this->ThumbnailSize); } widget->setPixmap(pix); QVariant var; var.setValue(QPersistentModelIndex(sourceIndex)); widget->setProperty("sourceIndex", var); this->addThumbnail(widget); }
// -------------------------------------------------------------------------- void ctkSettingsPanel::setSetting(const QString& key, const QVariant& newVal) { Q_D(ctkSettingsPanel); QSettings* settings = d->settings(key); if (!settings) { return; } QVariant oldVal = settings->value(key); settings->setValue(key, newVal); d->Properties[key].setValue(newVal); if (settings->status() != QSettings::NoError) { logger.warn( QString("Error #%1 while writing setting \"%2\"") .arg(static_cast<int>(settings->status())) .arg(key)); } if (oldVal != newVal) { emit settingChanged(key, newVal); } }
//---------------------------------------------------------------------------- void ctkDICOMThumbnailListWidgetPrivate ::addSeriesThumbnails(const QModelIndex &index) { QModelIndex studyIndex = index.parent(); QModelIndex seriesIndex = index; ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model())); if (!model) { return; } model->fetchMore(seriesIndex); const 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); this->addThumbnailWidget(imageIndex, imageIndex, QString("Image %1").arg(i)); } }
//------------------------------------------------------------------------------ ctkDICOMRetrievePrivate::ctkDICOMRetrievePrivate(ctkDICOMRetrieve& obj) : q_ptr(&obj) { this->Database = QSharedPointer<ctkDICOMDatabase> (0); this->WasCanceled = false; this->KeepAssociationOpen = true; this->ConnectionParamsChanged = false; this->LastRetrieveType = RetrieveNone; // Register the JPEG libraries in case we need them // (registration only happens once, so it's okay to call repeatedly) // register global JPEG decompression codecs DJDecoderRegistration::registerCodecs(); // register global JPEG compression codecs DJEncoderRegistration::registerCodecs(); // register RLE compression codec DcmRLEEncoderRegistration::registerCodecs(); // register RLE decompression codec DcmRLEDecoderRegistration::registerCodecs(); logger.info ( "Setting Transfer Syntaxes" ); OFList<OFString> transferSyntaxes; transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax ); transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax ); transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax ); this->SCU.addPresentationContext ( UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes ); this->SCU.addPresentationContext ( UID_GETStudyRootQueryRetrieveInformationModel, transferSyntaxes ); for (Uint16 i = 0; i < numberOfDcmLongSCUStorageSOPClassUIDs; i++) { this->SCU.addPresentationContext(dcmLongSCUStorageSOPClassUIDs[i], transferSyntaxes, ASC_SC_ROLE_SCP); } }
//------------------------------------------------------------------------------ bool ctkDICOMRetrieve::retrieveStudy( const QString& studyInstanceUID ) { Q_D(ctkDICOMRetrieve); logger.info ( "Starting retrieveStudy" ); return d->retrieve ( studyInstanceUID, ctkDICOMRetrievePrivate::RetrieveStudy ); }
//------------------------------------------------------------------------------ bool ctkDICOMRetrieve::retrieveSeries( const QString& seriesInstanceUID ) { Q_D(ctkDICOMRetrieve); logger.info ( "Starting retrieveSeries" ); return d->retrieve ( seriesInstanceUID, ctkDICOMRetrievePrivate::RetrieveSeries ); }
//------------------------------------------------------------------------------ bool ctkDICOMRetrievePrivate::retrieve ( QString UID, RetrieveType retriveType ) { if ( !this->RetrieveDatabase ) { logger.error ( "Must have RetrieveDatabase for retrieve transaction" ); return false; } // Register the JPEG libraries in case we need them // (registration only happens once, so it's okay to call repeatedly) // register global JPEG decompression codecs DJDecoderRegistration::registerCodecs(); // register global JPEG compression codecs DJEncoderRegistration::registerCodecs(); // register RLE compression codec DcmRLEEncoderRegistration::registerCodecs(); // register RLE decompression codec DcmRLEDecoderRegistration::registerCodecs(); // Set the DCMTK log level log4cplus::Logger rootLogger = log4cplus::Logger::getRoot(); rootLogger.setLogLevel(log4cplus::DEBUG_LOG_LEVEL); // TODO: use this->SCU instead ? DcmSCU scu; scu.setAETitle ( OFString(this->CallingAETitle.toStdString().c_str()) ); scu.setPort ( this->CallingPort ); scu.setPeerAETitle ( OFString(this->CalledAETitle.toStdString().c_str()) ); scu.setPeerHostName ( OFString(this->Host.toStdString().c_str()) ); scu.setPeerPort ( this->CalledPort ); scu.setMoveDestinationAETitle ( OFString(this->MoveDestinationAETitle.toStdString().c_str()) ); logger.info ( "Setting Transfer Syntaxes" ); OFList<OFString> transferSyntaxes; transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax ); transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax ); transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax ); scu.addPresentationContext ( UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes ); scu.addPresentationContext ( UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes ); if ( !scu.initNetwork().good() ) { logger.error ( "Error initializing the network" ); return false; } logger.debug ( "Negotiating Association" ); if ( !scu.negotiateAssociation().good() ) { logger.error ( "Error negotiating association" ); return false;; } logger.debug ( "Setting Parameters" ); // Clear the query unsigned long elements = this->parameters->card(); // Clean it out for ( unsigned long i = 0; i < elements; i++ ) { this->parameters->remove ( 0ul ); } if ( retriveType == RetrieveSeries ) { this->parameters->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" ); this->parameters->putAndInsertString ( DCM_SeriesInstanceUID, UID.toStdString().c_str() ); } else { this->parameters->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" ); this->parameters->putAndInsertString ( DCM_StudyInstanceUID, UID.toStdString().c_str() ); } logger.debug ( "Sending Move Request" ); MOVEResponses *responses = new MOVEResponses(); OFCondition status = scu.sendMOVERequest ( 0, this->parameters, responses ); if (!status.good()) { logger.error ( "MOVE Request failed: " + QString ( status.text() ) ); return false; } logger.debug ( "Find succeded" ); logger.debug ( "Making Output Directory" ); QDir directory = QDir( RetrieveDatabase->databaseDirectory() ); if ( responses->begin() == responses->end() ) { logger.error ( "No responses!" ); throw std::runtime_error( std::string("No responses!") ); } // Write the responses out to disk for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ ) { DcmDataset *dataset = (*it)->m_dataset; if ( dataset != NULL ) { logger.debug ( "Got a valid dataset" ); // Save in correct directory E_TransferSyntax output_transfersyntax = dataset->getOriginalXfer(); dataset->chooseRepresentation( output_transfersyntax, NULL ); if ( !dataset->canWriteXfer( output_transfersyntax ) ) { // Pick EXS_LittleEndianExplicit as our default output_transfersyntax = EXS_LittleEndianExplicit; } DcmXfer opt_oxferSyn( output_transfersyntax ); if ( !dataset->chooseRepresentation( opt_oxferSyn.getXfer(), NULL ).bad() ) { DcmFileFormat* fileformat = new DcmFileFormat ( dataset ); // Follow dcmdjpeg example fileformat->loadAllDataIntoMemory(); OFString SOPInstanceUID; dataset->findAndGetOFString ( DCM_SOPInstanceUID, SOPInstanceUID ); QFileInfo fi ( directory, QString ( SOPInstanceUID.c_str() ) ); logger.debug ( "Saving file: " + fi.absoluteFilePath() ); status = fileformat->saveFile ( fi.absoluteFilePath().toStdString().c_str(), opt_oxferSyn.getXfer() ); if ( !status.good() ) { logger.error ( "Error saving file: " + fi.absoluteFilePath() + " Error is " + status.text() ); } RetrieveDatabase->insert( dataset, true ); delete fileformat; } } } delete responses; //if ( !scu.dropNetwork().good() ) //{ //logger.error ( "Error dropping the network" ); //return false; //} return true; }
//------------------------------------------------------------------------------ void ctkDICOMDatabase::insert ( DcmDataset *dataset, bool storeFile, bool generateThumbnail) { Q_D(ctkDICOMDatabase); if (!dataset) { return; } // Check to see if the file has already been loaded OFString sopInstanceUID ; dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID); QSqlQuery fileExists ( d->Database ); fileExists.prepare("SELECT InsertTimestamp,Filename FROM Images WHERE SOPInstanceUID == ?"); fileExists.bindValue(0,QString(sopInstanceUID.c_str())); fileExists.exec(); if ( fileExists.next() && QFileInfo(fileExists.value(1).toString()).lastModified() < QDateTime::fromString(fileExists.value(0).toString(),Qt::ISODate) ) { logger.debug ( "File " + fileExists.value(1).toString() + " already added" ); 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_PatientName, patientsName); dataset->findAndGetOFString(DCM_StudyInstanceUID, studyInstanceUID); dataset->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID); dataset->findAndGetOFString(DCM_PatientID, patientID); dataset->findAndGetOFString(DCM_PatientBirthDate, patientsBirthDate); dataset->findAndGetOFString(DCM_PatientBirthTime, patientsBirthTime); dataset->findAndGetOFString(DCM_PatientSex, patientsSex); dataset->findAndGetOFString(DCM_PatientAge, 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_PerformingPhysicianName, performingPhysiciansName); dataset->findAndGetOFString(DCM_ReferringPhysicianName, 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); // store the file if the database is not in memomry QString filename; if ( storeFile && !this->isInMemory() ) { DcmFileFormat* fileformat = new DcmFileFormat ( dataset ); QString destinationDirectoryName = databaseDirectory() + "/dicom/"; QDir destinationDir(destinationDirectoryName); QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + seriesInstanceUID.c_str(); destinationDir.mkpath(studySeriesDirectory); filename = databaseDirectory() + "/dicom/" + pathForDataset(dataset); logger.debug ( "Saving file: " + filename ); OFCondition status = fileformat->saveFile ( filename.toAscii() ); if ( !status.good() ) { logger.error ( "Error saving file: " + filename + "\nError is " + status.text() ); delete fileformat; return; } delete fileformat; } QSqlQuery check_exists_query(d->Database); //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 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(); if (check_exists_query.next()) { patientUID = check_exists_query.value(check_exists_query.record().indexOf("UID")).toInt(); } else { // Insert it QSqlQuery statement ( d->Database ); statement.prepare ( "INSERT INTO Patients ('UID', 'PatientsName', 'PatientID', 'PatientsBirthDate', 'PatientsBirthTime', 'PatientsSex', 'PatientsAge', 'PatientsComments' ) values ( NULL, ?, ?, ?, ?, ?, ?, ? )" ); statement.bindValue ( 0, QString ( patientsName.c_str() ) ); statement.bindValue ( 1, QString ( patientID.c_str() ) ); statement.bindValue ( 2, QString ( patientsBirthDate.c_str() ) ); statement.bindValue ( 3, QString ( patientsBirthTime.c_str() ) ); statement.bindValue ( 4, QString ( patientsSex.c_str() ) ); // TODO: shift patient's age to study, since this is not a patient level attribute in images // statement.bindValue ( 5, QString ( patientsAge.c_str() ) ); statement.bindValue ( 6, QString ( patientComments.c_str() ) ); statement.exec (); 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->Database ); 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->Database ); 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, static_cast<int>(seriesNumber) ); statement.bindValue ( 3, QString ( seriesDate.c_str() ) ); statement.bindValue ( 4, QDate::fromString ( seriesTime.c_str(), "yyyyMMdd" ) ); statement.bindValue ( 5, QString ( seriesDescription.c_str() ) ); statement.bindValue ( 6, QString ( bodyPartExamined.c_str() ) ); statement.bindValue ( 7, QString ( frameOfReferenceUID.c_str() ) ); statement.bindValue ( 8, static_cast<int>(acquisitionNumber) ); statement.bindValue ( 9, QString ( contrastAgent.c_str() ) ); statement.bindValue ( 10, QString ( scanningSequence.c_str() ) ); statement.bindValue ( 11, static_cast<int>(echoNumber) ); statement.bindValue ( 12, static_cast<int>(temporalPosition) ); if ( !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->Database ); statement.prepare ( "INSERT INTO Images ( 'SOPInstanceUID', 'Filename', 'SeriesInstanceUID', 'InsertTimestamp' ) VALUES ( ?, ?, ?, ? )" ); statement.bindValue ( 0, QString ( sopInstanceUID.c_str() ) ); statement.bindValue ( 1, filename ); statement.bindValue ( 2, QString ( seriesInstanceUID.c_str() ) ); statement.bindValue ( 3, QDateTime::currentDateTime() ); statement.exec(); } } if(generateThumbnail){ if(d->thumbnailGenerator){ QString studySeriesDirectory = QString(studyInstanceUID.c_str()) + "/" + QString(seriesInstanceUID.c_str()); //Create thumbnail here QString thumbnailPath = databaseDirectory() + "/thumbs/" + this->pathForDataset(dataset) + ".png"; //QString(studyInstanceUID.c_str()) + "/" + //QString(seriesInstanceUID.c_str()) + "/" + //QString(sopInstanceUID.c_str()) + ".png"; QFileInfo thumbnailInfo(thumbnailPath); if(!(thumbnailInfo.exists() && (thumbnailInfo.lastModified() > QFileInfo(filename).lastModified()))){ QDir(databaseDirectory() + "/thumbs/").mkpath(studySeriesDirectory); DicomImage dcmImage(QDir::toNativeSeparators(filename).toAscii()); d->thumbnailGenerator->generateThumbnail(&dcmImage, thumbnailPath); } } } if (isInMemory()) { emit databaseChanged(); } }
// ------------------------------------------------------------------------- void ctkDICOMDatasetView::addImage( DicomImage & dcmImage, bool defaultIntensity ) { Q_D(ctkDICOMDatasetView); QImage image; // Check whether we have a valid image EI_Status result = dcmImage.getStatus(); if (result != EIS_Normal) { logger.error(QString("Rendering of DICOM image failed for thumbnail failed: ") + DicomImage::getString(result)); return; } // Select first window defined in image. If none, compute min/max window as best guess. // Only relevant for monochrome if (d->AutoWindowLevel) { if (dcmImage.isMonochrome()) { if (defaultIntensity && dcmImage.getWindowCount() > 0) { dcmImage.setWindow(0); } else { dcmImage.setMinMaxWindow(OFTrue /* ignore extreme values */); dcmImage.getWindow(d->DicomIntensityLevel, d->DicomIntensityWindow); } } } else { dcmImage.setWindow(d->DicomIntensityLevel, d->DicomIntensityWindow); } /* get image extension and prepare image header */ const unsigned long width = dcmImage.getWidth(); const unsigned long height = dcmImage.getHeight(); unsigned long offset = 0; unsigned long length = 0; QString header; if (dcmImage.isMonochrome()) { // write PGM header (binary monochrome image format) header = QString("P5 %1 %2 255\n").arg(width).arg(height); offset = header.length(); length = width * height + offset; } else { // write PPM header (binary color image format) header = QString("P6 %1 %2 255\n").arg(width).arg(height); offset = header.length(); length = width * height * 3 /* RGB */ + offset; } /* create output buffer for DicomImage class */ QByteArray buffer; /* copy header to output buffer and resize it for pixel data */ buffer.append(header); buffer.resize(length); /* render pixel data 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"); } } this->addImage(image); }
//------------------------------------------------------------------------------ bool ctkDICOMRetrievePrivate::get ( const QString& studyInstanceUID, const QString& seriesInstanceUID, const RetrieveType retrieveType ) { Q_Q(ctkDICOMRetrieve); DcmDataset *retrieveParameters = new DcmDataset(); if (! this->initializeSCU(studyInstanceUID, seriesInstanceUID, retrieveType, retrieveParameters) ) { delete retrieveParameters; return false; } // Issue request logger.debug ( "Sending Get Request" ); emit q->progress("Sending Get Request"); emit q->progress(0); OFList<RetrieveResponse*> responses; T_ASC_PresentationContextID presID = this->SCU.findPresentationContextID( UID_GETStudyRootQueryRetrieveInformationModel, "" /* don't care about transfer syntax */ ); if (presID == 0) { logger.error ( "GET Request failed: No valid Study Root GET Presentation Context available" ); if (!this->KeepAssociationOpen) { this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); } delete retrieveParameters; return false; } emit q->progress("Found Presentation Context"); emit q->progress(1); // do the actual move request OFCondition status = this->SCU.sendCGETRequest ( presID, retrieveParameters, &responses ); emit q->progress("Sent Get Request"); emit q->progress(2); // Close association if we do not want to explicitly keep it open if (!this->KeepAssociationOpen) { this->SCU.closeAssociation(DCMSCU_RELEASE_ASSOCIATION); } // Free some (little) memory delete retrieveParameters; // If we do not receive a single response, something is fishy if ( responses.begin() == responses.end() ) { logger.error ( "No responses received at all! (at least one empty response always expected)" ); //throw std::runtime_error( std::string("No responses received from server!") ); emit q->progress("No Responses from Server!"); return false; } emit q->progress("Got Responses"); emit q->progress(3); /* The server is permitted to acknowledge every image that was received, or * to send a single move response. * If there is only a single response, this can mean the following: * 1) No images to transfer (Status Success and Number of Completed Subops = 0) * 2) All images transferred (Status Success and Number of Completed Subops > 0) * 3) Error code, i.e. no images transferred * 4) Warning (one or more failures, i.e. some images transferred) */ if ( responses.size() == 1 ) { RetrieveResponse* rsp = *responses.begin(); logger.debug ( "GET response receveid with status: " + QString(DU_cmoveStatusString(rsp->m_status)) ); if ( (rsp->m_status == STATUS_Success) || (rsp->m_status == STATUS_GET_Warning_SubOperationsCompleteOneOrMoreFailures)) { if (rsp->m_numberOfCompletedSubops == 0) { logger.error ( "No images transferred by PACS!" ); //throw std::runtime_error( std::string("No images transferred by PACS!") ); return false; } } else { logger.error("GET request failed, server does report error"); QString statusDetail("No details"); if (rsp->m_statusDetail != NULL) { std::ostringstream out; rsp->m_statusDetail->print(out); statusDetail = "Status Detail: " + statusDetail.fromStdString(out.str()); } statusDetail.prepend("GET request failed: "); logger.error(statusDetail); //throw std::runtime_error( statusDetail.toStdString() ); return false; } } // Select the last GET response to output meaningful status information OFIterator<RetrieveResponse*> it = responses.begin(); Uint32 numResults = responses.size(); for (Uint32 i = 1; i < numResults; i++) { it++; } logger.debug ( "GET responses report for study: " + studyInstanceUID +"\n" + QString::number(static_cast<unsigned int>((*it)->m_numberOfCompletedSubops)) + " images transferred, and\n" + QString::number(static_cast<unsigned int>((*it)->m_numberOfWarningSubops)) + " images transferred with warning, and\n" + QString::number(static_cast<unsigned int>((*it)->m_numberOfFailedSubops)) + " images transfers failed"); emit q->progress("Finished Get"); emit q->progress(100); return true; }