diff --git a/Modules/CppRestSdk/include/mitkRESTClient.h b/Modules/CppRestSdk/include/mitkRESTClient.h index 2e989c21d7..65337aae13 100644 --- a/Modules/CppRestSdk/include/mitkRESTClient.h +++ b/Modules/CppRestSdk/include/mitkRESTClient.h @@ -1,62 +1,63 @@ /*=================================================================== 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 "cpprest/asyncrt_utils.h" #include "cpprest/containerstream.h" #include "cpprest/filestream.h" #include "cpprest/http_client.h" #include "cpprest/json.h" #include "cpprest/producerconsumerstream.h" #include "cpprest/uri.h" #include "MitkCppRestSdkExports.h" typedef web::http::client::http_client MitkClient; typedef web::http::http_request MitkRequest; typedef web::http::http_response MitkResponse; typedef web::http::methods MitkRESTMethods; typedef web::http::uri_builder MitkUriBuilder; typedef web::http::status_codes MitkRestStatusCodes; typedef web::json::json_exception MitkJsonException; namespace mitk { class MITKCPPRESTSDK_EXPORT RESTClient { public:; RESTClient(utility::string_t url); virtual ~RESTClient(); pplx::task Post(utility::string_t uri, utility::string_t contentType, concurrency::streams::basic_istream fileStream); pplx::task Post(utility::string_t uri, utility::string_t contentType, utility::string_t filePath); pplx::task Get(const utility::string_t filePath, utility::string_t uri); pplx::task WadoRS(const utility::string_t filePath, std::string studyUID, std::string seriesUID, std::string instanceUID); pplx::task WadoRS(const utility::string_t filePath, std::string studyUID, std::string seriesUID); pplx::task StowRS(utility::string_t filePath, std::string studyUID); + pplx::task QuidoRSInstances(std::map params); private: MitkClient* m_Client; }; }; #endif // MITKRESTCLIENT_H diff --git a/Modules/CppRestSdk/src/mitkRESTClient.cpp b/Modules/CppRestSdk/src/mitkRESTClient.cpp index ee568a7307..e740d53d50 100644 --- a/Modules/CppRestSdk/src/mitkRESTClient.cpp +++ b/Modules/CppRestSdk/src/mitkRESTClient.cpp @@ -1,214 +1,228 @@ /*=================================================================== 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 "mitkRESTClient.h" #include "mitkRestUtil.h" #include #include mitk::RESTClient::RESTClient(utility::string_t url) { m_Client = new MitkClient(url); } mitk::RESTClient::~RESTClient() { delete m_Client; } pplx::task mitk::RESTClient::Get(utility::string_t filePath, utility::string_t uri) { MITK_DEBUG << "Calling GET with " << utility::conversions::to_utf8string(uri) << " on client " << mitk::RESTUtil::convertToUtf8(m_Client->base_uri().to_string()) << " save into " << mitk::RESTUtil::convertToUtf8(filePath); auto fileBuffer = std::make_shared>(); return concurrency::streams::file_buffer::open(filePath, std::ios::out).then([=](concurrency::streams::streambuf outFile) -> pplx::task { *fileBuffer = outFile; return m_Client->request(MitkRESTMethods::GET, uri); }) // Write the response body into the file buffer. .then([=](MitkResponse response) -> pplx::task { MITK_DEBUG << "Status code: " << response.status_code(); return response.body().read_to_end(*fileBuffer); }) // Close the file buffer. .then([=](size_t) { return fileBuffer->close(); }); } pplx::task mitk::RESTClient::Post(utility::string_t uri, utility::string_t contentType, concurrency::streams::basic_istream fileStream) { MITK_INFO << "Calling POST with " << mitk::RESTUtil::convertToUtf8(uri) << " on client " << mitk::RESTUtil::convertToUtf8(m_Client->base_uri().to_string()); // currently not working, but stream approach may be useful for later.. don't use string streams for dcm files... concurrency::streams::container_buffer inStringBuffer; return fileStream.read(inStringBuffer, fileStream.streambuf().size()).then([=](size_t bytesRead) -> pplx::task { const std::string &text = inStringBuffer.collection(); std::string body = ""; body += "\r\n--boundary"; body += "\r\nContentType: " + mitk::RESTUtil::convertToUtf8(L"application/dicom") + "\r\n\r\n"; body += text; body += "\r\n--boundary--"; auto utf8String = utility::conversions::to_utf8string(body); auto binaryVector = std::vector(utf8String.begin(), utf8String.end()); MitkRequest postRequest(MitkRESTMethods::POST); postRequest.set_request_uri(uri); postRequest.headers().add(U("Content-Type"), contentType); postRequest.set_body(binaryVector); MITK_INFO << "Request: " << mitk::RESTUtil::convertToUtf8(postRequest.to_string()); return m_Client->request(postRequest).then([fileStream](MitkResponse response) { fileStream.close(); MITK_INFO << "Response: " << mitk::RESTUtil::convertToUtf8(response.to_string()); }); }); } pplx::task mitk::RESTClient::Post(utility::string_t uri, utility::string_t contentType, utility::string_t filePath) { // 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(L"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()); MitkRequest postRequest(MitkRESTMethods::POST); postRequest.set_request_uri(uri); postRequest.headers().add(U("Content-Type"), "multipart/related; type=\"application/dicom\"; boundary=boundary"); postRequest.set_body(result); MITK_INFO << "Request: " << mitk::RESTUtil::convertToUtf8(postRequest.to_string()); return m_Client->request(postRequest).then([](MitkResponse response) { MITK_INFO << "Response: " << mitk::RESTUtil::convertToUtf8(response.to_string()); }); } +pplx::task mitk::RESTClient::QuidoRSInstances(std::map params) +{ + MitkUriBuilder queryBuilder(U("rs/instances")); + + for (auto const& element : params) + { + queryBuilder.append_query(mitk::RESTUtil::convertToTString(element.first), mitk::RESTUtil::convertToTString(element.second)); + } + + MITK_INFO << utility::conversions::to_utf8string(queryBuilder.to_string()); + MitkRequest instances(MitkRESTMethods::GET); + instances.set_request_uri(queryBuilder.to_string()); + instances.headers().add(U("Accept"), U("application/json")); + + return m_Client->request(instances).then([=](MitkResponse response) + { + MITK_INFO << " status: " << response.status_code(); + + return response.extract_json().get(); + }); +} + pplx::task mitk::RESTClient::WadoRS(utility::string_t filePath, std::string studyUID, std::string seriesUID, std::string instanceUID) { MitkUriBuilder builder(U("wado")); builder.append_query(U("requestType"), U("WADO")); builder.append_query(U("studyUID"), mitk::RESTUtil::convertToTString(studyUID)); builder.append_query(U("seriesUID"), mitk::RESTUtil::convertToTString(seriesUID)); builder.append_query(U("objectUID"), mitk::RESTUtil::convertToTString(instanceUID)); builder.append_query(U("contentType"), U("application/dicom")); return Get(filePath, builder.to_string()); } pplx::task mitk::RESTClient::WadoRS(const utility::string_t folderPath, std::string studyUID, std::string seriesUID) { - // this is actually a quido-rs request, should be packed into a seperate method.. at some time.. - //but there are many possible requests to support: http://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_6.7.html - MitkUriBuilder builder(U("rs/instances")); - builder.append_query(U("StudyInstanceUID"), mitk::RESTUtil::convertToTString(studyUID)); - builder.append_query(U("SeriesInstanceUID"), mitk::RESTUtil::convertToTString(seriesUID)); - - MITK_INFO << utility::conversions::to_utf8string(builder.to_string()); - MitkRequest getSeries(MitkRESTMethods::GET); - getSeries.set_request_uri(builder.to_string()); - getSeries.headers().add(U("Accept"), U("application/json")); - - return m_Client->request(getSeries).then([=](MitkResponse response) -> pplx::task - { - MITK_INFO << "search for instances in series with uid " << seriesUID - << " status: " << response.status_code(); + typedef std::map ParamMap; + ParamMap seriesInstancesParams; + + seriesInstancesParams.insert(ParamMap::value_type({"StudyInstanceUID"}, studyUID)); + seriesInstancesParams.insert(ParamMap::value_type({"SeriesInstanceUID"}, seriesUID)); - auto jsonListResult = response.extract_json().get(); + return QuidoRSInstances(seriesInstancesParams).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 = WadoRS(filePath, studyUID, seriesUID, mitk::RESTUtil::convertToUtf8(sopInstanceUID)); tasks.push_back(task); } catch (const web::json::json_exception& e) { MITK_ERROR << e.what(); } } auto joinTask = pplx::when_all(begin(tasks), end(tasks)); return joinTask.then([=](void) { return utility::conversions::to_utf8string(folderPath).append(firstFileName); }); }); } pplx::task mitk::RESTClient::StowRS(utility::string_t filePath, std::string studyUID) { // TODO: add data MitkUriBuilder builder(U("rs/studies")); builder.append_path(mitk::RESTUtil::convertToTString(studyUID)); return Post(builder.to_string(), U("multipart/related; type='application/dicom'; boundary='boundary'"), filePath); } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.cpp index 7cba7e9434..d334bed42c 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.cpp @@ -1,129 +1,131 @@ /*=================================================================== 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 "SegmentationReworkREST.h" #include #include SegmentationReworkREST::SegmentationReworkREST() {} SegmentationReworkREST::SegmentationReworkREST(utility::string_t url) : mitk::RESTServer(url) { m_Listener.support(MitkRESTMethods::PUT, std::bind(&SegmentationReworkREST::HandlePut, this, std::placeholders::_1)); m_Listener.support(MitkRESTMethods::GET, std::bind(&SegmentationReworkREST::HandleGet, this, std::placeholders::_1)); } SegmentationReworkREST::~SegmentationReworkREST() {} void SegmentationReworkREST::HandleGet(MitkRequest message) { auto messageString = message.to_string(); MITK_INFO << "Message GET incoming..."; MITK_INFO << mitk::RESTUtil::convertToUtf8(messageString); auto httpParams = web::uri::split_query(message.request_uri().query()); // IHE Invoke Image Display style auto requestType = httpParams.find(L"requestType"); if (requestType != httpParams.end() && requestType->second == L"IMAGE_SEG") { try { auto studyUID = httpParams.at(L"studyUID"); auto imageSeriesUID = httpParams.at(L"imageSeriesUID"); auto segSeriesUID = httpParams.at(L"segSeriesUID"); DicomDTO dto; dto.imageSeriesUID = mitk::RESTUtil::convertToUtf8(imageSeriesUID); dto.studyUID = mitk::RESTUtil::convertToUtf8(studyUID); dto.segSeriesUIDA = mitk::RESTUtil::convertToUtf8(segSeriesUID); m_GetCallback(dto); MitkResponse response(MitkRestStatusCodes::OK); response.set_body("Sure, i got you.. have an awesome day"); message.reply(response); } catch (std::out_of_range& e) { message.reply(MitkRestStatusCodes::BadRequest, "oh man, that was not expected: " + std::string(e.what())); } } - else { + else if (requestType != httpParams.end() && requestType->second == L"SEG_EVALUATION") + { + // TODO: implement PUT handling as GET + } else + { message.reply(MitkRestStatusCodes::BadRequest, "Oh man, i can only deal with 'requestType' = 'IMAGE+SEG'..."); } } void SegmentationReworkREST::HandlePut(MitkRequest message) { auto messageString = message.to_string(); MITK_INFO << "Message PUT incoming..."; MITK_INFO << mitk::RESTUtil::convertToUtf8(messageString); try { auto jsonMessage = message.extract_json().get(); auto messageTypeKey = jsonMessage.at(U("messageType")); if (messageTypeKey.as_string() == U("downloadData")) { auto imageStudyUIDKey = jsonMessage.at(U("studyUID")); - auto imageSeriesUIDKey = jsonMessage.at(U("imageSeriesUID")); - auto segSeriesUIDAKey = jsonMessage.at(U("segSeriesUIDA")); - auto segSeriesUIDBKey = jsonMessage.at(U("segSeriesUIDB")); + auto srSeriesUIDKey = jsonMessage.at(U("srSeriesUID")); + auto groundTruthKey = jsonMessage.at(U("groundTruth")); auto simScoreKey = jsonMessage.at(U("simScoreArray")); auto minSliceStartKey = jsonMessage.at(U("minSliceStart")); DicomDTO dto; - dto.segSeriesUIDA = mitk::RESTUtil::convertToUtf8(segSeriesUIDAKey.as_string()); - dto.segSeriesUIDB = mitk::RESTUtil::convertToUtf8(segSeriesUIDBKey.as_string()); - dto.imageSeriesUID = mitk::RESTUtil::convertToUtf8(imageSeriesUIDKey.as_string()); + dto.srSeriesUID = mitk::RESTUtil::convertToUtf8(srSeriesUIDKey.as_string()); + dto.groundTruth = mitk::RESTUtil::convertToUtf8(groundTruthKey.as_string()); dto.studyUID = mitk::RESTUtil::convertToUtf8(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()); } dto.simScoreArray = vec; m_PutCallback(dto); emit InvokeUpdateChartWidget(); } else { message.reply(MitkRestStatusCodes::BadRequest, "Oh man, i can only deal with 'messageType' = 'downloadData'..."); } } catch (MitkJsonException &e) { MITK_ERROR << e.what() << ".. oh man, that was not expected"; message.reply(MitkRestStatusCodes::BadRequest, "oh man, that was not expected"); return; } MitkResponse response(MitkRestStatusCodes::OK); response.headers().add(U("Access-Control-Allow-Origin"), U("localhost:9000/*")); response.set_body("Sure, i got you.. have an awesome day"); message.reply(response); return; } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.h b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.h index 1ee83e0ac5..890f9ea0f2 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.h +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkREST.h @@ -1,64 +1,65 @@ /*=================================================================== 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 SegmentationReworkREST_h #define SegmentationReworkREST_h #include class SegmentationReworkREST : public mitk::RESTServer { Q_OBJECT public: struct DicomDTO { std::string segSeriesUIDA; std::string segSeriesUIDB; std::string imageSeriesUID; std::string studyUID; std::string segInstanceUIDA; std::string segInstanceUIDB; + std::string srSeriesUID; std::vector simScoreArray; int minSliceStart; std::string groundTruth; }; SegmentationReworkREST(); SegmentationReworkREST(utility::string_t url); ~SegmentationReworkREST(); void HandlePut(MitkRequest message); void HandleGet(MitkRequest message); void SetPutCallback(std::function callback) { m_PutCallback = callback; } void SetGetCallback(std::function callback) { m_GetCallback = callback; } signals: void InvokeUpdateChartWidget(); private: std::function m_PutCallback; std::function m_GetCallback; }; #endif // SegmentationReworkREST_h 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 8b319a9fc1..9882a54311 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,258 +1,376 @@ /*=================================================================== 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" // Qt #include #include #include "QmitkNewSegmentationDialog.h" #include "mitkSegTool2D.h" #include "mitkToolManagerProvider.h" +#include "mitkIOMimeTypes.h" + #include // uuid class #include // generators #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; qRegisterMetaType< std::vector >("std::vector"); 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); m_downloadBaseDir = std::experimental::filesystem::path("/temp/"); + m_tempSegDir = std::experimental::filesystem::path("/tempSeg/"); if (!std::experimental::filesystem::exists(m_downloadBaseDir)) { std::experimental::filesystem::create_directory(m_downloadBaseDir); } + if (!std::experimental::filesystem::exists(m_tempSegDir)) + { + std::experimental::filesystem::create_directory(m_tempSegDir); + } utility::string_t port = U("2020"); utility::string_t address = U("http://127.0.0.1:"); address.append(port); m_HttpHandler = std::unique_ptr(new SegmentationReworkREST(address)); connect(m_HttpHandler.get(), &SegmentationReworkREST::InvokeUpdateChartWidget, this, &SegmentationReworkView::UpdateChartWidget); connect(this, &SegmentationReworkView::InvokeLoadData, this, &SegmentationReworkView::LoadData); m_HttpHandler->SetPutCallback(std::bind(&SegmentationReworkView::RESTPutCallback, this, std::placeholders::_1)); m_HttpHandler->SetGetCallback(std::bind(&SegmentationReworkView::RESTGetCallback, this, std::placeholders::_1)); m_HttpHandler->Open().wait(); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); utility::string_t pacsHost = U("http://193.174.48.78:8090/dcm4chee-arc/aets/DCM4CHEE"); m_RestClient = new mitk::RESTClient(pacsHost); } void SegmentationReworkView::RESTPutCallback(const SegmentationReworkREST::DicomDTO &dto) { SetSimilarityGraph(dto.simScoreArray, dto.minSliceStart); - MITK_INFO << "Load related dicom series ..."; - boost::uuids::random_generator generator; - - std::string folderPathSeries = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; - std::experimental::filesystem::create_directory(folderPathSeries); + typedef std::map ParamMap; + ParamMap seriesInstancesParams; - std::string pathSegA = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; - std::string pathSegB = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; + seriesInstancesParams.insert((ParamMap::value_type({"StudyInstanceUID"}, dto.studyUID))); + seriesInstancesParams.insert((ParamMap::value_type({"SeriesInstanceUID"}, dto.srSeriesUID))); + seriesInstancesParams.insert((ParamMap::value_type({"includefield"}, {"0040A375"}))); // Current Requested Procedure Evidence Sequence - auto folderPathSegA = utility::conversions::to_string_t(pathSegA); - auto folderPathSegB = utility::conversions::to_string_t(pathSegB); + m_RestClient->QuidoRSInstances(seriesInstancesParams).then([=](web::json::value jsonResult) { - std::experimental::filesystem::create_directory(pathSegA); - std::experimental::filesystem::create_directory(pathSegB); + 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(); - m_CurrentStudyUID = dto.studyUID; + std::string segSeriesUIDA = ""; + std::string segSeriesUIDB = ""; + std::string imageSeriesUID = ""; - std::vector> tasks; - auto imageSeriesTask = m_RestClient->WadoRS(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, dto.imageSeriesUID); - auto segATask = m_RestClient->WadoRS(folderPathSegA, dto.studyUID, dto.segSeriesUIDA); - auto segBTask = m_RestClient->WadoRS(folderPathSegB, dto.studyUID, dto.segSeriesUIDB); - tasks.push_back(imageSeriesTask); - tasks.push_back(segATask); - tasks.push_back(segBTask); + 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 = utility::conversions::to_utf8string(element.at(U("0020000E")).as_object().at(U("Value")).as_array()[0].as_string()); + + if (sopClassUID == L"1.2.840.10008.5.1.4.1.1.66.4") // SEG + { + if (segSeriesUIDA.length() == 0) + { + segSeriesUIDA = seriesUID; + } + else + { + segSeriesUIDB = seriesUID; + } + } + else if (sopClassUID == L"1.2.840.10008.5.1.4.1.1.2") // CT + { + imageSeriesUID = seriesUID; + } + } + + MITK_INFO << "image series UID " << imageSeriesUID; + MITK_INFO << "seg A series UID " << segSeriesUIDA; + MITK_INFO << "seg B series UID " << segSeriesUIDB; + + MITK_INFO << "Load related dicom series ..."; + boost::uuids::random_generator generator; + + std::string folderPathSeries = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; + std::experimental::filesystem::create_directory(folderPathSeries); + + std::string pathSegA = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; + std::string pathSegB = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; + + auto folderPathSegA = utility::conversions::to_string_t(pathSegA); + auto folderPathSegB = utility::conversions::to_string_t(pathSegB); + + std::experimental::filesystem::create_directory(pathSegA); + std::experimental::filesystem::create_directory(pathSegB); + + m_CurrentStudyUID = dto.studyUID; + + std::vector> tasks; + auto imageSeriesTask = m_RestClient->WadoRS(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, imageSeriesUID); + auto segATask = m_RestClient->WadoRS(folderPathSegA, dto.studyUID, segSeriesUIDA); + auto segBTask = m_RestClient->WadoRS(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) { + auto fileNameA = std::experimental::filesystem::path(filePathList[1]).filename(); + auto fileNameB = std::experimental::filesystem::path(filePathList[2]).filename(); + m_Controls.labelSegA->setText(m_Controls.labelSegA->text() + " " + QString::fromUtf8(fileNameA.string().c_str())); + m_Controls.labelSegB->setText(m_Controls.labelSegB->text() + " " + QString::fromUtf8(fileNameB.string().c_str())); + InvokeLoadData(filePathList); + }); - auto joinTask = pplx::when_all(begin(tasks), end(tasks)); - auto filePathList = joinTask.then([&](std::vector filePathList) { - auto fileNameA = std::experimental::filesystem::path(filePathList[1]).filename(); - auto fileNameB = std::experimental::filesystem::path(filePathList[2]).filename(); - m_Controls.labelSegA->setText(m_Controls.labelSegA->text() + " " + QString::fromUtf8(fileNameA.string().c_str())); - m_Controls.labelSegB->setText(m_Controls.labelSegB->text() + " " + QString::fromUtf8(fileNameB.string().c_str())); - InvokeLoadData(filePathList); }); + } void SegmentationReworkView::RESTGetCallback(const SegmentationReworkREST::DicomDTO &dto) { boost::uuids::random_generator generator; std::string folderPathSeries = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; std::experimental::filesystem::create_directory(folderPathSeries); std::string pathSeg = std::experimental::filesystem::path(m_downloadBaseDir).append(boost::uuids::to_string(generator())).string() + "/"; auto folderPathSeg = utility::conversions::to_string_t(pathSeg); std::experimental::filesystem::create_directory(pathSeg); std::vector> tasks; auto imageSeriesTask = m_RestClient->WadoRS(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, dto.imageSeriesUID); auto segATask = m_RestClient->WadoRS(folderPathSeg, dto.studyUID, dto.segSeriesUIDA); tasks.push_back(imageSeriesTask); tasks.push_back(segATask); auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto filePathList = joinTask.then([&](std::vector filePathList) { InvokeLoadData(filePathList); }); } 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_SegA = dataNodes->at(1); m_SegB = dataNodes->at(2); + } void SegmentationReworkView::UpdateChartWidget() { m_Controls.chartWidget->Show(); } void SegmentationReworkView::SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart) { std::map map; double sliceIndex = sliceMinStart; for (double score : simScoreArray) { map.insert(std::map::value_type(sliceIndex, score)); sliceIndex++; } m_Controls.chartWidget->AddData2D(map, "similarity score"); m_Controls.chartWidget->SetChartType("similarity score", QmitkChartWidget::ChartType::line); m_Controls.chartWidget->SetXAxisLabel("slice number"); m_Controls.chartWidget->SetYAxisLabel("similarity score"); } void SegmentationReworkView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { // iterate all selected objects, adjust warning visibility foreach(mitk::DataNode::Pointer node, nodes) { if (node.IsNotNull() && dynamic_cast(node->GetData())) { m_Controls.labelWarning->setVisible(false); return; } } m_Controls.labelWarning->setVisible(true); } void SegmentationReworkView::UploadNewSegmentation() { - auto filePath = U("1.2.276.0.7230010.3.1.4.296485376.8.1533635734.141264.dcm"); - m_CurrentStudyUID = "1.2.840.113654.2.70.1.159145727925405623564217141386659468090"; + boost::uuids::random_generator generator; + + std::string folderPathSeg = std::experimental::filesystem::path(m_tempSegDir).append(boost::uuids::to_string(generator())).string() + "/"; + std::experimental::filesystem::create_directory(folderPathSeg); + + const std::string savePath = folderPathSeg + m_SegC->GetName() + ".dcm"; + const std::string mimeType = mitk::IOMimeTypes::DICOM_MIMETYPE_NAME(); + mitk::IOUtil::Save(m_SegC->GetData(), mimeType, savePath); + + auto filePath = utility::conversions::to_string_t(savePath); try { m_RestClient->StowRS(filePath, m_CurrentStudyUID).wait(); } catch (const std::exception &exception) { std::cout << exception.what() << std::endl; } } void SegmentationReworkView::CreateNewSegmentationC() { mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + toolManager->InitializeTools(); toolManager->SetReferenceData(m_Image); - toolManager->SetWorkingData(m_SegC); + //toolManager->SetWorkingData(m_SegC); + + // using seg A as new base image + mitk::Image::Pointer image = dynamic_cast(m_SegA->GetData()); + + 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(image, 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; + } + 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() { std::experimental::filesystem::remove_all(m_downloadBaseDir); std::experimental::filesystem::create_directory(m_downloadBaseDir); } void SegmentationReworkView::DoImageProcessing() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "Template", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { std::stringstream message; std::string name; message << "Performing image processing for image "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; } message << "."; MITK_INFO << message.str(); // actually do something here... } } } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.h b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.h index fabc0389c1..2bafe39253 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.h +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.h @@ -1,91 +1,94 @@ /*=================================================================== 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 SegmentationReworkView_h #define SegmentationReworkView_h #include #include #include "ui_SegmentationReworkViewControls.h" #include "SegmentationReworkRest.h" #include #include /** \brief SegmentationReworkView \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class SegmentationReworkView : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; void RESTPutCallback(const SegmentationReworkREST::DicomDTO& dto); void RESTGetCallback(const SegmentationReworkREST::DicomDTO& dto); void UpdateChartWidget(); void LoadData(std::vector filePathList); signals: void InvokeLoadData(std::vector filePathList); protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer source, const QList &nodes) override; /// \brief Called when the user clicks the GUI button void DoImageProcessing(); void CreateNewSegmentationC(); void CleanDicomFolder(); void UploadNewSegmentation(); Ui::SegmentationReworkViewControls m_Controls; private: void SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart); - std::unique_ptr m_HttpHandler; mitk::RESTClient* m_RestClient; std::string m_CurrentStudyUID; - // use filesystem::path later... + std::experimental::filesystem::path m_downloadBaseDir; + std::experimental::filesystem::path m_tempSegDir; mitk::DataNode::Pointer m_Image; mitk::DataNode::Pointer m_SegA; mitk::DataNode::Pointer m_SegB; mitk::DataNode::Pointer m_SegC; + std::string m_GroundTruth; + + QWidget* m_Parent; }; #endif // SegmentationReworkView_h