diff --git a/Modules/REST/include/mitkIRESTManager.h b/Modules/REST/include/mitkIRESTManager.h index 215b045b91..162c37e0fe 100644 --- a/Modules/REST/include/mitkIRESTManager.h +++ b/Modules/REST/include/mitkIRESTManager.h @@ -1,104 +1,118 @@ /*=================================================================== 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 mitkIRESTManager_h #define mitkIRESTManager_h #include #include #include #include #include namespace mitk { class IRESTObserver; class RESTServer; /** * @class IRESTManager * @brief This is a microservice interface for managing REST requests. */ class MITKREST_EXPORT IRESTManager { public: virtual ~IRESTManager(); /** * @brief request type for client requests by calling SendRequest */ enum class RequestType { Get, Post, Put }; /** * @brief Executes a HTTP request in the mitkRESTClient class * * @param uri defines the URI the request is send to * @param type the RequestType of the HTTP request (optional) * @param body the body for the request (optional) * @return task to wait for */ virtual pplx::task SendRequest( const web::uri &uri, const RequestType &type = RequestType::Get, const web::json::value *body = nullptr, const std::map headers = {}, const utility::string_t &filePath = {} ) = 0; + /** + * @brief Executes a HTTP request in the mitkRESTClient class + * + * @param uri defines the URI the request is send to + * @param type the RequestType of the HTTP request (optional) + * @param body the body for the request (optional) + * @return task to wait for + */ + virtual pplx::task SendRequest(const web::uri &uri, + const RequestType &type = RequestType::Get, + const std::vector *body = {}, + const std::map headers = {}, + const utility::string_t &filePath = {}) = 0; + /** * @brief starts listening for requests if there isn't another observer listening and the port is free * * @param uri defines the URI for which incoming requests should be send to the observer * @param observer the observer which handles the incoming requests */ virtual void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) = 0; /** * @brief Handles incoming requests by notifying the observer which should receive it * * @param uri defines the URI of the request * @param body the body of the request * @param method the http method of the request * @return the response */ virtual web::http::http_response Handle(const web::uri &uri, const web::json::value &body, const web::http::method &method) = 0; /** * @brief Handles the deletion of an observer for all or a specific uri * * @param observer the observer which shouldn't receive requests anymore * @param uri the uri for which the observer doesn't handle requests anymore (optional) */ virtual void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) = 0; virtual const std::map& GetServerMap() = 0; virtual const std::map, IRESTObserver *>& GetObservers() = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IRESTManager, "org.mitk.IRESTManager") #endif diff --git a/Modules/REST/include/mitkRESTClient.h b/Modules/REST/include/mitkRESTClient.h index acf76f42c2..d3c359140b 100644 --- a/Modules/REST/include/mitkRESTClient.h +++ b/Modules/REST/include/mitkRESTClient.h @@ -1,78 +1,95 @@ /*=================================================================== 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 mitkRESTClient_h #define mitkRESTClient_h #include #include namespace mitk { class MITKREST_EXPORT RESTClient { public: 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); + 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); }; } #endif diff --git a/Modules/REST/src/mitkRESTClient.cpp b/Modules/REST/src/mitkRESTClient.cpp index 7c1405fc77..bfe3ea034b 100644 --- a/Modules/REST/src/mitkRESTClient.cpp +++ b/Modules/REST/src/mitkRESTClient.cpp @@ -1,177 +1,223 @@ /*=================================================================== 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 #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() { } mitk::RESTClient::~RESTClient() { } pplx::task mitk::RESTClient::Get(const web::uri &uri, const std::map headers) { MITK_DEBUG << "Executing GET with URI: " << mitk::RESTUtil::convertToUtf8(uri.to_string()); auto client = new http_client(uri); 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(); auto status = response.status_code(); if (status_codes::OK != status) mitkThrow(); auto requestContentType = response.headers().content_type(); if (_XPLATSTR("application/json") != requestContentType) response.headers().set_content_type(_XPLATSTR("application/json")); return response.extract_json().get(); } catch (...) { mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers) { MITK_DEBUG << "Executing GET with URI: " << mitk::RESTUtil::convertToUtf8(uri.to_string()); auto client = new http_client(uri); 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) 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 mitk::RESTClient::Put(const web::uri &uri, const web::json::value *content) { auto client = new http_client(uri); 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(); 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 (_XPLATSTR("application/json") != requestContentType) response.headers().set_content_type(_XPLATSTR("application/json")); return response.extract_json().get(); } catch (...) { mitkThrow() << "Getting response went wrong"; } }); } -pplx::task mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content) +pplx::task mitk::RESTClient::Post(const web::uri &uri, + const std::vector *content, + const std::map headers) { auto client = new http_client(uri); http_request request(methods::POST); + if (nullptr != content) + request.set_body(*content); + + for (auto param : headers) + { + request.headers().add(param.first, param.second); + } + + return client->request(request).then([=](pplx::task responseTask) { + try + { + auto response = responseTask.get(); + auto status = response.status_code(); + + if (status_codes::Created != 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 (_XPLATSTR("application/json") != requestContentType) + response.headers().set_content_type(_XPLATSTR("application/json")); + + return response.extract_json().get(); + } + catch (...) + { + mitkThrow() << "Getting response went wrong"; + } + }); +} + +pplx::task mitk::RESTClient::Post(const web::uri &uri, + const web::json::value *content, + const std::map headers) +{ + auto client = new http_client(uri); + http_request request(methods::POST); + + for (auto param : headers) + { + request.headers().add(param.first, param.second); + } + if (nullptr != content) request.set_body(*content); return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::Created != 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 (_XPLATSTR("application/json") != requestContentType) response.headers().set_content_type(_XPLATSTR("application/json")); return response.extract_json().get(); } catch(...) { mitkThrow() << "Getting response went wrong"; } }); } diff --git a/Modules/RESTService/include/mitkRESTManager.h b/Modules/RESTService/include/mitkRESTManager.h index 7990bba036..fe85920d66 100644 --- a/Modules/RESTService/include/mitkRESTManager.h +++ b/Modules/RESTService/include/mitkRESTManager.h @@ -1,123 +1,139 @@ /*=================================================================== 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 mitkRESTManager_h #define mitkRESTManager_h #include #include namespace mitk { /** * @class RESTManager * @brief this is a microservice for managing REST-requests, used for non-qt applications. * * RESTManagerQt in the CppRestSdkQt module inherits from this class and is the equivalent microservice * used for Qt applications. */ class MITKRESTSERVICE_EXPORT RESTManager : public IRESTManager { public: RESTManager(); ~RESTManager() override; /** * @brief Executes a HTTP request in the mitkRESTClient class * * @throw mitk::Exception if RequestType is not suported * @param uri defines the URI the request is send to * @param type the RequestType of the HTTP request (optional) * @param body the body for the request (optional) * @param filePath the file path to store the request to * @return task to wait for */ pplx::task SendRequest(const web::uri &uri, const RequestType &type = RequestType::Get, const web::json::value *body = nullptr, const std::map headers = {}, const utility::string_t &filePath = {}) override; + /** + * @brief Executes a HTTP request in the mitkRESTClient class + * + * @throw mitk::Exception if RequestType is not suported + * @param uri defines the URI the request is send to + * @param type the RequestType of the HTTP request (optional) + * @param body the body for the request (optional) + * @param filePath the file path to store the request to + * @return task to wait for + */ + pplx::task SendRequest(const web::uri &uri, + const RequestType &type = RequestType::Get, + const std::vector* = {}, + const std::map headers = {}, + const utility::string_t &filePath = {}) override; + /** * @brief starts listening for requests if there isn't another observer listening and the port is free * * @param uri defines the URI for which incoming requests should be send to the observer * @param observer the observer which handles the incoming requests */ void ReceiveRequest(const web::uri &uri, IRESTObserver *observer) override; /** * @brief Handles incoming requests by notifying the observer which should receive it * * @param uri defines the URI of the request * @param body the body of the request * @param method the http method of the request * @return the response */ web::http::http_response Handle(const web::uri &uri, const web::json::value &body, const web::http::method &method) override; /** * @brief Handles the deletion of an observer for all or a specific uri * * @param observer the observer which shouldn't receive requests anymore * @param uri the uri for which the observer doesn't handle requests anymore (optional) */ void HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri = {}) override; /** * @brief internal use only */ const std::map &GetServerMap() override; std::map, IRESTObserver *> &GetObservers() override; private: /** * @brief adds an observer if a port is free, called by ReceiveRequest method * * @param uri the uri which builds the key for the observer map * @param observer the observer which is added */ void AddObserver(const web::uri &uri, IRESTObserver *observer); /** * @brief handles server management if there is already a server under a port, called by ReceiveRequest method * * @param uri the uri which which is requested to be added * @param observer the observer which proceeds the request */ void RequestForATakenPort(const web::uri &uri, IRESTObserver *observer); /** * @brief deletes an observer, called by HandleDeleteObserver method * * @param it the iterator comparing the observers in HandleDeleteObserver method * @return bool if there is another observer under the port */ bool DeleteObserver(std::map, IRESTObserver *>::iterator &it); void SetServerMap(const int port, RESTServer *server); void DeleteFromServerMap(const int port); void SetObservers(const std::pair key, IRESTObserver *observer); std::map m_ServerMap; // Map with port server pairs std::map, IRESTObserver *> m_Observers; // Map with all observers }; } // namespace mitk #endif diff --git a/Modules/RESTService/src/mitkRESTManager.cpp b/Modules/RESTService/src/mitkRESTManager.cpp index 0e0ddb901f..f23c442adb 100644 --- a/Modules/RESTService/src/mitkRESTManager.cpp +++ b/Modules/RESTService/src/mitkRESTManager.cpp @@ -1,222 +1,248 @@ /*=================================================================== 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 #include #include #include mitk::RESTManager::RESTManager() { } mitk::RESTManager::~RESTManager() { } +pplx::task mitk::RESTManager::SendRequest( + const web::uri &uri, + const RequestType &type, + const std::vector *content, + const std::map headers, + const utility::string_t &filePath) +{ + pplx::task answer; + auto client = new RESTClient; + + switch (type) + { + case RequestType::Post: + if (nullptr == content) + MITK_WARN << "Content for post is empty, this will create an empty resource"; + + answer = client->Post(uri, content, headers); + break; + + default: + mitkThrow() << "Request Type not supported"; + } + + return answer; +} + pplx::task mitk::RESTManager::SendRequest(const web::uri &uri, const RequestType &type, const web::json::value *content, const std::map headers, const utility::string_t &filePath) { pplx::task answer; auto client = new RESTClient; switch (type) { case RequestType::Get: answer = !filePath.empty() ? client->Get(uri, filePath, headers) : client->Get(uri, headers); break; case RequestType::Post: if (nullptr == content) - MITK_WARN << "Content for put is empty, this will create an empty resource"; + MITK_WARN << "Content for post is empty, this will create an empty resource"; - answer = client->Post(uri, content); + answer = client->Post(uri, content, headers); break; case RequestType::Put: if (nullptr == content) MITK_WARN << "Content for put is empty, this will empty the ressource"; answer = client->Put(uri, content); break; default: mitkThrow() << "Request Type not supported"; } return answer; } void mitk::RESTManager::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer) { // New instance of RESTServer in m_ServerMap, key is port of the request auto port = uri.port(); // Checking if port is free to add a new Server if (0 == m_ServerMap.count(port)) { this->AddObserver(uri, observer); // creating server instance auto server = new RESTServer(uri.authority()); // add reference to server instance to map m_ServerMap[port] = server; // start Server server->OpenListener(); } // If there is already a server under this port else { this->RequestForATakenPort(uri, observer); } } web::http::http_response mitk::RESTManager::Handle(const web::uri &uri, const web::json::value &body, const web::http::method &method) { // Checking if there is an observer for the port and path auto key = std::make_pair(uri.port(), uri.path()); if (0 != m_Observers.count(key)) { return m_Observers[key]->Notify(uri, body, method); } // No observer under this port, return null which results in status code 404 (s. RESTServer) else { MITK_WARN << "No Observer can handle the data"; web::http::http_response response(web::http::status_codes::BadGateway); response.set_body(_XPLATSTR("No one can handle the request under the given port.")); return response; } } void mitk::RESTManager::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri) { for (auto it = m_Observers.begin(); it != m_Observers.end();) { mitk::IRESTObserver *obsMap = it->second; // Check wether observer is at this place in map if (observer == obsMap) { // Check wether it is the right uri to be deleted if (uri.is_empty() || uri.path() == it->first.second) { int port = it->first.first; bool noObserverForPort = this->DeleteObserver(it); if (noObserverForPort) { // there isn't an observer at this port, delete m_ServerMap entry for this port // close listener m_ServerMap[port]->CloseListener(); delete m_ServerMap[port]; // delete server from map m_ServerMap.erase(port); } } else { ++it; } } else { ++it; } } } const std::map &mitk::RESTManager::GetServerMap() { return m_ServerMap; } std::map, mitk::IRESTObserver *> &mitk::RESTManager::GetObservers() { return m_Observers; } void mitk::RESTManager::AddObserver(const web::uri &uri, IRESTObserver *observer) { // new observer has to be added std::pair key(uri.port(), uri.path()); m_Observers[key] = observer; } void mitk::RESTManager::RequestForATakenPort(const web::uri &uri, IRESTObserver *observer) { // Same host, means new observer but not a new server instance if (uri.authority() == m_ServerMap[uri.port()]->GetUri()) { // new observer has to be added std::pair key(uri.port(), uri.path()); // only add a new observer if there isn't already an observer for this uri if (0 == m_Observers.count(key)) { this->AddObserver(uri, observer); } else { MITK_ERROR << "Threre is already a observer handeling this data"; } } // Error, since another server can't be added under this port else { MITK_ERROR << "There is already another server listening under this port"; } } bool mitk::RESTManager::DeleteObserver(std::map, IRESTObserver *>::iterator &it) { int port = it->first.first; it = m_Observers.erase(it); for (auto observer : m_Observers) { if (port == observer.first.first) { // there still exists an observer for this port return false; } } return true; } void mitk::RESTManager::SetServerMap(const int port, RESTServer *server) { m_ServerMap[port] = server; } void mitk::RESTManager::DeleteFromServerMap(const int port) { m_ServerMap.erase(port); } void mitk::RESTManager::SetObservers(const std::pair key, IRESTObserver *observer) { m_Observers[key] = observer; } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp index 0b04ec1fba..207846a767 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp @@ -1,203 +1,210 @@ /*=================================================================== 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 "DICOMweb.h" DICOMweb::DICOMweb() {} DICOMweb::DICOMweb(utility::string_t baseURI) : m_BaseURI(baseURI) { MITK_INFO << "base uri: " << mitk::RESTUtil::convertToUtf8(m_BaseURI); } utility::string_t DICOMweb::CreateQIDOUri(mitk::RESTUtil::ParamMap map) { MitkUriBuilder queryBuilder(m_BaseURI + U("rs/instances")); for (auto const &element : map) { queryBuilder.append_query(element.first, element.second); } return queryBuilder.to_string(); } /** * @brief Creates a WADO request URI with the given parameter */ utility::string_t DICOMweb::CreateWADOUri(utility::string_t studyUID, utility::string_t seriesUID, utility::string_t instanceUID) { MitkUriBuilder builder(m_BaseURI + U("wado")); builder.append_query(U("requestType"), U("WADO")); builder.append_query(U("studyUID"), studyUID); builder.append_query(U("seriesUID"), seriesUID); builder.append_query(U("objectUID"), instanceUID); builder.append_query(U("contentType"), U("application/dicom")); return builder.to_string(); } /** * @brief Creates a STOW request URI with the given parameter map */ utility::string_t DICOMweb::CreateSTOWUri(utility::string_t studyUID) { MitkUriBuilder builder(m_BaseURI + U("rs/studies")); builder.append_path(studyUID); return builder.to_string(); } pplx::task DICOMweb::SendSTOW(utility::string_t filePath, utility::string_t studyUID) { auto uri = CreateSTOWUri(studyUID); auto manager = RESTManager(); // TODO: complete STOW - return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Post, {}, {}, filePath) - .then([=](web::json::value result){}); + // this is the working stow-rs request which supports just one dicom file packed into a multipart message + std::basic_ifstream input(filePath, std::ios::binary); + + std::vector result; + std::vector buffer((std::istreambuf_iterator(input)), + (std::istreambuf_iterator())); + + // reuse 'content-type' variable or struct to be more flexible, in future more than one file should also be + // supported.. + std::string head = ""; + head += "\r\n--boundary"; + head += "\r\nContent-Type: " + mitk::RESTUtil::convertToUtf8(U("application/dicom")) + "\r\n\r\n"; + + std::vector bodyVector(head.begin(), head.end()); + + std::string tail = ""; + tail += "\r\n--boundary--"; + + result.insert(result.end(), bodyVector.begin(), bodyVector.end()); + result.insert(result.end(), buffer.begin(), buffer.end()); + result.insert(result.end(), tail.begin(), tail.end()); + + mitk::RESTUtil::ParamMap headers; + headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Content-Type"), U("multipart/related; type=\"application/dicom\"; boundary=boundary"))); + + return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Post, &result, headers, filePath) + .then([=](web::json::value result) { + if (result.is_null()) + { + } + }); } pplx::task DICOMweb::SendWADO(utility::string_t filePath, utility::string_t studyUID, utility::string_t seriesUID, utility::string_t instanceUID, mitk::IRESTManager *manager) { auto uri = CreateWADOUri(studyUID, seriesUID, instanceUID); // don't want return something - return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, {}, {}, filePath) - .then([=](web::json::value result){}); + auto content = web::json::value(); + return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, {}, filePath) + .then([=](web::json::value result) + { + if (result.is_null()) + { + } + }); } pplx::task DICOMweb::SendWADO(utility::string_t folderPath, utility::string_t studyUID, utility::string_t seriesUID) { mitk::RESTUtil::ParamMap seriesInstances; seriesInstances.insert(mitk::RESTUtil::ParamMap::value_type(U("StudyInstanceUID"), studyUID)); seriesInstances.insert(mitk::RESTUtil::ParamMap::value_type(U("SeriesInstanceUID"), seriesUID)); auto *manager = RESTManager(); return SendQIDO(seriesInstances).then([=](web::json::value jsonResult) -> pplx::task { auto jsonListResult = jsonResult; auto resultArray = jsonListResult.as_array(); auto firstFileName = std::string(); std::vector> tasks; for (unsigned short i = 0; i < resultArray.size(); i++) { try { auto firstResult = resultArray[i]; auto sopInstanceUIDKey = firstResult.at(U("00080018")); auto sopInstanceObject = sopInstanceUIDKey.as_object(); auto valueKey = sopInstanceObject.at(U("Value")); auto valueArray = valueKey.as_array(); auto sopInstanceUID = valueArray[0].as_string(); auto fileName = utility::string_t(sopInstanceUID).append(U(".dcm")); // save first file name as result to load series if (i == 0) { firstFileName = utility::conversions::to_utf8string(fileName); } auto filePath = utility::string_t(folderPath).append(fileName); auto task = SendWADO(filePath, studyUID, seriesUID, sopInstanceUID, manager); tasks.push_back(task); } catch (const web::json::json_exception &e) { MITK_ERROR << e.what(); } } auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto returnTask = joinTask.then([=](void) -> std::string { auto folderPathUtf8 = utility::conversions::to_utf8string(folderPath); auto result = folderPathUtf8 + firstFileName; return result; }); return returnTask; }); } pplx::task DICOMweb::SendQIDO(mitk::RESTUtil::ParamMap map) { auto uri = CreateQIDOUri(map); mitk::IRESTManager *manager = RESTManager(); mitk::RESTUtil::ParamMap headers; headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Accept"), U("application/json"))); - - return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, {}, headers); - // return manager->SendRequest(queryBuilder.to_string()).then([=](pplx::task taskResponse) { - // try - // { - // MitkResponse response = taskResponse.get(); - // auto status = response.status_code(); - // MITK_INFO << " status: " << status; - - // if (status != web::http::status_codes::OK) - // { - // MITK_ERROR << "no dicom series found for query " - // << utility::conversions::to_utf8string(queryBuilder.to_string()); - // mitkThrow() << "no dicom series found for query " - // << utility::conversions::to_utf8string(queryBuilder.to_string()); - // } - - // if (response.headers()[U("Content-Type")] == U("application/dicom+json")) - // { - // response.headers().remove(U("Content-Type")); - // response.headers().add(U("Content-Type"), U("application/json")); - // } - - // return response.extract_json().get(); - // } - // catch (std::exception &e) - // { - // mitkThrow() << e.what(); - // } - //}); + auto content = web::json::value(); + return manager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, headers); } mitk::IRESTManager* DICOMweb::RESTManager() { auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { return managerService; } } return nullptr; } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp index a57f94ae37..341610ce5f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp @@ -1,257 +1,258 @@ /*=================================================================== 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 "DICOMweb.h" #include "ReworkRequestHandler.h" #include #include #include US_INITIALIZE_MODULE ReworkRequestHandler::ReworkRequestHandler() { m_DicomWeb = DICOMweb(U("http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE/")); } ReworkRequestHandler::ReworkRequestHandler(std::string downloadDir) : m_DownloadDir{downloadDir} { auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_ManagerService = managerService; } } m_DicomWeb = DICOMweb(U("http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE/")); } ReworkRequestHandler::~ReworkRequestHandler() {} ReworkRequestHandler::DicomDTO ReworkRequestHandler::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 ReworkRequestHandler::Notify(const web::uri &uri, const web::json::value &data, const web::http::method &method) { 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::OPTIONS) { return HandleOptions(uri, data); } web::http::http_response response(web::http::status_codes::BadGateway); response.set_body(_XPLATSTR("No one can handle http method from request")); // TODO: include method name return response; } web::http::http_response ReworkRequestHandler::HandlePut(const web::uri &uri, const web::json::value &data) { if (uri.to_string() != U("/inject")) 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.to_string()); 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 { auto requestURI = m_DicomWeb.CreateQIDOUri(seriesInstancesParams); MITK_INFO << "send qido request"; - m_ManagerService->SendRequest(requestURI).then([=](web::json::value jsonResult) { + auto content = web::json::value(); + m_ManagerService->SendRequest(requestURI, mitk::IRESTManager::RequestType::Get, &content).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 InvokeLoadData(filePathList); }); }); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } web::http::http_response response(web::http::status_codes::InternalError); response.set_body(_XPLATSTR("Something went wrong while processing the request.")); // TODO: include method name return response; } web::http::http_response ReworkRequestHandler::HandlePost(const web::uri &uri, const web::json::value &data) { return web::http::http_response(web::http::status_codes::NotImplemented); } web::http::http_response ReworkRequestHandler::HandleGet(const web::uri &uri, const web::json::value &data) { if (uri.to_string() != U("/inject")) MITK_INFO << "no inject path"; if (!data.is_null()) MITK_INFO << "data was not null"; MITK_INFO << "GET incoming"; return web::http::http_response(web::http::status_codes::NotImplemented); } web::http::http_response ReworkRequestHandler::HandleOptions(const web::uri &uri, const web::json::value &data) { if (uri.to_string() != U("/inject")) MITK_INFO << "no inject path"; if (!data.is_null()) MITK_INFO << "data was not null"; MITK_INFO << "OPTIONS incoming"; web::http::http_response response(web::http::status_codes::OK); response.headers().add(_XPLATSTR("Access-Control-Allow-Methods"), "PUT"); response.headers().add(_XPLATSTR("Access-Control-Allow-Headers"), "Content-Type"); response.headers().add(_XPLATSTR("Access-Control-Allow-Origin"), "http://localhost:8002"); return response; } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp index ccac9cc736..447c3c3774 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp @@ -1,536 +1,540 @@ /*=================================================================== 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 "SegmentationReworkView.h" #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DICOMweb.h" #include "ReworkRequestHandler.h" #include #include const std::string SegmentationReworkView::VIEW_ID = "org.mitk.views.segmentationreworkview"; void SegmentationReworkView::SetFocus() {} void SegmentationReworkView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; counter = 0; qRegisterMetaType>("std::vector"); qRegisterMetaType>("std::vector"); qRegisterMetaType("DicomDTO"); // m_Controls.verticalWidget->setVisible(false); m_Controls.cleanDicomBtn->setVisible(false); 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, &SegmentationReworkView::UploadNewSegmentation); connect(m_Controls.buttonNewSeg, &QPushButton::clicked, this, &SegmentationReworkView::CreateNewSegmentationC); connect(m_Controls.cleanDicomBtn, &QPushButton::clicked, this, &SegmentationReworkView::CleanDicomFolder); connect(m_Controls.restartConnection, &QPushButton::clicked, this, &SegmentationReworkView::OnRestartConnection); connect(m_Controls.testConnection, &QPushButton::clicked, this, &SegmentationReworkView::TestConnection); connect(m_Controls.checkIndiv, &QCheckBox::stateChanged, this, &SegmentationReworkView::OnIndividualCheckChange); connect( m_Controls.sliderWidget, &ctkSliderWidget::valueChanged, this, &SegmentationReworkView::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); } utility::string_t port = U("2020"); utility::string_t address = U("http://127.0.0.1:"); address.append(port); m_Host = "localhost:8000"; m_RequestHandler = new ReworkRequestHandler(m_DownloadBaseDir); connect(this, &SegmentationReworkView::InvokeLoadData, this, &SegmentationReworkView::LoadData); connect(this, &SegmentationReworkView::InvokeProgress, this, &SegmentationReworkView::AddProgress); connect(m_RequestHandler, &ReworkRequestHandler::InvokeProgress, this, &SegmentationReworkView::AddProgress); connect( m_RequestHandler, &ReworkRequestHandler::InvokeSimilarityGraph, this, &SegmentationReworkView::SetSimilarityGraph); connect(m_RequestHandler, &ReworkRequestHandler::InvokeUpdateDcmMeta, this, &SegmentationReworkView::UpdateDcmMeta); connect(m_RequestHandler, &ReworkRequestHandler::InvokeLoadData, this, &SegmentationReworkView::LoadData); // 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; } } // Setup PACS connection utility::string_t pacsURL = U("http://193.174.48.78:8090"); RestartConnection(pacsURL); // Setup listening server - m_ManagerService->ReceiveRequest(U("http://127.0.0.1:2020/inject"), m_RequestHandler); + m_ManagerService->ReceiveRequest(U("http://127.0.0.1:4040/inject"), m_RequestHandler); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); } void SegmentationReworkView::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 SegmentationReworkView::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 SegmentationReworkView::UpdateDcmMeta(ReworkRequestHandler::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 SegmentationReworkView::TestConnection() { mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("limit"), U("1"))); m_Controls.connectionStatus->setText(QString("Testing connection ...")); auto requestURI = m_DicomWeb.CreateQIDOUri(seriesInstancesParams); + auto content = web::json::value(); - return m_ManagerService->SendRequest(requestURI).then([=](pplx::task resultTask) { + return m_ManagerService->SendRequest(requestURI, mitk::IRESTManager::RequestType::Get, &content).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 SegmentationReworkView::OnRestartConnection() { - RestartConnection(m_Controls.dcm4cheeHostValue->text().toStdWString()); + RestartConnection(mitk::RESTUtil::convertToTString(m_Controls.dcm4cheeHostValue->text().toStdString())); } void SegmentationReworkView::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 + _XPLATSTR("/dcm4chee-arc/aets/DCM4CHEE"); + utility::string_t url = newHost + _XPLATSTR("/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 = 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 SegmentationReworkView::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 SegmentationReworkView::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; } void SegmentationReworkView::LoadData(std::vector filePathList) { MITK_INFO << "Loading finished. Pushing data to data storage ..."; auto ds = GetDataStorage(); auto dataNodes = mitk::IOUtil::Load(filePathList, *ds); // reinit view mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(ds); // 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 SegmentationReworkView::UpdateChartWidget() { m_Controls.chartWidget->Show(); } void SegmentationReworkView::SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart) { std::string label = "similarity graph"; m_thresholdLabel = "threshold"; // m_Controls.chartWidget->Clear(); 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 SegmentationReworkView::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::MitkDICOMQIIOMimeTypes::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"}); DICOMweb::MitkUriBuilder queryBuilder(mitk::RESTUtil::convertToTString(m_Host) + U("tasks/evaluations/")); queryBuilder.append_query(U("srUID"), utility::conversions::to_string_t(m_SRUID)); - m_ManagerService->SendRequest(queryBuilder.to_uri()).then([=](web::json::value result) { + auto content = web::json::value(); + + m_ManagerService->SendRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Get, &content) + .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("/"); DICOMweb::MitkUriBuilder queryBuilder(mitk::RESTUtil::convertToTString(m_Host) + U("tasks/evaluations")); queryBuilder.append_path(utility::conversions::to_string_t(idParam)); m_ManagerService->SendRequest(queryBuilder.to_string(), mitk::IRESTManager::RequestType::Post, &updatedContent) .then([=](web::json::value result) { 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 SegmentationReworkView::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 SegmentationReworkView::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); // needs a QWidget as parent, "this" is not QWidget 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 SegmentationReworkView::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; } // std::experimental::filesystem::remove_all(m_DownloadBaseDir); // TODO : use POCO // itk::FileTools::CreateDirectory(m_DownloadBaseDir); }