diff --git a/CMake/BuildConfigurations/camiPhotoacousticsSetup.cmake b/CMake/BuildConfigurations/camiPhotoacousticsSetup.cmake index 3f745c3d34..4a881e5fb6 100644 --- a/CMake/BuildConfigurations/camiPhotoacousticsSetup.cmake +++ b/CMake/BuildConfigurations/camiPhotoacousticsSetup.cmake @@ -1,25 +1,25 @@ message(STATUS "Configuring MITK Photoacoustics Setup Build") # Enable open cv and open igt link, which is a necessary configuration set(MITK_USE_OpenCV ON CACHE BOOL "MITK Use OpenCV Library" FORCE) set(MITK_USE_OpenIGTLink ON CACHE BOOL "MITK Use OpenIGTLink Library" FORCE) set(MITK_USE_US_DiPhAS_SDK ON CACHE BOOL "Use DiPhAS SDK" FORCE) set(MITK_US_DiPhAS_SDK_PATH "C:/Users/dkfz/Source/Repos/UltrasoundResearchPlatform_SDK/UltrasoundResearchPlatformSDK_Cpp/x64" CACHE PATH "DiPhAS SDK Path") set(MITK_USE_OPHIR_PYRO_HARDWARE ON CACHE BOOL "Use the Ophir sensor build-in in OPOTEK Phocus Mobile" FORCE) set(MITK_OPHIR_API_PATH "C:/OphirCppWrapper" CACHE PATH "Ophir API Path") -set(MITK_USE_OPOTEK_HARDWARE ON CACHE BOOL "Use hardware build-in in OPOTEK Phocus Mobile" FORCE) -set(MITK_GALIL_API_PATH "C:/OphirCppWrapper" CACHE PATH "Galil API Path") +set(MITK_USE_GALIL_HARDWARE ON CACHE BOOL "Use hardware build-in in OPOTEK Phocus Mobile" FORCE) +set(MITK_GALIL_API_PATH "C:/Program Files (x86)/Galil/gclib" CACHE PATH "Galil API Path") # Enable default plugins and the navigation modules set(MITK_CONFIG_PLUGINS org.mitk.gui.qt.datamanager org.mitk.gui.qt.stdmultiwidgeteditor org.mitk.gui.qt.imagenavigator org.mitk.gui.qt.properties org.mitk.gui.qt.viewnavigator org.mitk.gui.qt.ultrasound org.mitk.gui.qt.lasercontrol ) diff --git a/CMake/FindOpenCL.cmake b/CMake/FindOpenCL.cmake index 848594e9e3..551d768b13 100644 --- a/CMake/FindOpenCL.cmake +++ b/CMake/FindOpenCL.cmake @@ -1,88 +1,88 @@ # - Try to find OpenCL # This module tries to find an OpenCL implementation on your system. It supports # AMD / ATI, Apple and NVIDIA implementations, but should work, too. # # To set manually the paths, define these environment variables: # OpenCL_INCPATH - Include path (e.g. OpenCL_INCPATH=/opt/cuda/4.0/cuda/include) # OpenCL_LIBPATH - Library path (e.h. OpenCL_LIBPATH=/usr/lib64/nvidia) # # Once done this will define # OPENCL_FOUND - system has OpenCL # OPENCL_INCLUDE_DIRS - the OpenCL include directory # OPENCL_LIBRARIES - link these to use OpenCL # # WIN32 should work, but is untested FIND_PACKAGE(PackageHandleStandardArgs) SET (OPENCL_VERSION_STRING "0.1.0") SET(OPENCL_VERSION_MAJOR 0) SET(OPENCL_VERSION_MINOR 1) SET(OPENCL_VERSION_PATCH 0) IF(APPLE) FIND_LIBRARY(OPENCL_LIBRARIES OpenCL DOC "OpenCL lib for OSX") FIND_PATH(OPENCL_INCLUDE_DIRS OpenCL/cl.h DOC "Include for OpenCL on OSX") FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS OpenCL/cl.hpp DOC "Include for OpenCL CPP bindings on OSX") ELSE() IF (WIN32) FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h) FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp) IF(CMAKE_SIZEOF_VOID_P MATCHES "8") SET(OPENCL_LIB_DIR "$ENV{ATISTREAMSDKROOT}/lib/x86_64") if(NOT IS_DIRECTORY ${OPENCL_LIB_DIR}) SET(OPENCL_LIB_DIR "$ENV{CUDA_PATH}/lib/x64") endif() ELSE() SET(OPENCL_LIB_DIR "$ENV{ATISTREAMSDKROOT}/lib/x86") if(NOT IS_DIRECTORY ${OPENCL_LIB_DIR}) # need to convert path in the cmake style ? SET(OPENCL_LIB_DIR "$ENV{CUDA_PATH}/lib/Win32") endif() ENDIF() file(TO_CMAKE_PATH ${OPENCL_LIB_DIR} OPENCL_LIB_DIR) GET_FILENAME_COMPONENT(OPENCL_LIB_DIR ${OPENCL_LIB_DIR} ABSOLUTE) FIND_LIBRARY(OPENCL_LIBRARIES OpenCL.lib PATHS ${OPENCL_LIB_DIR} ENV OpenCL_LIBPATH) GET_FILENAME_COMPONENT(_OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE) # On Win32 search relative to the library FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h PATHS "${_OPENCL_INC_CAND}" ENV OpenCL_INCPATH) FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp PATHS "${_OPENCL_INC_CAND}" ENV OpenCL_INCPATH) ELSE () # Unix style platforms FIND_LIBRARY(OPENCL_LIBRARIES libOpenCL.so PATHS "/usr/local/cuda/lib64" "/opt/AMDAPP/lib64" ENV LD_LIBRARY_PATH ENV OpenCL_LIBPATH ) GET_FILENAME_COMPONENT(OPENCL_LIB_DIR ${OPENCL_LIBRARIES} PATH) GET_FILENAME_COMPONENT(_OPENCL_INC_CAND ${OPENCL_LIB_DIR}/../../include ABSOLUTE) # The AMD SDK currently does not place its headers # in /usr/include, therefore also search relative # to the library FIND_PATH(OPENCL_INCLUDE_DIRS CL/cl.h PATHS ${_OPENCL_INC_CAND} "/usr/local/cuda/include" "/opt/AMDAPP/include" ENV OpenCL_INCPATH) FIND_PATH(_OPENCL_CPP_INCLUDE_DIRS CL/cl.hpp PATHS ${_OPENCL_INC_CAND} "/usr/local/cuda/include" "/opt/AMDAPP/include" ENV OpenCL_INCPATH) ENDIF () ENDIF () FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenCL DEFAULT_MSG OPENCL_LIBRARIES OPENCL_INCLUDE_DIRS) IF(_OPENCL_CPP_INCLUDE_DIRS) SET( OPENCL_HAS_CPP_BINDINGS TRUE ) LIST( APPEND OPENCL_INCLUDE_DIRS ${_OPENCL_CPP_INCLUDE_DIRS} ) # This is often the same, so clean up LIST( REMOVE_DUPLICATES OPENCL_INCLUDE_DIRS ) -ENDIF(_OPENCL_CPP_INCLUDE_DIRS) +ENDIF() MARK_AS_ADVANCED( OPENCL_INCLUDE_DIRS ) diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index a8f927532c..23b083927e 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,671 +1,671 @@ /*=================================================================== 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 "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if (imageIOName == "GE4ImageIO" || imageIOName == "GE5ImageIO") { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } /**Helper function that converts the content of a meta data into a time point vector. * If MetaData is not valid or cannot be converted an empty vector is returned.*/ std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase *data) { const auto *timeGeometryTimeData = dynamic_cast *>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; std::vector ItkImageIO::Read() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass() << std::endl; typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback" << std::endl; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass() << std::endl; ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfoByKey(key); auto predicate = [mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info PropertyPersistenceInfo::Pointer newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { mitk::CoreServices::GetPropertyPersistence()->AddInfo(info); } } } MITK_INFO << "...finished!" << std::endl; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } - // use compression if available - m_ImageIO->UseCompressionOn(); + // dont use compression because it takes rediculusly long + m_ImageIO->UseCompressionOff(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); std::stringstream stream; stream << arbitraryTG->GetTimeBounds(0)[0]; for (TimeStepType pos = 0; pos < arbitraryTG->CountTimeSteps(); ++pos) { stream << " " << arbitraryTG->GetTimeBounds(pos)[1]; } std::string data = stream.str(); itk::EncapsulateMetaData( m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, data); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } ImageReadAccessor imageAccess(image); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/PhotoacousticsHardware/CMakeLists.txt b/Modules/PhotoacousticsHardware/CMakeLists.txt index fa90f023da..06bde63943 100644 --- a/Modules/PhotoacousticsHardware/CMakeLists.txt +++ b/Modules/PhotoacousticsHardware/CMakeLists.txt @@ -1,21 +1,21 @@ if(WIN32) option(MITK_USE_GALIL_HARDWARE "Enable support for OPOTEK Lasers" OFF) - option(MITK_USE_OPHIR_PRYO_HARDWARE "Enable support for Ophir Pyroelectrical Sensors" OFF) + option(MITK_USE_OPHIR_PYRO_HARDWARE "Enable support for Ophir Pyroelectrical Sensors" OFF) - if(MITK_USE_OPHIR_PRYO_HARDWARE) + if(MITK_USE_OPHIR_PYRO_HARDWARE) set(MITK_OPHIR_API_PATH "" CACHE PATH "Path to Ophir API lib.") endif() if(MITK_USE_GALIL_HARDWARE) set(MITK_GALIL_API_PATH "" CACHE PATH "Path to Galil API header files.") MITK_CREATE_MODULE( SUBPROJECTS DEPENDS MitkIGT INCLUDE_DIRS PUBLIC "${MITK_GALIL_API_PATH}/include" "${MITK_OPHIR_API_PATH}" INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} ADDITIONAL_LIBS "${MITK_GALIL_API_PATH}/lib/dynamic/x64/gclib.lib" "${MITK_GALIL_API_PATH}/lib/dynamic/x64/gclibo.lib" tinyxml "${MITK_OPHIR_API_PATH}/OphirPyroWrapper.lib" PACKAGE_DEPENDS tinyxml ) endif() endif() diff --git a/Modules/PhotoacousticsHardware/Resources/OpotekPhocusMobile.xml b/Modules/PhotoacousticsHardware/Resources/OpotekPhocusMobile.xml index 37242e3275..04f9701ac9 100644 --- a/Modules/PhotoacousticsHardware/Resources/OpotekPhocusMobile.xml +++ b/Modules/PhotoacousticsHardware/Resources/OpotekPhocusMobile.xml @@ -1,42 +1,42 @@ - -3.26731848e+07 - 2.35443306e+05 - -7.04376397e+02 - 1.12232174e+00 - -1.00519593e-03 - 4.79945765e-07 - -9.54424480e-11 + -4.30076111e+07 + 3.12495804e+05 + -9.43122022e+02 + 1.51584088e+00 + -1.36913735e-03 + 6.59021774e-07 + -1.32069448e-10 32561 690.000 750.000 950.000 5 115200 8 0 10 690.0|695.2|700.4|705.6|710.8|716.0|721.2|726.4|731.6|736.8|742.0|747.2|752.4|757.6|762.8|768.0|773.2|778.4|783.6|788.8|794.0|799.2|804.4|809.6|814.8|820.0|825.2|830.4|835.6|840.8|846.0|851.2|856.4|861.6|866.8|872.0|877.2|882.4|887.6|892.8|898.0|903.2|908.4|913.6|918.8|924.0|929.2|934.4|939.6|944.8 0.000727|0.000726|0.000764|0.000795|0.000824|0.000817|0.000832|0.000893|0.000898|0.000881|0.000870|0.000834|0.000849|0.000793|0.000778|0.000753|0.000755|0.000812|0.000843|0.000818|0.000843|0.000824|0.000829|0.000837|0.000832|0.000796|0.000808|0.000796|0.000774|0.000778|0.000760|0.000741|0.000724|0.000725|0.000703|0.000703|0.000695|0.000697|0.000671|0.000702|0.000679|0.000671|0.000676|0.000647|0.000660|0.000674|0.000686|0.000664|0.000658|0.000643 0.042647|0.043251|0.045291|0.047462|0.048515|0.049305|0.049513|0.050434|0.049656|0.050895|0.048269|0.049129|0.050182|0.049334|0.046761|0.045675|0.045061|0.049129|0.050357|0.049206|0.050050|0.048745|0.048822|0.049041|0.048976|0.047813|0.047082|0.046980|0.046980|0.046442|0.045445|0.044063|0.043909|0.042451|0.041058|0.041760|0.042374|0.041683|0.040269|0.042374|0.041299|0.038996|0.041069|0.040736|0.039994|0.040839|0.040225|0.039994|0.038613|0.037692 4 115200 8 N 1 \ No newline at end of file diff --git a/Modules/US/Testing/mitkUSImageVideoSourceTest.cpp b/Modules/US/Testing/mitkUSImageVideoSourceTest.cpp index 70f426402a..9b060a5ad8 100644 --- a/Modules/US/Testing/mitkUSImageVideoSourceTest.cpp +++ b/Modules/US/Testing/mitkUSImageVideoSourceTest.cpp @@ -1,83 +1,83 @@ /*=================================================================== 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 "mitkUSImageVideoSource.h" #include "mitkTestingMacros.h" class mitkUSImageVideoSourceTestClass { public: static void TestInstantiation() { // let's create an object of our class mitk::USImageVideoSource::Pointer usSource = mitk::USImageVideoSource::New(); MITK_TEST_CONDITION_REQUIRED(usSource.IsNotNull(), "USImageVideoSource should not be null after instantiation"); } static void TestOpenVideoFile(std::string videoFilePath) { mitk::USImageVideoSource::Pointer usSource = mitk::USImageVideoSource::New(); usSource->SetVideoFileInput(videoFilePath); MITK_TEST_CONDITION_REQUIRED(usSource->GetIsVideoReady(), "USImageVideoSource should have isVideoReady flag set after opening a Video File"); mitk::Image::Pointer frame; - frame = usSource->GetNextImage(); + frame = usSource->GetNextImage()[0]; MITK_TEST_CONDITION_REQUIRED(frame.IsNotNull(), "First frame should not be null."); - frame = usSource->GetNextImage(); + frame = usSource->GetNextImage()[0]; MITK_TEST_CONDITION_REQUIRED(frame.IsNotNull(), "Second frame should not be null."); - frame = usSource->GetNextImage(); + frame = usSource->GetNextImage()[0]; MITK_TEST_CONDITION_REQUIRED(frame.IsNotNull(), "Third frame should not be null."); - frame = usSource->GetNextImage(); + frame = usSource->GetNextImage()[0]; MITK_TEST_CONDITION_REQUIRED(frame.IsNotNull(), "Fourth frame should not be null."); - frame = usSource->GetNextImage(); + frame = usSource->GetNextImage()[0]; MITK_TEST_CONDITION_REQUIRED(frame.IsNotNull(), "Fifth frame should not be null."); } /** This Test will fail if no device is attached. Since it basically covers the same non-OpenCV Functionality as TestOpenVideoFile, it is ommited static void TestOpenDevice() { mitk::USImageVideoSource::Pointer usSource = mitk::USImageVideoSource::New(); usSource->SetCameraInput(-1); MITK_TEST_CONDITION_REQUIRED(usSource->GetIsVideoReady(), "USImageVideoSource should have isVideoReady flag set after opening a Camera device"); } */ }; #ifdef WIN32 // Video file compression is currently only supported under windows. /** * This function is testing methods of the class USImageVideoSource. */ int mitkUSImageVideoSourceTest(int, char* argv[]) { MITK_TEST_BEGIN("mitkUSImageVideoSourceTest"); mitkUSImageVideoSourceTestClass::TestInstantiation(); mitkUSImageVideoSourceTestClass::TestOpenVideoFile(argv[1]); // This test is commented out since no videodevcie ist steadily connected to the dart clients. // Functionality should sufficiently be tested through TestOpenVideoFile anyway //mitkUSImageVideoSourceTestClass::TestOpenDevice(); MITK_TEST_END(); } #else int mitkUSImageVideoSourceTest(int, char* [] ) { MITK_TEST_BEGIN("mitkUSImageVideoSourceTest"); MITK_TEST_END(); } #endif diff --git a/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.cpp b/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.cpp index f09adb68d0..25c6c847ad 100644 --- a/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.cpp +++ b/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.cpp @@ -1,188 +1,192 @@ /*=================================================================== 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 void mitk::IGTLMessageToUSImageFilter::GetNextRawImage( - mitk::Image::Pointer& img) + std::vector& imgVector) { + if (imgVector.size() != 1) + imgVector.resize(1); + + mitk::Image::Pointer& img = imgVector[0]; m_upstream->Update(); mitk::IGTLMessage* msg = m_upstream->GetOutput(); if (msg != nullptr && (!msg->IsDataValid() || std::strcmp(msg->GetIGTLMessageType(), "IMAGE") != 0)) { img = m_previousImage; return; } igtl::MessageBase::Pointer msgBase = msg->GetMessage(); igtl::ImageMessage* imgMsg = (igtl::ImageMessage*)(msgBase.GetPointer()); bool big_endian = (imgMsg->GetEndian() == igtl::ImageMessage::ENDIAN_BIG); if (imgMsg->GetCoordinateSystem() != igtl::ImageMessage::COORDINATE_RAS) { // TODO: Which coordinate system does MITK use? mitkThrow() << "Can not handle messages with LPS coordinate system"; } switch (imgMsg->GetScalarType()) { case igtl::ImageMessage::TYPE_UINT8: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_INT8: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_UINT16: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_INT16: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_UINT32: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_INT32: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_FLOAT32: Initiate(img, imgMsg, big_endian); break; case igtl::ImageMessage::TYPE_FLOAT64: Initiate(img, imgMsg, big_endian); break; default: mitkThrow() << "Incompatible PixelType " << imgMsg; } } template void mitk::IGTLMessageToUSImageFilter::Initiate(mitk::Image::Pointer& img, igtl::ImageMessage* msg, bool big_endian) { typedef itk::Image ImageType; typename ImageType::Pointer output = ImageType::New(); typename ImageType::RegionType region; typename ImageType::RegionType::SizeType size; typename ImageType::RegionType::IndexType index; typename ImageType::SpacingType spacing; typename ImageType::PointType origin; // Copy dimensions int dims[3]; msg->GetDimensions(dims); size_t num_pixel = 1; for (size_t i = 0; i < 3; i++) { size[i] = dims[i]; num_pixel *= dims[i]; } // Handle subvolume information. We want the subvolume to be the whole image // for now. int sdims[3], offs[3]; msg->GetSubVolume(sdims, offs); for (size_t i = 0; i < 3; i++) { if (offs[i] != 0 || sdims[i] != dims[i]) { // TODO: Handle messages with smaller subvolume than whole image throw("Can not handle message with smaller subvolume than whole image"); } } index.Fill(0); float matF[4][4]; msg->GetMatrix(matF); vtkSmartPointer vtkMatrix = vtkSmartPointer::New(); for (size_t i = 0; i < 4; ++i) for (size_t j = 0; j < 4; ++j) vtkMatrix->SetElement(i, j, matF[i][j]); float spacingMsg[3]; msg->GetSpacing(spacingMsg); for (int i = 0; i < 3; ++i) spacing[i] = spacingMsg[i]; region.SetSize(size); region.SetIndex(index); output->SetRegions(region); output->SetSpacing(spacing); output->Allocate(); TPixel* in = (TPixel*)msg->GetScalarPointer(); TPixel* out = (TPixel*)output->GetBufferPointer(); memcpy(out, in, num_pixel * sizeof(TPixel)); if (big_endian) { // Even though this method is called "FromSystemToBigEndian", it also swaps // "FromBigEndianToSystem". // This makes sense, but might be confusing at first glance. itk::ByteSwapper::SwapRangeFromSystemToBigEndian(out, num_pixel); } else { itk::ByteSwapper::SwapRangeFromSystemToLittleEndian(out, num_pixel); } img = mitk::Image::New(); img->InitializeByItk(output.GetPointer()); img->SetVolume(output->GetBufferPointer()); //img->GetGeometry()->SetIndexToWorldTransformByVtkMatrix(vtkMatrix); m_previousImage = img; float iorigin[3]; msg->GetOrigin(iorigin); for (size_t i = 0; i < 3; i++) { origin[i] = iorigin[i]; } output->SetOrigin(origin); } mitk::IGTLMessageToUSImageFilter::IGTLMessageToUSImageFilter() : m_upstream(nullptr) { MITK_DEBUG << "Instantiated this (" << this << ") mitkIGTMessageToUSImageFilter\n"; } void mitk::IGTLMessageToUSImageFilter::SetNumberOfExpectedOutputs( unsigned int numOutputs) { if (numOutputs > 1) { throw("Can only have 1 output for IGTLMessageToUSImageFilter."); } } void mitk::IGTLMessageToUSImageFilter::ConnectTo( mitk::IGTLMessageSource* UpstreamFilter) { MITK_DEBUG << "Connected this (" << this << ") mitkIGTLMessageToUSImageFilter to MessageSource (" << UpstreamFilter << ")\n"; m_upstream = UpstreamFilter; } \ No newline at end of file diff --git a/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.h b/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.h index 378200a7e8..39d5950341 100644 --- a/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.h +++ b/Modules/US/USFilters/mitkIGTLMessageToUSImageFilter.h @@ -1,82 +1,82 @@ /*=================================================================== 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 MITKIGTLMessageToUSImageFilter_H_HEADER_INCLUDED_ #define MITKIGTLMessageToUSImageFilter_H_HEADER_INCLUDED_ #include #include #include #include #include namespace mitk { class MITKUS_EXPORT IGTLMessageToUSImageFilter : public USImageSource { public: mitkClassMacro(IGTLMessageToUSImageFilter, USImageSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** *\brief Sets the number of expected outputs. * * Normally, this is done automatically by the filter concept. However, in our * case we can not know, for example, how many tracking elements are stored * in the incoming igtl message. Therefore, we have to set the number here to * the expected value. */ void SetNumberOfExpectedOutputs(unsigned int numOutputs); /** *\brief Connects the input of this filter to the outputs of the given * IGTLMessageSource * * This method does not support smartpointer. use FilterX.GetPointer() to * retrieve a dumbpointer. */ void ConnectTo(mitk::IGTLMessageSource* UpstreamFilter); protected: IGTLMessageToUSImageFilter(); using Superclass::GetNextRawImage; /** * \brief Copies the data from the next OIGTL message to an mitk::Image. * * \param img the image to fill with the data from the OIGTL message. */ - virtual void GetNextRawImage(mitk::Image::Pointer& img); + virtual void GetNextRawImage(std::vector& imgVector); private: mitk::IGTLMessageSource* m_upstream; mitk::Image::Pointer m_previousImage; /** * \brief Templated method to copy the data of the OIGTL message to the image, depending * on the pixel type contained in the message. * * \param img the image to fill with the data from msg * \param msg the OIGTL message to copy the data from * \param big_endian whether the data is in big endian byte order */ template void Initiate(mitk::Image::Pointer& img, igtl::ImageMessage* msg, bool big_endian); }; } // namespace mitk #endif // MITKIGTLMessageToUSImageFilter_H_HEADER_INCLUDED_ diff --git a/Modules/US/USFilters/mitkUSImageSource.cpp b/Modules/US/USFilters/mitkUSImageSource.cpp index e46f349fdd..3599025b56 100644 --- a/Modules/US/USFilters/mitkUSImageSource.cpp +++ b/Modules/US/USFilters/mitkUSImageSource.cpp @@ -1,117 +1,126 @@ /*=================================================================== 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 "mitkUSImageSource.h" #include "mitkProperties.h" const char* mitk::USImageSource::IMAGE_PROPERTY_IDENTIFIER = "id_nummer"; mitk::USImageSource::USImageSource() : m_OpenCVToMitkFilter(mitk::OpenCVToMitkImageFilter::New()), m_MitkToOpenCVFilter(nullptr), m_ImageFilter(mitk::BasicCombinationOpenCVImageFilter::New()), m_CurrentImageId(0), m_ImageFilterMutex(itk::FastMutexLock::New()) { } mitk::USImageSource::~USImageSource() { } void mitk::USImageSource::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { m_ImageFilter->PushFilter(filter); } bool mitk::USImageSource::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->RemoveFilter(filter); } bool mitk::USImageSource::GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->GetIsFilterOnTheList(filter); } -mitk::Image::Pointer mitk::USImageSource::GetNextImage() +std::vector mitk::USImageSource::GetNextImage() { - mitk::Image::Pointer result; + std::vector result; - // Get next image and apply OpenCV based filters beforehand + // Apply OpenCV based filters beforehand if (m_ImageFilter.IsNotNull() && !m_ImageFilter->GetIsEmpty()) { - cv::Mat image; - this->GetNextRawImage(image); + std::vector imageVector; + GetNextRawImage(imageVector); + if(result.size() != imageVector.size()) + result.resize(imageVector.size()); - if (!image.empty()) + for (int i = 0; i < imageVector.size(); ++i) { - m_ImageFilterMutex->Lock(); - m_ImageFilter->FilterImage(image, m_CurrentImageId); - m_ImageFilterMutex->Unlock(); - - // convert to MITK image - this->m_OpenCVToMitkFilter->SetOpenCVMat(image); - this->m_OpenCVToMitkFilter->Update(); - - // OpenCVToMitkImageFilter returns a standard mitk::image. - result = this->m_OpenCVToMitkFilter->GetOutput(); + if (!imageVector[i].empty()) + { + m_ImageFilterMutex->Lock(); + m_ImageFilter->FilterImage(imageVector[i], m_CurrentImageId); + m_ImageFilterMutex->Unlock(); + + // convert to MITK image + this->m_OpenCVToMitkFilter->SetOpenCVMat(imageVector[i]); + this->m_OpenCVToMitkFilter->Update(); + + // OpenCVToMitkImageFilter returns a standard mitk::image. + result[i] = this->m_OpenCVToMitkFilter->GetOutput(); + } } } - // Get next image without filtering else { - // mitk image can be received direclty if no filtering is necessary this->GetNextRawImage(result); } - if (result.IsNotNull()) - { - result->SetProperty(IMAGE_PROPERTY_IDENTIFIER, mitk::IntProperty::New(m_CurrentImageId)); - m_CurrentImageId++; - - // Everything as expected, return result - return result; - } - else + for (int i = 0; i < result.size(); ++i) { - //MITK_WARN("mitkUSImageSource") << "Result image is not set."; - return mitk::Image::New(); + if (result[i].IsNotNull()) + { + result[i]->SetProperty(IMAGE_PROPERTY_IDENTIFIER, mitk::IntProperty::New(m_CurrentImageId)); + } + else + { + //MITK_WARN("mitkUSImageSource") << "Result image " << i << " is not set."; + result[i] = mitk::Image::New(); + } } + m_CurrentImageId++; + + return result; } -void mitk::USImageSource::GetNextRawImage(cv::Mat& image) +void mitk::USImageSource::GetNextRawImage(std::vector& imageVector) { // create filter object if it does not exist yet if (!m_MitkToOpenCVFilter) { m_MitkToOpenCVFilter = mitk::ImageToOpenCVImageFilter::New(); } // get mitk image through virtual method of the subclass - mitk::Image::Pointer mitkImg; + std::vector mitkImg; this->GetNextRawImage(mitkImg); - if (mitkImg.IsNull() || !mitkImg->IsInitialized()) + for (unsigned int i = 0; i < mitkImg.size(); ++i) { - image = cv::Mat(); - return; + if (mitkImg[i].IsNull() || !mitkImg[i]->IsInitialized()) + { + imageVector[i] = cv::Mat(); + } + else + { + // convert mitk::Image to an OpenCV image + m_MitkToOpenCVFilter->SetImage(mitkImg[i]); + imageVector[i] = m_MitkToOpenCVFilter->GetOpenCVMat(); + } } - - // convert mitk::Image to an OpenCV image - m_MitkToOpenCVFilter->SetImage(mitkImg); - image = m_MitkToOpenCVFilter->GetOpenCVMat(); } diff --git a/Modules/US/USFilters/mitkUSImageSource.h b/Modules/US/USFilters/mitkUSImageSource.h index a5dd5bf4cf..499224bf16 100644 --- a/Modules/US/USFilters/mitkUSImageSource.h +++ b/Modules/US/USFilters/mitkUSImageSource.h @@ -1,105 +1,105 @@ /*=================================================================== 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 MITKUSImageSource_H_HEADER_INCLUDED_ #define MITKUSImageSource_H_HEADER_INCLUDED_ // ITK #include #include // MITK #include #include #include "mitkBasicCombinationOpenCVImageFilter.h" #include "mitkOpenCVToMitkImageFilter.h" #include "mitkImageToOpenCVImageFilter.h" // OpenCV #include "cv.h" namespace mitk { /** * \brief This is an abstract superclass for delivering USImages. * Each subclass must implement the method mitk::USImageSource::GetNextRawImage(). * The public method mitk::USImageSource::GetNextImage() can the be used to * get the next image from the image source. This image will be filtered by * the filter set with mitk::USImageSource::SetImageFilter(). * * \ingroup US */ class MITKUS_EXPORT USImageSource : public itk::Object { public: static const char* IMAGE_PROPERTY_IDENTIFIER; mitkClassMacroItkParent(USImageSource, itk::Object); itkGetMacro(ImageFilter, mitk::BasicCombinationOpenCVImageFilter::Pointer); void PushFilter(AbstractOpenCVImageFilter::Pointer filter); bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter); bool GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter); /** * \brief Retrieves the next frame. This will typically be the next frame * in a file or the last cached file in a device. The image is filtered if * a filter was set by mitk::USImageSource::SetImageFilter(). * * \return pointer to the next USImage (filtered if set) */ - mitk::Image::Pointer GetNextImage(); + std::vector GetNextImage(); protected: USImageSource(); virtual ~USImageSource(); /** * \brief Set the given OpenCV image matrix to the next image received * from the device or file. * * The standard implementation calls the overloaded function with an * mitk::Image and converts this image to OpenCV then. One should reimplement * this method for a better performance if an image filter is set. */ - virtual void GetNextRawImage(cv::Mat&); + virtual void GetNextRawImage(std::vector&); /** * \brief Set mitk::Image to the next image received from the device or file. * This method must be implemented in every subclass. */ - virtual void GetNextRawImage(mitk::Image::Pointer&) = 0; + virtual void GetNextRawImage(std::vector&) = 0; /** * \brief Used to convert from OpenCV Images to MITK Images. */ mitk::OpenCVToMitkImageFilter::Pointer m_OpenCVToMitkFilter; /** * \brief Used to convert from MITK Images to OpenCV Images. */ mitk::ImageToOpenCVImageFilter::Pointer m_MitkToOpenCVFilter; private: /** * \brief Filter is executed during mitk::USImageVideoSource::GetNextImage(). */ BasicCombinationOpenCVImageFilter::Pointer m_ImageFilter; int m_CurrentImageId; itk::FastMutexLock::Pointer m_ImageFilterMutex; }; } // namespace mitk #endif /* MITKUSImageSource_H_HEADER_INCLUDED_ */ diff --git a/Modules/US/USFilters/mitkUSImageVideoSource.cpp b/Modules/US/USFilters/mitkUSImageVideoSource.cpp index d94f2045b6..d27a1b83d7 100644 --- a/Modules/US/USFilters/mitkUSImageVideoSource.cpp +++ b/Modules/US/USFilters/mitkUSImageVideoSource.cpp @@ -1,227 +1,233 @@ /*=================================================================== 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. ===================================================================*/ // MITK HEADER #include "mitkUSImageVideoSource.h" #include "mitkImage.h" //OpenCV HEADER #include #include //Other #include mitk::USImageVideoSource::USImageVideoSource() : m_VideoCapture(new cv::VideoCapture()), m_IsVideoReady(false), m_IsGreyscale(false), m_IsCropped(false), m_ResolutionOverrideWidth(0), m_ResolutionOverrideHeight(0), m_ResolutionOverride(false), m_GrayscaleFilter(mitk::ConvertGrayscaleOpenCVImageFilter::New()), m_CropFilter(mitk::CropOpenCVImageFilter::New()) { } mitk::USImageVideoSource::~USImageVideoSource() { m_VideoCapture->release(); delete m_VideoCapture; } void mitk::USImageVideoSource::SetVideoFileInput(std::string path) { m_VideoCapture->open(path.c_str()); // check if we succeeded if(!m_VideoCapture->isOpened()) { m_IsVideoReady = false; } else { m_IsVideoReady = true; } // if Override is enabled, use it if (m_ResolutionOverride) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight); } } void mitk::USImageVideoSource::SetCameraInput(int deviceID) { m_VideoCapture->open(deviceID); if(!m_VideoCapture->isOpened()) // check if we succeeded m_IsVideoReady = false; else m_IsVideoReady = true; // if Override is enabled, use it if (m_ResolutionOverride) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, this->m_ResolutionOverrideWidth); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, this->m_ResolutionOverrideHeight); } } void mitk::USImageVideoSource::ReleaseInput() { m_VideoCapture->release(); delete m_VideoCapture; m_VideoCapture = new cv::VideoCapture(); } void mitk::USImageVideoSource::SetColorOutput(bool isColor){ if ( ! isColor && ! m_IsGreyscale ) { this->PushFilter(m_GrayscaleFilter.GetPointer()); } else if ( isColor && m_IsGreyscale ) { this->RemoveFilter(m_GrayscaleFilter.GetPointer()); } m_IsGreyscale = !isColor; } int mitk::USImageVideoSource::GetImageHeight() { if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_HEIGHT); } else { return 0; } } int mitk::USImageVideoSource::GetImageWidth() { if (m_VideoCapture) { return m_VideoCapture->get(CV_CAP_PROP_FRAME_WIDTH); } else { return 0; } } bool mitk::USImageVideoSource::GetIsReady() { if (!m_VideoCapture) { return false; } return m_VideoCapture->isOpened(); } void mitk::USImageVideoSource::SetRegionOfInterest(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY) { m_CropFilter->SetCropRegion(topLeftX, topLeftY, bottomRightX, bottomRightY); if (! m_IsCropped && ! m_CropFilter->GetIsCropRegionEmpty()) { this->PushFilter(m_CropFilter.GetPointer()); m_IsCropped = true; } } void mitk::USImageVideoSource::SetRegionOfInterest(USImageRoi roi) { this->SetRegionOfInterest(roi.topLeftX, roi.topLeftY, roi.bottomRightX, roi.bottomRightY); } void mitk::USImageVideoSource::SetCropping(USImageCropping cropping) { int width = this->GetImageWidth(); int height = this->GetImageHeight(); this->SetRegionOfInterest(cropping.left, cropping.top, width - cropping.right, height - cropping.bottom); } mitk::USImageVideoSource::USImageCropping mitk::USImageVideoSource::GetCropping() { cv::Rect cropRect = m_CropFilter->GetCropRegion(); USImageCropping cropping; cropping.left = cropRect.x; cropping.top = cropRect.y; if ( cropRect.height == 0 ) { cropping.bottom = 0; } else { cropping.bottom = this->GetImageHeight() - (cropRect.y + cropRect.height); } if ( cropRect.width == 0 ) { cropping.right = 0; } else { cropping.right = this->GetImageWidth() - (cropRect.x + cropRect.width); } return cropping; } mitk::USImageVideoSource::USImageRoi mitk::USImageVideoSource::GetRegionOfInterest() { cv::Rect cropRect = m_CropFilter->GetCropRegion(); return USImageRoi(cropRect.x, cropRect.y, cropRect.x + cropRect.width, cropRect.y + cropRect.height); } void mitk::USImageVideoSource::RemoveRegionOfInterest() { this->RemoveFilter(m_CropFilter.GetPointer()); m_IsCropped = false; } -void mitk::USImageVideoSource::GetNextRawImage( cv::Mat& image ) +void mitk::USImageVideoSource::GetNextRawImage(std::vector& image ) { // loop video if necessary //Commented out because setting and getting of these properties is not supported. Therefore on Linux //you'll always get some Highgui errors from OpenCV /*if (m_VideoCapture->get(CV_CAP_PROP_POS_FRAMES) == m_VideoCapture->get(CV_CAP_PROP_FRAME_COUNT)) { m_VideoCapture->set(CV_CAP_PROP_POS_FRAMES, 0); }*/ + if (image.size() != 1) + image.resize(1); + // retrieve image - *m_VideoCapture >> image; // get a new frame from camera + *m_VideoCapture >> image[0]; // get a new frame from camera } -void mitk::USImageVideoSource::GetNextRawImage( mitk::Image::Pointer& image ) +void mitk::USImageVideoSource::GetNextRawImage(std::vector& image ) { - cv::Mat cv_img; + if (image.size() != 1) + image.resize(1); + + std::vector cv_img; this->GetNextRawImage(cv_img); // convert to MITK-Image - IplImage ipl_img = cv_img; + IplImage ipl_img = cv_img[0]; this->m_OpenCVToMitkFilter->SetOpenCVImage(&ipl_img); this->m_OpenCVToMitkFilter->Update(); // OpenCVToMitkImageFilter returns a standard mitk::image. We then transform it into an USImage - image = this->m_OpenCVToMitkFilter->GetOutput(); + image[0] = this->m_OpenCVToMitkFilter->GetOutput(); // clean up - cv_img.release(); + cv_img[0].release(); } void mitk::USImageVideoSource::OverrideResolution(int width, int height) { this->m_ResolutionOverrideHeight = height; this->m_ResolutionOverrideWidth = width; if (m_VideoCapture != nullptr) { m_VideoCapture->set(CV_CAP_PROP_FRAME_WIDTH, width); m_VideoCapture->set(CV_CAP_PROP_FRAME_HEIGHT, height); } } diff --git a/Modules/US/USFilters/mitkUSImageVideoSource.h b/Modules/US/USFilters/mitkUSImageVideoSource.h index 525dcf3958..8c18751210 100644 --- a/Modules/US/USFilters/mitkUSImageVideoSource.h +++ b/Modules/US/USFilters/mitkUSImageVideoSource.h @@ -1,221 +1,221 @@ /*=================================================================== 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 MITKUSImageVideoSource_H_HEADER_INCLUDED_ #define MITKUSImageVideoSource_H_HEADER_INCLUDED_ // ITK #include // MITK #include "mitkUSImageSource.h" #include "mitkConvertGrayscaleOpenCVImageFilter.h" #include "mitkCropOpenCVImageFilter.h" #include "mitkBasicCombinationOpenCVImageFilter.h" // OpenCV #include namespace mitk { /** * \brief This class can be pointed to a video file or a videodevice and delivers USImages. * * Images are in color by default, but can be set to greyscale via SetColorOutput(false), * which significantly improves performance. * * Images can also be cropped to a region of interest, further increasing performance. * * \ingroup US */ class MITKUS_EXPORT USImageVideoSource : public mitk::USImageSource { public: mitkClassMacroItkParent(USImageVideoSource, itk::ProcessObject); itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * \brief Defines a region of interest by top left and bottom right corner. */ struct USImageRoi { int topLeftX; int topLeftY; int bottomRightX; int bottomRightY; USImageRoi() : topLeftX(0), topLeftY(0), bottomRightX(0), bottomRightY(0) { }; USImageRoi(unsigned int topLeftX, unsigned int topLeftY, unsigned int bottomRightX, unsigned int bottomRightY) : topLeftX(topLeftX), topLeftY(topLeftY), bottomRightX(bottomRightX), bottomRightY(bottomRightY) { }; }; /** * \brief Defines a region of interest by distances to the four image borders. */ struct USImageCropping { unsigned int top; unsigned int bottom; unsigned int left; unsigned int right; USImageCropping() : top(0), bottom(0), left(0), right(0) { }; USImageCropping(unsigned int top, unsigned int bottom, unsigned int left, unsigned int right) : top(top), bottom(bottom), left(left), right(right) { }; }; /** * \brief Opens a video file for streaming. If nothing goes wrong, the * VideoSource is ready to deliver images after calling this function. */ void SetVideoFileInput(std::string path); /** * \brief Opens a video device for streaming. Takes the Device id. Try -1 for "grab the first you can get" * which works quite well if only one device is available. If nothing goes wrong, the * VideoSource is ready to deliver images after calling this function. */ void SetCameraInput(int deviceID); void ReleaseInput(); /** * \brief Sets the output image to rgb or grayscale. * Output is color by default * and can be set to color by passing true, or to grayscale again by passing false. */ void SetColorOutput(bool isColor); /** * \brief Defines the cropping area. * The rectangle will be justified to the image borders if the given * rectangle is larger than the video source. If a correct rectangle is * given, the dimensions of the output image will be equal to those of the * rectangle. */ void SetRegionOfInterest(int topLeftX, int topLeftY, int bottomRightX, int bottomRightY); /** * \brief Defines the cropping area. * The rectangle will be justified to the image borders if the given * rectangle is larger than the video source. If a correct rectangle is * given, the dimensions of the output image will be equal to those of the * rectangle. * * \param regionOfInterest struct defining x and y coordinates of top left and bottom right corner */ void SetRegionOfInterest(USImageRoi regionOfInterest); /** * \brief Defines the cropping area. * The rectangle will be justified to the image borders if the given * rectangle is larger than the video source. If a correct rectangle is * given, the dimensions of the output image will be equal to those of the * rectangle. * * \param cropping struct defining distances to the four image borders */ void SetCropping(USImageCropping cropping); /** * /brief Removes the region of interest. * Produced images will be uncropped after call of this method. */ void RemoveRegionOfInterest(); /** * \brief This is a workaround for a problem that happens with some video device drivers. * * If you encounter OpenCV Warnings that buffer sizes do not match while calling getNextFrame, * then do the following: Using the drivers control panel to force a certain resolution, then call * this method with the same Dimensions after opening the device. * Before retrieving images one should call mitk::USImageVideoSource::isReady(). */ void OverrideResolution(int width, int height); // Getter & Setter itkGetMacro(IsVideoReady, bool); itkGetMacro(ResolutionOverride, bool); itkSetMacro(ResolutionOverride, bool); itkGetMacro(IsGreyscale,bool); itkGetMacro(ResolutionOverrideWidth,int); itkGetMacro(ResolutionOverrideHeight,int); int GetImageHeight(); int GetImageWidth(); USImageCropping GetCropping(); USImageRoi GetRegionOfInterest(); /** * \brief Returns true if images can be delivered. * * Only if true is returned one can retrieve images via * mitk::USImageVideoSource::GetNextImage(). * If false is returned, behaviour is undefined. */ bool GetIsReady(); protected: USImageVideoSource(); virtual ~USImageVideoSource(); /** * \brief Next image is gathered from the image source. * * \param[out] image an OpenCV-Matrix containing this image */ - virtual void GetNextRawImage( cv::Mat& image ) override; + virtual void GetNextRawImage( std::vector& image ) override; /** * \brief Next image is gathered from the image source. * * \param[out] image an mitk::Image containing this image */ - virtual void GetNextRawImage( mitk::Image::Pointer& image ) override; + virtual void GetNextRawImage( std::vector& image ) override; /** * \brief The source of the video, managed internally */ cv::VideoCapture* m_VideoCapture; /** * \brief If true, a frame can be grabbed anytime. */ bool m_IsVideoReady; /** * \brief If true, image output will be greyscale. */ bool m_IsGreyscale; /** * \brief If true, image will be cropped according to settings of crop filter. */ bool m_IsCropped; /** * These Variables determined whether Resolution Override is on, what dimensions to use. */ int m_ResolutionOverrideWidth; int m_ResolutionOverrideHeight; bool m_ResolutionOverride; ConvertGrayscaleOpenCVImageFilter::Pointer m_GrayscaleFilter; CropOpenCVImageFilter::Pointer m_CropFilter; }; } // namespace mitk #endif /* MITKUSImageVideoSource_H_HEADER_INCLUDED_ */ diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp index e10cb22f60..5cb68f1dcd 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASDevice.cpp @@ -1,304 +1,307 @@ /*=================================================================== 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 "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASCustomControls.h" mitk::USDiPhASDevice::USDiPhASDevice(std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model), m_ControlsProbes(mitk::USDiPhASProbesControls::New(this)), m_ImageSource(mitk::USDiPhASImageSource::New(this)), m_ControlInterfaceCustom(mitk::USDiPhASCustomControls::New(this)), m_IsRunning(false), m_BurstHalfwaveClockCount(7), - m_Interleaved(true) + m_Interleaved(true) { - SetNumberOfOutputs(1); + m_NumberOfOutputs = 2; + this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); + SetNthOutput(0, this->MakeOutput(0)); + SetNthOutput(1, this->MakeOutput(1)); } mitk::USDiPhASDevice::~USDiPhASDevice() { } //Gets std::string mitk::USDiPhASDevice::GetDeviceClass() { return "org.mitk.modules.us.USDiPhASDevice"; } mitk::USControlInterfaceProbes::Pointer mitk::USDiPhASDevice::GetControlInterfaceProbes() { return m_ControlsProbes.GetPointer(); }; mitk::USAbstractControlInterface::Pointer mitk::USDiPhASDevice::GetControlInterfaceCustom() { return m_ControlInterfaceCustom.GetPointer(); } mitk::USImageSource::Pointer mitk::USDiPhASDevice::GetUSImageSource() { return m_ImageSource.GetPointer(); } ScanModeNative& mitk::USDiPhASDevice::GetScanMode() { return m_ScanMode; } // Setup and Cleanup bool mitk::USDiPhASDevice::OnInitialization() { return true; } //---------------------------------------------------------------------------------------------------------------------------- /* ugly wrapper stuff - find better solution so it isn't necessary to create a global pointer to USDiPhASDevice... * passing a lambda function would be nicer - sadly something goes wrong when passing the adress of a lambda function: * the API produces access violations. Passing the Lambda function itself would be preferable, but that's not possible */ mitk::USDiPhASDevice* w_device; mitk::USDiPhASImageSource* w_ISource; void WrapperMessageCallback(const char* message) { w_device->MessageCallback(message); } void WrapperImageDataCallback( short* rfDataChannelData, int channelDatalinesPerDataset, int channelDataSamplesPerChannel, int channelDataTotalDatasets, short* rfDataArrayBeamformed, int beamformedLines, int beamformedSamples, int beamformedTotalDatasets, unsigned char* imageData, int imageWidth, int imageHeight, int imagePixelFormat, int imageSetsTotal, double timeStamp) { w_ISource->ImageDataCallback( rfDataChannelData, channelDatalinesPerDataset, channelDataSamplesPerChannel, channelDataTotalDatasets, rfDataArrayBeamformed, beamformedLines, beamformedSamples, beamformedTotalDatasets, imageData, imageWidth, imageHeight, imagePixelFormat, imageSetsTotal, timeStamp); } //---------------------------------------------------------------------------------------------------------------------------- bool mitk::USDiPhASDevice::OnConnection() { w_device = this; w_ISource = m_ImageSource; // Need those pointers for the forwarders to call member functions; createBeamformer expects non-member function pointers. createBeamformer((StringMessageCallback)&WrapperMessageCallback, (NewDataCallback)&WrapperImageDataCallback); InitializeScanMode(); initBeamformer(); //start the hardware connection m_ImageSource->UpdateImageGeometry(); //make sure the image geometry is initialized! // pass the new scanmode to the device: setupScan(this->m_ScanMode); return true; } bool mitk::USDiPhASDevice::OnDisconnection() { //close the beamformer so hardware is disconnected closeBeamformer(); return true; } bool mitk::USDiPhASDevice::OnActivation() { // probe controls are available now m_ControlsProbes->SetIsActive(true); if (m_ControlsProbes->GetProbesCount() < 1) { MITK_WARN("USDevice")("USDiPhASDevice") << "No probe found."; return false; } m_ControlsProbes->SelectProbe(0); // toggle the beamformer of the API if(!m_IsRunning) m_IsRunning=toggleFreeze(); return true; } bool mitk::USDiPhASDevice::OnDeactivation() { if(m_IsRunning) m_IsRunning=toggleFreeze(); return true; } void mitk::USDiPhASDevice::OnFreeze(bool freeze) { if(m_IsRunning==freeze) m_IsRunning=toggleFreeze(); // toggleFreeze() returns true if it starts running the beamformer, otherwise false } void mitk::USDiPhASDevice::UpdateScanmode() { OnFreeze(true); SetInterleaved(m_Interleaved); // update the beamforming parameters... UpdateTransmitEvents(); if (!(dynamic_cast(this->m_ControlInterfaceCustom.GetPointer())->GetSilentUpdate())) { setupScan(this->m_ScanMode); m_ImageSource->UpdateImageGeometry(); } OnFreeze(false); } void mitk::USDiPhASDevice::UpdateTransmitEvents() { int numChannels = m_ScanMode.reconstructionLines; // transmitEventsCount defines only the number of acoustic measurements (angles); there will be one event added to the start for OA measurement m_ScanMode.TransmitEvents = new TransmitEventNative[m_ScanMode.transmitEventsCount]; for (int ev = 0; ev < m_ScanMode.transmitEventsCount; ++ev) { m_ScanMode.TransmitEvents[ev].transmitEventDelays = new float[numChannels]; m_ScanMode.TransmitEvents[ev].BurstHalfwaveClockCountPerChannel = new int[numChannels]; m_ScanMode.TransmitEvents[ev].BurstCountPerChannel = new int[numChannels]; m_ScanMode.TransmitEvents[ev].BurstUseNegativePolarityPerChannel = new bool[numChannels]; m_ScanMode.TransmitEvents[ev].ChannelMultiplexerSetups = nullptr; float tiltStrength = ((m_ScanMode.transmitEventsCount - 1) / 2 - ev) * 20e-9f; for (int i = 0; i < numChannels; ++i) { m_ScanMode.TransmitEvents[ev].BurstHalfwaveClockCountPerChannel[i] = m_BurstHalfwaveClockCount; // 120 MHz / (2 * (predefinedBurstHalfwaveClockCount + 1)) --> 7.5 MHz m_ScanMode.TransmitEvents[ev].BurstCountPerChannel[i] = 1; // Burst with 1 cycle m_ScanMode.TransmitEvents[ev].BurstUseNegativePolarityPerChannel[i] = true; m_ScanMode.TransmitEvents[ev].transmitEventDelays[i] = 2e-6f + (i - numChannels / 2) * tiltStrength; } } m_ScanMode.transmitSequenceCount = 1; m_ScanMode.transmitSequences = new SequenceNative[m_ScanMode.transmitSequenceCount]; m_ScanMode.transmitSequences[0].startEvent = 0; m_ScanMode.transmitSequences[0].endEvent = m_ScanMode.transmitEventsCount; } void mitk::USDiPhASDevice::InitializeScanMode() { // create a scanmode to be used for measurements: m_ScanMode.scanModeName = "InterleavedMode"; // configure a linear transducer m_ScanMode.transducerName = "L5-10"; m_ScanMode.transducerCurvedRadiusMeter = 0; m_ScanMode.transducerElementCount = 128; m_ScanMode.transducerFrequencyHz = 7500000; m_ScanMode.transducerPitchMeter = 0.0003f; m_ScanMode.transducerType = 1; // configure the receive paramters: m_ScanMode.receivePhaseLengthSeconds = 185e-6f; // about 15 cm imaging depth m_ScanMode.tgcdB = new unsigned char[8]; for (int tgc = 0; tgc < 8; ++tgc) m_ScanMode.tgcdB[tgc] = tgc * 2 + 10; m_ScanMode.accumulation = 1; m_ScanMode.bandpassApply = false; m_ScanMode.averagingCount = 1; // configure general processing: m_ScanMode.transferChannelData = true; // configure reconstruction processing: m_ScanMode.averageSpeedOfSound = 1540; m_ScanMode.computeBeamforming = true; // setup beamforming parameters: SetInterleaved(true); m_ScanMode.reconstructedLinePitchMmOrAngleDegree = 0.3f; m_ScanMode.reconstructionLines = 128; m_ScanMode.reconstructionSamplesPerLine = 2048; m_ScanMode.transferBeamformedData = true; // configure the transmit sequence(s): m_ScanMode.transmitEventsCount = 1; m_ScanMode.transmitPhaseLengthSeconds = 1e-6f; m_ScanMode.voltageV = 75; UpdateTransmitEvents(); // configure bandpass: m_ScanMode.bandpassApply = false; m_ScanMode.bandpassFrequencyLowHz = 1e6f; m_ScanMode.bandpassFrequencyHighHz = 20e6f; // configure image generation: m_ScanMode.imageWidth = 512; m_ScanMode.imageHeight = 512; m_ScanMode.imageMultiplier = 1; m_ScanMode.imageLeveling = 0; m_ScanMode.transferImageData = false; // Trigger setup: m_ScanMode.triggerSetup.enabled = true; m_ScanMode.triggerSetup.constantPulseRepetitionRateHz = 20; m_ScanMode.triggerSetup.triggerWidthMicroseconds = 15; m_ScanMode.triggerSetup.delayTrigger2Microseconds = 300; } // callback for the DiPhAS API void mitk::USDiPhASDevice::MessageCallback(const char* message) { MITK_INFO << "DiPhAS API: " << message << '\n'; } void mitk::USDiPhASDevice::SetBursts(int bursts) { m_BurstHalfwaveClockCount = bursts; } bool mitk::USDiPhASDevice::IsInterleaved() { return m_Interleaved; } void mitk::USDiPhASDevice::SetInterleaved(bool interleaved) { m_Interleaved = interleaved; if (interleaved) { m_ScanMode.scanModeName = "Interleaved Beamforming Mode"; m_CurrentBeamformingAlgorithm = Beamforming::Interleaved_OA_US; paramsInterleaved.SpeedOfSoundMeterPerSecond = m_ScanMode.averageSpeedOfSound; paramsInterleaved.angleSkipFactor = 1; paramsInterleaved.OptoacousticDelay = 0.0000003; // 300ns paramsInterleaved.filter = Filter::None; m_ScanMode.beamformingAlgorithmParameters = ¶msInterleaved; } else { m_ScanMode.scanModeName = "Plane Wave Beamforming Mode"; m_CurrentBeamformingAlgorithm = Beamforming::PlaneWaveCompound; paramsPlaneWave.SpeedOfSoundMeterPerSecond = m_ScanMode.averageSpeedOfSound; paramsPlaneWave.angleSkipFactor = 0; paramsPlaneWave.usePhaseCoherence = 0; m_ScanMode.beamformingAlgorithmParameters = ¶msPlaneWave; } m_ScanMode.beamformingAlgorithm = m_CurrentBeamformingAlgorithm; } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp index cba81bd664..549ce8ef16 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.cpp @@ -1,894 +1,931 @@ /*=================================================================== 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. ===================================================================*/ // std dependencies #include #include #include // mitk dependencies #include "mitkUSDiPhASDevice.h" #include "mitkUSDiPhASImageSource.h" #include #include "mitkUSDiPhASBModeImageFilter.h" #include "ITKUltrasound/itkBModeImageFilter.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" // itk dependencies #include "itkImage.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCropImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include #include "itkMultiplyImageFilter.h" - mitk::USDiPhASImageSource::USDiPhASImageSource(mitk::USDiPhASDevice* device) : m_Device(device), m_StartTime(((float)std::clock()) / CLOCKS_PER_SEC), m_UseGUIOutPut(false), m_DataType(DataType::Image_uChar), m_GUIOutput(nullptr), m_UseBModeFilter(false), m_CurrentlyRecording(false), m_DataTypeModified(true), m_DataTypeNext(DataType::Image_uChar), m_CurrentImageTimestamp(0), m_PyroConnected(false), m_ImageTimestampBuffer(), m_VerticalSpacing(0), m_UseBModeFilterModified(false), m_UseBModeFilterNext(false), m_ScatteringCoefficientModified(false), m_CompensateForScatteringModified(false), m_VerticalSpacingModified(false), m_ScatteringCoefficient(15), m_CompensateForScattering(false), m_CompensateEnergy(false), m_CompensateEnergyNext(false), m_CompensateEnergyModified(false) { m_BufferSize = 100; m_ImageTimestampBuffer.insert(m_ImageTimestampBuffer.begin(), m_BufferSize, 0); m_LastWrittenImage = m_BufferSize - 1; m_ImageBuffer.insert(m_ImageBuffer.begin(), m_BufferSize, nullptr); us::ModuleResource resourceFile; std::string name; m_FluenceCompOriginal.insert(m_FluenceCompOriginal.begin(), 5, Image::New()); for (int i = 5; i <= 25; ++i) { name = "c:\\HomogeneousScatteringAssumptions\\Scattering" + std::to_string(i) + ".nrrd"; m_FluenceCompOriginal.push_back(mitk::IOUtil::LoadImage(name)); } m_FluenceCompResized.insert(m_FluenceCompResized.begin(), 26, Image::New()); m_FluenceCompResizedItk.insert(m_FluenceCompResizedItk.begin(), 26, itk::Image::New()); } mitk::USDiPhASImageSource::~USDiPhASImageSource() { // close the pyro MITK_INFO("Pyro Debug") << "StopDataAcquisition: " << m_Pyro->StopDataAcquisition(); MITK_INFO("Pyro Debug") << "CloseConnection: " << m_Pyro->CloseConnection(); m_PyroConnected = false; m_Pyro = nullptr; } -void mitk::USDiPhASImageSource::GetNextRawImage( mitk::Image::Pointer& image) +void mitk::USDiPhASImageSource::GetNextRawImage(std::vector& imageVector) { // modify all settings that have been changed here, so we don't get multithreading issues if (m_DataTypeModified) { SetDataType(m_DataTypeNext); m_DataTypeModified = false; UpdateImageGeometry(); } if (m_UseBModeFilterModified) { SetUseBModeFilter(m_UseBModeFilterNext); m_UseBModeFilterModified = false; } if (m_VerticalSpacingModified) { m_VerticalSpacing = m_VerticalSpacingNext; m_VerticalSpacingModified = false; } if (m_ScatteringCoefficientModified) { m_ScatteringCoefficient = m_ScatteringCoefficientNext; m_ScatteringCoefficientModified = false; } if (m_CompensateForScatteringModified) { m_CompensateForScattering = m_CompensateForScatteringNext; m_CompensateForScatteringModified = false; } if (m_CompensateEnergyModified) { m_CompensateEnergy = m_CompensateEnergyNext; m_CompensateEnergyModified = false; } + if (imageVector.size() != 2) + { + imageVector.resize(2); + } + // make sure image is nullptr - image = nullptr; + mitk::Image::Pointer image = nullptr; float ImageEnergyValue = 0; for (int i = 100; i > 90 && ImageEnergyValue <= 0; --i) { if (m_ImageTimestampBuffer[(m_LastWrittenImage + i) % 100] != 0) { ImageEnergyValue = m_Pyro->GetClosestEnergyInmJ(m_ImageTimestampBuffer[(m_LastWrittenImage + i) % 100]); if (ImageEnergyValue > 0) { image = &(*m_ImageBuffer[(m_LastWrittenImage + i) % 100]); } } } // if we did not get any usable Energy value, compensate using this default value if (image == nullptr) { image = &(*m_ImageBuffer[m_LastWrittenImage]); ImageEnergyValue = 40; if (image == nullptr) return; } // do image processing before displaying it if (image.IsNotNull()) { - typedef itk::Image< float, 3 > itkFloatImageType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); image = mitk::GrabItkImageMemory(itkImage); //thereby using float images + image = CutOffTop(image, 165); // now apply filters to the image, if the options have been selected. if ((m_CompensateForScattering || m_UseBModeFilter) && m_DataType == DataType::Beamformed_Short) { if (m_Device->GetScanMode().beamformingAlgorithm == Beamforming::PlaneWaveCompound) // this is for ultrasound only mode { if (m_UseBModeFilter) { image = ApplyBmodeFilter(image, true); if (m_VerticalSpacing) image = ResampleOutputVertical(image, m_VerticalSpacing); } } else { Image::Pointer imagePA = Image::New(); unsigned int dim[] = { image->GetDimension(0),image->GetDimension(1),1}; imagePA->Initialize(image->GetPixelType(), 3, dim); imagePA->SetGeometry(image->GetGeometry()); Image::Pointer imageUS = Image::New(); imageUS->Initialize(image->GetPixelType(), 3, dim); imageUS->SetGeometry(image->GetGeometry()); ImageReadAccessor inputReadAccessorCopyPA(image, image->GetSliceData(0)); imagePA->SetSlice(inputReadAccessorCopyPA.GetData(), 0); ImageReadAccessor inputReadAccessorCopyUS(image, image->GetSliceData(1)); imageUS->SetSlice(inputReadAccessorCopyUS.GetData(), 0); // first, seperate the PA image from the USImages // then, we compensate the PAImage using our ImageEnergyValue if(m_CompensateEnergy) imagePA = MultiplyImage(imagePA, 1/ImageEnergyValue); // TODO: add the correct prefactor here!!!! // now we apply the BModeFilter if (m_UseBModeFilter) { imageUS = ApplyBmodeFilter(imageUS, true); // the US Images get a logarithmic filter imagePA = ApplyBmodeFilter(imagePA, false); } ImageReadAccessor inputReadAccessorPA(imagePA, imagePA->GetSliceData(0)); image->SetSlice(inputReadAccessorPA.GetData(), 0); ImageReadAccessor inputReadAccessorUS(imageUS, imageUS->GetSliceData(0)); image->SetSlice(inputReadAccessorUS.GetData(), 1); if (m_VerticalSpacing) { image = ResampleOutputVertical(image, m_VerticalSpacing); } // and finally the scattering corrections if (m_CompensateForScattering) { auto curResizeImage = m_FluenceCompResized.at(m_ScatteringCoefficient); // just for convenience // update the fluence reference images! bool doResampling = image->GetDimension(0) != curResizeImage->GetDimension(0) || image->GetDimension(1) != curResizeImage->GetDimension(1) || image->GetGeometry()->GetSpacing()[0] != curResizeImage->GetGeometry()->GetSpacing()[0] || image->GetGeometry()->GetSpacing()[1] != curResizeImage->GetGeometry()->GetSpacing()[1]; if (doResampling) { curResizeImage = ApplyResampling(m_FluenceCompOriginal.at(m_ScatteringCoefficient), image->GetGeometry()->GetSpacing(), image->GetDimensions()); double* rawOutputData = new double[image->GetDimension(0)*image->GetDimension(1)]; double* rawScatteringData = (double*)curResizeImage->GetData(); int sizeRawScatteringData = curResizeImage->GetDimension(0) * curResizeImage->GetDimension(1); int imageSize = image->GetDimension(0)*image->GetDimension(1); //everything above 1.5mm is still inside the transducer; therefore the fluence compensation image has to be positioned a little lower float upperCutoffmm = 1.5; int lowerBound = std::round(upperCutoffmm / image->GetGeometry()->GetSpacing()[1])*image->GetDimension(0); int upperBound = lowerBound + sizeRawScatteringData; for (int i = 0; i < lowerBound && i < imageSize; ++i) { rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage, here the upper 0.15mm } for (int i = lowerBound; i < upperBound && i < imageSize; ++i) { rawOutputData[i] = 1 / rawScatteringData[i-lowerBound]; } for (int i = upperBound; i < imageSize; ++i) { rawOutputData[i] = 0; // everything than cannot be compensated shall be treated as garbage } unsigned int dim[] = { image->GetDimension(0), image->GetDimension(1), 1 }; curResizeImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); curResizeImage->SetGeometry(image->GetGeometry()); curResizeImage->SetSlice(rawOutputData,0); delete[] rawOutputData; mitk::CastToItkImage(curResizeImage, m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); m_FluenceCompResized.at(m_ScatteringCoefficient) = mitk::GrabItkImageMemory(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); MITK_INFO << "Resized a fluence image."; } // actually apply the scattering compensation imagePA = ApplyScatteringCompensation(imagePA, m_ScatteringCoefficient); ImageReadAccessor inputReadAccessorPA(imagePA, imagePA->GetSliceData(0)); image->SetSlice(inputReadAccessorPA.GetData(), 0); } } } + + //TODO: completely rewrite this mess + + imageVector[0] = Image::New(); + unsigned int dim[] = { image->GetDimension(0),image->GetDimension(1),1 }; + imageVector[0]->Initialize(image->GetPixelType(), 3, dim); + imageVector[0]->SetGeometry(image->GetGeometry()); + + imageVector[1] = Image::New(); + imageVector[1]->Initialize(image->GetPixelType(), 3, dim); + imageVector[1]->SetGeometry(image->GetGeometry()); + + ImageReadAccessor inputReadAccessorCopyPA(image, image->GetSliceData(0)); + imageVector[0]->SetSlice(inputReadAccessorCopyPA.GetData(), 0); + ImageReadAccessor inputReadAccessorCopyUS(image, image->GetSliceData(1)); + imageVector[1]->SetSlice(inputReadAccessorCopyUS.GetData(), 0); } } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyBmodeFilter(mitk::Image::Pointer image, bool useLogFilter) { // we use this seperate ApplyBmodeFilter Method for processing of two-dimensional images // the image needs to be of floating point type for the envelope filter to work; the casting is done automatically by the CastToItkImage - typedef itk::Image< float, 3 > itkFloatImageType; - + typedef itk::BModeImageFilter < itkFloatImageType, itkFloatImageType > BModeFilterType; BModeFilterType::Pointer bModeFilter = BModeFilterType::New(); // LogFilter typedef itk::PhotoacousticBModeImageFilter < itkFloatImageType, itkFloatImageType > PhotoacousticBModeImageFilter; PhotoacousticBModeImageFilter::Pointer photoacousticBModeFilter = PhotoacousticBModeImageFilter::New(); // No LogFilter itkFloatImageType::Pointer itkImage; - - mitk::CastToItkImage(image, itkImage); - itkFloatImageType::Pointer bmode; + mitk::CastToItkImage(image, itkImage); if (useLogFilter) { bModeFilter->SetInput(itkImage); bModeFilter->SetDirection(1); bmode = bModeFilter->GetOutput(); } else { photoacousticBModeFilter->SetInput(itkImage); photoacousticBModeFilter->SetDirection(1); bmode = photoacousticBModeFilter->GetOutput(); } return mitk::GrabItkImageMemory(bmode); } +mitk::Image::Pointer mitk::USDiPhASImageSource::CutOffTop(mitk::Image::Pointer image, int cutOffSize) +{ + typedef itk::CropImageFilter < itkFloatImageType, itkFloatImageType > CutImageFilter; + itkFloatImageType::SizeType cropSize; + itkFloatImageType::Pointer itkImage; + mitk::CastToItkImage(image, itkImage); + + cropSize[0] = 0; + if(itkImage->GetLargestPossibleRegion().GetSize()[1] == 2048) + cropSize[1] = cutOffSize; + else + cropSize[1] = 0; + cropSize[2] = 0; + CutImageFilter::Pointer cutOffFilter = CutImageFilter::New(); + cutOffFilter->SetInput(itkImage); + cutOffFilter->SetLowerBoundaryCropSize(cropSize); + cutOffFilter->UpdateLargestPossibleRegion(); + return mitk::GrabItkImageMemory(cutOffFilter->GetOutput()); +} + mitk::Image::Pointer mitk::USDiPhASImageSource::ResampleOutputVertical(mitk::Image::Pointer image, float verticalSpacing) { - typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(image, itkImage); itkFloatImageType::SpacingType outputSpacing; itkFloatImageType::SizeType inputSize = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSize = inputSize; outputSpacing[0] = itkImage->GetSpacing()[0] * (static_cast(inputSize[0]) / static_cast(outputSize[0])); outputSpacing[1] = verticalSpacing; outputSpacing[2] = itkImage->GetSpacing()[2]; outputSize[1] = inputSize[1] * itkImage->GetSpacing()[1] / outputSpacing[1]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSize); resampleImageFilter->SetOutputSpacing(outputSpacing); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scattering) { - typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetInput2(m_FluenceCompResizedItk.at(m_ScatteringCoefficient)); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::ApplyResampling(mitk::Image::Pointer inputImage, mitk::Vector3D outputSpacing, unsigned int outputSize[3]) { - typedef itk::Image< double, 3 > itkFloatImageType; - typedef itk::ResampleImageFilter < itkFloatImageType, itkFloatImageType > ResampleImageFilter; ResampleImageFilter::Pointer resampleImageFilter = ResampleImageFilter::New(); itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); itkFloatImageType::SpacingType outputSpacingItk; itkFloatImageType::SizeType inputSizeItk = itkImage->GetLargestPossibleRegion().GetSize(); itkFloatImageType::SizeType outputSizeItk = inputSizeItk; itkFloatImageType::SpacingType inputSpacing = itkImage->GetSpacing(); outputSizeItk[0] = outputSize[0]; outputSizeItk[1] = 10*(inputSpacing[1] * inputSizeItk[1]) / (outputSpacing[1]); outputSizeItk[2] = 1; outputSpacingItk[0] = 0.996 * inputSpacing[0] * (static_cast(inputSizeItk[0]) / static_cast(outputSizeItk[0])); // TODO: find out why the spacing is not correct, so we need that factor; ?!?! outputSpacingItk[1] = inputSpacing[1] * (static_cast(inputSizeItk[1]) / static_cast(outputSizeItk[1])); outputSpacingItk[2] = outputSpacing[2]; typedef itk::IdentityTransform TransformType; resampleImageFilter->SetInput(itkImage); resampleImageFilter->SetSize(outputSizeItk); resampleImageFilter->SetOutputSpacing(outputSpacingItk); resampleImageFilter->SetTransform(TransformType::New()); resampleImageFilter->UpdateLargestPossibleRegion(); return mitk::GrabItkImageMemory(resampleImageFilter->GetOutput()); } mitk::Image::Pointer mitk::USDiPhASImageSource::MultiplyImage(mitk::Image::Pointer inputImage, double value) { - typedef itk::Image< float, 3 > itkFloatImageType; typedef itk::MultiplyImageFilter MultiplyImageFilterType; itkFloatImageType::Pointer itkImage; mitk::CastToItkImage(inputImage, itkImage); MultiplyImageFilterType::Pointer multiplyFilter = MultiplyImageFilterType::New(); multiplyFilter->SetInput1(itkImage); multiplyFilter->SetConstant(value); return mitk::GrabItkImageMemory(multiplyFilter->GetOutput()); } void mitk::USDiPhASImageSource::ImageDataCallback( short* rfDataChannelData, int& channelDataChannelsPerDataset, int& channelDataSamplesPerChannel, int& channelDataTotalDatasets, short* rfDataArrayBeamformed, int& beamformedLines, int& beamformedSamples, int& beamformedTotalDatasets, unsigned char* imageData, int& imageWidth, int& imageHeight, int& imageBytesPerPixel, int& imageSetsTotal, double& timeStamp) { if (m_DataTypeModified) return; if (!m_PyroConnected) { m_Pyro = mitk::OphirPyro::New(); MITK_INFO << "[Pyro Debug] OpenConnection: " << m_Pyro->OpenConnection(); MITK_INFO << "[Pyro Debug] StartDataAcquisition: " << m_Pyro->StartDataAcquisition(); m_PyroConnected = true; } bool writeImage = ((m_DataType == DataType::Image_uChar) && (imageData != nullptr)) || ((m_DataType == DataType::Beamformed_Short) && (rfDataArrayBeamformed != nullptr)); if (writeImage) { //get the timestamp we might save later on m_CurrentImageTimestamp = std::chrono::high_resolution_clock::now().time_since_epoch().count(); // create a new image and initialize it mitk::Image::Pointer image = mitk::Image::New(); switch (m_DataType) { case DataType::Image_uChar: { m_ImageDimensions[0] = imageWidth; m_ImageDimensions[1] = imageHeight; m_ImageDimensions[2] = imageSetsTotal; image->Initialize(mitk::MakeScalarPixelType(), 3, m_ImageDimensions); break; } case DataType::Beamformed_Short: { m_ImageDimensions[0] = beamformedLines; m_ImageDimensions[1] = beamformedSamples; m_ImageDimensions[2] = beamformedTotalDatasets; image->Initialize(mitk::MakeScalarPixelType(), 3, m_ImageDimensions); break; } } image->GetGeometry()->SetSpacing(m_ImageSpacing); image->GetGeometry()->Modified(); // write the given buffer into the image switch (m_DataType) { case DataType::Image_uChar: { for (unsigned char i = 0; i < imageSetsTotal; i++) { image->SetSlice(&imageData[i*imageHeight*imageWidth], i); } break; } case DataType::Beamformed_Short: { short* flipme = new short[beamformedLines*beamformedSamples*beamformedTotalDatasets]; int pixelsPerImage = beamformedLines*beamformedSamples; for (unsigned char currentSet = 0; currentSet < beamformedTotalDatasets; currentSet++) { for (unsigned short sample = 0; sample < beamformedSamples; sample++) { for (unsigned short line = 0; line < beamformedLines; line++) { flipme[sample*beamformedLines + line + pixelsPerImage*currentSet] = rfDataArrayBeamformed[line*beamformedSamples + sample + pixelsPerImage*currentSet]; } } // the beamformed pa image is flipped by 90 degrees; we need to flip it manually } for (unsigned char i = 0; i < beamformedTotalDatasets; i++) { image->SetSlice(&flipme[i*beamformedLines*beamformedSamples], i); // set every image to a different slice } delete[] flipme; break; } } if (m_SavingSettings.saveRaw && m_CurrentlyRecording && rfDataChannelData != nullptr) { unsigned int dim[3]; dim[0] = channelDataChannelsPerDataset; dim[1] = channelDataSamplesPerChannel; dim[2] = 1; short offset = m_Device->GetScanMode().accumulation * 2048; short* noOffset = new short[channelDataChannelsPerDataset*channelDataSamplesPerChannel*channelDataTotalDatasets]; - for (unsigned char set = 0; set < channelDataTotalDatasets; ++set) + for (unsigned char set = 0; set < 1; ++set)// channelDataTotalDatasets; ++set) // we ignore the raw US images for now { for (unsigned short sam = 0; sam < channelDataSamplesPerChannel; ++sam) { for (unsigned short chan = 0; chan < channelDataChannelsPerDataset; ++chan) { noOffset[set*channelDataSamplesPerChannel*channelDataChannelsPerDataset + sam * channelDataChannelsPerDataset + chan] = rfDataChannelData[set*channelDataSamplesPerChannel*channelDataChannelsPerDataset + sam * channelDataChannelsPerDataset + chan] - offset; // this offset in the raw Images is given by the API... } } } // save the raw images when recording - for (unsigned char i = 0; i < channelDataTotalDatasets; ++i) + for (unsigned char i = 0; i < 1; ++i)// channelDataTotalDatasets; ++i) // we ignore the raw US images for now { mitk::Image::Pointer rawImage = mitk::Image::New(); rawImage->Initialize(mitk::MakeScalarPixelType(), 3, dim); rawImage->SetSlice(&noOffset[i*channelDataChannelsPerDataset*channelDataSamplesPerChannel]); float& recordTime = m_Device->GetScanMode().receivePhaseLengthSeconds; int& speedOfSound = m_Device->GetScanMode().averageSpeedOfSound; mitk::Vector3D rawSpacing; rawSpacing[0] = m_Device->GetScanMode().transducerPitchMeter * 1000; // save in mm - rawSpacing[1] = recordTime / channelDataSamplesPerChannel / 2 * 1000000; // save in us + rawSpacing[1] = recordTime / channelDataSamplesPerChannel * 1000000; // save in us rawSpacing[2] = 1; rawImage->GetGeometry()->SetSpacing(rawSpacing); rawImage->GetGeometry()->Modified(); m_RawRecordedImages.push_back(rawImage); } delete[] noOffset; } itk::Index<3> pixel = { { (itk::Index<3>::IndexValueType)(image->GetDimension(0) / 2), (itk::Index<3>::IndexValueType)(22.0/532.0*m_Device->GetScanMode().reconstructionSamplesPerLine), 0 } }; //22/532*2048 = 84 if (!m_Pyro->IsSyncDelaySet() &&(image->GetPixelValueByIndex(pixel) < -30)) // #MagicNumber { MITK_INFO << "Setting SyncDelay now"; m_Pyro->SetSyncDelay(m_CurrentImageTimestamp); } m_ImageTimestampBuffer[(m_LastWrittenImage + 1) % m_BufferSize] = m_CurrentImageTimestamp; m_ImageBuffer[(m_LastWrittenImage + 1) % m_BufferSize] = image; m_LastWrittenImage = (m_LastWrittenImage + 1) % m_BufferSize; // if the user decides to start recording, we feed the vector the generated images if (m_CurrentlyRecording) { for (unsigned char index = 0; index < image->GetDimension(2); ++index) { if (image->IsSliceSet(index)) { m_RecordedImages.push_back(Image::New()); unsigned int dim[] = { image ->GetDimension(0), image->GetDimension(1), 1}; m_RecordedImages.back()->Initialize(image->GetPixelType(), 3, dim); m_RecordedImages.back()->SetGeometry(image->GetGeometry()); mitk::ImageReadAccessor inputReadAccessor(image, image->GetSliceData(index)); m_RecordedImages.back()->SetSlice(inputReadAccessor.GetData(),0); } } m_ImageTimestampRecord.push_back(m_CurrentImageTimestamp); // save timestamps for each laser image! } } } void mitk::USDiPhASImageSource::UpdateImageGeometry() { MITK_INFO << "Retreaving Image Geometry Information for Spacing..."; float& recordTime = m_Device->GetScanMode().receivePhaseLengthSeconds; int& speedOfSound = m_Device->GetScanMode().averageSpeedOfSound; float& pitch = m_Device->GetScanMode().reconstructedLinePitchMmOrAngleDegree; int& reconstructionLines = m_Device->GetScanMode().reconstructionLines; switch (m_DataType) { case DataType::Image_uChar : { int& imageWidth = m_Device->GetScanMode().imageWidth; int& imageHeight = m_Device->GetScanMode().imageHeight; m_ImageSpacing[0] = pitch * reconstructionLines / imageWidth; m_ImageSpacing[1] = recordTime * speedOfSound / 2 * 1000 / imageHeight; break; } case DataType::Beamformed_Short : { int& imageWidth = reconstructionLines; int& imageHeight = m_Device->GetScanMode().reconstructionSamplesPerLine; m_ImageSpacing[0] = pitch; m_ImageSpacing[1] = recordTime * speedOfSound / 2 * 1000 / imageHeight; break; } } m_ImageSpacing[2] = 1; MITK_INFO << "Retreaving Image Geometry Information for Spacing " << m_ImageSpacing[0] << " ... " << m_ImageSpacing[1] << " ... " << m_ImageSpacing[2] << " ...[DONE]"; } void mitk::USDiPhASImageSource::ModifyDataType(DataType dataT) { m_DataTypeModified = true; m_DataTypeNext = dataT; } void mitk::USDiPhASImageSource::ModifyUseBModeFilter(bool isSet) { m_UseBModeFilterModified = true; m_UseBModeFilterNext = isSet; } void mitk::USDiPhASImageSource::ModifyScatteringCoefficient(int coeff) { m_ScatteringCoefficientNext = coeff; m_ScatteringCoefficientModified = true; } void mitk::USDiPhASImageSource::ModifyCompensateForScattering(bool useIt) { m_CompensateForScatteringNext = useIt; m_CompensateForScatteringModified = true; } void mitk::USDiPhASImageSource::ModifyEnergyCompensation(bool compensate) { m_CompensateEnergyNext = compensate; m_CompensateEnergyModified = true; } void mitk::USDiPhASImageSource::SetDataType(DataType dataT) { if (dataT != m_DataType) { m_DataType = dataT; MITK_INFO << "Setting new DataType..." << dataT; switch (m_DataType) { case DataType::Image_uChar : MITK_INFO << "height: " << m_Device->GetScanMode().imageHeight << " width: " << m_Device->GetScanMode().imageWidth; break; case DataType::Beamformed_Short : MITK_INFO << "samples: " << m_Device->GetScanMode().reconstructionSamplesPerLine << " lines: " << m_Device->GetScanMode().reconstructionLines; break; } } } void mitk::USDiPhASImageSource::SetGUIOutput(std::function out) { USDiPhASImageSource::m_GUIOutput = out; m_StartTime = ((float)std::clock()) / CLOCKS_PER_SEC; //wait till the callback is available again m_UseGUIOutPut = false; } void mitk::USDiPhASImageSource::SetUseBModeFilter(bool isSet) { m_UseBModeFilter = isSet; } void mitk::USDiPhASImageSource::SetVerticalSpacing(float mm) { m_VerticalSpacingNext = mm; m_VerticalSpacingModified = true; } void mitk::USDiPhASImageSource::SetSavingSettings(SavingSettings settings) { m_SavingSettings = settings; } // this is just a little function to set the filenames below right inline void replaceAll(std::string& str, const std::string& from, const std::string& to) { if (from.empty()) return; size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' } } void mitk::USDiPhASImageSource::SetRecordingStatus(bool record) { // start the recording process if (record) { m_RecordedImages.clear(); m_RawRecordedImages.clear(); // we make sure there are no leftovers m_ImageTimestampRecord.clear(); // also for the timestamps m_PixelValues.clear(); // aaaand for the pixel values if (m_SavingSettings.saveRaw) { m_Device->GetScanMode().transferChannelData = true; m_Device->UpdateScanmode(); // set the raw Data to be transfered } // tell the callback to start recording images m_CurrentlyRecording = true; } // save images, end recording, and clean up else { m_CurrentlyRecording = false; m_Device->GetScanMode().transferChannelData = false; // make sure raw Channel Data is not transferred anymore! m_Device->UpdateScanmode(); // get the time and date, put them into a nice string and create a folder for the images time_t time = std::time(nullptr); time_t* timeptr = &time; std::string currentDate = std::ctime(timeptr); replaceAll(currentDate, ":", "-"); currentDate.pop_back(); //std::string MakeFolder = "mkdir \"c:/DiPhASImageData/" + currentDate + "\""; //system(MakeFolder.c_str()); // initialize file paths and the images Image::Pointer PAImage = Image::New(); Image::Pointer USImage = Image::New(); std::string pathPA = "c:\\ImageData\\" + currentDate + "-" + "PAbeamformed" + ".nrrd"; std::string pathUS = "c:\\ImageData\\" + currentDate + "-" + "USImages" + ".nrrd"; std::string pathTS = "c:\\ImageData\\" + currentDate + "-" + "ts" + ".csv"; std::string pathS = "c:\\ImageData\\" + currentDate + "-" + "Settings" + ".txt"; // idon't forget the raw Images (if chosen to be saved) Image::Pointer PAImageRaw = Image::New(); Image::Pointer USImageRaw = Image::New(); std::string pathPARaw = "c:\\ImageData\\" + currentDate + "-" + "PAraw" + ".nrrd"; std::string pathUSRaw = "c:\\ImageData\\" + currentDate + "-" + "USImagesRaw" + ".nrrd"; if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::Interleaved_OA_US) // save a PAImage if we used interleaved mode { // first, save the data, so the pyro does not aquire more unneccessary timestamps m_Pyro->SaveData(); // now order the images and save them // the beamformed ones... if (m_SavingSettings.saveBeamformed) { OrderImagesInterleaved(PAImage, USImage, m_RecordedImages, false); mitk::IOUtil::Save(USImage, pathUS); mitk::IOUtil::Save(PAImage, pathPA); } // ...and the raw images if (m_SavingSettings.saveRaw) { OrderImagesInterleaved(PAImageRaw, USImageRaw, m_RawRecordedImages, true); - mitk::IOUtil::Save(USImageRaw, pathUSRaw); + // mitk::IOUtil::Save(USImageRaw, pathUSRaw); mitk::IOUtil::Save(PAImageRaw, pathPARaw); } // read the pixelvalues of the enveloped images at this position itk::Index<3> pixel = { { - (itk::Index<3>::IndexValueType)(m_RecordedImages.at(1)->GetDimension(0) / 2), + (itk::Index<3>::IndexValueType)(m_RecordedImages.at(0)->GetDimension(0) / 2), (itk::Index<3>::IndexValueType)(22.0 / 532.0*m_Device->GetScanMode().reconstructionSamplesPerLine), 0 } }; //22/532*2048 = 84 + GetPixelValues(pixel, m_PixelValues); // write the Pixelvalues to m_PixelValues // save the timestamps! ofstream timestampFile; timestampFile.open(pathTS); timestampFile << ",timestamp,pixelvalue"; // write the header for (int index = 0; index < m_ImageTimestampRecord.size(); ++index) { timestampFile << "\n" << index << "," << m_ImageTimestampRecord.at(index) << "," << m_PixelValues.at(index); } timestampFile.close(); //save the settings! ofstream settingsFile; settingsFile.open(pathS); auto& sM = m_Device->GetScanMode(); settingsFile << "[General Parameters]\n"; settingsFile << "Scan Depth [mm] = " << sM.receivePhaseLengthSeconds * sM.averageSpeedOfSound / 2 * 1000 << "\n"; settingsFile << "Speed of Sound [m/s] = " << sM.averageSpeedOfSound << "\n"; settingsFile << "Excitation Frequency [MHz] = " << sM.transducerFrequencyHz/1000000 << "\n"; settingsFile << "Voltage [V] = " << sM.voltageV << "\n"; - settingsFile << "TGC min = " << (int)sM.tgcdB[0] << " max = " << (int)sM.tgcdB[6] << "\n"; + settingsFile << "TGC min = " << (int)sM.tgcdB[0] << " max = " << (int)sM.tgcdB[7] << "\n"; settingsFile << "[Beamforming Parameters]\n"; settingsFile << "Reconstructed Lines = " << sM.reconstructionLines << "\n"; settingsFile << "Samples per Line = " << sM.reconstructionSamplesPerLine << "\n"; settingsFile.close(); } else if (m_Device->GetScanMode().beamformingAlgorithm == (int)Beamforming::PlaneWaveCompound) // save no PAImage if we used US only mode { OrderImagesUltrasound(USImage, m_RecordedImages); mitk::IOUtil::Save(USImage, pathUS); } m_PixelValues.clear(); m_RawRecordedImages.clear(); // clean up the pixel values m_RecordedImages.clear(); // clean up the images m_ImageTimestampRecord.clear(); // clean up the timestamps } } void mitk::USDiPhASImageSource::GetPixelValues(itk::Index<3> pixel, std::vector& values) { - unsigned int events = m_Device->GetScanMode().transmitEventsCount + 1; // the PA event is not included in the transmitEvents, so we add 1 here + unsigned int events = 2; for (int index = 0; index < m_RecordedImages.size(); index += events) // omit sound images { Image::Pointer image = m_RecordedImages.at(index); image = ApplyBmodeFilter(image); values.push_back(image.GetPointer()->GetPixelValueByIndex(pixel)); } } void mitk::USDiPhASImageSource::OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage, std::vector recordedList, bool raw) { unsigned int width = 32; unsigned int height = 32; unsigned int events = m_Device->GetScanMode().transmitEventsCount + 1; // the PA event is not included in the transmitEvents, so we add 1 here + if (!raw) + events = 2; // the beamformed image array contains only the resulting image of multiple events if (raw) { width = recordedList.at(0)->GetDimension(0); height = recordedList.at(0)->GetDimension(1); } else if (m_DataType == DataType::Beamformed_Short) { width = m_Device->GetScanMode().reconstructionLines; height = m_Device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = m_Device->GetScanMode().imageWidth; height = m_Device->GetScanMode().imageHeight; } unsigned int dimLaser[] = { (unsigned int)width, (unsigned int)height, (unsigned int)(recordedList.size() / events)}; unsigned int dimSound[] = { (unsigned int)width, (unsigned int)height, (unsigned int)(recordedList.size() / events * (events-1))}; PAImage->Initialize(recordedList.back()->GetPixelType(), 3, dimLaser); PAImage->SetGeometry(recordedList.back()->GetGeometry()); USImage->Initialize(recordedList.back()->GetPixelType(), 3, dimSound); USImage->SetGeometry(recordedList.back()->GetGeometry()); for (int index = 0; index < recordedList.size(); ++index) { mitk::ImageReadAccessor inputReadAccessor(recordedList.at(index)); + if (index % events == 0) { PAImage->SetSlice(inputReadAccessor.GetData(), index / events); } else { - USImage->SetSlice(inputReadAccessor.GetData(), ((index - (index % events)) / events) + (index % events)-1); + if(!raw) + USImage->SetSlice(inputReadAccessor.GetData(), ((index - (index % events)) / events) + (index % events)-1); } } } void mitk::USDiPhASImageSource::OrderImagesUltrasound(Image::Pointer USImage, std::vector recordedList) { unsigned int width = 32; unsigned int height = 32; unsigned int events = m_Device->GetScanMode().transmitEventsCount; if (m_DataType == DataType::Beamformed_Short) { width = (unsigned int)m_Device->GetScanMode().reconstructionLines; height = (unsigned int)m_Device->GetScanMode().reconstructionSamplesPerLine; } else if (m_DataType == DataType::Image_uChar) { width = (unsigned int)m_Device->GetScanMode().imageWidth; height = (unsigned int)m_Device->GetScanMode().imageHeight; } unsigned int dimSound[] = { (unsigned int)width, (unsigned int)height, (unsigned int)recordedList.size()}; USImage->Initialize(recordedList.back()->GetPixelType(), 3, dimSound); USImage->SetGeometry(recordedList.back()->GetGeometry()); for (int index = 0; index < recordedList.size(); ++index) { mitk::ImageReadAccessor inputReadAccessor(recordedList.at(index)); USImage->SetSlice(inputReadAccessor.GetData(), index); } } \ No newline at end of file diff --git a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h index 3f75369e14..54ac576ffd 100644 --- a/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h +++ b/Modules/US/USHardwareDiPhAS/mitkUSDiPhASImageSource.h @@ -1,191 +1,193 @@ /*=================================================================== 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 MITKUSDiPhASImageSource_H_HEADER_INCLUDED_ #define MITKUSDiPhASImageSource_H_HEADER_INCLUDED_ #include "mitkUSImageSource.h" #include "mitkUSDiPhASCustomControls.h" #include "Framework.IBMT.US.CWrapper.h" #include "mitkImageReadAccessor.h" #include "itkFastMutexLock.h" #include #include #include #include #include #include #include #include namespace mitk { class USDiPhASDevice; /** * \brief Implementation of mitk::USImageSource for DiPhAS API devices. * The method mitk::USImageSource::GetNextRawImage() is implemented for * getting images from the DiPhAS API. * * The image data is given to this class from the DiPhAS API by calling * a callback method that writes the image data to an mitk::image */ class USDiPhASImageSource : public USImageSource { public: mitkClassMacro(USDiPhASImageSource, USImageSource); mitkNewMacro1Param(Self, mitk::USDiPhASDevice*); itkCloneMacro(Self); + typedef itk::Image< float, 3 > itkFloatImageType; typedef mitk::USDiPhASDeviceCustomControls::DataType DataType; typedef mitk::USDiPhASDeviceCustomControls::SavingSettings SavingSettings; /** * Implementation of the superclass method. Returns the pointer * to the mitk::Image filled by DiPhAS API callback. */ - virtual void GetNextRawImage( mitk::Image::Pointer& ); + virtual void GetNextRawImage( std::vector& ) override; /** * The API calls this function to pass the image data to the * user; here the m_Image is updated */ void mitk::USDiPhASImageSource::ImageDataCallback( short* rfDataChannelData, int& channelDataChannelsPerDataset, int& channelDataSamplesPerChannel, int& channelDataTotalDatasets, short* rfDataArrayBeamformed, int& beamformedLines, int& beamformedSamples, int& beamformedTotalDatasets, unsigned char* imageData, int& imageWidth, int& imageHeight, int& imagePixelFormat, int& imageSetsTotal, double& timeStamp); void SetGUIOutput(std::function out); /** This starts or ends the recording session*/ void SetRecordingStatus(bool record); void SetSavingSettings(SavingSettings settings); void SetVerticalSpacing(float mm); void ModifyDataType(DataType dataT); void ModifyUseBModeFilter(bool isSet); void ModifyScatteringCoefficient(int coeff); void ModifyCompensateForScattering(bool useIt); void ModifyEnergyCompensation(bool compensate); /** * Sets the spacing used in the image based on the informations of the ScanMode in USDiPhAS Device */ void UpdateImageGeometry(); protected: void SetDataType(DataType dataT); void SetUseBModeFilter(bool isSet); USDiPhASImageSource(mitk::USDiPhASDevice* device); virtual ~USDiPhASImageSource( ); /** This vector holds all the images we record, if recording is set to active. */ std::vector m_RecordedImages; std::vector m_RawRecordedImages; std::vector m_ImageTimestampRecord; std::vector m_ImageTimestampBuffer; long long m_CurrentImageTimestamp; bool m_CurrentlyRecording; mitk::OphirPyro::Pointer m_Pyro; bool m_PyroConnected; std::vector m_FluenceCompOriginal; std::vector m_FluenceCompResized; std::vector::Pointer> m_FluenceCompResizedItk; std::vector m_ImageBuffer; int m_LastWrittenImage; int m_BufferSize; unsigned int m_ImageDimensions[3]; mitk::Vector3D m_ImageSpacing; mitk::Image::Pointer ApplyBmodeFilter(mitk::Image::Pointer image, bool useLogFilter = false); + mitk::Image::Pointer CutOffTop(mitk::Image::Pointer image, int cutOffSize = 165); mitk::Image::Pointer ResampleOutputVertical(mitk::Image::Pointer image, float verticalSpacing = 0.1); mitk::Image::Pointer ApplyScatteringCompensation(mitk::Image::Pointer inputImage, int scatteringCoefficient); mitk::Image::Pointer ApplyResampling(mitk::Image::Pointer inputImage, mitk::Vector3D outputSpacing, unsigned int outputSize[3]); mitk::Image::Pointer MultiplyImage(mitk::Image::Pointer inputImage, double value); void OrderImagesInterleaved(Image::Pointer PAImage, Image::Pointer USImage, std::vector recordedList, bool raw); void OrderImagesUltrasound(Image::Pointer USImage, std::vector recordedList); void GetPixelValues(itk::Index<3> pixel, std::vector& values); float GetPixelValue(itk::Index<3> pixel); std::vector m_PixelValues; mitk::USDiPhASDevice* m_Device; /** This is a callback to pass text data to the GUI. */ std::function m_GUIOutput; /** * Variables for management of current state. */ SavingSettings m_SavingSettings; float m_StartTime; bool m_UseGUIOutPut; BeamformerStateInfoNative m_BeamformerInfos; bool m_UseBModeFilter; bool m_DataTypeModified; DataType m_DataTypeNext; bool m_UseBModeFilterModified; bool m_UseBModeFilterNext; float m_VerticalSpacing; float m_VerticalSpacingNext; bool m_VerticalSpacingModified; int m_ScatteringCoefficient; int m_ScatteringCoefficientNext; bool m_ScatteringCoefficientModified; bool m_CompensateForScattering; bool m_CompensateForScatteringNext; bool m_CompensateForScatteringModified; bool m_CompensateEnergy; bool m_CompensateEnergyNext; bool m_CompensateEnergyModified; DataType m_DataType; }; } // namespace mitk #endif // MITKUSDiPhASImageSource_H diff --git a/Modules/US/USModel/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp index bfdd6783c3..c992d1a62d 100644 --- a/Modules/US/USModel/mitkUSDevice.cpp +++ b/Modules/US/USModel/mitkUSDevice.cpp @@ -1,671 +1,670 @@ /*=================================================================== 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 "mitkUSDevice.h" #include "mitkImageReadAccessor.h" // US Control Interfaces #include "mitkUSControlInterfaceProbes.h" #include "mitkUSControlInterfaceBMode.h" #include "mitkUSControlInterfaceDoppler.h" // Microservices #include #include #include #include mitk::USDevice::PropertyKeys mitk::USDevice::GetPropertyKeys() { static mitk::USDevice::PropertyKeys propertyKeys; return propertyKeys; } mitk::USDevice::USImageCropArea mitk::USDevice::GetCropArea() { MITK_INFO << "Return Crop Area L:" << m_CropArea.cropLeft << " R:" << m_CropArea.cropRight << " T:" << m_CropArea.cropTop << " B:" << m_CropArea.cropBottom; return m_CropArea; } mitk::USDevice::USDevice(std::string manufacturer, std::string model) : mitk::ImageSource(), m_IsFreezed(false), m_DeviceState(State_NoState), m_Manufacturer(manufacturer), m_Name(model), m_SpawnAcquireThread(true), m_MultiThreader(itk::MultiThreader::New()), m_ImageMutex(itk::FastMutexLock::New()), m_ThreadID(-1), - m_UnregisteringStarted(false) + m_UnregisteringStarted(false), + m_NumberOfOutputs(1) { USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs - this->SetNumberOfIndexedOutputs(1); + this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata) : mitk::ImageSource(), m_IsFreezed(false), m_DeviceState(State_NoState), m_SpawnAcquireThread(true), m_MultiThreader(itk::MultiThreader::New()), m_ImageMutex(itk::FastMutexLock::New()), m_ThreadID(-1), m_UnregisteringStarted(false) { m_Manufacturer = metadata->GetDeviceManufacturer(); m_Name = metadata->GetDeviceModel(); m_Comment = metadata->GetDeviceComment(); USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs this->SetNumberOfIndexedOutputs(1); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::~USDevice() { if (m_ThreadID >= 0) { m_MultiThreader->TerminateThread(m_ThreadID); } // make sure that the us device is not registered at the micro service // anymore after it is destructed this->UnregisterOnService(); } mitk::USAbstractControlInterface::Pointer mitk::USDevice::GetControlInterfaceCustom() { MITK_INFO << "Custom control interface does not exist for this object."; return 0; } mitk::USControlInterfaceBMode::Pointer mitk::USDevice::GetControlInterfaceBMode() { MITK_INFO << "Control interface BMode does not exist for this object."; return 0; } mitk::USControlInterfaceProbes::Pointer mitk::USDevice::GetControlInterfaceProbes() { MITK_INFO << "Control interface Probes does not exist for this object."; return 0; } mitk::USControlInterfaceDoppler::Pointer mitk::USDevice::GetControlInterfaceDoppler() { MITK_INFO << "Control interface Doppler does not exist for this object."; return 0; } void mitk::USDevice::SetManufacturer(std::string manufacturer) { m_Manufacturer = manufacturer; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER, manufacturer); } } void mitk::USDevice::SetName(std::string name) { m_Name = name; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME, name); } } void mitk::USDevice::SetComment(std::string comment) { m_Comment = comment; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_COMMENT, comment); } } us::ServiceProperties mitk::USDevice::ConstructServiceProperties() { mitk::USDevice::PropertyKeys propertyKeys = mitk::USDevice::GetPropertyKeys(); us::ServiceProperties props; props[propertyKeys.US_PROPKEY_ISCONNECTED] = this->GetIsConnected() ? "true" : "false"; props[propertyKeys.US_PROPKEY_ISACTIVE] = this->GetIsActive() ? "true" : "false"; props[propertyKeys.US_PROPKEY_LABEL] = this->GetServicePropertyLabel(); // get identifier of selected probe if there is one selected mitk::USControlInterfaceProbes::Pointer probesControls = this->GetControlInterfaceProbes(); if (probesControls.IsNotNull() && probesControls->GetIsActive()) { mitk::USProbe::Pointer probe = probesControls->GetSelectedProbe(); if (probe.IsNotNull()) { props[propertyKeys.US_PROPKEY_PROBES_SELECTED] = probe->GetName(); } } props[propertyKeys.US_PROPKEY_CLASS] = GetDeviceClass(); props[propertyKeys.US_PROPKEY_MANUFACTURER] = m_Manufacturer; props[propertyKeys.US_PROPKEY_NAME] = m_Name; props[propertyKeys.US_PROPKEY_COMMENT] = m_Comment; m_ServiceProperties = props; return props; } void mitk::USDevice::UnregisterOnService() { // unregister on micro service if (m_ServiceRegistration && !m_UnregisteringStarted) { // make sure that unregister is not started a second // time due to a callback during unregister for example m_UnregisteringStarted = true; m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } } bool mitk::USDevice::Initialize() { if (!this->OnInitialization()) { return false; } m_DeviceState = State_Initialized; // Get Context and Module us::ModuleContext* context = us::GetModuleContext(); us::ServiceProperties props = this->ConstructServiceProperties(); m_ServiceRegistration = context->RegisterService(this, props); return true; } bool mitk::USDevice::Connect() { MITK_DEBUG << "mitk::USDevice::Connect() called"; if (this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Tried to connect an ultrasound device that " "was already connected. Ignoring call..."; return true; } if (!this->GetIsInitialized()) { MITK_ERROR("mitkUSDevice") << "Cannot connect device if it is not in initialized state."; return false; } // Prepare connection, fail if this fails. if (!this->OnConnection()) { return false; } // Update state m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, true); return true; } void mitk::USDevice::ConnectAsynchron() { this->m_MultiThreader->SpawnThread(this->ConnectThread, this); } bool mitk::USDevice::Disconnect() { if (!GetIsConnected()) { MITK_WARN << "Tried to disconnect an ultrasound device that was not " "connected. Ignoring call..."; return false; } // Prepare connection, fail if this fails. if (!this->OnDisconnection()) return false; // Update state m_DeviceState = State_Initialized; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, false); return true; } bool mitk::USDevice::Activate() { if (!this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Cannot activate device if it is not in connected state."; return true; } if (OnActivation()) { m_DeviceState = State_Activated; m_FreezeBarrier = itk::ConditionVariable::New(); // spawn thread for aquire images if us device is active if (m_SpawnAcquireThread) { this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this); } this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, true); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); // initialize the b mode control properties of the micro service mitk::USControlInterfaceBMode::Pointer bmodeControls = this->GetControlInterfaceBMode(); if (bmodeControls.IsNotNull()) { bmodeControls->Initialize(); } } this->ProvideViaOIGTL(); return m_DeviceState == State_Activated; } void mitk::USDevice::ProvideViaOIGTL() { // create a new OpenIGTLink Server if (m_IGTLServer.IsNull()) m_IGTLServer = mitk::IGTLServer::New(true); m_IGTLServer->SetName(this->GetName()); // create a new OpenIGTLink Device source if (m_IGTLMessageProvider.IsNull()) m_IGTLMessageProvider = mitk::IGTLMessageProvider::New(); // set the OpenIGTLink server as the source for the device source m_IGTLMessageProvider->SetIGTLDevice(m_IGTLServer); // register the provider so that it can be configured with the IGTL manager // plugin. This could be hardcoded but now I already have the fancy plugin. m_IGTLMessageProvider->RegisterAsMicroservice(); m_ImageToIGTLMsgFilter = mitk::ImageToIGTLMessageFilter::New(); m_ImageToIGTLMsgFilter->ConnectTo(this); // set the name of this filter to identify it easier m_ImageToIGTLMsgFilter->SetName(this->GetName()); // register this filter as micro service. The message provider looks for // provided IGTLMessageSources, once it found this microservice and someone // requested this data type then the provider will connect with this filter // automatically. m_ImageToIGTLMsgFilter->RegisterAsMicroservice(); } void mitk::USDevice::Deactivate() { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot deactivate a device which is not activae."; return; } if (!OnDeactivation()) { return; } DisableOIGTL(); m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, false); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); } void mitk::USDevice::DisableOIGTL() { // TODO: This seems not to be enough cleanup to catch all cases. For example, if the device is disconnected // from the OIGTL GUI, this won't get cleaned up correctly. m_IGTLServer->CloseConnection(); m_IGTLMessageProvider->UnRegisterMicroservice(); m_ImageToIGTLMsgFilter->UnRegisterMicroservice(); } void mitk::USDevice::SetIsFreezed(bool freeze) { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot freeze or unfreeze if device is not active."; return; } this->OnFreeze(freeze); if (freeze) { m_IsFreezed = true; } else { m_IsFreezed = false; // wake up the image acquisition thread m_FreezeBarrier->Signal(); } } bool mitk::USDevice::GetIsFreezed() { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice")("mitkUSTelemedDevice") << "Cannot get freeze state if the hardware interface is not ready. " "Returning false..."; return false; } return m_IsFreezed; } void mitk::USDevice::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } imageSource->PushFilter(filter); } void mitk::USDevice::PushFilterIfNotPushedBefore( AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } if (!imageSource->GetIsFilterInThePipeline(filter)) { imageSource->PushFilter(filter); } } bool mitk::USDevice::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when removing a filter."; } return imageSource->RemoveFilter(filter); } void mitk::USDevice::UpdateServiceProperty(std::string key, std::string value) { m_ServiceProperties[key] = value; m_ServiceRegistration.SetProperties(m_ServiceProperties); // send event to notify listeners about the changed property m_PropertyChangedMessage(key, value); } void mitk::USDevice::UpdateServiceProperty(std::string key, double value) { std::stringstream stream; stream << value; this->UpdateServiceProperty(key, stream.str()); } void mitk::USDevice::UpdateServiceProperty(std::string key, bool value) { this->UpdateServiceProperty( key, value ? std::string("true") : std::string("false")); } /** mitk::Image* mitk::USDevice::GetOutput() { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::Image* mitk::USDevice::GetOutput(unsigned int idx) { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetOutput(idx)); } void mitk::USDevice::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::USDevice::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter only has " << this->GetNumberOfOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on USImage to copy member data output->Graft( graft ); } */ void mitk::USDevice::GrabImage() { - mitk::Image::Pointer image = this->GetUSImageSource()->GetNextImage(); + std::vector image = this->GetUSImageSource()->GetNextImage(); m_ImageMutex->Lock(); - this->SetImage(image); + this->SetImageVector(image); m_ImageMutex->Unlock(); - // if (image.IsNotNull() && (image->GetGeometry()!=nullptr)){ - // MITK_INFO << "Spacing: " << image->GetGeometry()->GetSpacing();} } //########### GETTER & SETTER ##################// bool mitk::USDevice::GetIsInitialized() { return m_DeviceState == State_Initialized; } bool mitk::USDevice::GetIsActive() { return m_DeviceState == State_Activated; } bool mitk::USDevice::GetIsConnected() { return m_DeviceState == State_Connected; } std::string mitk::USDevice::GetDeviceManufacturer() { return m_Manufacturer; } std::string mitk::USDevice::GetDeviceModel() { return m_Name; } std::string mitk::USDevice::GetDeviceComment() { return m_Comment; } void mitk::USDevice::GenerateData() { m_ImageMutex->Lock(); - if (m_Image.IsNull() || !m_Image->IsInitialized()) + for (unsigned int i = 0; i < m_ImageVector.size() && i < this->GetNumberOfIndexedOutputs(); ++i) { - m_ImageMutex->Unlock(); - return; - } + auto& image = m_ImageVector[i]; + if (image.IsNull() || !image->IsInitialized()) + { + // skip image + } + else + { + mitk::Image::Pointer output = this->GetOutput(i); - mitk::Image::Pointer output = this->GetOutput(); + if (!output->IsInitialized() || + output->GetDimension(0) != image->GetDimension(0) || + output->GetDimension(1) != image->GetDimension(1) || + output->GetDimension(2) != image->GetDimension(2) || + output->GetPixelType() != image->GetPixelType()) + { + output->Initialize(image->GetPixelType(), image->GetDimension(), + image->GetDimensions()); + } - if (!output->IsInitialized() || - output->GetDimension(0) != m_Image->GetDimension(0) || - output->GetDimension(1) != m_Image->GetDimension(1) || - output->GetDimension(2) != m_Image->GetDimension(2) || - output->GetPixelType() != m_Image->GetPixelType()) - { - output->Initialize(m_Image->GetPixelType(), m_Image->GetDimension(), - m_Image->GetDimensions()); - } + // copy contents of the given image into the member variable + mitk::ImageReadAccessor inputReadAccessor(image); + output->SetImportVolume(inputReadAccessor.GetData()); - // copy contents of the given image into the member variable, slice after slice - for (unsigned int sliceNumber = 0; sliceNumber < m_Image->GetDimension(2); ++sliceNumber) - { - if (m_Image->IsSliceSet(sliceNumber)) { - mitk::ImageReadAccessor inputReadAccessor(m_Image, m_Image->GetSliceData(sliceNumber, 0, 0)); - output->SetSlice(inputReadAccessor.GetData(), sliceNumber); + output->SetGeometry(image->GetGeometry()); } - } - - output->SetGeometry(m_Image->GetGeometry()); + } m_ImageMutex->Unlock(); }; std::string mitk::USDevice::GetServicePropertyLabel() { std::string isActive; if (this->GetIsActive()) { isActive = " (Active)"; } else { isActive = " (Inactive)"; } // e.g.: Zonare MyLab5 (Active) return m_Manufacturer + " " + m_Name + isActive; } ITK_THREAD_RETURN_TYPE mitk::USDevice::Acquire(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct* pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; while (device->GetIsActive()) { // lock this thread when ultrasound device is freezed if (device->m_IsFreezed) { itk::SimpleMutexLock* mutex = &(device->m_FreezeMutex); mutex->Lock(); if (device->m_FreezeBarrier.IsNotNull()) { device->m_FreezeBarrier->Wait(mutex); } } device->GrabImage(); } return ITK_THREAD_RETURN_VALUE; } ITK_THREAD_RETURN_TYPE mitk::USDevice::ConnectThread(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct* pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; device->Connect(); return ITK_THREAD_RETURN_VALUE; } void mitk::USDevice::ProbeChanged(std::string probename) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED, probename); } void mitk::USDevice::DepthChanged(double depth) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH, depth); } diff --git a/Modules/US/USModel/mitkUSDevice.h b/Modules/US/USModel/mitkUSDevice.h index a7ba223766..7563fb3728 100644 --- a/Modules/US/USModel/mitkUSDevice.h +++ b/Modules/US/USModel/mitkUSDevice.h @@ -1,470 +1,479 @@ /*=================================================================== 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 MITKUSDevice_H_HEADER_INCLUDED_ #define MITKUSDevice_H_HEADER_INCLUDED_ // STL #include // MitkUS #include "mitkUSProbe.h" #include #include "mitkUSImageSource.h" // MitkIGTL #include "mitkIGTLMessageProvider.h" #include "mitkIGTLServer.h" #include "mitkIGTLDeviceSource.h" #include "mitkImageToIGTLMessageFilter.h" // MITK #include #include #include // ITK #include #include // Microservices #include #include #include // DEPRECATED #include "mitkUSImageMetadata.h" namespace itk { template class SmartPointer; } namespace mitk { class USAbstractControlInterface; class USControlInterfaceBMode; class USControlInterfaceProbes; class USControlInterfaceDoppler; /** * \brief A device holds information about it's model, make and the connected probes. It is the * common super class for all devices and acts as an image source for mitkUSImages. It is the base class * for all US Devices, and every new device should extend it. * * US Devices support output of calibrated images, i.e. images that include a specific geometry. * To achieve this, call SetCalibration, and make sure that the subclass also calls apply * transformation at some point (The USDevice does not automatically apply the transformation to the image) * * Note that USDevices will be removed from micro servive when their * destructor is called. Registering into micro service is done when * mitk::USDevice::Initialize() is called. * * \ingroup US */ class MITKUS_EXPORT USDevice : public mitk::ImageSource { public: enum DeviceStates { State_NoState, State_Initialized, State_Connected, State_Activated }; mitkClassMacro(USDevice, mitk::ImageSource); struct USImageCropArea { int cropLeft; int cropRight; int cropBottom; int cropTop; }; /** * \brief These constants are used in conjunction with Microservices. * The constants aren't defined as static member attributes to avoid the * "static initialization order fiasco", which would occur when objects of * this class are used in module activators (for restoring stored device, * for example). */ struct PropertyKeys { const std::string US_INTERFACE_NAME; // Common Interface name of all US Devices. Used to refer to this device via Microservices const std::string US_PROPKEY_MANUFACTURER; const std::string US_PROPKEY_NAME; const std::string US_PROPKEY_COMMENT; const std::string US_PROPKEY_LABEL; // Human readable text represntation of this device const std::string US_PROPKEY_ISCONNECTED; // Whether this device is connected or not. const std::string US_PROPKEY_ISACTIVE; // Whether this device is active or not. const std::string US_PROPKEY_CLASS; // Class Name of this Object const std::string US_PROPKEY_PROBES_SELECTED; const std::string US_PROPKEY_BMODE_FREQUENCY; const std::string US_PROPKEY_BMODE_POWER; const std::string US_PROPKEY_BMODE_DEPTH; const std::string US_PROPKEY_BMODE_GAIN; const std::string US_PROPKEY_BMODE_REJECTION; const std::string US_PROPKEY_BMODE_DYNAMIC_RANGE; PropertyKeys() : US_INTERFACE_NAME("org.mitk.services.UltrasoundDevice"), US_PROPKEY_MANUFACTURER(US_INTERFACE_NAME + ".manufacturer"), US_PROPKEY_NAME(US_INTERFACE_NAME + ".name"), US_PROPKEY_COMMENT(US_INTERFACE_NAME + ".comment"), US_PROPKEY_LABEL(US_INTERFACE_NAME + ".label"), US_PROPKEY_ISCONNECTED(US_INTERFACE_NAME + ".isConnected"), US_PROPKEY_ISACTIVE(US_INTERFACE_NAME + ".isActive"), US_PROPKEY_CLASS(US_INTERFACE_NAME + ".class"), US_PROPKEY_PROBES_SELECTED(US_INTERFACE_NAME + ".probes.selected"), US_PROPKEY_BMODE_FREQUENCY(US_INTERFACE_NAME + ".bmode.frequency"), US_PROPKEY_BMODE_POWER(US_INTERFACE_NAME + ".bmode.power"), US_PROPKEY_BMODE_DEPTH(US_INTERFACE_NAME + ".bmode.depth"), US_PROPKEY_BMODE_GAIN(US_INTERFACE_NAME + ".bmode.gain"), US_PROPKEY_BMODE_REJECTION(US_INTERFACE_NAME + ".bmode.rejection"), US_PROPKEY_BMODE_DYNAMIC_RANGE(US_INTERFACE_NAME + ".bmode.dynamicRange") {} }; /** * \brief Event for being notified about changes of the micro service properties. * This event can be used if no micro service context is available. */ mitkNewMessage2Macro(PropertyChanged, const std::string&, const std::string&) /** * \return keys for the microservice properties of ultrasound devices */ static mitk::USDevice::PropertyKeys GetPropertyKeys(); /** * \brief Default getter for the custom control interface. * Has to be implemented in a subclass if a custom control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceCustom(); /** * \brief Default getter for the b mode control interface. * Has to be implemented in a subclass if a b mode control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceBMode(); /** * \brief Default getter for the probes control interface. * Has to be implemented in a subclass if a probes control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceProbes(); /** * \brief Default getter for the doppler control interface. * Has to be implemented in a subclass if a doppler control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceDoppler(); /** * \brief Changes device state to mitk::USDevice::State_Initialized. * During initialization the virtual method * mitk::USDevice::OnInitialization will be called. If this method * returns false the initialization process will be canceled. Otherwise * the mitk::USDevice is registered in a micro service. */ bool Initialize(); /** * \brief Connects this device. A connected device is ready to deliver images (i.e. be Activated). A Connected Device can be active. A disconnected Device cannot be active. * Internally calls onConnect and then registers the device with the service. A device usually should * override the OnConnection() method, but never the Connect() method, since this will possibly exclude the device * from normal service management. The exact flow of events is: * 0. Check if the device is already connected. If yes, return true anyway, but don't do anything. * 1. Call OnConnection() Here, a device should establish it's connection with the hardware Afterwards, it should be ready to start transmitting images at any time. * 2. If OnConnection() returns true ("successful"), then the device is registered with the service. * 3. if not, it the method itself returns false or may throw an expection, depeneding on the device implementation. * */ bool Connect(); void ConnectAsynchron(); /** * \brief Works analogously to mitk::USDevice::Connect(). Don't override this Method, but onDisconnection instead. */ bool Disconnect(); /** * \brief Activates this device. * After the activation process, the device will start to produce images. * This Method will fail, if the device is not connected. */ bool Activate(); /** * \brief Deactivates this device. * After the deactivation process, the device will no longer produce * images, but still be connected. */ void Deactivate(); /** * \brief Can toggle if ultrasound image is currently updated or freezed. * * \param freeze true to stop updating the ultrasound image, false to start updating again */ virtual void SetIsFreezed(bool freeze); /** * \return true if device is currently freezed (no image update is done), false otherwise */ virtual bool GetIsFreezed(); void PushFilter(AbstractOpenCVImageFilter::Pointer filter); void PushFilterIfNotPushedBefore(AbstractOpenCVImageFilter::Pointer filter); bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter); /** * @brief To be called when the used probe changed. Will update the service properties * @param probename of the now used probe */ void ProbeChanged(std::string probename); /** * @brief To be called when the scanning depth of the probe changed. Will update the service properties * @param depth that is now used */ void DepthChanged(double depth); /** * \brief Given property is updated in the device micro service. * This method is mainly for being used by the control interface * superclasses. You do not need to call it by yoursefs in your * concrete control interface classes. */ void UpdateServiceProperty(std::string key, std::string value); void UpdateServiceProperty(std::string key, double value); void UpdateServiceProperty(std::string key, bool value); //########### GETTER & SETTER ##################// /** * \brief Returns the Class of the Device. This Method must be reimplemented by every Inheriting Class. */ virtual std::string GetDeviceClass() = 0; /** * \brief True, if the device object is created and initialized, false otherwise. */ bool GetIsInitialized(); /** * \brief True, if the device is currently generating image data, false otherwise. */ bool GetIsActive(); /** * \brief True, if the device is currently ready to start transmitting image data or is already * transmitting image data. A disconnected device cannot be activated. */ bool GetIsConnected(); /* @return Returns the area that will be cropped from the US image. Is disabled / [0,0,0,0] by default. */ mitk::USDevice::USImageCropArea GetCropArea(); /** @return Returns the current image source of this device. */ virtual USImageSource::Pointer GetUSImageSource() = 0; /** \brief Deprecated -> use GetManufacturer() instead */ DEPRECATED(std::string GetDeviceManufacturer()); /** \brief Deprecated -> use GetName() instead */ DEPRECATED(std::string GetDeviceModel()); /** \brief Deprecated -> use GetCommend() instead */ DEPRECATED(std::string GetDeviceComment()); itkGetMacro(Manufacturer, std::string); itkGetMacro(Name, std::string); itkGetMacro(Comment, std::string); void SetManufacturer(std::string manufacturer); void SetName(std::string name); void SetComment(std::string comment); itkGetMacro(DeviceState, DeviceStates) itkGetMacro(ServiceProperties, us::ServiceProperties) void GrabImage(); protected: - itkSetMacro(Image, mitk::Image::Pointer); + virtual void SetImageVector(std::vector vec) + { + if (this->m_ImageVector != vec) + { + this->m_ImageVector = vec; + this->Modified(); + } + } itkSetMacro(SpawnAcquireThread, bool); itkGetMacro(SpawnAcquireThread, bool); static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct); static ITK_THREAD_RETURN_TYPE ConnectThread(void* pInfoStruct); - mitk::Image::Pointer m_Image; - mitk::Image::Pointer m_OutputImage; + std::vector m_ImageVector; + //mitk::Image::Pointer m_OutputImage; /** * \brief Registers an OpenIGTLink device as a microservice so that we can send the images of * this device via the network. */ void ProvideViaOIGTL(); /** * \brief Deregisters the microservices for OpenIGTLink. */ void DisableOIGTL(); mitk::IGTLServer::Pointer m_IGTLServer; mitk::IGTLMessageProvider::Pointer m_IGTLMessageProvider; mitk::ImageToIGTLMessageFilter::Pointer m_ImageToIGTLMsgFilter; bool m_IsFreezed; DeviceStates m_DeviceState; /* @brief defines the area that should be cropped from the US image */ USImageCropArea m_CropArea; /** * \brief This Method constructs the service properties which can later be used to * register the object with the Microservices * Return service properties */ us::ServiceProperties ConstructServiceProperties(); /** * \brief Remove this device from the micro service. */ void UnregisterOnService(); /** * \brief Is called during the initialization process. * Override this method in a subclass to handle the actual initialization. * If it returns false, the initialization process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnInitialization() = 0; /** * \brief Is called during the connection process. * Override this method in a subclass to handle the actual connection. * If it returns false, the connection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnConnection() = 0; /** * \brief Is called during the disconnection process. * Override this method in a subclass to handle the actual disconnection. * If it returns false, the disconnection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDisconnection() = 0; /** * \brief Is called during the activation process. * After this method is finished, the device should be generating images. * If it returns false, the activation process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnActivation() = 0; /** * \brief Is called during the deactivation process. * After a call to this method the device should still be connected, * but not producing images anymore. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDeactivation() = 0; /** * \brief Called when mitk::USDevice::SetIsFreezed() is called. * Subclasses can overwrite this method to do additional actions. Default * implementation does noting. */ virtual void OnFreeze(bool) { } /** * \brief Enforces minimal Metadata to be set. */ USDevice(std::string manufacturer, std::string model); /** * \brief Constructs a device with the given Metadata. Make sure the Metadata contains meaningful content! * \deprecated Use USDevice(std::string manufacturer, std::string model) instead. */ USDevice(mitk::USImageMetadata::Pointer metadata); virtual ~USDevice(); /** * \brief Grabs the next frame from the Video input. * This method is called internally, whenever Update() is invoked by an Output. */ virtual void GenerateData() override; std::string GetServicePropertyLabel(); + unsigned int m_NumberOfOutputs; + private: std::string m_Manufacturer; std::string m_Name; std::string m_Comment; bool m_SpawnAcquireThread; /** * \brief The device's ServiceRegistration object that allows to modify it's Microservice registraton details. */ us::ServiceRegistration m_ServiceRegistration; /** * \brief Properties of the device's Microservice. */ us::ServiceProperties m_ServiceProperties; // Threading-Related itk::ConditionVariable::Pointer m_FreezeBarrier; itk::SimpleMutexLock m_FreezeMutex; itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the image source int m_ThreadID; ///< ID of the started thread bool m_UnregisteringStarted; }; } // namespace mitk // This is the microservice declaration. Do not meddle! MITK_DECLARE_SERVICE_INTERFACE(mitk::USDevice, "org.mitk.services.UltrasoundDevice") #endif // MITKUSDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSVideoDevice.h b/Modules/US/USModel/mitkUSVideoDevice.h index 840f624b4d..58ca76a857 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.h +++ b/Modules/US/USModel/mitkUSVideoDevice.h @@ -1,225 +1,225 @@ /*=================================================================== 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 MITKUSVideoDevice_H_HEADER_INCLUDED_ #define MITKUSVideoDevice_H_HEADER_INCLUDED_ #include #include #include "mitkUSDevice.h" #include "mitkUSImageVideoSource.h" #include "mitkUSProbe.h" #include namespace itk { template class SmartPointer; } namespace mitk { class USVideoDeviceCustomControls; class USAbstractControlInterface; /** * \brief A mitk::USVideoDevice is the common class for video only devices. * They capture video input either from a file or from a device and * transform the output into an mitk::USImage with attached metadata. * This simple implementation does only capture and display 2d images without * registration for example. * * \ingroup US */ class MITKUS_EXPORT USVideoDevice : public mitk::USDevice { public: mitkClassMacro(USVideoDevice, mitk::USDevice); // To open a device (DeviceID, Manufacturer, Model) mitkNewMacro3Param(Self, int, std::string, std::string); // To open A VideoFile (Path, Manufacturer, Model) mitkNewMacro3Param(Self, std::string, std::string, std::string); // To open a device (DeviceID, Metadata) mitkNewMacro2Param(Self, int, mitk::USImageMetadata::Pointer); // To open A VideoFile (Path, Metadata) mitkNewMacro2Param(Self, std::string, mitk::USImageMetadata::Pointer); /** * \return the qualified name of this class (as returned by GetDeviceClassStatic()) */ virtual std::string GetDeviceClass() override; /** * This methode is necessary instead of a static member attribute to avoid * "static initialization order fiasco" when an instance of this class is * used in a module activator. * * \return the qualified name of this class */ static std::string GetDeviceClassStatic(); /** * Getter for the custom control interface which was created during the * construction process of mitk::USVideoDevice. * * \return custom control interface of the video device */ virtual itk::SmartPointer GetControlInterfaceCustom() override; /** * \brief Remove this device from the micro service. * This method is public for mitk::USVideoDevice, because this devices * can be completly removed. This is not possible for API devices, which * should be available while their sub module is loaded. */ void UnregisterOnService(); /** * \return mitk::USImageSource connected to this device */ virtual USImageSource::Pointer GetUSImageSource() override; /** * \brief Return all probes for this USVideoDevice or an empty vector it no probes were set * Returns a std::vector of all probes that exist for this USVideoDevice if there were probes set while creating or modifying this USVideoDevice. * Otherwise it returns an empty vector. Therefore always check if vector is filled, before using it! */ std::vector GetAllProbes(); /** * \brief Return current active probe for this USVideoDevice * Returns a pointer to the probe that is currently in use. If there were probes set while creating or modifying this USVideoDevice. * Returns null otherwise */ mitk::USProbe::Pointer GetCurrentProbe(); /** \brief adds a new probe to the device */ void AddNewProbe(mitk::USProbe::Pointer probe); /** * \brief get the probe by its name * Returns a pointer to the probe identified by the given name. If no probe of given name exists for this Device 0 is returned. */ mitk::USProbe::Pointer GetProbeByName(std::string name); /** * \brief Removes the Probe with the given name */ void RemoveProbeByName(std::string name); /** \brief True, if this Device plays back a file, false if it recieves data from a device */ bool GetIsSourceFile(); - itkGetMacro(Image, mitk::Image::Pointer); + itkGetMacro(ImageVector, std::vector); itkGetMacro(DeviceID, int); itkGetMacro(FilePath, std::string); protected: /** * \brief Creates a new device that will deliver USImages taken from a video device. * under windows, try -1 for device number, which will grab the first available one * (Open CV functionality) */ USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model); /** * \brief Creates a new device that will deliver USImages taken from a video file. */ USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model); /** * \brief Creates a new device that will deliver USImages taken from a video device. * under windows, try -1 for device number, which will grab the first available one * (Open CV functionality) */ USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata); /** * \brief Creates a new device that will deliver USImages taken from a video file. */ USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata); virtual ~USVideoDevice(); /** * \brief Initializes common properties for all constructors. */ void Init(); /** * \brief Is called during the initialization process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ virtual bool OnInitialization() override; /** * \brief Is called during the connection process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ virtual bool OnConnection() override; /** * \brief Is called during the disconnection process. * Returns true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. */ virtual bool OnDisconnection() override; /** * \brief Is called during the activation process. After this method is finsihed, the device should be generating images */ virtual bool OnActivation() override; /** * \brief Is called during the deactivation process. After a call to this method the device should still be connected, but not producing images anymore. */ virtual bool OnDeactivation() override; /** * \brief The image source that we use to aquire data */ mitk::USImageVideoSource::Pointer m_Source; /** * \brief True, if this source plays back a file, false if it recieves data from a device */ bool m_SourceIsFile; /** * \brief The device id to connect to. Undefined, if m_SourceIsFile == true; */ int m_DeviceID; /** * \brief The Filepath id to connect to. Undefined, if m_SourceIsFile == false; */ std::string m_FilePath; /** * \brief custom control interface for us video device */ itk::SmartPointer m_ControlInterfaceCustom; /** * \brief probes for this USVideoDevice */ std::vector < mitk::USProbe::Pointer > m_Probes; /** \brief probe that is currently in use */ mitk::USProbe::Pointer m_CurrentProbe; }; } // namespace mitk #endif // MITKUSVideoDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USNavigation/mitkUSCombinedModality.cpp b/Modules/US/USNavigation/mitkUSCombinedModality.cpp index 0ddbef3fea..0acadcdb3c 100644 --- a/Modules/US/USNavigation/mitkUSCombinedModality.cpp +++ b/Modules/US/USNavigation/mitkUSCombinedModality.cpp @@ -1,581 +1,581 @@ /*=================================================================== 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 "mitkUSCombinedModality.h" #include "mitkUSDevice.h" #include "mitkNavigationDataSource.h" #include "mitkImageReadAccessor.h" #include #include #include "mitkTrackingDeviceSource.h" // US Control Interfaces #include "mitkUSControlInterfaceProbes.h" #include "mitkUSControlInterfaceBMode.h" #include "mitkUSControlInterfaceDoppler.h" //Microservices #include #include #include #include #include //TempIncludes #include const std::string mitk::USCombinedModality::DeviceClassIdentifier = "org.mitk.modules.us.USCombinedModality"; const char* mitk::USCombinedModality::DefaultProbeIdentifier = "default"; const char* mitk::USCombinedModality::ProbeAndDepthSeperator = "_"; const std::string mitk::USCombinedModality::US_INTERFACE_NAME = "org.mitk.services.USCombinedModality"; const std::string mitk::USCombinedModality::US_PROPKEY_DEVICENAME = US_INTERFACE_NAME + ".devicename"; const std::string mitk::USCombinedModality::US_PROPKEY_CLASS = US_INTERFACE_NAME + ".class"; const std::string mitk::USCombinedModality::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; mitk::USCombinedModality::USCombinedModality(USDevice::Pointer usDevice, NavigationDataSource::Pointer trackingDevice, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model), m_UltrasoundDevice(usDevice), m_TrackingDevice(trackingDevice), m_SmoothingFilter(mitk::NavigationDataSmoothingFilter::New()), m_DelayFilter(mitk::NavigationDataDelayFilter::New(0)), m_NumberOfSmoothingValues(0), m_DelayCount(0) { this->RebuildFilterPipeline(); //create a new output (for the image data) mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); // Combined Modality should not spawn an own acquire thread, because // image acquiring is done by the included us device this->SetSpawnAcquireThread(false); } mitk::USCombinedModality::~USCombinedModality() { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } std::string mitk::USCombinedModality::GetDeviceClass() { return DeviceClassIdentifier; } mitk::USImageSource::Pointer mitk::USCombinedModality::GetUSImageSource() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetUSImageSource(); } mitk::USAbstractControlInterface::Pointer mitk::USCombinedModality::GetControlInterfaceCustom() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceCustom(); } mitk::USControlInterfaceBMode::Pointer mitk::USCombinedModality::GetControlInterfaceBMode() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceBMode(); } mitk::USControlInterfaceProbes::Pointer mitk::USCombinedModality::GetControlInterfaceProbes() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceProbes(); } mitk::USControlInterfaceDoppler::Pointer mitk::USCombinedModality::GetControlInterfaceDoppler() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceDoppler(); } void mitk::USCombinedModality::UnregisterOnService() { if (m_DeviceState == State_Activated) { this->Deactivate(); } if (m_DeviceState == State_Connected) { this->Disconnect(); } if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } mitk::AffineTransform3D::Pointer mitk::USCombinedModality::GetCalibration() { return this->GetCalibration(this->GetCurrentDepthValue(), this->GetIdentifierForCurrentProbe()); } mitk::AffineTransform3D::Pointer mitk::USCombinedModality::GetCalibration(std::string depth) { return this->GetCalibration(depth, this->GetIdentifierForCurrentProbe()); } mitk::AffineTransform3D::Pointer mitk::USCombinedModality::GetCalibration(std::string depth, std::string probe) { // make sure that there is no '/' which would cause problems for TinyXML std::replace(probe.begin(), probe.end(), '/', '-'); // create identifier for calibration from probe and depth std::string calibrationKey = probe + mitk::USCombinedModality::ProbeAndDepthSeperator + depth; // find calibration for combination of probe identifier and depth std::map::iterator calibrationIterator = m_Calibrations.find(calibrationKey); if (calibrationIterator == m_Calibrations.end()) { return 0; } return calibrationIterator->second; } void mitk::USCombinedModality::SetCalibration(mitk::AffineTransform3D::Pointer calibration) { if (calibration.IsNull()) { MITK_WARN << "Null pointer passed to SetCalibration of mitk::USDevice. Ignoring call."; return; } std::string calibrationKey = this->GetIdentifierForCurrentCalibration(); if (calibrationKey.empty()) { MITK_WARN << "Could not get a key for the calibration -> Calibration cannot be set."; return; } m_Calibrations[calibrationKey] = calibration; } bool mitk::USCombinedModality::RemoveCalibration() { return this->RemoveCalibration(this->GetCurrentDepthValue(), this->GetIdentifierForCurrentProbe()); } bool mitk::USCombinedModality::RemoveCalibration(std::string depth) { return this->RemoveCalibration(depth, this->GetIdentifierForCurrentProbe()); } bool mitk::USCombinedModality::RemoveCalibration(std::string depth, std::string probe) { // make sure that there is no '/' which would cause problems for TinyXML std::replace(probe.begin(), probe.end(), '/', '-'); // create identifier for calibration from probe and depth std::string calibrationKey = probe + mitk::USCombinedModality::ProbeAndDepthSeperator + depth; return m_Calibrations.erase(calibrationKey) > 0; } void mitk::USCombinedModality::SetNumberOfSmoothingValues(unsigned int numberOfSmoothingValues) { unsigned int oldNumber = m_NumberOfSmoothingValues; m_NumberOfSmoothingValues = numberOfSmoothingValues; // if filter should be activated or deactivated if ((oldNumber == 0 && numberOfSmoothingValues != 0) || (oldNumber != 0 && numberOfSmoothingValues == 0)) { this->RebuildFilterPipeline(); } m_SmoothingFilter->SetNumerOfValues(numberOfSmoothingValues); } void mitk::USCombinedModality::SetDelayCount(unsigned int delayCount) { unsigned int oldCount = m_DelayCount; m_DelayCount = delayCount; // if filter should be activated or deactivated if ((oldCount == 0 && delayCount != 0) || (oldCount != 0 && delayCount == 0)) { this->RebuildFilterPipeline(); } m_DelayFilter->SetDelay(delayCount); } bool mitk::USCombinedModality::OnInitialization() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } if (m_UltrasoundDevice->GetDeviceState() < mitk::USDevice::State_Initialized) { return m_UltrasoundDevice->Initialize(); } else { return true; } } bool mitk::USCombinedModality::OnConnection() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } // connect ultrasound device only if it is not already connected if (m_UltrasoundDevice->GetDeviceState() >= mitk::USDevice::State_Connected) { return true; } else { return m_UltrasoundDevice->Connect(); } } bool mitk::USCombinedModality::OnDisconnection() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->Disconnect(); } bool mitk::USCombinedModality::OnActivation() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } mitk::TrackingDeviceSource::Pointer trackingDeviceSource = dynamic_cast(m_TrackingDevice.GetPointer()); if (trackingDeviceSource.IsNull()) { MITK_WARN("USCombinedModality")("USDevice") << "Cannot start tracking as TrackingDeviceSource is null."; } trackingDeviceSource->StartTracking(); // activate ultrasound device only if it is not already activated if (m_UltrasoundDevice->GetDeviceState() >= mitk::USDevice::State_Activated) { return true; } else { return m_UltrasoundDevice->Activate(); } } bool mitk::USCombinedModality::OnDeactivation() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } mitk::TrackingDeviceSource::Pointer trackingDeviceSource = dynamic_cast(m_TrackingDevice.GetPointer()); if (trackingDeviceSource.IsNull()) { MITK_WARN("USCombinedModality")("USDevice") << "Cannot stop tracking as TrackingDeviceSource is null."; } trackingDeviceSource->StopTracking(); m_UltrasoundDevice->Deactivate(); return m_UltrasoundDevice->GetIsConnected(); } void mitk::USCombinedModality::OnFreeze(bool freeze) { mitk::TrackingDeviceSource::Pointer trackingDeviceSource = dynamic_cast(m_TrackingDevice.GetPointer()); if (trackingDeviceSource.IsNull()) { MITK_WARN("USCombinedModality")("USDevice") << "Cannot freeze tracking."; } else { if (freeze) { trackingDeviceSource->Freeze(); } else { trackingDeviceSource->UnFreeze(); } } if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } m_UltrasoundDevice->SetIsFreezed(freeze); } mitk::NavigationDataSource::Pointer mitk::USCombinedModality::GetNavigationDataSource() { return m_LastFilter.GetPointer(); } bool mitk::USCombinedModality::GetIsCalibratedForCurrentStatus() { return m_Calibrations.find(this->GetIdentifierForCurrentCalibration()) != m_Calibrations.end(); } bool mitk::USCombinedModality::GetContainsAtLeastOneCalibration() { return !m_Calibrations.empty(); } void mitk::USCombinedModality::GenerateData() { if (m_UltrasoundDevice->GetIsFreezed()) { return; } //if the image is freezed: do nothing //get next image from ultrasound image source - mitk::Image::Pointer image = m_UltrasoundDevice->GetUSImageSource()->GetNextImage(); + mitk::Image::Pointer image = m_UltrasoundDevice->GetUSImageSource()->GetNextImage()[0]; if (image.IsNull() || !image->IsInitialized()) //check the image { MITK_WARN << "Invalid image in USCombinedModality, aborting!"; return; } //get output and initialize it if it wasn't initialized before mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) { output->Initialize(image); } //now update image data mitk::ImageReadAccessor inputReadAccessor(image, image->GetSliceData(0, 0, 0)); output->SetSlice(inputReadAccessor.GetData()); //copy image data output->GetGeometry()->SetSpacing(image->GetGeometry()->GetSpacing()); //copy spacing because this might also change //and update calibration (= transformation of the image) std::string calibrationKey = this->GetIdentifierForCurrentCalibration(); if (!calibrationKey.empty()) { std::map::iterator calibrationIterator = m_Calibrations.find(calibrationKey); if (calibrationIterator != m_Calibrations.end()) { // transform image according to callibration if one is set // for current configuration of probe and depth this->GetOutput()->GetGeometry()->SetIndexToWorldTransform(calibrationIterator->second); } } } std::string mitk::USCombinedModality::SerializeCalibration() { std::stringstream result; result << "" << std::endl; // For each calibration in the set for (std::map::iterator it = m_Calibrations.begin(); it != m_Calibrations.end(); it++) { mitk::AffineTransform3D::MatrixType matrix = it->second->GetMatrix(); mitk::AffineTransform3D::TranslationType translation = it->second->GetTranslation(); TiXmlElement elem(it->first); // Serialize Matrix elem.SetDoubleAttribute("M00", matrix[0][0]); elem.SetDoubleAttribute("M01", matrix[0][1]); elem.SetDoubleAttribute("M02", matrix[0][2]); elem.SetDoubleAttribute("M10", matrix[1][0]); elem.SetDoubleAttribute("M11", matrix[1][1]); elem.SetDoubleAttribute("M12", matrix[1][2]); elem.SetDoubleAttribute("M20", matrix[2][0]); elem.SetDoubleAttribute("M21", matrix[2][1]); elem.SetDoubleAttribute("M22", matrix[2][2]); // Serialize Offset elem.SetDoubleAttribute("T0", translation[0]); elem.SetDoubleAttribute("T1", translation[1]); elem.SetDoubleAttribute("T2", translation[2]); result << elem << std::endl; } result << "" << std::endl; return result.str(); } void mitk::USCombinedModality::DeserializeCalibration(const std::string& xmlString, bool clearPreviousCalibrations) { // Sanitize Input if (xmlString == "") { MITK_ERROR << "Empty string passed to Deserialize() method of CombinedModality. Aborting..."; mitkThrow() << "Empty string passed to Deserialize() method of CombinedModality. Aborting..."; return; } // Clear previous calibrations if necessary if (clearPreviousCalibrations) m_Calibrations.clear(); // Parse Input TiXmlDocument doc; if (!doc.Parse(xmlString.c_str())) { MITK_ERROR << "Unable to deserialize calibrations in CombinedModality. Error was: " << doc.ErrorDesc(); mitkThrow() << "Unable to deserialize calibrations in CombinedModality. Error was: " << doc.ErrorDesc(); return; } TiXmlElement* root = doc.FirstChildElement(); if (root == nullptr) { MITK_ERROR << "Unable to deserialize calibrations in CombinedModality. String contained no root element."; mitkThrow() << "Unable to deserialize calibrations in CombinedModality. String contained no root element."; return; } // Read Calibrations for (TiXmlElement* elem = root->FirstChildElement(); elem != nullptr; elem = elem->NextSiblingElement()) { mitk::AffineTransform3D::MatrixType matrix; mitk::AffineTransform3D::OffsetType translation; std::string calibName = elem->Value(); // Deserialize Matrix elem->QueryDoubleAttribute("M00", &matrix[0][0]); elem->QueryDoubleAttribute("M01", &matrix[0][1]); elem->QueryDoubleAttribute("M02", &matrix[0][2]); elem->QueryDoubleAttribute("M10", &matrix[1][0]); elem->QueryDoubleAttribute("M11", &matrix[1][1]); elem->QueryDoubleAttribute("M12", &matrix[1][2]); elem->QueryDoubleAttribute("M20", &matrix[2][0]); elem->QueryDoubleAttribute("M21", &matrix[2][1]); elem->QueryDoubleAttribute("M22", &matrix[2][2]); // Deserialize Offset elem->QueryDoubleAttribute("T0", &translation[0]); elem->QueryDoubleAttribute("T1", &translation[1]); elem->QueryDoubleAttribute("T2", &translation[2]); mitk::AffineTransform3D::Pointer calibration = mitk::AffineTransform3D::New(); calibration->SetMatrix(matrix); calibration->SetTranslation(translation); m_Calibrations[calibName] = calibration; } } std::string mitk::USCombinedModality::GetIdentifierForCurrentCalibration() { return this->GetIdentifierForCurrentProbe() + mitk::USCombinedModality::ProbeAndDepthSeperator + this->GetCurrentDepthValue(); } std::string mitk::USCombinedModality::GetIdentifierForCurrentProbe() { us::ServiceProperties usdeviceProperties = m_UltrasoundDevice->GetServiceProperties(); us::ServiceProperties::const_iterator probeIt = usdeviceProperties.find( mitk::USCombinedModality::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED); // get probe identifier from control interface for probes std::string probeName = mitk::USCombinedModality::DefaultProbeIdentifier; if (probeIt != usdeviceProperties.end()) { probeName = (probeIt->second).ToString(); } // make sure that there is no '/' which would cause problems for TinyXML std::replace(probeName.begin(), probeName.end(), '/', '-'); return probeName; } std::string mitk::USCombinedModality::GetCurrentDepthValue() { us::ServiceProperties usdeviceProperties = m_UltrasoundDevice->GetServiceProperties(); // get string for depth value from the micro service properties std::string depth; us::ServiceProperties::iterator depthIterator = usdeviceProperties.find( mitk::USCombinedModality::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH); if (depthIterator != usdeviceProperties.end()) { depth = depthIterator->second.ToString(); } else { depth = "0"; } return depth; } void mitk::USCombinedModality::RebuildFilterPipeline() { m_LastFilter = m_TrackingDevice; if (m_NumberOfSmoothingValues > 0) { for (unsigned int i = 0; i < m_TrackingDevice->GetNumberOfOutputs(); i++) { m_SmoothingFilter->SetInput(i, m_LastFilter->GetOutput(i)); } m_LastFilter = m_SmoothingFilter; } if (m_DelayCount > 0) { for (unsigned int i = 0; i < m_TrackingDevice->GetNumberOfOutputs(); i++) { m_DelayFilter->SetInput(i, m_LastFilter->GetOutput(i)); } m_LastFilter = m_DelayFilter; } } void mitk::USCombinedModality::RegisterAsMicroservice() { //Get Context us::ModuleContext* context = us::GetModuleContext(); //Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.USCombinedModality", 16); props[US_PROPKEY_ID] = uidGen.GetUID(); props[US_PROPKEY_DEVICENAME] = this->GetName(); props[US_PROPKEY_CLASS] = this->GetDeviceClass(); m_ServiceProperties = props; m_ServiceRegistration = context->RegisterService(this, props); } diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/CMakeLists.txt b/Plugins/org.mitk.gui.qt.lasercontrol/CMakeLists.txt index 114e4aec48..e1e61eaca6 100644 --- a/Plugins/org.mitk.gui.qt.lasercontrol/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.lasercontrol/CMakeLists.txt @@ -1,24 +1,24 @@ project(org_mitk_gui_qt_lasercontrol) if(WIN32) if(MITK_USE_GALIL_HARDWARE) configure_file( ${MITK_GALIL_API_PATH}/dll/x64/gclib.dll ${MITK_BINARY_DIR}/bin/Debug/ COPYONLY ) configure_file( ${MITK_GALIL_API_PATH}/dll/x64/gclibo.dll ${MITK_BINARY_DIR}/bin/Debug/ COPYONLY ) configure_file( ${MITK_GALIL_API_PATH}/dll/x64/gclib.dll ${MITK_BINARY_DIR}/bin/Release/ COPYONLY ) configure_file( ${MITK_GALIL_API_PATH}/dll/x64/gclibo.dll ${MITK_BINARY_DIR}/bin/Release/ COPYONLY ) endif() - if(MITK_USE_OPHIR_PRYO_HARDWARE) + if(MITK_USE_OPHIR_PYRO_HARDWARE) configure_file( ${MITK_OPHIR_API_PATH}/Debug/OphirPyroWrapper.dll ${MITK_BINARY_DIR}/bin/Debug/ COPYONLY ) configure_file( ${MITK_OPHIR_API_PATH}/Release/OphirPyroWrapper.dll ${MITK_BINARY_DIR}/bin/Release/ COPYONLY ) configure_file( ${MITK_OPHIR_API_PATH}/Debug/OphirLMMeasurement.dll ${MITK_BINARY_DIR}/bin/Debug/ COPYONLY ) configure_file( ${MITK_OPHIR_API_PATH}/Release/OphirLMMeasurement.dll ${MITK_BINARY_DIR}/bin/Release/ COPYONLY ) configure_file( ${MITK_OPHIR_API_PATH}/Debug/Interop.OphirLMMeasurementLib.dll ${MITK_BINARY_DIR}/bin/Debug/ COPYONLY ) configure_file( ${MITK_OPHIR_API_PATH}/Release/Interop.OphirLMMeasurementLib.dll ${MITK_BINARY_DIR}/bin/Release/ COPYONLY ) endif() endif() mitk_create_plugin( EXPORT_DIRECTIVE LASERCONTROL_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkQtWidgetsExt MitkPhotoacousticsHardware ) diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/files.cmake b/Plugins/org.mitk.gui.qt.lasercontrol/files.cmake index 55ea3038dc..ebb9b41a94 100644 --- a/Plugins/org.mitk.gui.qt.lasercontrol/files.cmake +++ b/Plugins/org.mitk.gui.qt.lasercontrol/files.cmake @@ -1,37 +1,37 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkLaserControlPluginActivator.cpp QmitkLaserControl.cpp ) set(UI_FILES src/internal/QmitkLaserControlControls.ui ) set(MOC_H_FILES src/internal/mitkLaserControlPluginActivator.h src/internal/QmitkLaserControl.h ) set(CACHED_RESOURCE_FILES - resources/iconLaser.xpm + resources/iconLaserControl.svg plugin.xml ) set(QRC_FILES ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/plugin.xml b/Plugins/org.mitk.gui.qt.lasercontrol/plugin.xml index e480fff300..e2c8781409 100644 --- a/Plugins/org.mitk.gui.qt.lasercontrol/plugin.xml +++ b/Plugins/org.mitk.gui.qt.lasercontrol/plugin.xml @@ -1,12 +1,12 @@ + icon="resources/iconLaserControl.svg" /> diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/resources/iconLaserControl.svg b/Plugins/org.mitk.gui.qt.lasercontrol/resources/iconLaserControl.svg new file mode 100644 index 0000000000..949e311a9f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.lasercontrol/resources/iconLaserControl.svg @@ -0,0 +1,88 @@ + +image/svg+xml \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControlControls.ui b/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControlControls.ui index 2854825c4f..8eb85c86e6 100644 --- a/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControlControls.ui +++ b/Plugins/org.mitk.gui.qt.lasercontrol/src/internal/QmitkLaserControlControls.ui @@ -1,775 +1,775 @@ OPOLaserControlControls 0 0 343 528 0 0 9 QmitkTemplate 9 OPOTEK Phocus Control 10 20 301 431 true 10 Trigger Externally true 10 Init Laser false 10 Start Lamp false 10 Start Laser 11 75 true Laser Status Current Wavelength 10 75 true PreferDefault true 750.0 nm Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Pulse Energy false 10 75 true PreferDefault true 0 mJ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 6900 9500 5 100 7500 Qt::Horizontal QSlider::NoTicks 10000 1 690.000000000000000 950.000000000000000 0.500000000000000 750.000000000000000 nm false Tune 10 50 false Fast Tuning Wavelengths 0 75 16777215 75 10 true Qt::SolidLine 42 50 30 30 λ/nm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 - 690 + 700 - 690 + 710 - 690 + 720 - 690 + 730 - 690 + 740 - 800 + 750 - 800 + 760 - 800 + 770 - 800 + 780 - 800 + 790 800 - 800 + 810 - 800 + 820 - 800 + 830 - 800 + 840 - + 850 - + 860 - + 870 - + 880 - + 890 - + 900 - + 910 - + 920 - + 930 - + 940 false Start Fast Tuning Qt::Vertical 20 40 calibrate OPO 50000 400 24400 diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/files.cmake b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/files.cmake index a9177ae3a3..eabd5d2d68 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/files.cmake +++ b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/files.cmake @@ -1,29 +1,29 @@ set(SRC_CPP_FILES QmitkPAUSViewerView.cpp ) set(INTERNAL_CPP_FILES org_mitk_gui_qt_photoacoustics_pausviewer_Activator.cpp ) set(UI_FILES src/internal/QmitkPAUSViewerViewControls.ui ) set(MOC_H_FILES src/QmitkPAUSViewerView.h src/internal/org_mitk_gui_qt_photoacoustics_pausviewer_Activator.h ) set(CACHED_RESOURCE_FILES - resources/PAUSViewer.png + resources/iconPAUSViewer.svg plugin.xml ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) -endforeach(file ${INTERNAL_CPP_FILES}) \ No newline at end of file +endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/plugin.xml b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/plugin.xml index 915faef1ce..841dacc1b8 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/plugin.xml +++ b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/plugin.xml @@ -1,12 +1,12 @@ + icon="resources/iconPAUSViewer.svg" /> diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/resources/iconPAUSViewer.svg b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/resources/iconPAUSViewer.svg new file mode 100644 index 0000000000..8e87197c19 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.photoacoustics.pausviewer/resources/iconPAUSViewer.svg @@ -0,0 +1,62 @@ + +image/svg+xml \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp index 1d491333e2..a5d3175660 100644 --- a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp @@ -1,697 +1,612 @@ /*=================================================================== 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 #include //Mitk #include #include #include #include #include #include #include #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include #include // Qmitk #include "QmitkUltrasoundSupport.h" // Qt #include #include #include // Ultrasound #include "mitkUSDevice.h" #include "QmitkUSAbstractCustomWidget.h" #include #include #include "usServiceReference.h" #include "internal/org_mitk_gui_qt_ultrasound_Activator.h" #include "mitkNodePredicateDataType.h" #include const std::string QmitkUltrasoundSupport::VIEW_ID = "org.mitk.views.ultrasoundsupport"; void QmitkUltrasoundSupport::SetFocus() { } void QmitkUltrasoundSupport::CreateQtPartControl(QWidget *parent) { //initialize timers m_UpdateTimer = new QTimer(this); m_RenderingTimer2d = new QTimer(this); m_RenderingTimer3d = new QTimer(this); // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); //load persistence data before connecting slots (so no slots are called in this phase...) LoadUISettings(); //connect signals and slots... connect(m_Controls.m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this, SLOT(OnClickedAddNewDevice())); // Change Widget Visibilities connect(m_Controls.m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this->m_Controls.m_NewVideoDeviceWidget, SLOT(CreateNewDevice())); // Init NewDeviceWidget connect(m_Controls.m_ActiveVideoDevices, SIGNAL(ServiceSelectionChanged(us::ServiceReferenceU)), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_RunImageTimer, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_ShowImageStream, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls.m_NewVideoDeviceWidget, SIGNAL(Finished()), this, SLOT(OnNewDeviceWidgetDone())); // After NewDeviceWidget finished editing connect(m_Controls.m_FrameRatePipeline, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FrameRate2d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FrameRate3d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls.m_FreezeButton, SIGNAL(clicked()), this, SLOT(OnClickedFreezeButton())); connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(UpdateImage())); connect(m_RenderingTimer2d, SIGNAL(timeout()), this, SLOT(RenderImage2d())); connect(m_RenderingTimer3d, SIGNAL(timeout()), this, SLOT(RenderImage3d())); connect(m_Controls.m_Update2DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls.m_Update3DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls.m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this, SLOT(OnClickedEditDevice())); //Change Widget Visibilities connect(m_Controls.m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this->m_Controls.m_NewVideoDeviceWidget, SLOT(EditDevice(mitk::USDevice::Pointer))); // Initializations m_Controls.m_NewVideoDeviceWidget->setVisible(false); std::string filter = "(&(" + us::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.UltrasoundDevice)(" + mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE + "=true))"; m_Controls.m_ActiveVideoDevices->Initialize( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, filter); m_Controls.m_ActiveVideoDevices->SetAutomaticallySelectFirstEntry(true); m_FrameCounterPipeline = 0; m_FrameCounter2d = 0; m_FrameCounter3d = 0; m_Controls.tabWidget->setTabEnabled(1, false); } -void QmitkUltrasoundSupport::CreateWindows() -{ - auto pausViewer = GetSite()->GetWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.photoacoustics.pausviewer"); - if (pausViewer != nullptr) - { - m_PausViewerView = dynamic_cast(pausViewer.GetPointer()); - m_PausViewerView->SetPADataStorage(m_PADataStorage); - m_PausViewerView->SetUSDataStorage(m_USDataStorage); - - m_PausViewerView->InitWindows(); - m_PausViewerView->SetUltrasoundReference(&m_PausViewerView); - UpdateLevelWindows(); - - m_ForceRequestUpdateAll = true; - } -} - void QmitkUltrasoundSupport::InitNewNode() { m_Node.push_back(nullptr); auto& Node = m_Node.back(); Node = mitk::DataNode::New(); - Node->SetName("No Data received yet ..."); + char name[30]; + sprintf(name, "US Viewing Stream - Image %d", (unsigned int)(m_Node.size() - 1)); + Node->SetName(name); //create a dummy image (gray values 0..255) for correct initialization of level window, etc. mitk::Image::Pointer dummyImage = mitk::ImageGenerator::GenerateRandomImage(100, 100, 1, 1, 1, 1, 1, 255, 0); Node->SetData(dummyImage); m_OldGeometry = dynamic_cast(dummyImage->GetGeometry()); - // Add to Control Datastorage this->GetDataStorage()->Add(Node); - - // Add to Display Datastorage - if (m_Node.size() > 1) - m_USDataStorage->Add(Node); - else if (m_Node.size() == 1) - m_PADataStorage->Add(Node); } void QmitkUltrasoundSupport::DestroyLastNode() { auto& Node = m_Node.back(); - // Remove from Control Datastorage - this->GetDataStorage()->Remove(Node); - - // Remove from Display Datastorage - if (m_Node.size() > 1) - m_USDataStorage->Remove(Node); - else if (m_Node.size() == 1) - m_PADataStorage->Remove(Node); + this->GetDataStorage()->Remove(Node); // clean up Node->ReleaseData(); m_Node.pop_back(); } void QmitkUltrasoundSupport::UpdateLevelWindows() { mitk::LevelWindow levelWindow; - if (m_Node.size() > 1) + if (m_Node.size() > 0) { for (unsigned int index = 0; index < m_AmountOfOutputs; ++index) { - m_Node.at(index)->GetLevelWindow(levelWindow); - if (!m_curOutput.at(index)->IsEmpty()) - //levelWindow.SetWindowBounds(0, 4 * (index+1)); - levelWindow.SetToImageRange(m_curOutput.at(index)); - m_Node.at(index)->SetLevelWindow(levelWindow); + m_Node[index]->GetLevelWindow(levelWindow); + if (!m_curOutput[index]->IsEmpty()) + levelWindow.SetToImageRange(m_curOutput[index]); + m_Node[index]->SetLevelWindow(levelWindow); } } - else if (m_Node.size() == 1) - { - m_Node.back()->GetLevelWindow(levelWindow); - if (!m_curOutput.at(0)->IsEmpty()) - levelWindow.SetToImageRange(m_curOutput.at(0)); - m_Node.back()->SetLevelWindow(levelWindow); - } } void QmitkUltrasoundSupport::SetColormap(mitk::DataNode::Pointer node, mitk::LookupTable::LookupTableType type) { - // consider removing this, unused for now, and probably forever - mitk::LookupTable::Pointer lookupTable = mitk::LookupTable::New(); mitk::LookupTableProperty::Pointer lookupTableProperty = mitk::LookupTableProperty::New(); lookupTable->SetType(type); lookupTableProperty->SetLookupTable(lookupTable); node->SetProperty("LookupTable", lookupTableProperty); mitk::RenderingModeProperty::Pointer renderingMode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); } void QmitkUltrasoundSupport::OnClickedAddNewDevice() { m_Controls.m_NewVideoDeviceWidget->setVisible(true); m_Controls.m_DeviceManagerWidget->setVisible(false); m_Controls.m_Headline->setText("Add New Video Device:"); m_Controls.m_WidgetActiveDevices->setVisible(false); } void QmitkUltrasoundSupport::OnClickedEditDevice() { m_Controls.m_NewVideoDeviceWidget->setVisible(true); m_Controls.m_DeviceManagerWidget->setVisible(false); m_Controls.m_WidgetActiveDevices->setVisible(false); m_Controls.m_Headline->setText("Edit Video Device:"); } void QmitkUltrasoundSupport::UpdateAmountOfOutputs() { - // Update the amount of Nodes; there should be one Node for every slide that is set. Note that we must check whether the slices are set, - // just using the m_Image->dimension(3) will produce nullpointers on slices of the image that were not set - bool isSet = true; - m_AmountOfOutputs = 0; - while (isSet) { - isSet = m_Image->IsSliceSet(m_AmountOfOutputs); - if (isSet) - ++m_AmountOfOutputs; - } - // correct the amount of Nodes to display data while (m_Node.size() < m_AmountOfOutputs) { InitNewNode(); } while (m_Node.size() > m_AmountOfOutputs) { DestroyLastNode(); } // correct the amount of image outputs that we feed the nodes with while (m_curOutput.size() < m_AmountOfOutputs) { m_curOutput.push_back(mitk::Image::New()); - // initialize the slice images as 2d images with the size of m_Images - unsigned int* dimOld = m_Image->GetDimensions(); - unsigned int dim[2] = { dimOld[0], dimOld[1] }; - m_curOutput.back()->Initialize(m_Image->GetPixelType(), 2, dim); + unsigned int dim[3] = { 2, 2, 1}; + m_curOutput.back()->Initialize(mitk::MakeScalarPixelType(), 3, dim); } while (m_curOutput.size() > m_AmountOfOutputs) { m_curOutput.pop_back(); } } void QmitkUltrasoundSupport::UpdateImage() { - if(m_Controls.m_ShowImageStream->isChecked()) + if (m_Controls.m_ShowImageStream->isChecked()) { m_Device->Modified(); m_Device->Update(); // Update device - m_Image = m_Device->GetOutput(); - // get the Image data to display - UpdateAmountOfOutputs(); - // create as many Nodes and Outputs as there are slices in m_Image - if (m_AmountOfOutputs == 0) return; // if there is no image to be displayed, skip the rest of this method for (unsigned int index = 0; index < m_AmountOfOutputs; ++index) { - if (m_curOutput.at(index)->GetDimension(0) != m_Image->GetDimension(0) || - m_curOutput.at(index)->GetDimension(1) != m_Image->GetDimension(1) || - m_curOutput.at(index)->GetDimension(2) != m_Image->GetDimension(2) || - m_curOutput.at(index)->GetPixelType() != m_Image->GetPixelType()) - { - unsigned int* dimOld = m_Image->GetDimensions(); - unsigned int dim[2] = { dimOld[0], dimOld[1]}; - m_curOutput.at(index)->Initialize(m_Image->GetPixelType(), 2, dim); - // if we switched image resolution or type the outputs must be reinitialized! - } + auto image = m_Device->GetOutput(index); + // get the Image data to display - if (!m_Image->IsEmpty()) + if (!image->IsEmpty()) { - mitk::ImageReadAccessor inputReadAccessor(m_Image, m_Image->GetSliceData(index,0,0,nullptr,mitk::Image::ReferenceMemory)); - // just reference the slices, to get a small performance gain - m_curOutput.at(index)->SetSlice(inputReadAccessor.GetData()); - m_curOutput.at(index)->GetGeometry()->SetIndexToWorldTransform(m_Image->GetSlicedGeometry()->GetIndexToWorldTransform()); - // Update the image Output with seperate slices + if (m_curOutput[index]->GetDimension(0) != image->GetDimension(0) || + m_curOutput[index]->GetDimension(1) != image->GetDimension(1) || + m_curOutput[index]->GetDimension(2) != image->GetDimension(2) || + m_curOutput[index]->GetPixelType() != image->GetPixelType()) + { + m_curOutput[index]->Initialize(image->GetPixelType(), image->GetDimension(), image->GetDimensions()); + // if we switched image resolution or type the outputs must be reinitialized! + } + + // copy the contents of the device output into the image the data storage will display + mitk::ImageReadAccessor inputReadAccessor(image); + m_curOutput[index]->SetImportVolume(inputReadAccessor.GetData()); + m_curOutput[index]->GetGeometry()->SetIndexToWorldTransform(image->GetSlicedGeometry()->GetIndexToWorldTransform()); } - if (m_curOutput.at(index)->IsEmpty()) + if (m_curOutput[index]->IsEmpty()) { - m_Node.at(index)->SetName("No Data received yet ..."); // create a noise image for correct initialization of level window, etc. mitk::Image::Pointer randomImage = mitk::ImageGenerator::GenerateRandomImage(32, 32, 1, 1, 1, 1, 1, 255, 0); - m_Node.at(index)->SetData(randomImage); - m_curOutput.at(index)->SetGeometry(randomImage->GetGeometry()); + m_Node[index]->SetData(randomImage); + m_curOutput[index]->SetGeometry(randomImage->GetGeometry()); } else { - char name[30]; - sprintf(name, "US Viewing Stream - Image %d", index); - m_Node.at(index)->SetName(name); - m_Node.at(index)->SetData(m_curOutput.at(index)); - // set the name of the Output + m_Node[index]->SetData(m_curOutput[index]); } } float eps = 0.000001f; - // if the geometry changed: reinitialize the ultrasound image. we use the m_curOutput.at(0) to readjust the geometry + // if the geometry changed: reinitialize the ultrasound image. we use the m_curOutput[0] to readjust the geometry if (((m_OldGeometry.IsNotNull()) && - (m_curOutput.at(0)->GetGeometry() != nullptr)) && + (m_curOutput[0]->GetGeometry() != nullptr)) && ( - (abs(m_OldGeometry->GetSpacing()[0] - m_curOutput.at(0)->GetGeometry()->GetSpacing()[0]) > eps )|| - (abs(m_OldGeometry->GetSpacing()[1] - m_curOutput.at(0)->GetGeometry()->GetSpacing()[1]) > eps )|| - (abs(m_OldGeometry->GetCenter()[0] - m_curOutput.at(0)->GetGeometry()->GetCenter()[0]) > eps) || - (abs(m_OldGeometry->GetCenter()[1] - m_curOutput.at(0)->GetGeometry()->GetCenter()[1]) > eps) || + (abs(m_OldGeometry->GetSpacing()[0] - m_curOutput[0]->GetGeometry()->GetSpacing()[0]) > eps) || + (abs(m_OldGeometry->GetSpacing()[1] - m_curOutput[0]->GetGeometry()->GetSpacing()[1]) > eps) || + (abs(m_OldGeometry->GetCenter()[0] - m_curOutput[0]->GetGeometry()->GetCenter()[0]) > eps) || + (abs(m_OldGeometry->GetCenter()[1] - m_curOutput[0]->GetGeometry()->GetCenter()[1]) > eps) || m_ForceRequestUpdateAll)) { - MITK_INFO << "now"; mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); - if ((renderWindow != nullptr) && (m_curOutput.at(0)->GetTimeGeometry()->IsValid()) && (m_Controls.m_ShowImageStream->isChecked())) + if ((renderWindow != nullptr) && (m_curOutput[0]->GetTimeGeometry()->IsValid()) && (m_Controls.m_ShowImageStream->isChecked())) { renderWindow->GetRenderingManager()->InitializeViews( - m_curOutput.at(0)->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); + m_curOutput[0]->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); renderWindow->GetRenderingManager()->RequestUpdateAll(); } - m_CurrentImageWidth = m_curOutput.at(0)->GetDimension(0); - m_CurrentImageHeight = m_curOutput.at(0)->GetDimension(1); - m_OldGeometry = dynamic_cast(m_curOutput.at(0)->GetGeometry()); + m_CurrentImageWidth = m_curOutput[0]->GetDimension(0); + m_CurrentImageHeight = m_curOutput[0]->GetDimension(1); + m_OldGeometry = dynamic_cast(m_curOutput[0]->GetGeometry()); UpdateLevelWindows(); m_ForceRequestUpdateAll = false; } } //Update frame counter m_FrameCounterPipeline++; if (m_FrameCounterPipeline >= 10) { // compute framerate of pipeline update int nMilliseconds = m_Clock.restart(); int fps = 10000.0f / (nMilliseconds); m_FPSPipeline = fps; m_FrameCounterPipeline = 0; // display lowest framerate in UI int lowestFPS = m_FPSPipeline; if (m_Controls.m_Update2DView->isChecked() && (m_FPS2d < lowestFPS)) { lowestFPS = m_FPS2d; } if (m_Controls.m_Update3DView->isChecked() && (m_FPS3d < lowestFPS)) { lowestFPS = m_FPS3d; } m_Controls.m_FramerateLabel->setText("Current Framerate: " + QString::number(lowestFPS) + " FPS"); } } void QmitkUltrasoundSupport::RenderImage2d() { if (!m_Controls.m_Update2DView->isChecked()) return; - if (m_PausViewerView != nullptr) - { - auto renderingManager = mitk::RenderingManager::GetInstance(); - renderingManager->RequestUpdate(m_PausViewerView->GetPARenderWindow()); - renderingManager->RequestUpdate(m_PausViewerView->GetUSRenderWindow()); - } - else - { - CreateWindows(); // try to check whether the PausViwer has been opened by now - } - //mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); //renderWindow->GetRenderingManager()->RequestUpdate(mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetRenderWindow()); - // TODO: figure out how to proceed with the standard display - //this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); + + this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); m_FrameCounter2d++; if (m_FrameCounter2d >= 10) { // compute framerate of 2d render window update int nMilliseconds = m_Clock2d.restart(); int fps = 10000.0f / (nMilliseconds); m_FPS2d = fps; m_FrameCounter2d = 0; } } void QmitkUltrasoundSupport::RenderImage3d() { if (!m_Controls.m_Update3DView->isChecked()) return; this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS); m_FrameCounter3d++; if (m_FrameCounter3d >= 10) { // compute framerate of 2d render window update int nMilliseconds = m_Clock3d.restart(); int fps = 10000.0f / (nMilliseconds); m_FPS3d = fps; m_FrameCounter3d = 0; } } void QmitkUltrasoundSupport::OnChangedFramerateLimit() { StopTimers(); int intervalPipeline = (1000 / m_Controls.m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls.m_FrameRate2d->value()); int interval3D = (1000 / m_Controls.m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); } void QmitkUltrasoundSupport::OnClickedFreezeButton() { if (m_Device.IsNull()) { MITK_WARN("UltrasoundSupport") << "Freeze button clicked though no device is selected."; return; } if (m_Device->GetIsFreezed()) { m_Device->SetIsFreezed(false); m_Controls.m_FreezeButton->setText("Freeze"); } else { m_Device->SetIsFreezed(true); m_Controls.m_FreezeButton->setText("Start Viewing Again"); } } void QmitkUltrasoundSupport::OnChangedActiveDevice() { //clean up, delete nodes and stop timer StopTimers(); this->RemoveControlWidgets(); for (auto& Node : m_Node) { this->GetDataStorage()->Remove(Node); Node->ReleaseData(); } m_Node.clear(); - // disconnect from the PausViewer - m_PausViewerView = nullptr; - //get current device, abort if it is invalid m_Device = m_Controls.m_ActiveVideoDevices->GetSelectedService(); if (m_Device.IsNull()) { m_Controls.tabWidget->setTabEnabled(1, false); return; } //create the widgets for this device and enable the widget tab this->CreateControlWidgets(); m_Controls.tabWidget->setTabEnabled(1, true); //start timer if (m_Controls.m_RunImageTimer->isChecked()) { int intervalPipeline = (1000 / m_Controls.m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls.m_FrameRate2d->value()); int interval3D = (1000 / m_Controls.m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); m_Controls.m_TimerWidget->setEnabled(true); } else { m_Controls.m_TimerWidget->setEnabled(false); } - // connect to PausViewer and Set it up - CreateWindows(); + m_AmountOfOutputs = m_Device->GetNumberOfIndexedOutputs(); + // create as many nodes and image outputs as needed + UpdateAmountOfOutputs(); } void QmitkUltrasoundSupport::OnNewDeviceWidgetDone() { m_Controls.m_NewVideoDeviceWidget->setVisible(false); m_Controls.m_DeviceManagerWidget->setVisible(true); m_Controls.m_Headline->setText("Ultrasound Devices:"); m_Controls.m_WidgetActiveDevices->setVisible(true); } void QmitkUltrasoundSupport::CreateControlWidgets() { m_ControlProbesWidget = new QmitkUSControlsProbesWidget(m_Device->GetControlInterfaceProbes(), m_Controls.m_ToolBoxControlWidgets); m_Controls.probesWidgetContainer->addWidget(m_ControlProbesWidget); // create b mode widget for current device m_ControlBModeWidget = new QmitkUSControlsBModeWidget(m_Device->GetControlInterfaceBMode(), m_Controls.m_ToolBoxControlWidgets); if (m_Device->GetControlInterfaceBMode()) { m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlBModeWidget, "B Mode Controls"); //m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } // create doppler widget for current device m_ControlDopplerWidget = new QmitkUSControlsDopplerWidget(m_Device->GetControlInterfaceDoppler(), m_Controls.m_ToolBoxControlWidgets); if (m_Device->GetControlInterfaceDoppler()) { m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlDopplerWidget, "Doppler Controls"); //m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { std::string filter = "(org.mitk.services.UltrasoundCustomWidget.deviceClass=" + m_Device->GetDeviceClass() + ")"; QString interfaceName = QString::fromStdString(us_service_interface_iid()); m_CustomWidgetServiceReference = pluginContext->getServiceReferences(interfaceName, QString::fromStdString(filter)); if (m_CustomWidgetServiceReference.size() > 0) { m_ControlCustomWidget = pluginContext->getService (m_CustomWidgetServiceReference.at(0))->CloneForQt(m_Controls.tab2); m_ControlCustomWidget->SetDevice(m_Device); m_Controls.m_ToolBoxControlWidgets->addItem(m_ControlCustomWidget, "Custom Controls"); } else { m_Controls.m_ToolBoxControlWidgets->addItem(new QWidget(m_Controls.m_ToolBoxControlWidgets), "Custom Controls"); m_Controls.m_ToolBoxControlWidgets->setItemEnabled(m_Controls.m_ToolBoxControlWidgets->count() - 1, false); } } // select first enabled control widget for (int n = 0; n < m_Controls.m_ToolBoxControlWidgets->count(); ++n) { if (m_Controls.m_ToolBoxControlWidgets->isItemEnabled(n)) { m_Controls.m_ToolBoxControlWidgets->setCurrentIndex(n); break; } } } void QmitkUltrasoundSupport::RemoveControlWidgets() { if (!m_ControlProbesWidget) { return; } //widgets do not exist... nothing to do // remove all control widgets from the tool box widget while (m_Controls.m_ToolBoxControlWidgets->count() > 0) { m_Controls.m_ToolBoxControlWidgets->removeItem(0); } // remove probes widget (which is not part of the tool box widget) m_Controls.probesWidgetContainer->removeWidget(m_ControlProbesWidget); delete m_ControlProbesWidget; m_ControlProbesWidget = 0; delete m_ControlBModeWidget; m_ControlBModeWidget = 0; delete m_ControlDopplerWidget; m_ControlDopplerWidget = 0; // delete custom widget if it is present if (m_ControlCustomWidget) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); delete m_ControlCustomWidget; m_ControlCustomWidget = 0; if (m_CustomWidgetServiceReference.size() > 0) { pluginContext->ungetService(m_CustomWidgetServiceReference.at(0)); } } } void QmitkUltrasoundSupport::OnDeciveServiceEvent(const ctkServiceEvent event) { if (m_Device.IsNull() || event.getType() != static_cast(us::ServiceEvent::MODIFIED)) { return; } ctkServiceReference service = event.getServiceReference(); if (m_Device->GetManufacturer() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER)).toString().toStdString() && m_Device->GetName() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME)).toString().toStdString()) { return; } if (!m_Device->GetIsActive() && m_UpdateTimer->isActive()) { StopTimers(); } if (m_CurrentDynamicRange != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble()) { m_CurrentDynamicRange = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble(); - - // update level window for the current dynamic range - mitk::LevelWindow levelWindow; - for (auto& Node : m_Node) - { - Node->GetLevelWindow(levelWindow); - levelWindow.SetAuto(m_Image, true, true); - levelWindow.SetWindowBounds(55, 125,true); - Node->SetLevelWindow(levelWindow); - } + UpdateLevelWindows(); } } QmitkUltrasoundSupport::QmitkUltrasoundSupport() : m_ControlCustomWidget(0), m_ControlBModeWidget(0), m_ControlProbesWidget(0), m_ImageAlreadySetToNode(false), - m_CurrentImageWidth(0), m_CurrentImageHeight(0), m_AmountOfOutputs(0), - m_PADataStorage(mitk::StandaloneDataStorage::New()), m_USDataStorage(mitk::StandaloneDataStorage::New()), m_ForceRequestUpdateAll(false) + m_CurrentImageWidth(0), m_CurrentImageHeight(0), m_AmountOfOutputs(0), m_ForceRequestUpdateAll(false) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeciveServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } QmitkUltrasoundSupport::~QmitkUltrasoundSupport() { try { StoreUISettings(); StopTimers(); // Get all active devicesand deactivate them to prevent freeze for (auto device : this->m_Controls.m_ActiveVideoDevices->GetAllServices()) { if (device != nullptr && device->GetIsActive()) { device->Deactivate(); device->Disconnect(); } } } catch (std::exception &e) { MITK_ERROR << "Exception during call of destructor! Message: " << e.what(); } } void QmitkUltrasoundSupport::StoreUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); settings.setValue("DisplayImage", QVariant(m_Controls.m_ShowImageStream->isChecked())); settings.setValue("RunImageTimer", QVariant(m_Controls.m_RunImageTimer->isChecked())); settings.setValue("Update2DView", QVariant(m_Controls.m_Update2DView->isChecked())); settings.setValue("Update3DView", QVariant(m_Controls.m_Update3DView->isChecked())); settings.setValue("UpdateRatePipeline", QVariant(m_Controls.m_FrameRatePipeline->value())); settings.setValue("UpdateRate2d", QVariant(m_Controls.m_FrameRate2d->value())); settings.setValue("UpdateRate3d", QVariant(m_Controls.m_FrameRate3d->value())); settings.endGroup(); } void QmitkUltrasoundSupport::LoadUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); m_Controls.m_ShowImageStream->setChecked(settings.value("DisplayImage", true).toBool()); m_Controls.m_RunImageTimer->setChecked(settings.value("RunImageTimer", true).toBool()); m_Controls.m_Update2DView->setChecked(settings.value("Update2DView", true).toBool()); m_Controls.m_Update3DView->setChecked(settings.value("Update3DView", true).toBool()); m_Controls.m_FrameRatePipeline->setValue(settings.value("UpdateRatePipeline", 50).toInt()); m_Controls.m_FrameRate2d->setValue(settings.value("UpdateRate2d", 20).toInt()); m_Controls.m_FrameRate3d->setValue(settings.value("UpdateRate3d", 5).toInt()); settings.endGroup(); } void QmitkUltrasoundSupport::StartTimers() { m_UpdateTimer->start(); if (m_Controls.m_Update2DView->isChecked()) { m_RenderingTimer2d->start(); } if (m_Controls.m_Update3DView->isChecked()) { m_RenderingTimer3d->start(); } } void QmitkUltrasoundSupport::StopTimers() { m_UpdateTimer->stop(); m_RenderingTimer2d->stop(); m_RenderingTimer3d->stop(); } void QmitkUltrasoundSupport::SetTimerIntervals(int intervalPipeline, int interval2D, int interval3D) { m_UpdateTimer->setInterval(intervalPipeline); m_RenderingTimer2d->setInterval(interval2D); m_RenderingTimer3d->setInterval(interval3D); } diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.h b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.h index a404b8851d..6568d405f9 100644 --- a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.h +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.h @@ -1,186 +1,174 @@ /*=================================================================== 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 QmitkUltrasoundSupport_h #define QmitkUltrasoundSupport_h #include #include #include #include #include "ui_QmitkUltrasoundSupportControls.h" #include "QmitkUSAbstractCustomWidget.h" #include "QmitkUSControlsBModeWidget.h" #include "QmitkUSControlsDopplerWidget.h" #include "QmitkUSControlsProbesWidget.h" #include #include "QmitkRenderWindow.h" #include #include #include -#include #include #include /*! \brief UltrasoundSupport This plugin provides functionality to manage Ultrasound devices, create video devices and to view device images. \ingroup ${plugin_target}_internal */ class QmitkUltrasoundSupport : 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: virtual void SetFocus() override; static const std::string VIEW_ID; virtual void CreateQtPartControl(QWidget *parent) override; QmitkUltrasoundSupport(); virtual ~QmitkUltrasoundSupport(); public slots: /* * \brief This is called when the newDeviceWidget is closed */ void OnNewDeviceWidgetDone(); protected slots: void OnClickedAddNewDevice(); void OnChangedFramerateLimit(); void OnClickedEditDevice(); /* *\brief Called, when the selection in the list of the active devices changes. */ void OnChangedActiveDevice(); void OnClickedFreezeButton(); void OnDeciveServiceEvent(const ctkServiceEvent event); /* * \brief This is the main imaging loop that updates the image and is called regularily during the imaging process */ void UpdateImage(); void RenderImage2d(); void RenderImage3d(); void StartTimers(); void StopTimers(); protected: void CreateControlWidgets(); void RemoveControlWidgets(); - void CreateWindows(); - - QmitkPAUSViewerView* m_PausViewerView; - Ui::UltrasoundSupportControls m_Controls; QmitkUSAbstractCustomWidget* m_ControlCustomWidget; QmitkUSControlsBModeWidget* m_ControlBModeWidget; QmitkUSControlsDopplerWidget* m_ControlDopplerWidget; QmitkUSControlsProbesWidget* m_ControlProbesWidget; bool m_ImageAlreadySetToNode; unsigned int m_CurrentImageWidth; unsigned int m_CurrentImageHeight; /** Keeps track of the amount of output Nodes*/ unsigned int m_AmountOfOutputs; - mitk::StandaloneDataStorage::Pointer m_PADataStorage; - mitk::StandaloneDataStorage::Pointer m_USDataStorage; - /** The device that is currently used to aquire images */ mitk::USDevice::Pointer m_Device; void SetTimerIntervals(int intervalPipeline, int interval2D, int interval3D); /** This timer triggers periodic updates to the pipeline */ QTimer* m_UpdateTimer; QTimer* m_RenderingTimer2d; QTimer* m_RenderingTimer3d; /** These clocks are used to compute the framerate in the methods DisplayImage(),RenderImage2d() and RenderImage3d(). */ QTime m_Clock; QTime m_Clock2d; QTime m_Clock3d; /** A counter to comute the framerate. */ int m_FrameCounterPipeline; int m_FrameCounter2d; int m_FrameCounter3d; int m_FPSPipeline, m_FPS2d, m_FPS3d; /** Stores the properties of some QWidgets (and the tool storage file name) to QSettings.*/ void StoreUISettings(); /** Loads the properties of some QWidgets (and the tool storage file name) from QSettings.*/ void LoadUISettings(); /** The nodes that we feed images into.*/ std::vector m_Node; /** Adds a new node to the m_Nodes vector*/ void InitNewNode(); /** Destroys the last node in the m_Nodes vector */ void DestroyLastNode(); /** Checks the amount of slices in the image from the USDevice and creates as many Nodes as there are slices */ void UpdateAmountOfOutputs(); /** This function just checks how many nodes there are currently and sets the laser image to a jet transparent colormap. */ void UpdateLevelWindows(); bool m_ForceRequestUpdateAll; void SetColormap(mitk::DataNode::Pointer node, mitk::LookupTable::LookupTableType type); - /** The image that holds all data given by the USDevice.*/ - mitk::Image::Pointer m_Image; - - /** The seperated slices from m_Image */ std::vector m_curOutput; /** The old geometry of m_Image. It is needed to check if the geometry changed (e.g. because * the zoom factor was modified) and the image needs to be reinitialized. */ mitk::SlicedGeometry3D::Pointer m_OldGeometry; QList m_CustomWidgetServiceReference; double m_CurrentDynamicRange; }; #endif // UltrasoundSupport_h