diff --git a/Modules/DICOMweb/src/mitkDICOMweb.cpp b/Modules/DICOMweb/src/mitkDICOMweb.cpp index c476a585d1..a7ec34f47a 100644 --- a/Modules/DICOMweb/src/mitkDICOMweb.cpp +++ b/Modules/DICOMweb/src/mitkDICOMweb.cpp @@ -1,219 +1,218 @@ /*=================================================================== 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 "mitkDICOMweb.h" mitk::DICOMweb::DICOMweb() {} mitk::DICOMweb::DICOMweb(utility::string_t baseURI) : m_BaseURI(baseURI) { MITK_INFO << "base uri: " << mitk::RESTUtil::convertToUtf8(m_BaseURI); InitializeRESTManager(); } utility::string_t mitk::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(); } utility::string_t mitk::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(); } utility::string_t mitk::DICOMweb::CreateSTOWUri(utility::string_t studyUID) { MitkUriBuilder builder(m_BaseURI + U("rs/studies")); builder.append_path(studyUID); return builder.to_string(); } pplx::task mitk::DICOMweb::SendSTOW(utility::string_t filePath, utility::string_t studyUID) { auto uri = CreateSTOWUri(studyUID); // this is the working stow-rs request which supports just one dicom file packed into a multipart message std::ifstream input(filePath, std::ios::binary); if (!input) { MITK_WARN << "could not read file to POST"; return pplx::task(); } std::vector result; std::vector buffer; // Stop eating new lines in binary mode!!! input.unsetf(std::ios::skipws); input.seekg(0, std::ios::end); const std::streampos fileSize = input.tellg(); input.seekg(0, std::ios::beg); MITK_INFO << fileSize << " bytes will be sent."; buffer.reserve(fileSize); // file size std::copy( std::istream_iterator(input), std::istream_iterator(), std::back_inserter(buffer)); // 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"))); try { return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Post, &result, headers) .then([=](web::json::value result) { - if (result.is_null()) - { - } + MITK_INFO << "after send"; + MITK_INFO << mitk::RESTUtil::convertToUtf8(result.to_string()); + result.is_null(); }); } catch (std::exception &e) { MITK_WARN << e.what(); } return pplx::task(); } pplx::task mitk::DICOMweb::SendWADO(utility::string_t filePath, utility::string_t studyUID, utility::string_t seriesUID, utility::string_t instanceUID) { auto uri = CreateWADOUri(studyUID, seriesUID, instanceUID); // don't want return something auto content = web::json::value(); return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, {}, filePath) .then([=](web::json::value result) { - if (result.is_null()) - { - } + result.is_null(); + }); } pplx::task mitk::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)); 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); 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 mitk::DICOMweb::SendQIDO(mitk::RESTUtil::ParamMap map) { auto uri = CreateQIDOUri(map); mitk::RESTUtil::ParamMap headers; headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Accept"), U("application/json"))); auto content = web::json::value(); return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, headers); } void mitk::DICOMweb::InitializeRESTManager() { auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_RESTManager = managerService; } } } diff --git a/Modules/REST/include/mitkRESTClient.h b/Modules/REST/include/mitkRESTClient.h index 191cc6b7dc..fc3c2dac42 100644 --- a/Modules/REST/include/mitkRESTClient.h +++ b/Modules/REST/include/mitkRESTClient.h @@ -1,105 +1,106 @@ /*=================================================================== 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: using http_request = web::http::http_request; RESTClient(); ~RESTClient(); /** * @brief Executes a HTTP GET request with the given uri and returns a task waiting for a json object * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Get(const web::uri &uri, const std::map headers); /** * @brief Executes a HTTP GET request with the given uri and and stores the byte stream in a file given by the * filePath * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for returning an empty json object */ pplx::task Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers); /** * @brief Executes a HTTP PUT request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resources * @return task to wait for with resulting json object */ pplx::task Put(const web::uri &uri, const web::json::value *content); /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const web::json::value *content, const std::map headers); /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const std::vector *content, const std::map headers); private: /** * @brief Use this to create and init a new request with the given headers. If needed, set the body on the resulting * request object to avoid an automatic change of the content type header when setting the body first. */ http_request InitRequest(const std::map headers); pplx::task ExecutePost(const web::uri &uri, http_request request); + web::http::client::http_client_config m_ClientConfig; }; } // namespace mitk #endif diff --git a/Modules/REST/src/mitkRESTClient.cpp b/Modules/REST/src/mitkRESTClient.cpp index 2071b386ad..da48c24691 100644 --- a/Modules/REST/src/mitkRESTClient.cpp +++ b/Modules/REST/src/mitkRESTClient.cpp @@ -1,205 +1,212 @@ /*=================================================================== 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() +{ + m_ClientConfig.set_validate_certificates(false); +} mitk::RESTClient::~RESTClient() {} pplx::task mitk::RESTClient::Get(const web::uri &uri, const std::map headers) { - auto client = new http_client(uri); + auto client = new http_client(uri, m_ClientConfig); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) - mitkThrow(); + MITK_INFO << status; + MITK_INFO << mitk::RESTUtil::convertToUtf8(response.to_string()); + mitkThrow(); auto requestContentType = response.headers().content_type(); if (U("application/json") != requestContentType) response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } - catch (...) + catch (std::exception &e) { + MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers) { - auto client = new http_client(uri); + auto client = new http_client(uri, m_ClientConfig); auto fileBuffer = std::make_shared>(); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } // Open file stream for the specified file path return file_buffer::open(filePath, std::ios::out) .then([=](streambuf outFile) -> pplx::task { *fileBuffer = outFile; return client->request(methods::GET); }) // Write the response body into the file buffer .then([=](http_response response) -> pplx::task { auto status = response.status_code(); if (status_codes::OK != status) 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); + auto client = new http_client(uri, m_ClientConfig); http_request request(methods::PUT); if (nullptr != content) request.set_body(*content); return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) mitkThrow(); // Parse content type to application/json if it isn't already. This is // important if the content type is e.g. application/dicom+json. auto requestContentType = response.headers().content_type(); if (U("application/json") != requestContentType) response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } - catch (...) + catch (std::exception &e) { + MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const std::vector *content, const std::map headers) { auto request = InitRequest(headers); request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); return ExecutePost(uri, request); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content, const std::map headers) { auto request = InitRequest(headers); request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); return ExecutePost(uri, request); } http_request mitk::RESTClient::InitRequest(const std::map headers) { http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return request; } pplx::task mitk::RESTClient::ExecutePost(const web::uri &uri, http_request request) { - auto client = new http_client(uri); + auto client = new http_client(uri, m_ClientConfig); return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); + auto requestContentType = response.headers().content_type(); + MITK_INFO << status; if (status_codes::OK != status) mitkThrow(); - // 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 (requestContentType.find(U("json"))) + MITK_INFO << mitk::RESTUtil::convertToUtf8(requestContentType); + if (U("application/json") != requestContentType) { + auto json = web::json::value::object(); + json[U("test")] = web::json::value(17); + response.set_body(json); // use empty json object in response body to perform return value response.headers().set_content_type(U("application/json")); - return response.extract_json().get(); - } - else - { - return web::json::value(); } + return response.extract_json().get(); } - catch (...) + catch (std::exception &e) { + MITK_INFO << e.what(); mitkThrow() << "Getting response went wrong"; } }); } diff --git a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp index 447a609fcf..e4cad879f0 100644 --- a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp +++ b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.cpp @@ -1,282 +1,312 @@ /*=================================================================== 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 "InjectView.h" #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string InjectView::VIEW_ID = "org.mitk.views.injectview"; void InjectView::SetFocus() {} void InjectView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; qRegisterMetaType>("std::vector"); qRegisterMetaType>("std::vector"); qRegisterMetaType("DicomDTO"); m_Controls.cleanDicomBtn->setVisible(true); connect(m_Controls.cleanDicomBtn, &QPushButton::clicked, this, &InjectView::CleanDicomFolder); connect(m_Controls.restartConnection, &QPushButton::clicked, this, &InjectView::OnRestartConnection); connect(m_Controls.testConnection, &QPushButton::clicked, this, &InjectView::TestConnection); - + connect(m_Controls.saveDICOMBtn, &QPushButton::clicked, this, &InjectView::SaveSelectedDICOMObject); + m_DownloadBaseDir = mitk::IOUtil::GetTempPath() + "segInject"; MITK_INFO << "using download base dir: " << m_DownloadBaseDir; m_UploadBaseDir = mitk::IOUtil::GetTempPath() + "uploadSeg"; if (!itksys::SystemTools::FileIsDirectory(m_DownloadBaseDir)) { itk::FileTools::CreateDirectory(m_DownloadBaseDir); } if (!itksys::SystemTools::FileIsDirectory(m_UploadBaseDir)) { itk::FileTools::CreateDirectory(m_UploadBaseDir); } m_HostURL = U("http://localhost"); auto host = m_HostURL; auto envHostURL = std::getenv("HOST_URL"); if (envHostURL) { MITK_INFO << "host url " << std::string(envHostURL); m_HostURL = mitk::RESTUtil::convertToTString(std::string(envHostURL)); host = m_HostURL; } else { host = host.append(U(":8080")); } m_restURL = host.append(U("/rest-srs")); MITK_INFO << "rest url: " << mitk::RESTUtil::convertToUtf8(m_restURL); utility::string_t pacsURL = U("http://193.174.48.78:8090"); auto envPacsURL = std::getenv("PACS_URL"); if (envPacsURL) { pacsURL = mitk::RESTUtil::convertToTString(std::string(envPacsURL)); } m_RequestHandler = new InjectRequestHandler(m_DownloadBaseDir, pacsURL); connect(this, &InjectView::InvokeProgress, this, &InjectView::AddProgress); connect(m_RequestHandler, &InjectRequestHandler::InvokeProgress, this, &InjectView::AddProgress); - connect( - m_RequestHandler, &InjectRequestHandler::InvokeUpdateDcmMeta, this, &InjectView::InitializeDcmMeta); + connect(m_RequestHandler, &InjectRequestHandler::InvokeUpdateDcmMeta, this, &InjectView::InitializeDcmMeta); connect(m_RequestHandler, &InjectRequestHandler::InvokeLoadData, this, &InjectView::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 RestartConnection(pacsURL); } void InjectView::Activated() { MITK_INFO << "activated"; StartServer(); } void InjectView::Deactivated() { MITK_INFO << "deactivated"; m_ManagerService->HandleDeleteObserver(m_RequestHandler); } -void InjectView::Visible() -{ -} +void InjectView::Visible() {} -void InjectView::Hidden() -{ -} +void InjectView::Hidden() {} void InjectView::StartServer() { utility::string_t path = U("/inject"); auto pathEnv = std::getenv("SERVER_PATH"); if (pathEnv) { path = mitk::RESTUtil::convertToTString(std::string(pathEnv)); } utility::string_t port = U(":4040"); utility::string_t address = U("http://+"); + if (m_HostURL == U("http://localhost")) + address = m_HostURL; + address.append(port); address.append(path); // Setup listening server m_ManagerService->ReceiveRequest(address, m_RequestHandler); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); } void InjectView::AddProgress(int progress, QString status) { auto futureValue = m_Controls.progressBar->value() + progress; if (futureValue >= 100) { m_Controls.progressBar->setValue(0); m_Controls.progressBar->setFormat(""); } else { m_Controls.progressBar->setFormat(status.append(" %p%")); m_Controls.progressBar->setValue(futureValue); } } void InjectView::InitializeDcmMeta(InjectRequestHandler::DicomDTO dto) { m_CurrentStudyUID = mitk::RESTUtil::convertToUtf8(dto.studyUID); m_SRUID = mitk::RESTUtil::convertToUtf8(dto.srSeriesUID); m_GroundTruth = mitk::RESTUtil::convertToUtf8(dto.groundTruth); } pplx::task InjectView::TestConnection() { mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("limit"), U("1"))); m_Controls.connectionStatus->setText(QString("Testing connection ...")); return m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](pplx::task resultTask) { try { auto result = resultTask.get(); if (!result.is_null()) { m_Controls.connectionStatus->setText(QString("Connection works!")); return true; } else { m_Controls.connectionStatus->setText(QString("Trouble with connection. Not valid!")); return false; } } catch (mitk::Exception &e) { MITK_WARN << e.what(); m_Controls.connectionStatus->setText(QString("No connection possible.")); return false; } }); } void InjectView::OnRestartConnection() { RestartConnection(mitk::RESTUtil::convertToTString(m_Controls.dcm4cheeHostValue->text().toStdString())); } void InjectView::RestartConnection(utility::string_t newHost) { utility::string_t host; if (newHost.empty()) { MITK_INFO << "Host was empty"; m_Controls.connectionStatus->setText(QString("Host must not be empty!")); return; } utility::string_t url = newHost + U("/dcm4chee-arc/aets/DCM4CHEE/"); MITK_INFO << "Restarting connection to " << mitk::RESTUtil::convertToUtf8(url) << " ..."; m_Controls.connectionStatus->setText(QString("Restarting connection...")); m_Controls.dcm4cheeURL->setText({(utility::conversions::to_utf8string(url).c_str())}); m_DicomWeb = mitk::DICOMweb(url); if (!TestConnection().get()) { MITK_INFO << "Restart did not work.."; m_Controls.connectionStatus->setText(QString("No PACS server available under given host!")); } else { MITK_INFO << "requests to pacs are sent to: " << mitk::RESTUtil::convertToUtf8(url); } } - mitk::DataStorage::SetOfObjects::Pointer InjectView::LoadData(std::vector filePathList) { MITK_INFO << "Pushing data to data storage ..."; auto ds = GetDataStorage(); auto dataNodes = mitk::IOUtil::Load(filePathList, *ds); // reinit view mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(ds); return dataNodes; } +void InjectView::SaveSelectedDICOMObject() +{ + auto ds = GetDataStorage(); + auto node = ds->GetAll()->front(); + std::string path; + node->GetStringProperty("path", path); + + auto scanner = mitk::DICOMDCMTKTagScanner::New(); + mitk::DICOMTagPath studyUID(0x0020, 0x000D); // study UID + + mitk::StringList files; + files.push_back(path); + scanner->SetInputFiles(files); + scanner->AddTagPath(studyUID); + + scanner->Scan(); + + mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); + auto findings = frames.front()->GetTagValueAsString(studyUID); + auto studyUIDValue = findings.front().value; + + try + { + m_DicomWeb.SendSTOW(mitk::RESTUtil::convertToTString(path), mitk::RESTUtil::convertToTString(studyUIDValue)) + .then([=] { MITK_INFO << "STOW was successfull"; }); + } + catch (const std::exception &exception) + { + std::cout << exception.what() << std::endl; + } +} + void InjectView::CleanDicomFolder() { if (m_SegA || m_SegB || m_SegC) { QMessageBox::warning(nullptr, tr("Clean dicom folder"), tr("Please remove the data in data storage before cleaning the download folder")); return; } auto downloadDir = Poco::File(m_DownloadBaseDir); downloadDir.remove(true); } diff --git a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.h b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.h index c2779bbd0f..7a653326b3 100644 --- a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.h +++ b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectView.h @@ -1,121 +1,123 @@ /*=================================================================== 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 InjectView_h #define InjectView_h #include #include #include "ui_InjectViewControls.h" #include "InjectRequestHandler.h" #include #include #include /** @brief InjectView This class represents the view to make connections to a PACS server and show some information to do a manual Inject upon two existing segmentation volumes. @author Tobias Stein \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class InjectView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { Q_OBJECT public: static const std::string VIEW_ID; /** * @brief Loads the files given by a list of file paths. * * @param filePathList a list of absolute file paths * @return set of objects containing the newly loaded data nodes */ mitk::DataStorage::SetOfObjects::Pointer LoadData(std::vector filePathList); /** * @brief Progress is added to the progressbar in a percent value and the given status is displayed. The progress will * not exceed 100 points. * * @param progress the progress in percent points to be added to the bar * @param status the status to be displayed after achieving the progress */ void AddProgress(int progress, QString status); /** * @brief Initializes the DICOM meta data used by this view. * * @param dto the data transfer object for received DICOM meta data. */ void InitializeDcmMeta(InjectRequestHandler::DicomDTO dto); + void SaveSelectedDICOMObject(); + virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; signals: void InvokeProgress(int, QString status); protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; void CleanDicomFolder(); void RestartConnection(utility::string_t newHost); void OnRestartConnection(); pplx::task TestConnection(); Ui::InjectViewControls m_Controls; private: void StartServer(); mitk::IRESTManager *m_ManagerService; InjectRequestHandler *m_RequestHandler; std::string m_CurrentStudyUID; std::string m_SRUID; std::string m_DownloadBaseDir; std::string m_UploadBaseDir; mitk::DataNode::Pointer m_Image; mitk::DataNode::Pointer m_SegA; mitk::DataNode::Pointer m_SegB; mitk::DataNode::Pointer m_SegC; std::map m_ScoreMap; std::string m_GroundTruth; std::string m_thresholdLabel; mitk::DICOMweb m_DicomWeb; QWidget *m_Parent; utility::string_t m_restURL; utility::string_t m_HostURL; }; #endif // InjectView_h diff --git a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui index a4f5abffe4..5cfa964388 100644 --- a/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui +++ b/Plugins/org.mitk.gui.qt.inject/src/internal/InjectViewControls.ui @@ -1,126 +1,133 @@ InjectViewControls 0 0 465 868 0 0 QmitkTemplate dcm4chee host example input: http://193.174.48.78:8090 0 Qt::AlignCenter true restart connection Qt::AlignCenter test connection + + + + Save Selected DICOM Object + + + Qt::Vertical QSizePolicy::Expanding 20 220 clean dicom download folder 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 9baa592b50..a86fe07942 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,588 +1,591 @@ /*=================================================================== 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 #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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"); qRegisterMetaType>("std::vector"); qRegisterMetaType("DicomDTO"); m_Controls.cleanDicomBtn->setVisible(true); m_Controls.individualWidget_2->setVisible(false); m_Controls.sliderWidget->setMinimum(1); m_Controls.sliderWidget->setMaximum(100); m_Controls.sliderWidget->setTickInterval(1); m_Controls.sliderWidget->setSingleStep(1); m_Controls.radioA->setChecked(true); connect(m_Controls.buttonUpload, &QPushButton::clicked, this, &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); } m_HostURL = U("http://localhost"); auto host = m_HostURL; auto envHostURL = std::getenv("HOST_URL"); if (envHostURL) { MITK_INFO << "host url " << std::string(envHostURL); m_HostURL = mitk::RESTUtil::convertToTString(std::string(envHostURL)); host = m_HostURL; - } - else + } + else { - host = host.append(U(":8080")); + host = host.append(U(":8000")); } m_restURL = host.append(U("/rest-srs")); MITK_INFO << "rest url: " << mitk::RESTUtil::convertToUtf8(m_restURL); utility::string_t pacsURL = U("http://193.174.48.78:8090"); auto envPacsURL = std::getenv("PACS_URL"); if (envPacsURL) { pacsURL = mitk::RESTUtil::convertToTString(std::string(envPacsURL)); } m_RequestHandler = new ReworkRequestHandler(m_DownloadBaseDir, pacsURL); 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::InitializeDcmMeta); connect(m_RequestHandler, &ReworkRequestHandler::InvokeLoadData, this, &SegmentationReworkView::LoadData); connect( m_RequestHandler, &ReworkRequestHandler::InvokeLoadDataSegRework, this, &SegmentationReworkView::LoadDataSegRework); // Get the micro service us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_ManagerService = managerService; } } RestartConnection(pacsURL); } void 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; + + 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::InitializeDcmMeta(ReworkRequestHandler::DicomDTO dto) { m_CurrentStudyUID = mitk::RESTUtil::convertToUtf8(dto.studyUID); m_SRUID = mitk::RESTUtil::convertToUtf8(dto.srSeriesUID); m_GroundTruth = mitk::RESTUtil::convertToUtf8(dto.groundTruth); } void SegmentationReworkView::Activated() { StartServer(); MITK_INFO << "activated"; } void SegmentationReworkView::Deactivated() { MITK_INFO << "deactivated"; m_ManagerService->HandleDeleteObserver(m_RequestHandler); } void SegmentationReworkView::Visible() { MITK_INFO << "visible"; } void SegmentationReworkView::Hidden() { MITK_INFO << "hidden"; } 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 ...")); return m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](pplx::task resultTask) { try { auto result = resultTask.get(); if (!result.is_null()) { m_Controls.connectionStatus->setText(QString("Connection works!")); return true; } else { m_Controls.connectionStatus->setText(QString("Trouble with connection. Not valid!")); return false; } } catch (mitk::Exception &e) { MITK_WARN << e.what(); m_Controls.connectionStatus->setText(QString("No connection possible.")); return false; } }); } void SegmentationReworkView::OnRestartConnection() { 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 + U("/dcm4chee-arc/aets/DCM4CHEE/"); MITK_INFO << "Restarting connection to " << mitk::RESTUtil::convertToUtf8(url) << " ..."; m_Controls.connectionStatus->setText(QString("Restarting connection...")); m_Controls.dcm4cheeURL->setText({(utility::conversions::to_utf8string(url).c_str())}); m_DicomWeb = mitk::DICOMweb(url); if (!TestConnection().get()) { MITK_INFO << "Restart did not work.."; m_Controls.connectionStatus->setText(QString("No PACS server available under given host!")); } else { MITK_INFO << "requests to pacs are sent to: " << mitk::RESTUtil::convertToUtf8(url); } } void 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; } mitk::DataStorage::SetOfObjects::Pointer SegmentationReworkView::LoadData(std::vector filePathList) { MITK_INFO << "Pushing data to data storage ..."; auto ds = GetDataStorage(); auto dataNodes = mitk::IOUtil::Load(filePathList, *ds); // reinit view mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(ds); return dataNodes; } void SegmentationReworkView::LoadDataSegRework(std::vector filePathList) { auto dataNodes = LoadData(filePathList); // find data nodes m_Image = dataNodes->at(0); m_Image->SetName("image data"); m_SegA = dataNodes->at(1); m_SegB = dataNodes->at(2); auto algorithmNameA = GetAlgorithmOfSegByPath(filePathList[1]); auto algorithmNameB = GetAlgorithmOfSegByPath(filePathList[2]); m_SegA->SetName(algorithmNameA); m_SegB->SetName(algorithmNameB); m_Controls.labelSegAValue->setText(algorithmNameA.c_str()); m_Controls.labelSegBValue->setText(algorithmNameB.c_str()); m_Controls.labelGroundTruthValue->setText(m_GroundTruth.c_str()); emit InvokeProgress(20, {""}); } void SegmentationReworkView::SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart) { std::string label = "similarity graph"; m_thresholdLabel = "threshold"; double sliceIndex = sliceMinStart; for (double score : simScoreArray) { m_ScoreMap.insert(std::map::value_type(sliceIndex, score)); sliceIndex++; } std::map thresholdMap; m_Controls.chartWidget->AddData2D(m_ScoreMap, label); m_Controls.chartWidget->AddData2D(thresholdMap, m_thresholdLabel); m_Controls.chartWidget->SetChartType(label, QmitkChartWidget::ChartType::line); m_Controls.chartWidget->SetChartType(m_thresholdLabel, QmitkChartWidget::ChartType::line); m_Controls.chartWidget->SetXAxisLabel("slice number"); m_Controls.chartWidget->SetYAxisLabel("similarity in percent"); m_Controls.chartWidget->SetTitle("Similartiy Score for Segmentation Comparison"); m_Controls.chartWidget->Show(); } void SegmentationReworkView::StartServer() { utility::string_t path = U("/inject"); auto pathEnv = std::getenv("SERVER_PATH"); if (pathEnv) { path = mitk::RESTUtil::convertToTString(std::string(pathEnv)); } utility::string_t port = U(":4040"); utility::string_t address = U("http://+"); + if (m_HostURL == U("http://localhost")) + address = m_HostURL; + address.append(port); address.append(path); // Setup listening server m_ManagerService->ReceiveRequest(address, m_RequestHandler); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); } void 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"}); mitk::DICOMweb::MitkUriBuilder queryBuilder(m_restURL + U("/tasks/evaluations/")); queryBuilder.append_query(U("srUID"), utility::conversions::to_string_t(m_SRUID)); 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("/"); mitk::DICOMweb::MitkUriBuilder queryBuilder(m_restURL + U("/tasks/evaluations")); queryBuilder.append_path(utility::conversions::to_string_t(idParam)); m_ManagerService->SendRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Put, &updatedContent) .then([=](web::json::value result) { MITK_INFO << utility::conversions::to_utf8string(result.serialize()); if (result[U("reworkedSegmentationUID")].as_string() == utility::conversions::to_string_t(segSeriesUID)) { MITK_INFO << "successfully stored"; emit InvokeProgress(30, {"successfully stored"}); } }); }); }); } catch (const std::exception &exception) { std::cout << exception.what() << std::endl; } } std::vector 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); 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; } auto downloadDir = Poco::File(m_DownloadBaseDir); downloadDir.remove(true); }