diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp index 9048356bb9..1be8f0c63e 100644 --- a/Modules/Chart/src/QmitkChartWidget.cpp +++ b/Modules/Chart/src/QmitkChartWidget.cpp @@ -1,545 +1,549 @@ /*=================================================================== 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 #include "mitkExceptionMacro.h" #include #include class QmitkChartWidget::Impl final { public: explicit Impl(QWidget *parent, bool unitTest); explicit Impl(QWidget *parent); ~Impl(); Impl(const Impl &) = delete; Impl &operator=(const Impl &) = delete; void AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType chartType); void AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType chartType); void RemoveData(const std::string &label); void ClearData(); void SetColor(const std::string &label, const std::string &colorName); void SetLineStyle(const std::string &label, LineStyle style); void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string &label); void SetYAxisLabel(const std::string &label); void SetTitle(const std::string &title); void SetChartType(QmitkChartWidget::ChartType chartType); void SetChartTypeByLabel(const std::string &label, QmitkChartWidget::ChartType chartType); void SetLegendPosition(LegendPosition position); void SetShowLegend(bool show); void SetStackedData(bool stacked); void Show(bool showSubChart); void SetShowDataPoints(bool showDataPoints = false); std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const; void ClearJavaScriptChart(); void InitializeJavaScriptChart(); void CallJavaScriptFuntion(const QString &command); // for testing QmitkChartData *GetC3Data(); std::vector> *GetC3xyData(); private: using ChartxyDataVector = std::vector>; std::string GetUniqueLabelName(const QList &labelList, const std::string &label) const; QmitkChartxyData *GetDataElementByLabel(const std::string &label) const; QList GetDataLabels(const ChartxyDataVector &c3xyData) const; void MapTypes(); QWebChannel *m_WebChannel; QWebEngineView *m_WebEngineView; QmitkChartData m_C3Data; ChartxyDataVector m_C3xyData; std::map m_ChartTypeToName; std::map m_LegendPositionToName; std::map m_LineStyleToName; std::map m_AxisScaleToName; }; QmitkChartWidget::Impl::Impl(QWidget *parent) : m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent)) { // disable context menu for QWebEngineView m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu); // Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); m_WebEngineView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool))); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); parent->setLayout(layout); MapTypes(); } QmitkChartWidget::Impl::Impl(QWidget *parent, bool unitTest) { + if (unitTest) { + parent->actions(); + } + parent->actions(); MapTypes(); } void QmitkChartWidget::Impl::MapTypes() { m_ChartTypeToName.emplace(ChartType::bar, "bar"); m_ChartTypeToName.emplace(ChartType::line, "line"); m_ChartTypeToName.emplace(ChartType::spline, "spline"); m_ChartTypeToName.emplace(ChartType::pie, "pie"); m_ChartTypeToName.emplace(ChartType::area, "area"); m_ChartTypeToName.emplace(ChartType::area_spline, "area-spline"); m_ChartTypeToName.emplace(ChartType::scatter, "scatter"); m_LegendPositionToName.emplace(LegendPosition::bottom, "bottom"); m_LegendPositionToName.emplace(LegendPosition::right, "right"); m_LegendPositionToName.emplace(LegendPosition::inset, "inset"); m_LineStyleToName.emplace(LineStyle::solid, "solid"); m_LineStyleToName.emplace(LineStyle::dashed, "dashed"); m_AxisScaleToName.emplace(AxisScale::linear, ""); m_AxisScaleToName.emplace(AxisScale::log, "log"); } QmitkChartWidget::Impl::~Impl() {} std::string CheckForCorrectHex(const std::string &colorName) { std::regex rgx("([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); std::smatch match; if (!colorName.empty() && colorName.at(0) != '#' && std::regex_search(colorName.begin(), colorName.end(), match, rgx)) { return "#" + colorName; } else { return colorName; } } void QmitkChartWidget::Impl::AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType chartType) { std::map transformedData2D; unsigned int count = 0; // transform the 1D data to 2D data for (const auto &ele : data1D) { transformedData2D[count] = ele; count++; } AddData2D(transformedData2D, label, chartType); } void QmitkChartWidget::Impl::AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType chartType) { QMap data2DConverted; for (const auto &aValue : data2D) { data2DConverted.insert(aValue.first, aValue.second); } const std::string chartTypeName(m_ChartTypeToName.at(chartType)); auto definedLabels = GetDataLabels(m_C3xyData); auto uniqueLabel = GetUniqueLabelName(definedLabels, label); if (chartType == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } m_C3xyData.push_back(std::make_unique( data2DConverted, QVariant(QString::fromStdString(uniqueLabel)), QVariant(QString::fromStdString(chartTypeName)))); } void QmitkChartWidget::Impl::RemoveData(const std::string &label) { for (ChartxyDataVector::iterator iter = m_C3xyData.begin(); iter != m_C3xyData.end(); ++iter) { if ((*iter)->GetLabel().toString().toStdString() == label) { m_C3xyData.erase(iter); return; } } throw std::invalid_argument("Cannot Remove Data because the label does not exist."); } void QmitkChartWidget::Impl::ClearData() { for (auto &xyData : m_C3xyData) { m_WebChannel->deregisterObject(xyData.get()); } m_C3xyData.clear(); } void QmitkChartWidget::Impl::SetColor(const std::string &label, const std::string &colorName) { auto element = GetDataElementByLabel(label); if (element) { auto colorChecked = CheckForCorrectHex(colorName); element->SetColor(QVariant(QString::fromStdString(colorName))); } } void QmitkChartWidget::Impl::SetLineStyle(const std::string &label, LineStyle style) { auto element = GetDataElementByLabel(label); // only has effect with chart type line if (element && element->GetChartType() == QVariant(QString::fromStdString(ConvertChartTypeToString(ChartType::line)))) { const std::string lineStyleName(m_LineStyleToName.at(style)); element->SetLineStyle(QVariant(QString::fromStdString(lineStyleName))); } } void QmitkChartWidget::Impl::SetYAxisScale(AxisScale scale) { const std::string axisScaleName(m_AxisScaleToName.at(scale)); m_C3Data.SetYAxisScale(QString::fromStdString(axisScaleName)); } void QmitkChartWidget::Impl::SetXAxisLabel(const std::string &label) { m_C3Data.SetXAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetYAxisLabel(const std::string &label) { m_C3Data.SetYAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetTitle(const std::string &title) { m_C3Data.SetTitle(QString::fromStdString(title)); } void QmitkChartWidget::Impl::SetChartType(QmitkChartWidget::ChartType chartType) { for (auto iterator = m_C3xyData.begin(); iterator != m_C3xyData.end(); ++iterator) { SetChartTypeByLabel((*iterator)->GetLabel().toString().toStdString(), chartType); } auto chartTypeName = ConvertChartTypeToString(chartType); const QString command = QString::fromStdString("transformView('" + chartTypeName + "')"); CallJavaScriptFuntion(command); } void QmitkChartWidget::Impl::SetChartTypeByLabel(const std::string &label, QmitkChartWidget::ChartType chartType) { auto element = GetDataElementByLabel(label); if (element) { if (chartType == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } auto chartTypeName = ConvertChartTypeToString(chartType); element->SetChartType(QVariant(QString::fromStdString(chartTypeName))); } } void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition) { const std::string legendPositionName(m_LegendPositionToName.at(legendPosition)); m_C3Data.SetLegendPosition(QString::fromStdString(legendPositionName)); } void QmitkChartWidget::Impl::SetShowLegend(bool show) { m_C3Data.SetShowLegend(show); } void QmitkChartWidget::Impl::SetStackedData(bool stacked) { m_C3Data.SetStackedData(stacked); } void QmitkChartWidget::Impl::Show(bool showSubChart) { if (m_C3xyData.empty()) { mitkThrow() << "no data available for display in chart"; } m_C3Data.SetAppearance(showSubChart, m_C3xyData.front()->GetChartType() == QVariant("pie")); InitializeJavaScriptChart(); } void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints) { if (showDataPoints == true) { m_C3Data.SetDataPointSize(3); } else { m_C3Data.SetDataPointSize(0); } } std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const { return m_ChartTypeToName.at(chartType); } QmitkChartData *QmitkChartWidget::Impl::GetC3Data() { return &m_C3Data; } std::vector> *QmitkChartWidget::Impl::GetC3xyData() { return &m_C3xyData; } void QmitkChartWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); } void QmitkChartWidget::Impl::InitializeJavaScriptChart() { m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data); unsigned count = 0; for (auto &xyData : m_C3xyData) { QString variableName = "xyData" + QString::number(count); m_WebChannel->registerObject(variableName, xyData.get()); count++; } m_WebEngineView->load(QUrl(QStringLiteral("qrc:///C3js/QmitkChartWidget.html"))); } void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString &command) { m_WebEngineView->page()->runJavaScript(command); } std::string QmitkChartWidget::Impl::GetUniqueLabelName(const QList &labelList, const std::string &label) const { QString currentLabel = QString::fromStdString(label); int counter = 0; while (labelList.contains(currentLabel)) { currentLabel = QString::fromStdString(label + std::to_string(counter)); counter++; } return currentLabel.toStdString(); } QmitkChartxyData *QmitkChartWidget::Impl::GetDataElementByLabel(const std::string &label) const { for (const auto &qmitkChartxyData : m_C3xyData) { if (qmitkChartxyData->GetLabel().toString() == label.c_str()) { return qmitkChartxyData.get(); } } MITK_WARN << "label " << label << " not found in QmitkChartWidget"; return nullptr; } QList QmitkChartWidget::Impl::GetDataLabels(const ChartxyDataVector &c3xyData) const { QList dataLabels; for (auto element = c3xyData.begin(); element != c3xyData.end(); ++element) { dataLabels.push_back((*element)->GetLabel()); } return dataLabels; } QmitkChartWidget::QmitkChartWidget(QWidget *parent) : QWidget(parent), m_Impl(new Impl(this)) {} QmitkChartWidget::QmitkChartWidget(QWidget *parent, bool unitTest) : QWidget(parent), m_Impl(new Impl(this, unitTest)) { } QmitkChartWidget::~QmitkChartWidget() {} std::vector> *QmitkChartWidget::GetData() const { return m_Impl->GetC3xyData(); } QmitkChartData *QmitkChartWidget::GetC3Data() const { return m_Impl->GetC3Data(); } void QmitkChartWidget::AddData1D(const std::vector &data1D, const std::string &label, ChartType type) { m_Impl->AddData1D(data1D, label, type); } void QmitkChartWidget::AddData2D(const std::map &data2D, const std::string &label, ChartType type) { m_Impl->AddData2D(data2D, label, type); } void QmitkChartWidget::RemoveData(const std::string &label) { m_Impl->RemoveData(label); } void QmitkChartWidget::SetColor(const std::string &label, const std::string &colorName) { m_Impl->SetColor(label, colorName); } void QmitkChartWidget::SetLineStyle(const std::string &label, LineStyle style) { m_Impl->SetLineStyle(label, style); } void QmitkChartWidget::SetYAxisScale(AxisScale scale) { m_Impl->SetYAxisScale(scale); } void QmitkChartWidget::SetXAxisLabel(const std::string &label) { m_Impl->SetXAxisLabel(label); } void QmitkChartWidget::SetYAxisLabel(const std::string &label) { m_Impl->SetYAxisLabel(label); } void QmitkChartWidget::SetTitle(const std::string &title) { m_Impl->SetTitle(title); } void QmitkChartWidget::SetChartTypeForAllDataAndReload(ChartType type) { m_Impl->SetChartType(type); } void QmitkChartWidget::SetChartType(const std::string &label, ChartType type) { m_Impl->SetChartTypeByLabel(label, type); } void QmitkChartWidget::SetLegendPosition(LegendPosition position) { m_Impl->SetLegendPosition(position); } void QmitkChartWidget::SetShowLegend(bool show) { m_Impl->SetShowLegend(show); } void QmitkChartWidget::SetStackedData(bool stacked) { m_Impl->SetStackedData(stacked); } void QmitkChartWidget::Show(bool showSubChart) { m_Impl->Show(showSubChart); } void QmitkChartWidget::SetShowDataPoints(bool showDataPoints) { m_Impl->SetShowDataPoints(showDataPoints); } void QmitkChartWidget::Clear() { m_Impl->ClearData(); m_Impl->ClearJavaScriptChart(); } void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessful) { if (isLoadSuccessful) { emit PageSuccessfullyLoaded(); } } void QmitkChartWidget::SetTheme(ChartStyle themeEnabled) { QString command; if (themeEnabled == ChartStyle::darkstyle) { command = QString("changeTheme('dark')"); } else { command = QString("changeTheme('light')"); } m_Impl->CallJavaScriptFuntion(command); } void QmitkChartWidget::Reload(bool showSubChart) { QString subChartString; if (showSubChart) { subChartString = "true"; } else { subChartString = "false"; } const QString command = QString("ReloadChart(" + subChartString + ")"); m_Impl->CallJavaScriptFuntion(command); } diff --git a/Modules/CppRestSdk/files.cmake b/Modules/CppRestSdk/files.cmake index 712619726b..f95f52c5a0 100644 --- a/Modules/CppRestSdk/files.cmake +++ b/Modules/CppRestSdk/files.cmake @@ -1,6 +1,7 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkRESTServer.cpp mitkRESTClient.cpp + mitkDICOMWeb.cpp ) diff --git a/Modules/CppRestSdk/include/mitkDICOMWeb.h b/Modules/CppRestSdk/include/mitkDICOMWeb.h new file mode 100644 index 0000000000..0b3b27bca1 --- /dev/null +++ b/Modules/CppRestSdk/include/mitkDICOMWeb.h @@ -0,0 +1,40 @@ +/*=================================================================== + +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 MITKDICOMWEB_H +#define MITKDICOMWEB_H + +#include "MitkCppRestSdkExports.h" + +#include "mitkRESTClient.h" + +namespace mitk +{ + class MITKCPPRESTSDK_EXPORT DICOMWeb : public RESTClient + { + public:; + DICOMWeb(utility::string_t url); + virtual ~DICOMWeb(); + + pplx::task WadoRS(const utility::string_t filePath, std::string studyUID, std::string seriesUID, std::string instanceUID); + pplx::task WadoRS(const utility::string_t folderPath, std::string studyUID, std::string seriesUID); + pplx::task StowRS(utility::string_t filePath, std::string studyUID); + pplx::task QuidoRSInstances(std::map params); + + }; +}; + +#endif // MITKDICOMWEB_H diff --git a/Modules/CppRestSdk/include/mitkRESTClient.h b/Modules/CppRestSdk/include/mitkRESTClient.h index 65337aae13..9150e9fdb1 100644 --- a/Modules/CppRestSdk/include/mitkRESTClient.h +++ b/Modules/CppRestSdk/include/mitkRESTClient.h @@ -1,63 +1,59 @@ /*=================================================================== 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: + protected: MitkClient* m_Client; }; }; #endif // MITKRESTCLIENT_H diff --git a/Modules/CppRestSdk/include/mitkRESTServer.h b/Modules/CppRestSdk/include/mitkRESTServer.h index e2ce8cd2d3..9bf281ff64 100644 --- a/Modules/CppRestSdk/include/mitkRESTServer.h +++ b/Modules/CppRestSdk/include/mitkRESTServer.h @@ -1,63 +1,77 @@ /*=================================================================== 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 MITKRESTSERVER_H #define MITKRESTSERVER_H #include #include "cpprest/asyncrt_utils.h" #include "cpprest/containerstream.h" #include "cpprest/filestream.h" #include "cpprest/http_listener.h" #include "cpprest/json.h" #include "cpprest/producerconsumerstream.h" #include "cpprest/uri.h" +#include +#include + #include "MitkCppRestSdkExports.h" typedef web::http::experimental::listener::http_listener MitkListener; typedef web::http::http_request MitkRequest; typedef web::http::http_response MitkResponse; typedef web::http::methods MitkRESTMethods; typedef web::http::status_codes MitkRestStatusCodes; typedef web::json::json_exception MitkJsonException; namespace mitk { class MITKCPPRESTSDK_EXPORT RESTServer : public QObject { - public: + + struct CallbackDTO { + }; + + typedef std::function CallbackType(); + typedef std::map CallbackMapType; + RESTServer(); RESTServer(utility::string_t url); virtual ~RESTServer(); pplx::task Open() { return m_Listener.open(); } pplx::task Close() { return m_Listener.close(); } + void addGETPathHandler(std::string uri, CallbackType callback) { + + } + protected: virtual void HandleGet(MitkRequest){}; virtual void HandlePut(MitkRequest){}; virtual void HandlePost(MitkRequest){}; virtual void HandleDelete(MitkRequest){}; void HandleError(pplx::task &t); MitkListener m_Listener; + //CallbackMapType uriResolverGET; }; }; #endif // MITKRESTSERVER_H diff --git a/Modules/CppRestSdk/src/mitkDICOMWeb.cpp b/Modules/CppRestSdk/src/mitkDICOMWeb.cpp new file mode 100644 index 0000000000..fba477e8d3 --- /dev/null +++ b/Modules/CppRestSdk/src/mitkDICOMWeb.cpp @@ -0,0 +1,128 @@ +/*=================================================================== + +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" +#include "mitkRestUtil.h" +#include + +mitk::DICOMWeb::DICOMWeb(utility::string_t url) : mitk::RESTClient(url) +{ +} + +mitk::DICOMWeb::~DICOMWeb() +{ +} + +pplx::task mitk::DICOMWeb::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) + { + auto status = response.status_code(); + MITK_INFO << " status: " << status; + + if (status != web::http::status_codes::OK) { + mitkThrow() << "QUIDO-RS ended up with response " << mitk::RESTUtil::convertToUtf8(response.to_string()); + } + + return response.extract_json().get(); + }); +} + +pplx::task mitk::DICOMWeb::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::DICOMWeb::WadoRS(const utility::string_t folderPath, std::string studyUID, std::string seriesUID) +{ + typedef std::map ParamMap; + ParamMap seriesInstancesParams; + + seriesInstancesParams.insert(ParamMap::value_type({"StudyInstanceUID"}, studyUID)); + seriesInstancesParams.insert(ParamMap::value_type({"SeriesInstanceUID"}, seriesUID)); + + 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::DICOMWeb::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/Modules/CppRestSdk/src/mitkRESTClient.cpp b/Modules/CppRestSdk/src/mitkRESTClient.cpp index e740d53d50..34262c3478 100644 --- a/Modules/CppRestSdk/src/mitkRESTClient.cpp +++ b/Modules/CppRestSdk/src/mitkRESTClient.cpp @@ -1,228 +1,144 @@ /*=================================================================== 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) +mitk::RESTClient::RESTClient(utility::string_t url) { m_Client = new MitkClient(url); } -mitk::RESTClient::~RESTClient() +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); + << 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(); + auto status = response.status_code(); + MITK_DEBUG << "Status code: " << status; + + if (status != web::http::status_codes::OK) { + mitkThrow() << "GET ended up with response " << mitk::RESTUtil::convertToUtf8(response.to_string()); + } 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) + 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()); + << 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()); + auto status = response.status_code(); + if (status != web::http::status_codes::OK) { + mitkThrow() << "POST ended up with 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())); + 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) -{ - typedef std::map ParamMap; - ParamMap seriesInstancesParams; - - seriesInstancesParams.insert(ParamMap::value_type({"StudyInstanceUID"}, studyUID)); - seriesInstancesParams.insert(ParamMap::value_type({"SeriesInstanceUID"}, seriesUID)); - - 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 status = response.status_code(); + if (status != web::http::status_codes::OK) { + mitkThrow() << "POST ended up with response " << mitk::RESTUtil::convertToUtf8(response.to_string()); } - - auto joinTask = pplx::when_all(begin(tasks), end(tasks)); - return joinTask.then([=](void) - { - return utility::conversions::to_utf8string(folderPath).append(firstFileName); - }); + MITK_INFO << "Response: " << mitk::RESTUtil::convertToUtf8(response.to_string()); }); -} - -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/SegmentationReworkView.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp index 9882a54311..d2efdff9c6 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,376 +1,344 @@ /*=================================================================== 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); + m_DICOMWeb = new mitk::DICOMWeb(pacsHost); } void SegmentationReworkView::RESTPutCallback(const SegmentationReworkREST::DicomDTO &dto) { SetSimilarityGraph(dto.simScoreArray, dto.minSliceStart); typedef std::map ParamMap; ParamMap seriesInstancesParams; 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 - m_RestClient->QuidoRSInstances(seriesInstancesParams).then([=](web::json::value jsonResult) { + try { + m_DICOMWeb->QuidoRSInstances(seriesInstancesParams).then([=](web::json::value jsonResult) { - auto firstResult = jsonResult[0]; - auto actualListKey = firstResult.at(U("0040A375")).as_object().at(U("Value")).as_array()[0].as_object().at(U("00081115")).as_object().at(U("Value")).as_array(); + 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(); - std::string segSeriesUIDA = ""; - std::string segSeriesUIDB = ""; - std::string imageSeriesUID = ""; + std::string segSeriesUIDA = ""; + std::string segSeriesUIDB = ""; + std::string 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(); + 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()); + 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) + if (sopClassUID == L"1.2.840.10008.5.1.4.1.1.66.4") // SEG { - segSeriesUIDA = seriesUID; + if (segSeriesUIDA.length() == 0) + { + segSeriesUIDA = seriesUID; + } + else + { + segSeriesUIDB = seriesUID; + } } - else + else if (sopClassUID == L"1.2.840.10008.5.1.4.1.1.2") // CT { - segSeriesUIDB = seriesUID; + imageSeriesUID = 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 << "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; + 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 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() + "/"; + 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); + 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); + std::experimental::filesystem::create_directory(pathSegA); + std::experimental::filesystem::create_directory(pathSegB); - m_CurrentStudyUID = dto.studyUID; + 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); + std::vector> tasks; + auto imageSeriesTask = m_DICOMWeb->WadoRS(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, imageSeriesUID); + auto segATask = m_DICOMWeb->WadoRS(folderPathSegA, dto.studyUID, segSeriesUIDA); + auto segBTask = m_DICOMWeb->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); + }); - }); + }); + } + catch (mitk::Exception& e) { + MITK_ERROR << e.what(); + } } 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); + auto imageSeriesTask = m_DICOMWeb->WadoRS(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, dto.imageSeriesUID); + auto segATask = m_DICOMWeb->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() { 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(); + m_DICOMWeb->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); - - // using seg A as new base image - mitk::Image::Pointer image = dynamic_cast(m_SegA->GetData()); + + mitk::Image::Pointer baseImage; + if (m_Controls.radioA->isChecked()) { + baseImage = dynamic_cast(m_SegA->GetData()); + } + else if (m_Controls.radioB->isChecked()) { + baseImage = dynamic_cast(m_SegB->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()); + 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; } 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 2bafe39253..2622cb320f 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,94 +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 #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; + mitk::DICOMWeb* m_DICOMWeb; std::string m_CurrentStudyUID; 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 diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkViewControls.ui b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkViewControls.ui index e9649e297e..9723f417a4 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkViewControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkViewControls.ui @@ -1,140 +1,197 @@ SegmentationReworkViewControls 0 0 465 673 0 0 QmitkTemplate true QLabel { color: rgb(255, 0, 0) } Please select an image! Segmentation A: Segmentation B: ground truth: Qt::Horizontal 0 400 16777215 400 Qt::Horizontal - - - Create new Segmentation C - + + + false + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + Select segmentation basis + + + true + + + + QLayout::SetDefaultConstraint + + + + + A + + + + + + + B + + + + + + + Individual + + + + + + + + + + Create new Segmentation + + + + Do image processing - Upload Segmentation C + Upload Segmentation Qt::Vertical QSizePolicy::Expanding 20 220 clean dicom download folder QmitkChartWidget QWidget
QmitkChartWidget.h
1