diff --git a/Modules/REST/src/mitkRESTClient.cpp b/Modules/REST/src/mitkRESTClient.cpp index da48c24691..f212cfe78b 100644 --- a/Modules/REST/src/mitkRESTClient.cpp +++ b/Modules/REST/src/mitkRESTClient.cpp @@ -1,212 +1,214 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include <mitkExceptionMacro.h> #include <mitkRESTClient.h> #include <mitkRESTUtil.h> #include <cpprest/filestream.h> #include <cpprest/http_client.h> using http_client = web::http::client::http_client; using http_request = web::http::http_request; using http_response = web::http::http_response; using methods = web::http::methods; using status_codes = web::http::status_codes; using file_buffer = concurrency::streams::file_buffer<uint8_t>; using streambuf = concurrency::streams::streambuf<uint8_t>; mitk::RESTClient::RESTClient() { m_ClientConfig.set_validate_certificates(false); } mitk::RESTClient::~RESTClient() {} pplx::task<web::json::value> mitk::RESTClient::Get(const web::uri &uri, const std::map<utility::string_t, utility::string_t> headers) { auto client = new http_client(uri, m_ClientConfig); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return client->request(request).then([=](pplx::task<web::http::http_response> responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) + { MITK_INFO << status; - MITK_INFO << mitk::RESTUtil::convertToUtf8(response.to_string()); - mitkThrow(); - + MITK_INFO << mitk::RESTUtil::convertToUtf8(response.to_string()); + mitkThrow(); + } + auto requestContentType = response.headers().content_type(); if (U("application/json") != requestContentType) response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } catch (std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } pplx::task<web::json::value> mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath, const std::map<utility::string_t, utility::string_t> headers) { auto client = new http_client(uri, m_ClientConfig); auto fileBuffer = std::make_shared<concurrency::streams::streambuf<uint8_t>>(); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } // Open file stream for the specified file path return file_buffer::open(filePath, std::ios::out) .then([=](streambuf outFile) -> pplx::task<http_response> { *fileBuffer = outFile; return client->request(methods::GET); }) // Write the response body into the file buffer .then([=](http_response response) -> pplx::task<size_t> { auto status = response.status_code(); if (status_codes::OK != status) mitkThrow() << "GET ended up with response " << RESTUtil::convertToUtf8(response.to_string()); return response.body().read_to_end(*fileBuffer); }) // Close the file buffer .then([=](size_t) { return fileBuffer->close(); }) // Return empty JSON object .then([=]() { return web::json::value(); }); } pplx::task<web::json::value> mitk::RESTClient::Put(const web::uri &uri, const web::json::value *content) { auto client = new http_client(uri, m_ClientConfig); http_request request(methods::PUT); if (nullptr != content) request.set_body(*content); return client->request(request).then([=](pplx::task<http_response> responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) mitkThrow(); // Parse content type to application/json if it isn't already. This is // important if the content type is e.g. application/dicom+json. auto requestContentType = response.headers().content_type(); if (U("application/json") != requestContentType) response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } catch (std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } pplx::task<web::json::value> mitk::RESTClient::Post(const web::uri &uri, const std::vector<unsigned char> *content, const std::map<utility::string_t, utility::string_t> headers) { auto request = InitRequest(headers); request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); return ExecutePost(uri, request); } pplx::task<web::json::value> mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content, const std::map<utility::string_t, utility::string_t> headers) { auto request = InitRequest(headers); request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); return ExecutePost(uri, request); } http_request mitk::RESTClient::InitRequest(const std::map<utility::string_t, utility::string_t> headers) { http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return request; } pplx::task<web::json::value> mitk::RESTClient::ExecutePost(const web::uri &uri, http_request request) { auto client = new http_client(uri, m_ClientConfig); return client->request(request).then([=](pplx::task<http_response> responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); auto requestContentType = response.headers().content_type(); MITK_INFO << status; if (status_codes::OK != status) mitkThrow(); MITK_INFO << mitk::RESTUtil::convertToUtf8(requestContentType); if (U("application/json") != requestContentType) { auto json = web::json::value::object(); json[U("test")] = web::json::value(17); response.set_body(json); // use empty json object in response body to perform return value response.headers().set_content_type(U("application/json")); } return response.extract_json().get(); } catch (std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } diff --git a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp index e4cad879f0..4d6fb12ab5 100644 --- a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp +++ b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp @@ -1,312 +1,356 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include <berryISelectionService.h> #include <berryIWorkbenchWindow.h> // Qmitk #include "InjectView.h" #include <Poco/File.h> #include <itkFileTools.h> // Qt #include <QMessageBox> #include <QProgressBar> #include <QmitkNewSegmentationDialog.h> #include <mitkExtractSliceFilter.h> #include <mitkIOUtil.h> #include <mitkImagePixelWriteAccessor.h> #include <mitkOverwriteSliceImageFilter.h> #include <mitkSegTool2D.h> #include <mitkSegmentationInterpolationController.h> #include <mitkToolManagerProvider.h> #include <mitkVtkImageOverwrite.h> #include <usGetModuleContext.h> #include <usModule.h> #include <usModuleRegistry.h> #include <usServiceTracker.h> #include <mitkDICOMweb.h> #include <mitkDICOMDCMTKTagScanner.h> #include <mitkDICOMQIIOMimeTypes.h> +#include <mitkUIDGenerator.h> const std::string InjectView::VIEW_ID = "org.mitk.views.injectview"; void InjectView::SetFocus() {} void InjectView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; qRegisterMetaType<std::vector<std::string>>("std::vector<std::string>"); qRegisterMetaType<std::vector<double>>("std::vector<double>"); qRegisterMetaType<InjectRequestHandler::DicomDTO>("DicomDTO"); m_Controls.cleanDicomBtn->setVisible(true); connect(m_Controls.cleanDicomBtn, &QPushButton::clicked, this, &InjectView::CleanDicomFolder); connect(m_Controls.restartConnection, &QPushButton::clicked, this, &InjectView::OnRestartConnection); connect(m_Controls.testConnection, &QPushButton::clicked, this, &InjectView::TestConnection); connect(m_Controls.saveDICOMBtn, &QPushButton::clicked, this, &InjectView::SaveSelectedDICOMObject); m_DownloadBaseDir = mitk::IOUtil::GetTempPath() + "segInject"; MITK_INFO << "using download base dir: " << m_DownloadBaseDir; m_UploadBaseDir = mitk::IOUtil::GetTempPath() + "uploadSeg"; if (!itksys::SystemTools::FileIsDirectory(m_DownloadBaseDir)) { itk::FileTools::CreateDirectory(m_DownloadBaseDir); } if (!itksys::SystemTools::FileIsDirectory(m_UploadBaseDir)) { itk::FileTools::CreateDirectory(m_UploadBaseDir); } m_HostURL = U("http://localhost"); auto host = m_HostURL; auto envHostURL = std::getenv("HOST_URL"); if (envHostURL) { MITK_INFO << "host url " << std::string(envHostURL); m_HostURL = mitk::RESTUtil::convertToTString(std::string(envHostURL)); host = m_HostURL; } else { host = host.append(U(":8080")); } m_restURL = host.append(U("/rest-srs")); MITK_INFO << "rest url: " << mitk::RESTUtil::convertToUtf8(m_restURL); utility::string_t pacsURL = U("http://193.174.48.78:8090"); auto envPacsURL = std::getenv("PACS_URL"); if (envPacsURL) { pacsURL = mitk::RESTUtil::convertToTString(std::string(envPacsURL)); } m_RequestHandler = new InjectRequestHandler(m_DownloadBaseDir, pacsURL); connect(this, &InjectView::InvokeProgress, this, &InjectView::AddProgress); connect(m_RequestHandler, &InjectRequestHandler::InvokeProgress, this, &InjectView::AddProgress); connect(m_RequestHandler, &InjectRequestHandler::InvokeUpdateDcmMeta, this, &InjectView::InitializeDcmMeta); connect(m_RequestHandler, &InjectRequestHandler::InvokeLoadData, this, &InjectView::LoadData); + // patient image selection predicates + auto isNotAHelperObject = + mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); + + mitk::NodePredicateProperty::Pointer isBinaryPredicate = + mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New(isBinaryPredicate); + + mitk::NodePredicateAnd::Pointer isABinaryImagePredicate = + mitk::NodePredicateAnd::New(isNotAHelperObject, isBinaryPredicate); + mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = + mitk::NodePredicateAnd::New(isNotAHelperObject, isNotBinaryPredicate); + + auto isAPatientImagePredicate = mitk::NodePredicateAnd::New( + isNotABinaryImagePredicate, mitk::NodePredicateNot::New(mitk::TNodePredicateDataType<mitk::LabelSetImage>::New())); + auto isASegmentationImagePredicate = + mitk::NodePredicateOr::New(isABinaryImagePredicate, mitk::TNodePredicateDataType<mitk::LabelSetImage>::New()); + + m_Controls.patImageSelector->SetPredicate(isAPatientImagePredicate); + m_Controls.patImageSelector->SetDataStorage(GetDataStorage()); + m_Controls.segImageSelector->SetPredicate(isASegmentationImagePredicate); + m_Controls.segImageSelector->SetDataStorage(GetDataStorage()); + + // Get the micro service us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference<mitk::IRESTManager>(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_ManagerService = managerService; } } // Setup PACS connection RestartConnection(pacsURL); } void InjectView::Activated() { MITK_INFO << "activated"; StartServer(); } void InjectView::Deactivated() { MITK_INFO << "deactivated"; m_ManagerService->HandleDeleteObserver(m_RequestHandler); } void InjectView::Visible() {} void InjectView::Hidden() {} void InjectView::StartServer() { utility::string_t path = U("/inject"); auto pathEnv = std::getenv("SERVER_PATH"); if (pathEnv) { path = mitk::RESTUtil::convertToTString(std::string(pathEnv)); } utility::string_t port = U(":4040"); utility::string_t address = U("http://+"); if (m_HostURL == U("http://localhost")) address = m_HostURL; address.append(port); address.append(path); // Setup listening server m_ManagerService->ReceiveRequest(address, m_RequestHandler); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); } void InjectView::AddProgress(int progress, QString status) { auto futureValue = m_Controls.progressBar->value() + progress; if (futureValue >= 100) { m_Controls.progressBar->setValue(0); m_Controls.progressBar->setFormat(""); } else { m_Controls.progressBar->setFormat(status.append(" %p%")); m_Controls.progressBar->setValue(futureValue); } } void InjectView::InitializeDcmMeta(InjectRequestHandler::DicomDTO dto) { m_CurrentStudyUID = mitk::RESTUtil::convertToUtf8(dto.studyUID); m_SRUID = mitk::RESTUtil::convertToUtf8(dto.srSeriesUID); m_GroundTruth = mitk::RESTUtil::convertToUtf8(dto.groundTruth); } pplx::task<bool> InjectView::TestConnection() { mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("limit"), U("1"))); m_Controls.connectionStatus->setText(QString("Testing connection ...")); return m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](pplx::task<web::json::value> resultTask) { try { auto result = resultTask.get(); if (!result.is_null()) { m_Controls.connectionStatus->setText(QString("Connection works!")); return true; } else { m_Controls.connectionStatus->setText(QString("Trouble with connection. Not valid!")); return false; } } catch (mitk::Exception &e) { MITK_WARN << e.what(); m_Controls.connectionStatus->setText(QString("No connection possible.")); return false; } }); } void InjectView::OnRestartConnection() { RestartConnection(mitk::RESTUtil::convertToTString(m_Controls.dcm4cheeHostValue->text().toStdString())); } void InjectView::RestartConnection(utility::string_t newHost) { utility::string_t host; if (newHost.empty()) { MITK_INFO << "Host was empty"; m_Controls.connectionStatus->setText(QString("Host must not be empty!")); return; } utility::string_t url = newHost + U("/dcm4chee-arc/aets/DCM4CHEE/"); MITK_INFO << "Restarting connection to " << mitk::RESTUtil::convertToUtf8(url) << " ..."; m_Controls.connectionStatus->setText(QString("Restarting connection...")); m_Controls.dcm4cheeURL->setText({(utility::conversions::to_utf8string(url).c_str())}); m_DicomWeb = mitk::DICOMweb(url); if (!TestConnection().get()) { MITK_INFO << "Restart did not work.."; m_Controls.connectionStatus->setText(QString("No PACS server available under given host!")); } else { MITK_INFO << "requests to pacs are sent to: " << mitk::RESTUtil::convertToUtf8(url); } } mitk::DataStorage::SetOfObjects::Pointer InjectView::LoadData(std::vector<std::string> filePathList) { MITK_INFO << "Pushing data to data storage ..."; auto ds = GetDataStorage(); auto dataNodes = mitk::IOUtil::Load(filePathList, *ds); // reinit view mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(ds); return dataNodes; } void InjectView::SaveSelectedDICOMObject() { - auto ds = GetDataStorage(); - auto node = ds->GetAll()->front(); + auto segNode = m_Controls.segImageSelector->GetSelectedNode(); std::string path; - node->GetStringProperty("path", path); + std::string name = mitk::UIDGenerator("", 8).GetUID(); + segNode->GetStringProperty("path", path); + path.append("/").append(name).append(".dcm"); + + MITK_INFO << path; + + // add property + auto imageNode = m_Controls.patImageSelector->GetSelectedNode(); + + auto referencedImages = imageNode->GetData()->GetProperty("files"); + segNode->GetData()->SetProperty("referenceFiles", referencedImages); + + const std::string mimeType = mitk::MitkDICOMQIIOMimeTypes::DICOMSEG_MIMETYPE_NAME(); + mitk::IOUtil::Save(segNode->GetData(), mimeType, path); + emit InvokeProgress(20, {"created new DICOM object locally"}); auto scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTagPath studyUID(0x0020, 0x000D); // study UID mitk::StringList files; files.push_back(path); scanner->SetInputFiles(files); scanner->AddTagPath(studyUID); scanner->Scan(); + emit InvokeProgress(10, {"dicom file scanned for tags"}); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); auto findings = frames.front()->GetTagValueAsString(studyUID); auto studyUIDValue = findings.front().value; + emit InvokeProgress(10, {"found study uid"}); try { + emit InvokeProgress(30, {"storing DICOM object..."}); m_DicomWeb.SendSTOW(mitk::RESTUtil::convertToTString(path), mitk::RESTUtil::convertToTString(studyUIDValue)) - .then([=] { MITK_INFO << "STOW was successfull"; }); + .then([=] { + emit InvokeProgress(30, {"DICOM object stored."}); + MITK_INFO << "STOW was successfull"; + }); } catch (const std::exception &exception) { std::cout << exception.what() << std::endl; } } void InjectView::CleanDicomFolder() { if (m_SegA || m_SegB || m_SegC) { QMessageBox::warning(nullptr, tr("Clean dicom folder"), tr("Please remove the data in data storage before cleaning the download folder")); return; } auto downloadDir = Poco::File(m_DownloadBaseDir); downloadDir.remove(true); } diff --git a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui index 5cfa964388..ffb6aafc85 100644 --- a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui +++ b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui @@ -1,133 +1,162 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>InjectViewControls</class> <widget class="QWidget" name="InjectViewControls"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>465</width> <height>868</height> </rect> </property> <property name="minimumSize"> <size> <width>0</width> <height>0</height> </size> </property> <property name="windowTitle"> <string>QmitkTemplate</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QFormLayout" name="formLayout"> <item row="0" column="0"> <widget class="QLabel" name="dcm4cheeHostLabel"> <property name="text"> <string>dcm4chee host</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QLineEdit" name="dcm4cheeHostValue"> <property name="placeholderText"> <string>example input: http://193.174.48.78:8090</string> </property> </widget> </item> <item row="6" column="1"> <widget class="QProgressBar" name="progressBar"> <property name="value"> <number>0</number> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="textVisible"> <bool>true</bool> </property> </widget> </item> <item row="3" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPushButton" name="restartConnection"> <property name="text"> <string>restart connection</string> </property> </widget> </item> </layout> </item> <item row="5" column="1"> <widget class="QLabel" name="connectionStatus"> <property name="text"> <string/> </property> </widget> </item> <item row="4" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> <widget class="QLabel" name="dcm4cheeURL"> <property name="text"> <string/> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> </widget> </item> <item> <widget class="QPushButton" name="testConnection"> <property name="text"> <string>test connection</string> </property> </widget> </item> </layout> </item> </layout> </item> <item> - <widget class="QWidget" name="verticalWidget" native="true"> - <layout class="QVBoxLayout" name="verticalLayout_3"/> - </widget> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QmitkDataStorageComboBox" name="patImageSelector"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Patient Image</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QmitkDataStorageComboBox" name="segImageSelector"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Segmentation</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"/> </item> <item> <widget class="QPushButton" name="saveDICOMBtn"> <property name="text"> <string>Save Selected DICOM Object</string> </property> </widget> </item> <item> <spacer name="spacer1"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeType"> <enum>QSizePolicy::Expanding</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>220</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="cleanDicomBtn"> <property name="text"> <string>clean dicom download folder</string> </property> </widget> </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>QmitkDataStorageComboBox</class> + <extends>QComboBox</extends> + <header location="global">QmitkDataStorageComboBox.h</header> + </customwidget> + </customwidgets> <resources/> <connections/> </ui>