diff --git a/Modules/REST/include/mitkRESTClient.h b/Modules/REST/include/mitkRESTClient.h index 5cf46e094a..ce62788691 100644 --- a/Modules/REST/include/mitkRESTClient.h +++ b/Modules/REST/include/mitkRESTClient.h @@ -1,104 +1,104 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkRESTClient_h #define mitkRESTClient_h #include #include namespace mitk { class MITKREST_EXPORT RESTClient { public: using http_request = web::http::http_request; RESTClient(); ~RESTClient(); /** * @brief Executes a HTTP GET request with the given uri and returns a task waiting for a json object * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Get(const web::uri &uri, const std::map headers); /** * @brief Executes a HTTP GET request with the given uri and and stores the byte stream in a file given by the * filePath * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for returning an empty json object */ pplx::task Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers); /** * @brief Executes a HTTP PUT request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resources * @return task to wait for with resulting json object */ pplx::task Put(const web::uri &uri, const web::json::value *content); /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const web::json::value *content, const std::map headers); /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const std::vector *content, const std::map headers); private: /** * @brief Use this to create and init a new request with the given headers. If needed, set the body on the resulting * request object to avoid an automatic change of the content type header when setting the body first. */ http_request InitRequest(const std::map headers); - void CheckResponseContentType(web::http::http_response &response); + bool CheckResponseContentType(web::http::http_response &response); pplx::task ExecutePost(const web::uri &uri, http_request request); web::http::client::http_client_config m_ClientConfig; }; } // namespace mitk #endif diff --git a/Modules/REST/src/mitkRESTClient.cpp b/Modules/REST/src/mitkRESTClient.cpp index 11c66a519f..d561c11156 100644 --- a/Modules/REST/src/mitkRESTClient.cpp +++ b/Modules/REST/src/mitkRESTClient.cpp @@ -1,203 +1,211 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include 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; using streambuf = concurrency::streams::streambuf; mitk::RESTClient::RESTClient() { m_ClientConfig.set_validate_certificates(false); } mitk::RESTClient::~RESTClient() {} -void mitk::RESTClient::CheckResponseContentType(web::http::http_response &response) +bool mitk::RESTClient::CheckResponseContentType(web::http::http_response &response) { auto status = response.status_code(); if (status_codes::OK != status) { MITK_WARN << "Status: " << status; MITK_WARN << "Response: " << mitk::RESTUtil::convertToUtf8(response.to_string()); mitkThrow() << mitk::RESTUtil::convertToUtf8(response.to_string()); } auto requestContentType = response.headers().content_type(); MITK_DEBUG << "Content Type: " << mitk::RESTUtil::convertToUtf8(requestContentType); MITK_DEBUG << "Body: " << mitk::RESTUtil::convertToUtf8(response.to_string()); if (requestContentType.find(U("json")) != std::wstring::npos) { MITK_DEBUG << "Caution! The given response content type was '" << mitk::RESTUtil::convertToUtf8(requestContentType) << "' but contains 'json'. So we awesome the answer actually contains a JSON message."; response.headers().set_content_type(U("application/json")); + return false; } + return true; } pplx::task mitk::RESTClient::Get(const web::uri &uri, const std::map 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 responseTask) { try { auto response = responseTask.get(); - CheckResponseContentType(response); - - return response.extract_json().get(); + bool isjson = CheckResponseContentType(response); + if (isjson) + return response.extract_json().get(); + web::http::http_response dummy; + return dummy.extract_json().get(); } catch (const std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong: " << e.what(); } }); } pplx::task mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers) { auto client = new http_client(uri, m_ClientConfig); auto fileBuffer = std::make_shared>(); 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 { *fileBuffer = outFile; return client->request(methods::GET); }) // Write the response body into the file buffer .then([=](http_response response) -> pplx::task { auto status = response.status_code(); if (status_codes::OK != status) { MITK_INFO << status; MITK_INFO << mitk::RESTUtil::convertToUtf8(response.to_string()); mitkThrow() << mitk::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 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 responseTask) { try { auto response = responseTask.get(); - CheckResponseContentType(response); - - return response.extract_json().get(); + bool isjson = CheckResponseContentType(response); + if (isjson) + return response.extract_json().get(); + web::http::http_response dummy; + return dummy.extract_json().get(); } catch (std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const std::vector *content, const std::map headers) { auto request = InitRequest(headers); request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); return ExecutePost(uri, request); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content, const std::map 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 headers) { http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return request; } pplx::task 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 responseTask) { try { auto response = responseTask.get(); - CheckResponseContentType(response); - - return response.extract_json().get(); + bool isjson = CheckResponseContentType(response); + if (isjson) + return response.extract_json().get(); + web::http::http_response dummy; + return dummy.extract_json().get(); } catch (std::exception &e) { MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } diff --git a/Plugins/org.mitk.gui.qt.dicomweb/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.dicomweb/manifest_headers.cmake index 3b91ca1325..d605fb6d39 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.dicomweb/manifest_headers.cmake @@ -1,5 +1,8 @@ set(Plugin-Name "Dicomweb") set(Plugin-Version "0.1") set(Plugin-Vendor "German Cancer Research Center (DKFZ)") set(Plugin-ContactAddress "http://www.mitk.org") -set(Require-Plugin org.mitk.gui.qt.common) +set(Require-Plugin + org.mitk.gui.qt.common + org.blueberry.ui.qt + ) \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.cpp b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.cpp index 2857617820..55e28c0793 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.cpp +++ b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.cpp @@ -1,303 +1,327 @@ /*=================================================================== 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 #include #include "DicomWebRequestHandler.h" #include #include #include US_INITIALIZE_MODULE DicomWebRequestHandler::DicomWebRequestHandler() {} DicomWebRequestHandler::DicomWebRequestHandler(std::string downloadDir, utility::string_t pacsURI) : m_DownloadDir{downloadDir} { m_DicomWeb = mitk::DICOMweb(pacsURI + U("/dcm4chee-arc/aets/DCM4CHEE/")); } DicomWebRequestHandler::DicomDTO DicomWebRequestHandler::ExtractDTO(const web::json::value &data) { DicomDTO dto; auto messageTypeKey = data.at(U("messageType")); if (messageTypeKey.as_string() == U("downloadData")) { MITK_INFO << "within extract dto"; auto imageStudyUIDKey = data.at(U("studyUID")); auto srSeriesUIDKey = data.at(U("srSeriesUID")); auto groundTruthKey = data.at(U("groundTruth")); auto simScoreKey = data.at(U("simScoreArray")); auto minSliceStartKey = data.at(U("minSliceStart")); dto.srSeriesUID = srSeriesUIDKey.as_string(); dto.groundTruth = groundTruthKey.as_string(); dto.studyUID = imageStudyUIDKey.as_string(); dto.minSliceStart = minSliceStartKey.as_integer(); std::vector vec; web::json::array simArray = simScoreKey.as_array(); for (web::json::value score : simArray) { vec.push_back(score.as_double() * 100); } dto.simScoreArray = vec; } return dto; } web::http::http_response DicomWebRequestHandler::Notify(const web::uri &uri, const web::json::value &data, const web::http::method &method, const mitk::RESTUtil::ParamMap &headers) { headers.size(); MITK_INFO << "Incoming notify"; if (method == web::http::methods::GET) { return HandleGet(uri, data); } else if (method == web::http::methods::PUT) { return HandlePut(uri, data); } + else if (method == web::http::methods::POST) + { + return HandlePost(uri, data, headers); + } else if (method == web::http::methods::OPTIONS) { return HandleOptions(uri, data); } web::http::http_response response(web::http::status_codes::BadGateway); response.set_body(U("No one can handle http method from request")); // TODO: include method name return response; } web::http::http_response DicomWebRequestHandler::HandlePut(const web::uri &uri, const web::json::value &data) { if (uri.to_string() != U("/inject")) // avoid unused warning MITK_INFO << "no inject path"; emit InvokeProgress(20, {"display graph and query structured report"}); if (data == web::json::value()) { MITK_INFO << "no data in body"; web::http::http_response response(web::http::status_codes::BadRequest); response.set_body(U("No data in body of request")); // TODO: include method name return response; } MITK_INFO << mitk::RESTUtil::convertToUtf8(data.serialize()); DicomDTO dto = ExtractDTO(data); MITK_INFO << mitk::RESTUtil::convertToUtf8(dto.imageSeriesUID); emit InvokeSimilarityGraph(dto.simScoreArray, dto.minSliceStart); emit InvokeUpdateDcmMeta(dto); mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("StudyInstanceUID"), dto.studyUID)); seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("SeriesInstanceUID"), dto.srSeriesUID)); seriesInstancesParams.insert( mitk::RESTUtil::ParamMap::value_type(U("includefield"), U("0040A375"))); // Current Requested Procedure Evidence Sequence try { MITK_INFO << "send qido request"; m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](web::json::value jsonResult) { auto firstResult = jsonResult[0]; auto actualListKey = firstResult.at(U("0040A375")) .as_object() .at(U("Value")) .as_array()[0] .as_object() .at(U("00081115")) .as_object() .at(U("Value")) .as_array(); MITK_INFO << "received qido response"; utility::string_t segSeriesUIDA = {}; utility::string_t segSeriesUIDB = {}; utility::string_t imageSeriesUID = {}; for (unsigned int index = 0; index < actualListKey.size(); index++) { auto element = actualListKey[index].as_object(); // get SOP class UID auto innerElement = element.at(U("00081199")).as_object().at(U("Value")).as_array()[0]; auto sopClassUID = innerElement.at(U("00081150")).as_object().at(U("Value")).as_array()[0].as_string(); auto seriesUID = element.at(U("0020000E")).as_object().at(U("Value")).as_array()[0].as_string(); if (sopClassUID == U("1.2.840.10008.5.1.4.1.1.66.4")) // SEG { if (segSeriesUIDA.length() == 0) { segSeriesUIDA = seriesUID; } else { segSeriesUIDB = seriesUID; } } else if (sopClassUID == U("1.2.840.10008.5.1.4.1.1.2")) // CT { imageSeriesUID = seriesUID; } } emit InvokeProgress(10, {"load composite context of structured report"}); MITK_INFO << "image series UID " << mitk::RESTUtil::convertToUtf8(imageSeriesUID); MITK_INFO << "seg A series UID " << mitk::RESTUtil::convertToUtf8(segSeriesUIDA); MITK_INFO << "seg B series UID " << mitk::RESTUtil::convertToUtf8(segSeriesUIDB); MITK_INFO << "Load related dicom series ..."; std::string folderPathSeries = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; std::string pathSegA = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; std::string pathSegB = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; auto folderPathSegA = utility::conversions::to_string_t(pathSegA); auto folderPathSegB = utility::conversions::to_string_t(pathSegB); std::vector> tasks; auto imageSeriesTask = m_DicomWeb.SendWADO(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, imageSeriesUID); auto segATask = m_DicomWeb.SendWADO(folderPathSegA, dto.studyUID, segSeriesUIDA); auto segBTask = m_DicomWeb.SendWADO(folderPathSegB, dto.studyUID, segSeriesUIDB); tasks.push_back(imageSeriesTask); tasks.push_back(segATask); tasks.push_back(segBTask); auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto filePathList = joinTask.then([&](std::vector filePathList) { emit InvokeProgress(50, {"load dicom files from disk"}); emit InvokeLoadDataSegDicomWeb(filePathList); }); }); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } web::http::http_response response(web::http::status_codes::InternalError); response.set_body(U("Something went wrong while processing the request.")); // TODO: include method name return response; } web::http::http_response DicomWebRequestHandler::HandleGet(const web::uri &uri, const web::json::value &data) { if (!data.is_null()) // avoid unused warning MITK_INFO << "data was not null"; auto query = web::uri(uri).query(); auto httpParams = web::uri::split_query(query); // IHE Invoke Image Display style auto requestType = httpParams.find(U("requestType")); auto errorResponse = web::http::http_response(web::http::status_codes::BadRequest); if (requestType != httpParams.end()) { if (requestType->second == U("LOAD_SERIES")) { // data extraction DicomDTO dto; dto.studyUID = httpParams.at(U("studyUID")); auto seriesUIDList = httpParams.at(U("seriesUIDList")); auto seriesUIDListUtf8 = mitk::RESTUtil::convertToUtf8(seriesUIDList); std::istringstream f(seriesUIDListUtf8); std::string s; while (getline(f, s, ',')) { dto.seriesUIDList.push_back(mitk::RESTUtil::convertToTString(s)); } emit InvokeProgress(20, {"incoming series request ..."}); + emit InvokeUpdateDcmMeta(dto); // tasks std::vector> tasks; if (dto.seriesUIDList.size() > 0) { for (auto segSeriesUID : dto.seriesUIDList) { utility::string_t folderPathSeries = utility::conversions::to_string_t(mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"); try { auto seriesTask = m_DicomWeb.SendWADO(folderPathSeries, dto.studyUID, segSeriesUID); tasks.push_back(seriesTask); } catch (const mitk::Exception &exception) { MITK_INFO << exception.what(); return errorResponse; } } } emit InvokeProgress(40, {"download series ..."}); try { auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto filePathList = joinTask.then([&](std::vector filePathList) { emit InvokeLoadData(filePathList); emit InvokeProgress(40, {""}); }); } catch (const mitk::Exception &exception) { MITK_INFO << exception.what(); return errorResponse; } } } else { MITK_INFO << "no requestType parameter was provided"; } return errorResponse; } +web::http::http_response DicomWebRequestHandler::HandlePost(const web::uri &uri, + const web::json::value &data, + const mitk::RESTUtil::ParamMap &headers) +{ + if (!data.is_null()) // avoid unused warning + MITK_INFO << "data was not null"; + + auto contentType = headers.find(U("Content-Type")); + if ((contentType->second.find(U("multipart/form-data")) != std::string::npos) || + contentType->second.find(U("multipart/related")) != std::string::npos && + contentType->second.find(U("application/dicom")) != std::string::npos) + { + } + + MITK_INFO <<"not implemented yet"; + auto errorResponse = web::http::http_response(web::http::status_codes::BadRequest); + return errorResponse; +} + web::http::http_response DicomWebRequestHandler::HandleOptions(const web::uri &uri, const web::json::value &data) { if (uri.to_string() != U("/inject")) // avoid unused warning MITK_INFO << "no inject path"; if (!data.is_null()) // avoid unused warning MITK_INFO << "data was not null"; MITK_INFO << "OPTIONS incoming"; web::http::http_response response(web::http::status_codes::OK); response.headers().add(U("Access-Control-Allow-Methods"), "PUT"); response.headers().add(U("Access-Control-Allow-Headers"), "Content-Type"); response.headers().add(U("Access-Control-Allow-Origin"), "http://localhost:8002"); return response; } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.h b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.h index c49b6b2709..3a46f06ba3 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.h +++ b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebRequestHandler.h @@ -1,81 +1,84 @@ /*=================================================================== 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. ===================================================================*/ #ifndef DicomWebRequestHandler_h #define DicomWebRequestHandler_h #include #include #include #include /** * @brief This class represents the http message handler for the segmentation DicomWeb. */ class DicomWebRequestHandler : public QObject, public mitk::IRESTObserver { Q_OBJECT public: struct DicomDTO { utility::string_t segSeriesUIDA; utility::string_t segSeriesUIDB; utility::string_t imageSeriesUID; utility::string_t studyUID; utility::string_t segInstanceUIDA; utility::string_t segInstanceUIDB; utility::string_t srSeriesUID; std::vector seriesUIDList; std::vector simScoreArray; int minSliceStart; utility::string_t groundTruth; }; DicomWebRequestHandler(); /** * Creates the request handler for segmentation DicomWeb. The received data is stored into the given download dir. * * @param downloadDir the directory to which received data is stored. */ DicomWebRequestHandler(std::string downloadDir, utility::string_t pacsURI); /** * Overrides IRESTObserver::Notify. Here arrive the incoming messages. */ web::http::http_response Notify(const web::uri &uri, const web::json::value &data, const web::http::method &method, const mitk::RESTUtil::ParamMap &headers); signals: void InvokeProgress(int, QString status); void InvokeSimilarityGraph(std::vector score, int sliceStart); void InvokeUpdateDcmMeta(DicomDTO dto); void InvokeLoadData(std::vector); void InvokeLoadDataSegDicomWeb(std::vector); private: DicomDTO ExtractDTO(const web::json::value &data); web::http::http_response HandlePut(const web::uri &uri, const web::json::value &data); web::http::http_response HandleGet(const web::uri &uri, const web::json::value &data); web::http::http_response HandleOptions(const web::uri &uri, const web::json::value &data); + web::http::http_response HandlePost(const web::uri &uri, + const web::json::value &data, + const mitk::RESTUtil::ParamMap &headers); std::string m_DownloadDir; mitk::DICOMweb m_DicomWeb; }; #endif // DicomWebRequestHandler_h diff --git a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.cpp b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.cpp index dd6bbbd09e..7090c29f0c 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.cpp +++ b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.cpp @@ -1,590 +1,438 @@ /*=================================================================== 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 #include // Qmitk #include "DicomWebView.h" #include #include // Qt #include #include // mitk #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string DicomWebView::VIEW_ID = "org.mitk.views.dicomwebview"; void DicomWebView::SetFocus() {} void DicomWebView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; qRegisterMetaType>("std::vector"); qRegisterMetaType>("std::vector"); qRegisterMetaType("DicomDTO"); m_Controls.cleanDicomBtn->setVisible(true); - m_Controls.individualWidget_2->setVisible(false); - m_Controls.sliderWidget->setMinimum(1); - m_Controls.sliderWidget->setMaximum(100); - m_Controls.sliderWidget->setTickInterval(1); - m_Controls.sliderWidget->setSingleStep(1); - m_Controls.radioA->setChecked(true); + connect(m_Controls.buttonUpload, &QPushButton::clicked, this, &DicomWebView::UploadNewSegmentation); - connect(m_Controls.buttonNewSeg, &QPushButton::clicked, this, &DicomWebView::CreateNewSegmentationC); connect(m_Controls.cleanDicomBtn, &QPushButton::clicked, this, &DicomWebView::CleanDicomFolder); connect(m_Controls.restartConnection, &QPushButton::clicked, this, &DicomWebView::OnRestartConnection); connect(m_Controls.testConnection, &QPushButton::clicked, this, &DicomWebView::TestConnection); - connect(m_Controls.checkIndiv, &QCheckBox::stateChanged, this, &DicomWebView::OnIndividualCheckChange); - connect( - m_Controls.sliderWidget, &ctkSliderWidget::valueChanged, this, &DicomWebView::OnSliderWidgetChanged); m_DownloadBaseDir = mitk::IOUtil::GetTempPath() + "segrework"; 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(":8000")); } 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"); + utility::string_t pacsURL = U("http://10.128.129.166:8080"); auto envPacsURL = std::getenv("PACS_URL"); if (envPacsURL) { pacsURL = mitk::RESTUtil::convertToTString(std::string(envPacsURL)); } m_RequestHandler = new DicomWebRequestHandler(m_DownloadBaseDir, pacsURL); connect(this, &DicomWebView::InvokeProgress, this, &DicomWebView::AddProgress); connect(m_RequestHandler, &DicomWebRequestHandler::InvokeProgress, this, &DicomWebView::AddProgress); - connect( - m_RequestHandler, &DicomWebRequestHandler::InvokeSimilarityGraph, this, &DicomWebView::SetSimilarityGraph); connect( m_RequestHandler, &DicomWebRequestHandler::InvokeUpdateDcmMeta, this, &DicomWebView::InitializeDcmMeta); connect(m_RequestHandler, &DicomWebRequestHandler::InvokeLoadData, this, &DicomWebView::LoadData); connect( m_RequestHandler, &DicomWebRequestHandler::InvokeLoadDataSegDicomWeb, this, &DicomWebView::LoadDataSegDicomWeb); // Get the micro service us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_ManagerService = managerService; } } RestartConnection(pacsURL); } -void DicomWebView::OnSliderWidgetChanged(double value) -{ - std::map::iterator it; - unsigned int count = 0; - for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) - { - if (it->second < value) - { - count++; - } - } - QString labelsToDelete = "slices to delete: " + QString::number(count); - m_Controls.slicesToDeleteLabel->setText(labelsToDelete); - - std::map thresholdMap; - - for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) - { - thresholdMap.insert(std::map::value_type(it->first, value)); - } - - m_Controls.chartWidget->RemoveData(m_thresholdLabel); - m_Controls.chartWidget->AddData2D(thresholdMap, m_thresholdLabel); - m_Controls.chartWidget->SetChartType(m_thresholdLabel, QmitkChartWidget::ChartType::line); - m_Controls.chartWidget->Show(); -} void DicomWebView::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 DicomWebView::InitializeDcmMeta(DicomWebRequestHandler::DicomDTO dto) { m_CurrentStudyUID = mitk::RESTUtil::convertToUtf8(dto.studyUID); m_SRUID = mitk::RESTUtil::convertToUtf8(dto.srSeriesUID); m_GroundTruth = mitk::RESTUtil::convertToUtf8(dto.groundTruth); } void DicomWebView::Activated() { StartServer(); MITK_INFO << "activated"; } void DicomWebView::Deactivated() { MITK_INFO << "deactivated"; m_ManagerService->HandleDeleteObserver(m_RequestHandler); } void DicomWebView::Visible() { MITK_INFO << "visible"; } void DicomWebView::Hidden() { MITK_INFO << "hidden"; } pplx::task DicomWebView::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 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 DicomWebView::OnRestartConnection() { RestartConnection(mitk::RESTUtil::convertToTString(m_Controls.dcm4cheeHostValue->text().toStdString())); } void DicomWebView::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); } } -void DicomWebView::OnIndividualCheckChange(int state) -{ - if (state == Qt::Unchecked) - { - m_Controls.individualWidget_2->setVisible(false); - } - else if (state == Qt::Checked) - { - m_Controls.individualWidget_2->setVisible(true); - } -} std::string DicomWebView::GetAlgorithmOfSegByPath(std::string path) { auto scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTagPath algorithmName; algorithmName.AddAnySelection(0x0062, 0x0002).AddElement(0x0062, 0x0009); mitk::StringList files; files.push_back(path); scanner->SetInputFiles(files); scanner->AddTagPath(algorithmName); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); auto findings = frames.front()->GetTagValueAsString(algorithmName); if (findings.size() != 0) MITK_INFO << findings.front().value; return findings.front().value; } mitk::DataStorage::SetOfObjects::Pointer DicomWebView::LoadData(std::vector 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 DicomWebView::LoadDataSegDicomWeb(std::vector filePathList) { auto dataNodes = LoadData(filePathList); // find data nodes m_Image = dataNodes->at(0); m_Image->SetName("image data"); m_SegA = dataNodes->at(1); m_SegB = dataNodes->at(2); auto algorithmNameA = GetAlgorithmOfSegByPath(filePathList[1]); auto algorithmNameB = GetAlgorithmOfSegByPath(filePathList[2]); m_SegA->SetName(algorithmNameA); m_SegB->SetName(algorithmNameB); - m_Controls.labelSegAValue->setText(algorithmNameA.c_str()); - m_Controls.labelSegBValue->setText(algorithmNameB.c_str()); - m_Controls.labelGroundTruthValue->setText(m_GroundTruth.c_str()); emit InvokeProgress(20, {""}); } -void DicomWebView::SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart) -{ - std::string label = "similarity graph"; - m_thresholdLabel = "threshold"; - - double sliceIndex = sliceMinStart; - for (double score : simScoreArray) - { - m_ScoreMap.insert(std::map::value_type(sliceIndex, score)); - sliceIndex++; - } - - std::map thresholdMap; - - m_Controls.chartWidget->AddData2D(m_ScoreMap, label); - m_Controls.chartWidget->AddData2D(thresholdMap, m_thresholdLabel); - m_Controls.chartWidget->SetChartType(label, QmitkChartWidget::ChartType::line); - m_Controls.chartWidget->SetChartType(m_thresholdLabel, QmitkChartWidget::ChartType::line); - m_Controls.chartWidget->SetXAxisLabel("slice number"); - m_Controls.chartWidget->SetYAxisLabel("similarity in percent"); - m_Controls.chartWidget->SetTitle("Similartiy Score for Segmentation Comparison"); - m_Controls.chartWidget->Show(); -} void DicomWebView::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 DicomWebView::UploadNewSegmentation() { AddProgress(10, {"save SEG to temp folder"}); std::string folderPathSeg = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_UploadBaseDir) + "/"; const std::string savePath = folderPathSeg + m_SegC->GetName() + ".dcm"; const std::string mimeType = mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(); mitk::IOUtil::Save(m_SegC->GetData(), mimeType, savePath); // get Series Instance UID from new SEG auto scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTagPath seriesUID(0x0020, 0x000E); mitk::StringList files; files.push_back(savePath); scanner->SetInputFiles(files); scanner->AddTagPath(seriesUID); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); auto findings = frames.front()->GetTagValueAsString(seriesUID); auto segSeriesUID = findings.front().value; AddProgress(20, {"push SEG to PACS"}); auto filePath = utility::conversions::to_string_t(savePath); try { m_DicomWeb.SendSTOW(filePath, mitk::RESTUtil::convertToTString(m_CurrentStudyUID)).then([=] { emit InvokeProgress(50, {"persist reworked SEG to evaluation database"}); mitk::DICOMweb::MitkUriBuilder queryBuilder(m_restURL + U("/tasks/evaluations/")); queryBuilder.append_query(U("srUID"), utility::conversions::to_string_t(m_SRUID)); m_ManagerService->SendRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Get) .then([=](web::json::value result) { MITK_INFO << "after GET"; MITK_INFO << utility::conversions::to_utf8string(result.serialize()); auto updatedContent = result.as_array()[0]; updatedContent[U("reworkedSegmentationUID")] = web::json::value::string(utility::conversions::to_string_t(segSeriesUID)); auto id = updatedContent.at(U("id")).as_integer(); MITK_INFO << id; auto idParam = std::to_string(id).append("/"); mitk::DICOMweb::MitkUriBuilder queryBuilder(m_restURL + U("/tasks/evaluations")); queryBuilder.append_path(utility::conversions::to_string_t(idParam)); m_ManagerService->SendJSONRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Put, &updatedContent) .then([=](web::json::value result) { MITK_INFO << utility::conversions::to_utf8string(result.serialize()); if (result[U("reworkedSegmentationUID")].as_string() == utility::conversions::to_string_t(segSeriesUID)) { MITK_INFO << "successfully stored"; emit InvokeProgress(30, {"successfully stored"}); } }); }); }); } catch (const std::exception &exception) { std::cout << exception.what() << std::endl; } } std::vector DicomWebView::CreateSegmentation(mitk::Image::Pointer baseSegmentation, double threshold) { MITK_INFO << "handle individual segmentation creation"; std::map::iterator it; std::vector sliceIndices; unsigned int count = 0; for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) { if (it->second < threshold) { auto index = it->first; try { mitk::ImagePixelWriteAccessor imageAccessor(baseSegmentation); for (unsigned int x = 0; x < baseSegmentation->GetDimension(0); x++) { for (unsigned int y = 0; y < baseSegmentation->GetDimension(1); y++) { imageAccessor.SetPixelByIndex({{x, y, int(index)}}, 0); } } } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } count++; sliceIndices.push_back(index); MITK_INFO << "slice " << it->first << " removed "; } } MITK_INFO << "slices deleted " << count; return sliceIndices; } -void DicomWebView::CreateNewSegmentationC() -{ - mitk::ToolManager *toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); - toolManager->InitializeTools(); - toolManager->SetReferenceData(m_Image); - - mitk::Image::Pointer baseImage; - if (m_Controls.radioA->isChecked()) - { - baseImage = dynamic_cast(m_SegA->GetData())->Clone(); - } - else if (m_Controls.radioB->isChecked()) - { - baseImage = dynamic_cast(m_SegB->GetData())->Clone(); - } - - if (m_Controls.checkIndiv->isChecked()) - { - auto sliceIndices = CreateSegmentation(baseImage, m_Controls.sliderWidget->value()); - } - - QmitkNewSegmentationDialog *dialog = new QmitkNewSegmentationDialog(m_Parent); - - int dialogReturnValue = dialog->exec(); - if (dialogReturnValue == QDialog::Rejected) - { - // user clicked cancel or pressed Esc or something similar - return; - } - - // ask the user about an organ type and name, add this information to the image's (!) propertylist - // create a new image of the same dimensions and smallest possible pixel type - mitk::Tool *firstTool = toolManager->GetToolById(0); - if (firstTool) - { - try - { - std::string newNodeName = dialog->GetSegmentationName().toStdString(); - if (newNodeName.empty()) - { - newNodeName = "no_name"; - } - - mitk::DataNode::Pointer newSegmentation = - firstTool->CreateSegmentationNode(baseImage, newNodeName, dialog->GetColor()); - // initialize showVolume to false to prevent recalculating the volume while working on the segmentation - newSegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false)); - if (!newSegmentation) - { - return; // could be aborted by user - } - - if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) - { - mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); - } - newSegmentation->SetSelected(true); - this->GetDataStorage()->Add( - newSegmentation, - toolManager->GetReferenceData(0)); // add as a child, because the segmentation "derives" from the original - - m_SegC = newSegmentation; - - auto referencedImages = m_Image->GetData()->GetProperty("files"); - m_SegC->GetData()->SetProperty("referenceFiles", referencedImages); - } - catch (std::bad_alloc) - { - QMessageBox::warning( - nullptr, tr("Create new segmentation"), tr("Could not allocate memory for new segmentation")); - } - } - else - { - MITK_INFO << "no tools..."; - } -} - void DicomWebView::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.dicomweb/src/internal/DicomWebView.h b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.h index 7588237c22..1377a819c2 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.h +++ b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebView.h @@ -1,148 +1,136 @@ /*=================================================================== 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. ===================================================================*/ #ifndef DicomWebView_h #define DicomWebView_h #include #include #include "ui_DicomWebViewControls.h" #include "DicomWebRequestHandler.h" #include #include #include #include #include /** @brief DicomWebView This class represents the view to make connections to a PACS server and show some information to do a manual DicomWeb upon two existing segmentation volumes. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class DicomWebView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { Q_OBJECT public: static const std::string VIEW_ID; /** * @brief Loads the files given by a list of file paths. * * @param filePathList a list of absolute file paths * @return set of objects containing the newly loaded data nodes */ mitk::DataStorage::SetOfObjects::Pointer LoadData(std::vector filePathList); /** * @brief Loads the files given by a list of file paths. This method expects exactly three items in the list (image * series, seg series a, seg series b). Additional information for segmentation DicomWeb is displayed. * * * @param filePathList a list of absolute file paths */ void LoadDataSegDicomWeb(std::vector filePathList); - /** - * @brief Calculates how many slices would be delete if the given threshold would be applied to the segmentation - * volume. The information about the segmentation similarity is used and slices below the threshold are to be - * discarded. Updates the display. - * - * @param value the threshold value (between 0 and 1). - */ - void OnSliderWidgetChanged(double value); - /** * @brief Progress is added to the progressbar in a percent value and the given status is displayed. The progress will * not exceed 100 points. * * @param progress the progress in percent points to be added to the bar * @param status the status to be displayed after achieving the progress */ void AddProgress(int progress, QString status); /** * @brief Initializes the DICOM meta data used by this view. * * @param dto the data transfer object for received DICOM meta data. */ void InitializeDcmMeta(DicomWebRequestHandler::DicomDTO dto); virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; signals: void InvokeProgress(int, QString status); protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; - void CreateNewSegmentationC(); - void CleanDicomFolder(); void UploadNewSegmentation(); void RestartConnection(utility::string_t newHost); void OnRestartConnection(); pplx::task TestConnection(); - void OnIndividualCheckChange(int state); Ui::DicomWebViewControls m_Controls; private: std::vector CreateSegmentation(mitk::Image::Pointer baseSegmentation, double threshold); std::string GetAlgorithmOfSegByPath(std::string path); void SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart); void StartServer(); mitk::IRESTManager *m_ManagerService; DicomWebRequestHandler *m_RequestHandler; std::string m_CurrentStudyUID; std::string m_SRUID; std::string m_DownloadBaseDir; std::string m_UploadBaseDir; mitk::DataNode::Pointer m_Image; mitk::DataNode::Pointer m_SegA; mitk::DataNode::Pointer m_SegB; mitk::DataNode::Pointer m_SegC; std::map m_ScoreMap; std::string m_GroundTruth; std::string m_thresholdLabel; mitk::DICOMweb m_DicomWeb; QWidget *m_Parent; utility::string_t m_restURL; utility::string_t m_HostURL; }; #endif // DicomWebView_h diff --git a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebViewControls.ui b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebViewControls.ui index 0a63bb7d9f..d3d3c0e13c 100644 --- a/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebViewControls.ui +++ b/Plugins/org.mitk.gui.qt.dicomweb/src/internal/DicomWebViewControls.ui @@ -1,313 +1,131 @@ DicomWebViewControls 0 0 465 868 0 0 QmitkTemplate dcm4chee host example input: http://193.174.48.78:8090 0 Qt::AlignCenter true restart connection Qt::AlignCenter test connection - - - - - - - - Segmentation B: - - - - - - - - - - - - - - Segmentation A: - - - - - - - - - - - - - - Ground Truth: - - - - - - - - - - - - - - - - - 0 - 400 - - - - - 16777215 - 400 - - - - - - - - false - - - - - - - 0 - 0 - - - - - 0 - 60 - - - - Select segmentation basis - - - true - - - - QLayout::SetDefaultConstraint - - - - - A - - - - - - - B - - - - - - - Individual - - - - - - - - - - - 0 - 0 - - - - - - - Slices to delete: - - - - - - - - - - - - - - - - - - - - Create new Segmentation - - - - - - - Qt::Horizontal - - - - - - - Do image processing - - - Upload Segmentation - - - - - - - + + + Do image processing + + + Upload Segmentation + Qt::Vertical QSizePolicy::Expanding 20 220 clean dicom download folder - - - QmitkChartWidget - QWidget -
QmitkChartWidget.h
- 1 -
- - ctkSliderWidget - QWidget -
ctkSliderWidget.h
- 1 -
-