diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..61b57594fa --- /dev/null +++ b/.gitattributes @@ -0,0 +1,64 @@ +.git* export-ignore +.hooks* export-ignore +.mailmap export-ignore + +# Set general file size limit on all files +* hooks.MaxObjectKiB=1024 + +*.bat -crlf +*.bin -crlf +*.blend -crlf +*.bmp -crlf +*.cpt -crlf +*.gif -crlf +*.icns -crlf +*.ico -crlf +*.jpeg -crlf +*.jpg -crlf +*.mha -crlf +*.odg -crlf +*.pbxproj -crlf +*.pdf -crlf +*.plist -crlf +*.png -crlf +*.ppt -crlf +*.pptx -crlf +*.raw -crlf +*.vtk -crlf +*.xpm -crlf +*.sh crlf=input +*.sh.in crlf=input +configure crlf=input +cvsrmvend crlf=input +imcp crlf=input +imglob crlf=input +imln crlf=input +immv crlf=input +imrm crlf=input +imtest crlf=input +install-sh crlf=input +newalpha crlf=input +newversion crlf=input +remove_ext crlf=input +vxl_doxy.pl crlf=input +zap.pl crlf=input + +*.c whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.cpp whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.h whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.cxx whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.hxx whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.txx whitespace=tab-in-indent,no-lf-at-eof copyright=mitk-license hooks.style=KWStyle,uncrustify +*.txt whitespace=tab-in-indent,no-lf-at-eof +*.cmake whitespace=tab-in-indent,no-lf-at-eof + +# The Microservices use an apache copyright +Core/Code/CppMicroServices/*.c copyright=apache-license +Core/Code/CppMicroServices/*.cpp copyright=apache-license +Core/Code/CppMicroServices/*.h copyright=apache-license + +# There is no need to check files in the utilities directory for copyright +Utilities/* -copyright + +# ExternalData content links must have LF newlines +*.md5 crlf=input diff --git a/CMake/mitkMacroInstall.cmake b/CMake/mitkMacroInstall.cmake index e9c4c7499b..53f84fb32d 100644 --- a/CMake/mitkMacroInstall.cmake +++ b/CMake/mitkMacroInstall.cmake @@ -1,125 +1,123 @@ # # MITK specific install macro # # On Mac everything is installed for each bundle listed in MACOSX_BUNDLE_NAMES # by replacing the DESTINATION parameter. Everything else is passed to the CMake INSTALL command # # Usage: MITK_INSTALL( ) # macro(MITK_INSTALL) set(ARGS ${ARGN}) set(install_directories "") list(FIND ARGS DESTINATION _destination_index) # set(_install_DESTINATION "") if(_destination_index GREATER -1) message(SEND_ERROR "MITK_INSTALL macro must not be called with a DESTINATION parameter.") ### This code was a try to replace a given DESTINATION #math(EXPR _destination_index ${_destination_index} + 1) #list(GET ARGS ${_destination_index} _install_DESTINATION) #string(REGEX REPLACE ^bin "" _install_DESTINATION ${_install_DESTINATION}) else() if(NOT MACOSX_BUNDLE_NAMES) install(${ARGS} DESTINATION bin) else() foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) install(${ARGS} DESTINATION ${bundle_name}.app/Contents/MacOS/${_install_DESTINATION}) endforeach() endif() endif() endmacro() # Fix _target_location # This is used in several install macros macro(_fixup_target) install(CODE " macro(gp_item_default_embedded_path_override item default_embedded_path_var) get_filename_component(_item_name \"\${item}\" NAME) get_filename_component(_item_path \"\${item}\" PATH) # We have to fix all path references to build trees for plugins if(NOT _item_path MATCHES \"\${CMAKE_INSTALL_PREFIX}/${_bundle_dest_dir}\") # item with relative path or embedded path pointing to some build dir set(full_path \"full_path-NOTFOUND\") file(GLOB_RECURSE full_path \${CMAKE_INSTALL_PREFIX}/${_bundle_dest_dir}/\${_item_name} ) get_filename_component(_item_path \"\${full_path}\" PATH) endif() if(_item_path STREQUAL \"\${CMAKE_INSTALL_PREFIX}/${_bundle_dest_dir}/plugins\" OR _item_name MATCHES \"liborg\" # this is for legacy BlueBerry bundle support ) # Only fix plugins message(\"override: \${item}\") message(\"found file: \${_item_path}/\${_item_name}\") if(APPLE) string(REPLACE \${CMAKE_INSTALL_PREFIX}/${_bundle_dest_dir} @executable_path \${default_embedded_path_var} \"\${_item_path}\" ) else() set(\${default_embedded_path_var} \"\${_item_path}\") endif() message(\"override result: \${\${default_embedded_path_var}}\") endif() endmacro(gp_item_default_embedded_path_override) macro(gp_resolved_file_type_override file type) if(NOT APPLE) get_filename_component(_file_path \"\${file}\" PATH) get_filename_component(_file_name \"\${file}\" NAME) if(_file_path MATCHES \"^\${CMAKE_INSTALL_PREFIX}\") set(\${type} \"local\") endif() if(_file_name MATCHES gdiplus) set(\${type} \"system\") endif(_file_name MATCHES gdiplus) endif() endmacro(gp_resolved_file_type_override) - if(NOT APPLE) - if(UNIX OR MINGW) - macro(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var) - if(\${item} MATCHES \"blueberry_osgi\") - get_filename_component(_item_name \${item} NAME) - set(\${resolved_item_var} \"\${exepath}/plugins/\${_item_name}\") - set(\${resolved_var} 1) - endif() - endmacro() - endif() + if(UNIX OR MINGW) + macro(gp_resolve_item_override context item exepath dirs resolved_item_var resolved_var) + if(\${item} MATCHES \"blueberry_osgi\") + get_filename_component(_item_name \${item} NAME) + set(\${resolved_item_var} \"\${exepath}/plugins/\${_item_name}\") + set(\${resolved_var} 1) + endif() + endmacro() endif() if(\"${_install_GLOB_PLUGINS}\" STREQUAL \"TRUE\") file(GLOB_RECURSE GLOBBED_BLUEBERRY_PLUGINS # glob for all blueberry bundles of this application \"\${CMAKE_INSTALL_PREFIX}/${_bundle_dest_dir}/liborg*${CMAKE_SHARED_LIBRARY_SUFFIX}\") endif() file(GLOB_RECURSE GLOBBED_QT_PLUGINS # glob for Qt plugins \"\${CMAKE_INSTALL_PREFIX}/${${_target_location}_qt_plugins_install_dir}/plugins/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") # use custom version of BundleUtilities message(\"globbed plugins: \${GLOBBED_QT_PLUGINS} \${GLOBBED_BLUEBERRY_PLUGINS}\") set(PLUGIN_DIRS) set(PLUGINS ${_install_PLUGINS} \${GLOBBED_QT_PLUGINS} \${GLOBBED_BLUEBERRY_PLUGINS}) if(PLUGINS) list(REMOVE_DUPLICATES PLUGINS) endif(PLUGINS) foreach(_plugin \${GLOBBED_BLUEBERRY_PLUGINS}) get_filename_component(_pluginpath \${_plugin} PATH) list(APPEND PLUGIN_DIRS \${_pluginpath}) endforeach(_plugin) set(DIRS ${DIRS}) list(APPEND DIRS \${PLUGIN_DIRS}) list(REMOVE_DUPLICATES DIRS) # use custom version of BundleUtilities set(CMAKE_MODULE_PATH ${MITK_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH} ) include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${_target_location}\" \"\${PLUGINS}\" \"\${DIRS}\") ") endmacro() diff --git a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp index 58dd938f99..3f1d857d5d 100644 --- a/Core/Code/Algorithms/mitkExtractSliceFilter.cpp +++ b/Core/Code/Algorithms/mitkExtractSliceFilter.cpp @@ -1,591 +1,591 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkExtractSliceFilter.h" #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice* reslicer ){ if(reslicer == NULL){ m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = NULL; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; } mitk::ExtractSliceFilter::~ExtractSliceFilter(){ m_ResliceTransform = NULL; m_WorldGeometry = NULL; delete [] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation(){ //TODO try figure out how to set the specs of the slice before it is actually extracted /*Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; unsigned int dimensions[2]; dimensions[0] = m_WorldGeometry->GetExtent(0); dimensions[1] = m_WorldGeometry->GetExtent(1); output->Initialize(input->GetPixelType(), 2, dimensions, 1);*/ } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion(){ //As we want all pixel information fo the image in our plane, the requested region //is set to the largest possible region in the image. //This is needed because an oblique plane has a larger extent then the image //and the in pipeline it is checked via PropagateResquestedRegion(). But the //extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType* mitk::ExtractSliceFilter::GetOutputSpacing(){ return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData(){ mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if(!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeSlicedGeometry *inputTimeGeometry = this->GetInput()->GetTimeSlicedGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->GetTimeSteps() == 0 ) ) { itkWarningMacro(<<"Error reading input image TimeSlicedGeometry."); return; } // is it a valid timeStep? if ( inputTimeGeometry->IsValidTime( m_TimeStep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<< m_TimeStep ); return; } // check if there is something to display. if ( ! input->IsVolumeSet( m_TimeStep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<< m_TimeStep ); return; } /*================#BEGIN setup vtkImageRslice properties================*/ Point3D origin; Vector3D right, bottom, normal; double widthInMM, heightInMM; Vector2D extent; const PlaneGeometry* planeGeometry = dynamic_cast(m_WorldGeometry); if ( planeGeometry != NULL ) { //if the worldGeomatry is a PlaneGeometry everthing is straight forward origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometry3D( m_TimeStep )->WorldToIndex( right, rightInIndex ); inputTimeGeometry->GetGeometry3D( m_TimeStep )->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += right * ( m_OutPutSpacing[0] * 0.5 ); origin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if(m_ResliceTransform != NULL) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D), // else the background of the image turns out gray m_Reslicer->SetBackgroundLevel( -32768 ); } else{ //Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); if(abstractGeometry != NULL) { m_ResliceTransform = abstractGeometry; extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometry3D( m_TimeStep )->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); composedResliceTransform->UnRegister( NULL ); // decrease RC // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -1023 ); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if(m_ResliceTransform != NULL){ //if the resliceTransform is set the reslice axis are recalculated. //Thus the geometry information is not fitting. Therefor a unitSpacingFilter //is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New() ; unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInput( input->GetVtkImageData(m_TimeStep) ); m_Reslicer->SetInput(unitSpacingImageFilter->GetOutput() ); } else { //if no tranform is set the image can be used directly m_Reslicer->SetInput(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ //ResliceAxesOrigin is the ancor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); //the cosines define the plane: x and y are the direction vectors, n is the planes normal //this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(right.GetVnlVector(), cosines);//x vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y vnl2vtk(normal.GetVnlVector(), cosines + 6);//n m_Reslicer->SetResliceAxesDirectionCosines(cosines); //we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); //set the interpolation mode for slicing switch(this->m_InterpolationMode){ case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); default: //the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; vtkFloatingPointType sliceBounds[6]; if(m_WorldGeometry->GetReferenceGeometry()){ for ( int i = 0; i < 6; ++i ) { sliceBounds[i] = 0.0; } this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds ); // Calculate output extent (integer values) xMin = static_cast< int >( sliceBounds[0] / m_OutPutSpacing[0] + 0.5 ); xMax = static_cast< int >( sliceBounds[1] / m_OutPutSpacing[0] + 0.5 ); yMin = static_cast< int >( sliceBounds[2] / m_OutPutSpacing[1] + 0.5 ); yMax = static_cast< int >( sliceBounds[3] / m_OutPutSpacing[1] + 0.5 ); }else { // If no reference geometry is available, we also don't know about the // maximum plane size; xMin = yMin = 0; xMax = static_cast< int >( extent[0]); yMax = static_cast< int >( extent[1]); } m_Reslicer->SetOutputExtent(xMin, xMax-1, yMin, yMax-1, m_ZMin, m_ZMax ); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); m_Reslicer->SetOutputSpacing( m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing ); //TODO check the following lines, they are responsible wether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); //this produces a bad allocation error for 2D images //m_Reslicer->GetOutput()->UpdateInformation(); //m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); //start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageRslice properties================*/ if(m_VtkOutputRequested){ return; //no converting to mitk //no mitk geometry will be set, as the output is vtkImageData only!!! } else { /*================ #BEGIN Get the slice from vtkImageReslice and convert it to mit::Image================*/ vtkImageData* reslicedImage; reslicedImage = m_Reslicer->GetOutput(); - if(!reslicedImage) { itkWarningMacro(<<"Reslicer returned empty image"); return; } mitk::Image::Pointer resultImage = this->GetOutput(); //initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice resultImage->Initialize(reslicedImage); //transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); //set the geometry from current worldgeometry for the reusultimage //this is needed that the image has the correct mitk geometry //the originalGeometry is the Geometry of the result slice AffineGeometryFrame3D::Pointer originalGeometryAGF = m_WorldGeometry->Clone(); Geometry2D::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); originalGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); //the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = originalGeometry->GetOrigin(); sliceOrigin += right * ( m_OutPutSpacing[0] * 0.5 ); sliceOrigin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //a worldGeometry is no imageGeometry, thus it is manually set to true originalGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transfering world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = originalGeometry->GetAxisVector(0); Vector3D axis1 = originalGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); //adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); originalGeometry->SetOrigin(sliceOrigin); originalGeometry->Modified(); resultImage->SetGeometry( originalGeometry ); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calcuted slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = xMax - xMin; boundsCopy[3] = yMax - yMin; resultImage->GetGeometry()->SetBounds(boundsCopy); /*================ #END Get the slice from vtkImageReslice and convert it to mitk Image================*/ } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(vtkFloatingPointType bounds[6]){ if(!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), dynamic_cast< const PlaneGeometry * >( m_WorldGeometry ), bounds); + } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { bool b = this->CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } bool mitk::ExtractSliceFilter ::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractSliceFilter ::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Core/Code/CppMicroServices/documentation/snippets/uServices-dictionaryservice/main.cpp b/Core/Code/CppMicroServices/documentation/snippets/uServices-dictionaryservice/main.cpp index 3fd7bf4df5..98a3450989 100644 --- a/Core/Code/CppMicroServices/documentation/snippets/uServices-dictionaryservice/main.cpp +++ b/Core/Code/CppMicroServices/documentation/snippets/uServices-dictionaryservice/main.cpp @@ -1,119 +1,120 @@ //! [Activator] #include "DictionaryService.h" #include #include #include // Replace that include with your own base class declaration #include US_BASECLASS_HEADER +#include #include #include US_USE_NAMESPACE /** * This class implements a module activator that uses the module * context to register an English language dictionary service * with the C++ Micro Services registry during static initialization * of the module. The dictionary service interface is * defined in a separate file and is implemented by a nested class. */ class US_ABI_LOCAL MyActivator : public ModuleActivator { private: /** * A private inner class that implements a dictionary service; * see DictionaryService for details of the service. */ class DictionaryImpl : public US_BASECLASS_NAME, public DictionaryService { // The set of words contained in the dictionary. - US_UNORDERED_SET_TYPE m_dictionary; + std::set m_dictionary; public: DictionaryImpl() { m_dictionary.insert("welcome"); m_dictionary.insert("to"); m_dictionary.insert("the"); m_dictionary.insert("micro"); m_dictionary.insert("services"); m_dictionary.insert("tutorial"); } /** * Implements DictionaryService.checkWord(). Determines * if the passed in word is contained in the dictionary. * @param word the word to be checked. * @return true if the word is in the dictionary, * false otherwise. **/ bool checkWord(const std::string& word) { std::string lword(word); std::transform(lword.begin(), lword.end(), lword.begin(), ::tolower); return m_dictionary.find(lword) != m_dictionary.end(); } }; std::auto_ptr m_dictionaryService; public: /** * Implements ModuleActivator::Load(). Registers an * instance of a dictionary service using the module context; * attaches properties to the service that can be queried * when performing a service look-up. * @param context the context for the module. */ void Load(ModuleContext* context) { m_dictionaryService.reset(new DictionaryImpl); ServiceProperties props; props["Language"] = std::string("English"); context->RegisterService(m_dictionaryService.get(), props); } /** * Implements ModuleActivator::Unload(). Does nothing since * the C++ Micro Services library will automatically unregister any registered services. * @param context the context for the module. */ void Unload(ModuleContext* /*context*/) { // NOTE: The service is automatically unregistered } }; US_EXPORT_MODULE_ACTIVATOR(DictionaryServiceModule, MyActivator) //![Activator] #include US_INITIALIZE_MODULE("DictionaryServiceModule", "", "", "1.0.0") int main(int /*argc*/, char* /*argv*/[]) { //![GetDictionaryService] ServiceReference dictionaryServiceRef = GetModuleContext()->GetServiceReference(); if (dictionaryServiceRef) { DictionaryService* dictionaryService = GetModuleContext()->GetService(dictionaryServiceRef); if (dictionaryService) { std::cout << "Dictionary contains 'Tutorial': " << dictionaryService->checkWord("Tutorial") << std::endl; } } //![GetDictionaryService] return 0; } diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index bb3cf16547..72f2184627 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1278 +1,1278 @@ /*=================================================================== 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 "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize( other.GetPixelType(), other.GetDimension(), other.GetDimensions()); //Since the above called "Initialize" method doesn't take the geometry into account we need to set it //here manually - this->SetGeometry(dynamic_cast(other.GetGeometry()->Clone().GetPointer())); + this->SetGeometry(dynamic_cast(other.GetTimeSlicedGeometry()->Clone().GetPointer())); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); if(m_OffsetTable != NULL) delete [] m_OffsetTable; if(m_ImageStatistics != NULL) delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if((i>=0) && (i<(int)m_Dimension)) return m_Dimensions[i]; return 1; } void* mitk::Image::GetData() { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is NULL // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel( const mitk::PixelType ptype, void* data, const unsigned int offset, double& value ) { value = 0.0; if( data == NULL ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); } else { const unsigned int rgboffset = 3 * offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( position[0] < 0 || position[1] < 0 || position[2] < 0 ) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if the dimension is not 0 else if ( (unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || ( imageDims[2] && (unsigned int)position[2] >= imageDims[2] )) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } else { const unsigned int offset = position[0] + position[1]*imageDims[0] + position[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex( itkIndex, timestep); return value; } vtkImageData* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData() == NULL) return NULL; float *fspacing = const_cast(GetSlicedGeometry(t)->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData()->SetSpacing( dspacing ); return volume->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsSliceSet(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); vol->SetComplete(true); return m_Volumes[pos]=vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete=true; unsigned int s; for(s=0;sSetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==NULL) vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); vol->SetComplete(true); size_t size=m_OffsetTable[2]*(ptypeSize); for(s=0;sGetParent()!=vol) { // copy data of slices in volume size_t offset = ((size_t) s)*size; std::memcpy(static_cast(vol->GetData())+offset, sl->GetData(), size); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*size); sl->SetComplete(true); //mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl]=sl; } } //if(vol->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos]=vol; } // volume is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsVolumeSet(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==NULL) ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(ptypeSize); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); //mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // REVIEW FIX // if(ch->GetPicDescriptor()->info->tags_head==NULL) // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n]=ch; } // channel is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); // did it work? if(IsChannelSet(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if(IsSliceSet(s,t,n)) { sl=GetSliceData(s,t,n,data,importMemoryManagement); if(sl->GetManageMemory()==false) { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); //we just added a missing slice, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if(IsVolumeSet(t,n)) { vol=GetVolumeData(t,n,data,importMemoryManagement); if(vol->GetManageMemory()==false) { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( vol->GetData() ); //we just added a missing Volume, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( ch->GetData() ); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; if( m_ImageStatistics == NULL) { m_ImageStatistics = new mitk::ImageStatisticsHolder( this ); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1 ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;im_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if(m_LargestPossibleRegion.GetNumberOfPixels()==0) { delete [] m_Dimensions; m_Dimensions = NULL; return; } for( unsigned int i=0u; im_ImageDescriptor->AddNewChannel( type ); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetExtent(2)+0.5); dimensions[3] = 0; dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( tDim > 0) { dimensions[3] = tDim; } else { const mitk::TimeSlicedGeometry* timeGeometry = dynamic_cast(&geometry); if ( timeGeometry != NULL ) { dimensions[3] = timeGeometry->GetTimeSteps(); } } if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); SetGeometry(static_cast(geometry.Clone().GetPointer())); mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBox()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.Get_vnl_vector().data_block()); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); } } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(image->GetPixelType(), *image->GetTimeSlicedGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: //pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_CHAR: //pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_SHORT: //pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_SHORT: //pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_INT: //pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_INT: //pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_LONG: //pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_LONG: //pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_FLOAT: //pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_DOUBLE: //pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; default: break; } /* Initialize(pixelType, m_Dimension, tmpDimensions, channels); */ const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if(m_Dimension>=2) spacing[1]=spacinglist[1]; if(m_Dimension>=3) spacing[2]=spacinglist[2]; // access origin of vtkImage Point3D origin; vtkFloatingPointType vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand(unsigned int timeSteps) { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; return ((size_t)s)+((size_t) t)*m_Dimensions[2]+((size_t) n)*m_Dimensions[3]*m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if(IsValidVolume(t,n)==false) return false; return ((size_t)t)+((size_t) n)*m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetSliceIndex(s,t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=NULL) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t,n)]=vol=AllocateVolumeData(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; ////ALTERNATIVE: //// allocate new slice //sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); //m_Slices[pos]=sl; //return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetVolumeIndex(t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 3, data,importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); return m_Volumes[pos]=vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(Geometry3D* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); GetTimeSlicedGeometry()->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for(unsigned int ch=0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetTypeId().name() << std::endl; os << indent << " BitsPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::Geometry3D* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); float ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } #include "mitkImageStatisticsHolder.h" //##Documentation mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //##Documentation //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //##Documentation //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute( unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index daac5f331e..15a824422d 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,1361 +1,1368 @@ /*=================================================================== 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. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType; DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult() :m_GantryTilt(false) { } DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames() { return m_GroupedFiles; } DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames() { return m_UnsortedFiles; } bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt() { return m_GantryTilt; } void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename) { m_GroupedFiles.push_back( filename ); } void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename) { m_UnsortedFiles.push_back( filename ); } void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt() { m_GantryTilt = true; } void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping() { assert( !m_GroupedFiles.empty() ); m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); m_GroupedFiles.pop_back(); } const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { if( filenames.empty() ) { MITK_WARN << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); switch (io->GetComponentType()) { case DcmIoType::UCHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::CHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::USHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::SHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::UINT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::INT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::ULONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::LONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::FLOAT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::DOUBLE: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; default: MITK_ERROR << "Found unsupported DICOM pixel type: (enum value) " << io->GetComponentType(); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // transversal gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } DicomSeriesReader::GantryTiltInformation::GantryTiltInformation() : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_NumberOfSlicesApart(1) { } DicomSeriesReader::GantryTiltInformation::GantryTiltInformation( const Point3D& origin1, const Point3D& origin2, const Vector3D& right, const Vector3D& up, unsigned int numberOfSlicesApart) : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_NumberOfSlicesApart(numberOfSlicesApart) { assert(numberOfSlicesApart); // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) // check if this line passes through origin 2 /* Determine if line (imagePosition2 + l * normal) contains imagePosition1. Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 / |pointAlongNormal - origin2| ^ 2 ( x meaning the cross product ) */ Vector3D normal = itk::CrossProduct(right, up); Point3D pointAlongNormal = origin2 + normal; double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); double distance = sqrt(numerator / denominator); if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; MITK_DEBUG << " ==> storing this shift for later analysis:"; MITK_DEBUG << " v right: " << right; MITK_DEBUG << " v up: " << up; MITK_DEBUG << " v normal: " << normal; Point3D projectionRight = projectPointOnLine( origin1, origin2, right ); Point3D projectionUp = projectPointOnLine( origin1, origin2, up ); Point3D projectionNormal = projectPointOnLine( origin1, origin2, normal ); m_ShiftRight = (projectionRight - origin2).GetNorm(); m_ShiftUp = (projectionUp - origin2).GetNorm(); m_ShiftNormal = (projectionNormal - origin2).GetNorm(); MITK_DEBUG << " shift normal: " << m_ShiftNormal; MITK_DEBUG << " shift up: " << m_ShiftUp; MITK_DEBUG << " shift right: " << m_ShiftRight; MITK_DEBUG << " tilt angle (rad): " << tanh( m_ShiftUp / m_ShiftNormal ); MITK_DEBUG << " tilt angle (deg): " << tanh( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; } } Point3D DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3D p, Point3D lineOrigin, Vector3D lineDirection ) { /** See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) */ Vector3D lineOriginToP = p - lineOrigin; ScalarType innerProduct = lineOriginToP * lineDirection; ScalarType factor = innerProduct / lineDirection.GetSquaredNorm(); Point3D projection = lineOrigin + factor * lineDirection; return projection; } ScalarType DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const { // this seems to be a bit too much sometimes, but better too much than cutting off parts of the image return int(m_ShiftUp + 1.0); // to next bigger int: plus 1, then cut off after point } ScalarType DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const { // so many mm shifted per slice! return m_ShiftUp / static_cast(m_NumberOfSlicesApart); } ScalarType DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const { return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); } bool DicomSeriesReader::GantryTiltInformation::IsSheared() const { return ( m_ShiftRight > 0.001 || m_ShiftUp > 0.001); } bool DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const { return ( m_ShiftRight < 0.001 && m_ShiftUp > 0.001); } std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { p[dim++]= atof(coordinate.c_str()); } if (dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " << thisOriginString << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } else { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ UidFileNamesMap groupsOfSimilarImages; // preliminary result, refined into the final result mapOf3DPlusTBlocks // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return groupsOfSimilarImages; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { //MITK_DEBUG << "Scan file " << fileIter->first << std::endl; if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); groupsOfSimilarImages [ moreUniqueSeriesId ].push_back( fileIter->first ); } // PART II: sort slices spatially for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { try { groupsOfSimilarImages[ groupIter->first ] = SortSeriesSlices( groupIter->second ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Catched something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks UidFileNamesMap mapOf3DPlusTBlocks; // final result of this function for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { UidFileNamesMap mapOf3DBlocks; // intermediate result for only this group(!) std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second; std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; mapOf3DBlocks[ newGroupUID.str() ] = analysisResult.GetBlockFilenames(); MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << mapOf3DBlocks[ newGroupUID.str() ].size() << " files"; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of mapOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output // TODO avoid collisions (or prove impossibility) mapOf3DPlusTBlocks.insert( mapOf3DBlocks.begin(), mapOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( UidFileNamesMap::const_iterator block3DIter = mapOf3DBlocks.begin(); block3DIter != mapOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block mapOf3DPlusTBlocks[previousBlockKey].insert( mapOf3DPlusTBlocks[previousBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " ==> group closed with " << mapOf3DPlusTBlocks[previousBlockKey].size() / numberOfFilesInPreviousBlock << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( UidFileNamesMap::const_iterator groupIter = mapOf3DPlusTBlocks.begin(); groupIter != mapOf3DPlusTBlocks.end(); ++groupIter ) { MITK_DEBUG << " Image volume " << groupIter->first << " with " << groupIter->second.size() << " files"; } MITK_DEBUG << "Done. "; MITK_DEBUG << "================================================================================"; return mapOf3DPlusTBlocks; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); try { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tagImageOrientation << ": " << e.what(); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { UidFileNamesMap allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( UidFileNamesMap::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { resultingFileList.insert( resultingFileList.end(), idIter->second.begin(), idIter->second.end() ); // append } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { sorter.Sort(unsortedFilenames); return sorter.GetFilenames(); } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // make sure we have Image Position and Orientation if ( ! ( ds1.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds1.FindDataElement(gdcm::Tag(0x0020,0x0037)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0037)) ) ) { MITK_WARN << "Dicom images are missing attributes for a meaningful sorting."; throw std::logic_error("Dicom images are missing attributes for a meaningful sorting."); } gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } if ( fabs(dist1 - dist2) < mitk::eps) { gdcm::Attribute<0x0008,0x0032> acq_time1; // Acquisition time (may be missing, so we check existence first) gdcm::Attribute<0x0008,0x0032> acq_time2; gdcm::Attribute<0x0020,0x0012> acq_number1; // Acquisition number (may also be missing, so we check existence first) gdcm::Attribute<0x0020,0x0012> acq_number2; if (ds1.FindDataElement(gdcm::Tag(0x0008,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0008,0x0032))) { acq_time1.Set(ds1); acq_time2.Set(ds2); return acq_time1 < acq_time2; } else if (ds1.FindDataElement(gdcm::Tag(0x0020,0x0012)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0012))) { acq_number1.Set(ds1); acq_number2.Set(ds2); return acq_number1 < acq_number2; } else { - return true; + // we need some reproducible sort criteria here + gdcm::Attribute<0x0008,0x0018> sop_uid1; // SOP instance UID, mandatory + gdcm::Attribute<0x0008,0x0018> sop_uid2; + + sop_uid1.Set(ds1); + sop_uid2.Set(ds2); + + return sop_uid1 < sop_uid2; } } else { // default: compare position return dist1 < dist2; } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { propertyKeySliceLocation.append(".t" + timeStep); propertyKeyInstanceNumber.append(".t" + timeStep); propertyKeySOPInstanceNumber.append(".t" + timeStep); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } } } // end namespace mitk #include diff --git a/Core/Code/files.cmake b/Core/Code/files.cmake index fd463e7704..2879048138 100644 --- a/Core/Code/files.cmake +++ b/Core/Code/files.cmake @@ -1,302 +1,302 @@ set(H_FILES Algorithms/itkImportMitkImageContainer.h Algorithms/itkImportMitkImageContainer.txx Algorithms/itkLocalVariationImageFilter.h Algorithms/itkLocalVariationImageFilter.txx Algorithms/itkMITKScalarImageToHistogramGenerator.h Algorithms/itkMITKScalarImageToHistogramGenerator.txx Algorithms/itkTotalVariationDenoisingImageFilter.h Algorithms/itkTotalVariationDenoisingImageFilter.txx Algorithms/itkTotalVariationSingleIterationImageFilter.h Algorithms/itkTotalVariationSingleIterationImageFilter.txx Algorithms/mitkBilateralFilter.h Algorithms/mitkBilateralFilter.cpp Algorithms/mitkInstantiateAccessFunctions.h Algorithms/mitkPixelTypeList.h # Preprocessor macros taken from Boost Algorithms/mitkPPArithmeticDec.h Algorithms/mitkPPArgCount.h Algorithms/mitkPPCat.h Algorithms/mitkPPConfig.h Algorithms/mitkPPControlExprIIf.h Algorithms/mitkPPControlIf.h Algorithms/mitkPPControlIIf.h Algorithms/mitkPPDebugError.h Algorithms/mitkPPDetailAutoRec.h Algorithms/mitkPPDetailDMCAutoRec.h Algorithms/mitkPPExpand.h Algorithms/mitkPPFacilitiesEmpty.h Algorithms/mitkPPFacilitiesExpand.h Algorithms/mitkPPLogicalBool.h Algorithms/mitkPPRepetitionDetailDMCFor.h Algorithms/mitkPPRepetitionDetailEDGFor.h Algorithms/mitkPPRepetitionDetailFor.h Algorithms/mitkPPRepetitionDetailMSVCFor.h Algorithms/mitkPPRepetitionFor.h Algorithms/mitkPPSeqElem.h Algorithms/mitkPPSeqForEach.h Algorithms/mitkPPSeqForEachProduct.h Algorithms/mitkPPSeq.h Algorithms/mitkPPSeqEnum.h Algorithms/mitkPPSeqSize.h Algorithms/mitkPPSeqToTuple.h Algorithms/mitkPPStringize.h Algorithms/mitkPPTupleEat.h Algorithms/mitkPPTupleElem.h Algorithms/mitkPPTupleRem.h Algorithms/mitkClippedSurfaceBoundsCalculator.h - Algorithms/mitkExtractSliceFilter.h + Algorithms/mitkExtractSliceFilter.h DataManagement/mitkImageAccessByItk.h DataManagement/mitkImageCast.h DataManagement/mitkITKImageImport.h DataManagement/mitkITKImageImport.txx DataManagement/mitkImageToItk.h DataManagement/mitkImageToItk.txx Interfaces/mitkIDataNodeReader.h - + IO/mitkPixelTypeTraits.h Interactions/mitkEventMapperAddOn.h Common/mitkExceptionMacro.h Common/mitkTestingMacros.h ) set(CPP_FILES Algorithms/mitkBaseDataSource.cpp Algorithms/mitkBaseProcess.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkGeometry2DDataToSurfaceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkExtractSliceFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCoreActivator.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataStorage.cpp #DataManagement/mitkDataTree.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataNodeFactory.cpp #DataManagement/mitkDataTreeStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkGeometry2D.cpp DataManagement/mitkGeometry2DData.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkLandmarkBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeSlicedGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkMaterial.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp Interactions/mitkDisplayVectorInteractor.cpp Interactions/mitkDisplayVectorInteractorLevelWindow.cpp Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEvent.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventMapper.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractor.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMoveSurfaceInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkState.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkTransition.cpp Interactions/mitkWheelEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkCrosshairPositionEvent.cpp IO/mitkBaseDataIOFactory.cpp IO/mitkCoreDataNodeReader.cpp IO/mitkDicomSeriesReader.cpp IO/mitkFileReader.cpp IO/mitkFileSeriesReader.cpp IO/mitkFileWriter.cpp #IO/mitkIpPicGet.c IO/mitkImageGenerator.cpp IO/mitkImageWriter.cpp IO/mitkImageWriterFactory.cpp IO/mitkItkImageFileIOFactory.cpp IO/mitkItkImageFileReader.cpp IO/mitkItkPictureWrite.cpp IO/mitkIOUtil.cpp IO/mitkLookupTableProperty.cpp IO/mitkOperation.cpp #IO/mitkPicFileIOFactory.cpp #IO/mitkPicFileReader.cpp #IO/mitkPicFileWriter.cpp #IO/mitkPicHelper.cpp #IO/mitkPicVolumeTimeSeriesIOFactory.cpp #IO/mitkPicVolumeTimeSeriesReader.cpp IO/mitkPixelType.cpp IO/mitkPointSetIOFactory.cpp IO/mitkPointSetReader.cpp IO/mitkPointSetWriter.cpp IO/mitkPointSetWriterFactory.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSTLFileIOFactory.cpp IO/mitkSTLFileReader.cpp IO/mitkSurfaceVtkWriter.cpp IO/mitkSurfaceVtkWriterFactory.cpp IO/mitkVtiFileIOFactory.cpp IO/mitkVtiFileReader.cpp IO/mitkVtkImageIOFactory.cpp IO/mitkVtkImageReader.cpp IO/mitkVtkSurfaceIOFactory.cpp IO/mitkVtkSurfaceReader.cpp IO/vtkPointSetXMLParser.cpp IO/mitkLog.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkVtkMapper2D.cpp Rendering/mitkVtkMapper3D.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkGeometry2DDataMapper2D.cpp Rendering/mitkGeometry2DDataVtkMapper3D.cpp Rendering/mitkGLMapper2D.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper2D.cpp Rendering/mitkMapper3D.cpp Rendering/mitkMapper.cpp Rendering/mitkPointSetGLMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkPolyDataGLMapper2D.cpp Rendering/mitkSurfaceGLMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVolumeDataVtkMapper3D.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkShaderRepository.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkMitkApplyLevelWindowToRGBFilter.cpp Common/mitkException.cpp Common/mitkCommon.h Common/mitkCoreObjectFactoryBase.cpp Common/mitkCoreObjectFactory.cpp ) list(APPEND CPP_FILES ${CppMicroServices_SOURCES}) diff --git a/Modules/CMakeLists.txt b/Modules/CMakeLists.txt index c823f5d4c2..2f624eb7c1 100644 --- a/Modules/CMakeLists.txt +++ b/Modules/CMakeLists.txt @@ -1,54 +1,55 @@ set(LIBPOSTFIX "Ext") # Modules must be listed according to their dependencies set(module_dirs SceneSerializationBase PlanarFigure ImageExtraction ImageStatistics LegacyAdaptors IpPicSupport MitkExt SceneSerialization Segmentation Qmitk QmitkExt GraphAlgorithms DiffusionImaging GPGPU IGT CameraCalibration IGTUI RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCVVideoSupport Overlays InputDevices ToFHardware ToFProcessing ToFUI ClippingTools US + USUI ) set(MITK_DEFAULT_SUBPROJECTS MITK-Modules) foreach(module_dir ${module_dirs}) add_subdirectory(${module_dir}) endforeach() if(MITK_PRIVATE_MODULES) file(GLOB all_subdirs RELATIVE ${MITK_PRIVATE_MODULES} ${MITK_PRIVATE_MODULES}/*) foreach(subdir ${all_subdirs}) string(FIND ${subdir} "." _result) if(_result EQUAL -1) if(EXISTS ${MITK_PRIVATE_MODULES}/${subdir}/CMakeLists.txt) message(STATUS "Found private module ${subdir}") add_subdirectory(${MITK_PRIVATE_MODULES}/${subdir} private_modules/${subdir}) endif() endif() endforeach() endif(MITK_PRIVATE_MODULES) diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp index f997ea047b..31d321c7db 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp @@ -1,500 +1,503 @@ /*=================================================================== 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 "mitkCameraIntrinsics.h" #include #include #include mitk::CameraIntrinsics::CameraIntrinsics() : m_Valid(false), m_Mutex(itk::FastMutexLock::New()) { m_CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); m_CameraMatrix.at(2,2) = 1.0; m_DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); } mitk::CameraIntrinsics::~CameraIntrinsics() { } bool mitk::CameraIntrinsics::Equals( const CameraIntrinsics* other ) const { return other->GetDistorsionCoeffsAsPoint4D()== this->GetDistorsionCoeffsAsPoint4D() && other->GetFocalPoint()== this->GetFocalPoint() && other->GetPrincipalPoint() == this->GetPrincipalPoint(); } void mitk::CameraIntrinsics::Copy(const CameraIntrinsics* other) { this->SetIntrinsics( other->GetCameraMatrix().clone() , other->GetDistorsionCoeffs().clone() ); + this->SetValid(other->m_Valid); } bool mitk::CameraIntrinsics::IsValid() const { itk::MutexLockHolder lock(*m_Mutex); return m_Valid; } mitk::CameraIntrinsics::Pointer mitk::CameraIntrinsics::Clone() const { mitk::CameraIntrinsics::Pointer copy = mitk::CameraIntrinsics::New(); copy->SetIntrinsics( this->GetCameraMatrix(), this->GetDistorsionCoeffs() ); + copy->SetValid(this->IsValid()); return copy; } vnl_matrix_fixed mitk::CameraIntrinsics::GetVnlCameraMatrix() const { vnl_matrix_fixed mat; mat.set_identity(); { itk::MutexLockHolder lock(*m_Mutex); mat(0,0) = m_CameraMatrix.at(0,0); mat(1,1) = m_CameraMatrix.at(1,1); mat(0,2) = m_CameraMatrix.at(0,2); mat(1,2) = m_CameraMatrix.at(1,2); } return mat; } void mitk::CameraIntrinsics::SetCameraMatrix( const vnl_matrix_fixed& _CameraMatrix ) { itk::MutexLockHolder lock(*m_Mutex); m_CameraMatrix.at(0,0) = _CameraMatrix(0,0); m_CameraMatrix.at(1,1) = _CameraMatrix(1,1); m_CameraMatrix.at(0,2) = _CameraMatrix(0,2); m_CameraMatrix.at(1,2) = _CameraMatrix(1,2); } vnl_matrix_fixed mitk::CameraIntrinsics::GetVnlCameraMatrix3x4() const { vnl_matrix_fixed mat; mat.fill(0); mat.update( this->GetVnlCameraMatrix().as_matrix() ); return mat; } void mitk::CameraIntrinsics::SetIntrinsics( const cv::Mat& _CameraMatrix , const cv::Mat& _DistorsionCoeffs) { { itk::MutexLockHolder lock(*m_Mutex); if( _CameraMatrix.cols != 3 || _CameraMatrix.rows != 3) throw std::invalid_argument("Wrong format of camera matrix. Should be 3x3" " double."); endoAssertMsg( (_DistorsionCoeffs.cols == 5) && _DistorsionCoeffs.rows == 1, "Wrong format of distorsion coefficients" " vector. Should be 5x1 double."); m_CameraMatrix = _CameraMatrix.clone(); m_DistorsionCoeffs = _DistorsionCoeffs.clone(); m_Valid = true; } this->Modified(); } void mitk::CameraIntrinsics::SetIntrinsics( const mitk::Point3D& focalPoint, const mitk::Point3D& principalPoint, const mitk::Point4D& distortionCoefficients) { { itk::MutexLockHolder lock(*m_Mutex); m_CameraMatrix.at(0,0) = focalPoint[0]; m_CameraMatrix.at(1,1) = focalPoint[1]; m_CameraMatrix.at(0,2) = principalPoint[0]; m_CameraMatrix.at(1,2) = principalPoint[1]; m_DistorsionCoeffs.at(0,0) = distortionCoefficients[0]; m_DistorsionCoeffs.at(0,1) = distortionCoefficients[1]; m_DistorsionCoeffs.at(0,2) = distortionCoefficients[2]; m_DistorsionCoeffs.at(0,3) = distortionCoefficients[3]; } this->Modified(); } void mitk::CameraIntrinsics::SetFocalLength( double x, double y ) { { itk::MutexLockHolder lock(*m_Mutex); m_CameraMatrix.at(0,0) = x; m_CameraMatrix.at(1,1) = y; } this->Modified(); } void mitk::CameraIntrinsics::SetPrincipalPoint( double x, double y ) { { itk::MutexLockHolder lock(*m_Mutex); m_CameraMatrix.at(0,2) = x; m_CameraMatrix.at(1,2) = y; } this->Modified(); } void mitk::CameraIntrinsics::SetDistorsionCoeffs( double k1, double k2, double p1, double p2 ) { { itk::MutexLockHolder lock(*m_Mutex); m_DistorsionCoeffs.at(0,0) = k1; m_DistorsionCoeffs.at(0,1) = k2; m_DistorsionCoeffs.at(0,2) = p1; m_DistorsionCoeffs.at(0,3) = p2; } this->Modified(); } cv::Mat mitk::CameraIntrinsics::GetCameraMatrix() const { itk::MutexLockHolder lock(*m_Mutex); return m_CameraMatrix.clone(); // return a copy of this small matrix } cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() const { itk::MutexLockHolder lock(*m_Mutex); return m_DistorsionCoeffs.clone(); // return a copy of this small matrix } cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() { const CameraIntrinsics* intrinsics = this; return intrinsics->GetDistorsionCoeffs(); } std::string mitk::CameraIntrinsics::ToString() const { itk::MutexLockHolder lock(*m_Mutex); std::ostringstream s; s.precision(12); const cv::Mat& CameraMatrix = m_CameraMatrix; const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; s.str(""); s << this->GetNameOfClass() << ": "; s << "fx = " << CameraMatrix.at(0,0); s << ", fy = " << CameraMatrix.at(1,1); s << ", cx = " << CameraMatrix.at(0,2); s << ", cy = " << CameraMatrix.at(1,2); s << ", k1 = " << DistorsionCoeffs.at(0,0); s << ", k2 = " << DistorsionCoeffs.at(0,1); s << ", p1 = " << DistorsionCoeffs.at(0,2); s << ", p2 = " << DistorsionCoeffs.at(0,3); //s << ", k3 = " << DistorsionCoeffs.at(0,4); return s.str(); } void mitk::CameraIntrinsics::ToXML(TiXmlElement* elem) const { itk::MutexLockHolder lock(*m_Mutex); elem->SetValue(this->GetNameOfClass()); std::ostringstream s; s.precision(12); const cv::Mat& CameraMatrix = m_CameraMatrix; s.str(""); s << CameraMatrix.at(0,0); elem->SetAttribute( "fx", s.str() ); s.str(""); s << CameraMatrix.at(1,1); elem->SetAttribute( "fy", s.str() ); s.str(""); s << CameraMatrix.at(0,2); elem->SetAttribute( "cx", s.str() ); s.str(""); s << CameraMatrix.at(1,2); elem->SetAttribute( "cy", s.str() ); const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; s.str(""); s << DistorsionCoeffs.at(0,0); elem->SetAttribute( "k1", s.str() ); s.str(""); s << DistorsionCoeffs.at(0,1); elem->SetAttribute( "k2", s.str() ); s.str(""); s << DistorsionCoeffs.at(0,2); elem->SetAttribute( "p1", s.str() ); s.str(""); s << DistorsionCoeffs.at(0,3); elem->SetAttribute( "p2", s.str() ); elem->SetAttribute("Valid", m_Valid); //s.str(""); s << DistorsionCoeffs.at(4,0); //elem->SetAttribute( "k3", s.str() ); } void mitk::CameraIntrinsics::FromGMLCalibrationXML(TiXmlElement* elem) { assert( elem ); assert( elem->ValueStr() == "results" ); cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); CameraMatrix.at(2,2) = 1.0; cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); TiXmlElement* focus_lenXElem = elem->FirstChildElement("focus_lenX"); endoAssert( focus_lenXElem != 0 ); CameraMatrix.at(0,0) = atof( focus_lenXElem->GetText() ); TiXmlElement* focus_lenYElem = elem->FirstChildElement("focus_lenY"); endoAssert( focus_lenYElem != 0 ); CameraMatrix.at(1,1) = atof( focus_lenYElem->GetText() ); TiXmlElement* PrincipalXElem = elem->FirstChildElement("PrincipalX"); endoAssert( PrincipalXElem != 0 ); CameraMatrix.at(0,2) = atof( PrincipalXElem->GetText() ); TiXmlElement* PrincipalYElem = elem->FirstChildElement("PrincipalY"); endoAssert( PrincipalYElem != 0 ); CameraMatrix.at(1,2) = atof( PrincipalYElem->GetText() ); // DISTORSION COEFFS TiXmlElement* Dist1Elem = elem->FirstChildElement("Dist1"); endoAssert( Dist1Elem != 0 ); DistorsionCoeffs.at(0,0) = atof( Dist1Elem->GetText() ); TiXmlElement* Dist2Elem = elem->FirstChildElement("Dist2"); endoAssert( Dist2Elem != 0 ); DistorsionCoeffs.at(0,1) = atof( Dist2Elem->GetText() ); TiXmlElement* Dist3Elem = elem->FirstChildElement("Dist3"); endoAssert( Dist3Elem != 0 ); DistorsionCoeffs.at(0,2) = atof( Dist3Elem->GetText() ); TiXmlElement* Dist4Elem = elem->FirstChildElement("Dist4"); endoAssert( Dist4Elem != 0 ); DistorsionCoeffs.at(0,3) = atof( Dist4Elem->GetText() ); int valid = 0; elem->QueryIntAttribute("Valid", &valid); { itk::MutexLockHolder lock(*m_Mutex); m_Valid = static_cast(valid); m_CameraMatrix = CameraMatrix; m_DistorsionCoeffs = DistorsionCoeffs; } this->Modified(); } void mitk::CameraIntrinsics::FromXML(TiXmlElement* elem) { endoAssert ( elem ); MITK_DEBUG << elem->Value(); std::string filename; if(elem->QueryStringAttribute("file", &filename) == TIXML_SUCCESS) { this->FromXMLFile(filename); return; } else if(strcmp(elem->Value(), "CalibrationProject") == 0) { this->FromGMLCalibrationXML(elem->FirstChildElement("results")); return; } assert ( elem ); if(strcmp(elem->Value(), this->GetNameOfClass()) != 0) elem = elem->FirstChildElement(this->GetNameOfClass()); std::ostringstream err; // CAMERA MATRIX cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); CameraMatrix.at(2,2) = 1.0; float val = 0.0f; if(elem->QueryFloatAttribute("fx", &val) == TIXML_SUCCESS) CameraMatrix.at(0,0) = val; else err << "fx, "; if(elem->QueryFloatAttribute("fy", &val) == TIXML_SUCCESS) CameraMatrix.at(1,1) = val; else err << "fy, "; if(elem->QueryFloatAttribute("cx", &val) == TIXML_SUCCESS) CameraMatrix.at(0,2) = val; else err << "cx, "; if(elem->QueryFloatAttribute("cy", &val) == TIXML_SUCCESS) CameraMatrix.at(1,2) = val; else err << "cy, "; // DISTORSION COEFFS endodebug( "creating DistorsionCoeffs from XML file") cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); if(elem->QueryFloatAttribute("k1", &val) == TIXML_SUCCESS) DistorsionCoeffs.at(0,0) = val; else err << "k1, "; if(elem->QueryFloatAttribute("k2", &val) == TIXML_SUCCESS) DistorsionCoeffs.at(0,1) = val; else err << "k2, "; if(elem->QueryFloatAttribute("p1", &val) == TIXML_SUCCESS) DistorsionCoeffs.at(0,2) = val; else err << "p1, "; if(elem->QueryFloatAttribute("p2", &val) == TIXML_SUCCESS) DistorsionCoeffs.at(0,3) = val; else err << "p2, "; DistorsionCoeffs.at(0,4) = 0.0; /*if(elem->QueryFloatAttribute("k3", &val) == TIXML_SUCCESS) DistorsionCoeffs.at(4,0) = val; else err << "k3, ";*/ std::string errorStr = err.str(); int errLength = errorStr.length(); if(errLength > 0) { errorStr = errorStr.substr(0, errLength-2); errorStr.append(" not found"); throw std::invalid_argument(err.str()); } int valid = 0; elem->QueryIntAttribute("Valid", &valid); { itk::MutexLockHolder lock(*m_Mutex); m_Valid = static_cast(valid); m_CameraMatrix = CameraMatrix; m_DistorsionCoeffs = DistorsionCoeffs; } this->Modified(); } double mitk::CameraIntrinsics::GetFocalLengthX() const { itk::MutexLockHolder lock(*m_Mutex); double FocalLengthX = m_CameraMatrix.at(0,0); return FocalLengthX; } double mitk::CameraIntrinsics::GetFocalLengthY() const { itk::MutexLockHolder lock(*m_Mutex); double FocalLengthY = m_CameraMatrix.at(1,1);; return FocalLengthY; } double mitk::CameraIntrinsics::GetPrincipalPointX() const { itk::MutexLockHolder lock(*m_Mutex); double PrincipalPointX = m_CameraMatrix.at(0,2); return PrincipalPointX; } double mitk::CameraIntrinsics::GetPrincipalPointY() const { itk::MutexLockHolder lock(*m_Mutex); double PrincipalPointY = m_CameraMatrix.at(1,2); return PrincipalPointY; } mitk::Point4D mitk::CameraIntrinsics::GetDistorsionCoeffsAsPoint4D() const { itk::MutexLockHolder lock(*m_Mutex); mitk::Point4D coeffs; coeffs[0] = m_DistorsionCoeffs.at(0,0); coeffs[1] = m_DistorsionCoeffs.at(0,1); coeffs[2] = m_DistorsionCoeffs.at(0,2); coeffs[3] = m_DistorsionCoeffs.at(0,3); return coeffs; } mitk::Point3D mitk::CameraIntrinsics::GetFocalPoint() const { mitk::Point3D p; p[0] = this->GetFocalLengthX(); p[1] = this->GetFocalLengthY(); p[2] = 0; return p; } mitk::Point3D mitk::CameraIntrinsics::GetPrincipalPoint() const { mitk::Point3D p; p[0] = this->GetPrincipalPointX(); p[1] = this->GetPrincipalPointY(); p[2] = 0; return p; } vnl_vector_fixed mitk::CameraIntrinsics::GetFocalPointAsVnlVector() const { vnl_vector_fixed vec; vec[0] = this->GetFocalLengthX(); vec[1] = this->GetFocalLengthY(); return vec; } vnl_vector_fixed mitk::CameraIntrinsics::GetPrincipalPointAsVnlVector() const { vnl_vector_fixed vec; vec[0] = this->GetPrincipalPointX(); vec[1] = this->GetPrincipalPointY(); return vec; } std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p) { os << p->ToString(); return os; } std::string mitk::CameraIntrinsics::GetString() { return this->ToString(); } std::string mitk::CameraIntrinsics::ToOctaveString( const std::string& varName) { std::ostringstream s; s << varName << " = [" << this->GetFocalLengthX() << " 0 " << this->GetPrincipalPointX() << "; 0 " << this->GetFocalLengthY() << " " << this->GetPrincipalPointY() << ";" << " 0 0 1 ];"; return s.str(); } void mitk::CameraIntrinsics::SetValid( bool valid ) { itk::MutexLockHolder lock(*m_Mutex); m_Valid = valid; } diff --git a/Modules/DiffusionImaging/IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp b/Modules/DiffusionImaging/IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp index 8bcb62292b..8b06a5b9db 100644 --- a/Modules/DiffusionImaging/IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp +++ b/Modules/DiffusionImaging/IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp @@ -1,535 +1,550 @@ /*=================================================================== 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 __mitkNrrdDiffusionImageReader_cpp #define __mitkNrrdDiffusionImageReader_cpp #include "mitkNrrdDiffusionImageReader.h" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkNiftiImageIO.h" #include #include #include "itksys/SystemTools.hxx" namespace mitk { template void NrrdDiffusionImageReader ::GenerateData() { // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if ( ( ! m_OutputCache ) || ( this->GetMTime( ) > m_CacheTime.GetMTime( ) ) ) { this->GenerateOutputInformation(); itkWarningMacro("Cache regenerated!"); } if (!m_OutputCache) { itkWarningMacro("cache is empty!"); } static_cast(this->GetOutput()) ->SetVectorImage(m_OutputCache->GetVectorImage()); static_cast(this->GetOutput()) ->SetB_Value(m_OutputCache->GetB_Value()); static_cast(this->GetOutput()) ->SetDirections(m_OutputCache->GetDirections()); static_cast(this->GetOutput()) ->SetOriginalDirections(m_OutputCache->GetOriginalDirections()); static_cast(this->GetOutput()) ->SetMeasurementFrame(m_OutputCache->GetMeasurementFrame()); static_cast(this->GetOutput()) ->InitializeFromVectorImage(); } template void NrrdDiffusionImageReader::GenerateOutputInformation() { typename OutputType::Pointer outputForCache = OutputType::New(); if ( m_FileName == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } MITK_INFO << "NrrdDiffusionImageReader: reading image information"; typename ImageType::Pointer img; std::string ext = itksys::SystemTools::GetFilenameLastExtension(m_FileName); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".hdwi" || ext == ".dwi") { typedef itk::ImageFileReader FileReaderType; typename FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->m_FileName); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); reader->SetImageIO(io); reader->Update(); img = reader->GetOutput(); int vecsize = img->GetVectorLength(); std::cout << vecsize << std::endl; } else if(ext == ".fsl" || ext == ".fslgz") { // create temporary file with correct ending for nifti-io std::string fname3 = m_FileName; fname3 += ext == ".fsl" ? ".nii" : ".nii.gz"; itksys::SystemTools::CopyAFile(m_FileName.c_str(), fname3.c_str()); // create reader and read file typedef itk::Image ImageType4D; itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; typename FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(fname3); reader->SetImageIO(io2); reader->Update(); typename ImageType4D::Pointer img4 = reader->GetOutput(); // delete temporary file itksys::SystemTools::RemoveFile(fname3.c_str()); // convert 4D file to vector image img = ImageType::New(); typename ImageType::SpacingType spacing; typename ImageType4D::SpacingType spacing4 = img4->GetSpacing(); for(int i=0; i<3; i++) spacing[i] = spacing4[i]; img->SetSpacing( spacing ); // Set the image spacing typename ImageType::PointType origin; typename ImageType4D::PointType origin4 = img4->GetOrigin(); for(int i=0; i<3; i++) origin[i] = origin4[i]; img->SetOrigin( origin ); // Set the image origin typename ImageType::DirectionType direction; typename ImageType4D::DirectionType direction4 = img4->GetDirection(); for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction[i][j] = direction4[i][j]; img->SetDirection( direction ); // Set the image direction typename ImageType::RegionType region; typename ImageType4D::RegionType region4 = img4->GetLargestPossibleRegion(); typename ImageType::RegionType::SizeType size; typename ImageType4D::RegionType::SizeType size4 = region4.GetSize(); for(int i=0; i<3; i++) size[i] = size4[i]; typename ImageType::RegionType::IndexType index; typename ImageType4D::RegionType::IndexType index4 = region4.GetIndex(); for(int i=0; i<3; i++) index[i] = index4[i]; region.SetSize(size); region.SetIndex(index); img->SetRegions( region ); img->SetVectorLength(size4[3]); img->Allocate(); itk::ImageRegionIterator it (img, img->GetLargestPossibleRegion() ); typedef typename ImageType::PixelType VecPixType; for (it = it.Begin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); typename ImageType::IndexType currentIndex = it.GetIndex(); for(int i=0; i<3; i++) index4[i] = currentIndex[i]; for(unsigned int ind=0; indGetPixel(index4); } it.Set(vec); } } m_DiffusionVectors = GradientDirectionContainerType::New(); m_OriginalDiffusionVectors = GradientDirectionContainerType::New(); if (ext == ".hdwi" || ext == ".dwi") { itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; GradientDirectionType vect3d; int numberOfImages = 0; int numberOfGradientImages = 0; bool readb0 = false; bool readFrame = false; double xx, xy, xz, yx, yy, yz, zx, zy, zz; for (; itKey != imgMetaKeys.end(); itKey ++) { double x,y,z; itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("DWMRI_gradient") != std::string::npos) { sscanf(metaString.c_str(), "%lf %lf %lf\n", &x, &y, &z); vect3d[0] = x; vect3d[1] = y; vect3d[2] = z; m_DiffusionVectors->InsertElement( numberOfImages, vect3d ); m_OriginalDiffusionVectors->InsertElement( numberOfImages, vect3d ); ++numberOfImages; // If the direction is 0.0, this is a reference image if (vect3d[0] == 0.0 && vect3d[1] == 0.0 && vect3d[2] == 0.0) { continue; } ++numberOfGradientImages;; } else if (itKey->find("DWMRI_b-value") != std::string::npos) { readb0 = true; m_B_Value = atof(metaString.c_str()); } else if (itKey->find("measurement frame") != std::string::npos) { sscanf(metaString.c_str(), " ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) \n", &xx, &xy, &xz, &yx, &yy, &yz, &zx, &zy, &zz); readFrame = true; if (xx>10e-10 || xy>10e-10 || xz>10e-10 || yx>10e-10 || yy>10e-10 || yz>10e-10 || zx>10e-10 || zy>10e-10 || zz>10e-10 ) { m_MeasurementFrame(0,0) = xx; m_MeasurementFrame(0,1) = xy; m_MeasurementFrame(0,2) = xz; m_MeasurementFrame(1,0) = yx; m_MeasurementFrame(1,1) = yy; m_MeasurementFrame(1,2) = yz; m_MeasurementFrame(2,0) = zx; m_MeasurementFrame(2,1) = zy; m_MeasurementFrame(2,2) = zz; } else { m_MeasurementFrame(0,0) = 1; m_MeasurementFrame(0,1) = 0; m_MeasurementFrame(0,2) = 0; m_MeasurementFrame(1,0) = 0; m_MeasurementFrame(1,1) = 1; m_MeasurementFrame(1,2) = 0; m_MeasurementFrame(2,0) = 0; m_MeasurementFrame(2,1) = 0; m_MeasurementFrame(2,2) = 1; } } } if(readFrame) { for(int i=0; i vec(3); vec.copy_in(m_DiffusionVectors->ElementAt(i).data_block()); vec = vec.pre_multiply(m_MeasurementFrame); m_DiffusionVectors->ElementAt(i).copy_in(vec.data_block()); } } if(!readb0) { MITK_INFO << "BValue not specified in header file"; } } else if(ext == ".fsl" || ext == ".fslgz") { std::string line; std::vector bvec_entries; std::string fname = m_FileName; fname += ".bvecs"; std::ifstream myfile (fname.c_str()); if (myfile.is_open()) { while ( myfile.good() ) { getline (myfile,line); char* pch = strtok (const_cast(line.c_str())," "); while (pch != NULL) { bvec_entries.push_back(atof(pch)); pch = strtok (NULL, " "); } } myfile.close(); } else { MITK_INFO << "Unable to open bvecs file"; } std::vector bval_entries; std::string fname2 = m_FileName; fname2 += ".bvals"; std::ifstream myfile2 (fname2.c_str()); if (myfile2.is_open()) { while ( myfile2.good() ) { getline (myfile2,line); char* pch = strtok (const_cast(line.c_str())," "); while (pch != NULL) { bval_entries.push_back(atof(pch)); pch = strtok (NULL, " "); } } myfile2.close(); } else { MITK_INFO << "Unable to open bvals file"; } + m_B_Value = -1; unsigned int numb = bval_entries.size(); for(unsigned int i=0; i vec; vec[0] = bvec_entries.at(i); vec[1] = bvec_entries.at(i+numb); vec[2] = bvec_entries.at(i+2*numb); + // Adjust the vector length to encode gradient strength + float factor = b_val/m_B_Value; + if(vec.magnitude() > 0) + { + vec[0] = sqrt(factor)*vec[0]; + vec[1] = sqrt(factor)*vec[1]; + vec[2] = sqrt(factor)*vec[2]; + } + m_DiffusionVectors->InsertElement(i,vec); m_OriginalDiffusionVectors->InsertElement(i,vec); } for(int i=0; i<3; i++) for(int j=0; j<3; j++) m_MeasurementFrame[i][j] = i==j ? 1 : 0; } outputForCache->SetVectorImage(img); outputForCache->SetB_Value(m_B_Value); outputForCache->SetDirections(m_DiffusionVectors); outputForCache->SetOriginalDirections(m_OriginalDiffusionVectors); outputForCache->SetMeasurementFrame(m_MeasurementFrame); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } template const char* NrrdDiffusionImageReader ::GetFileName() const { return m_FileName.c_str(); } template void NrrdDiffusionImageReader ::SetFileName(const char* aFileName) { m_FileName = aFileName; } template const char* NrrdDiffusionImageReader ::GetFilePrefix() const { return m_FilePrefix.c_str(); } template void NrrdDiffusionImageReader ::SetFilePrefix(const char* aFilePrefix) { m_FilePrefix = aFilePrefix; } template const char* NrrdDiffusionImageReader ::GetFilePattern() const { return m_FilePattern.c_str(); } template void NrrdDiffusionImageReader ::SetFilePattern(const char* aFilePattern) { m_FilePattern = aFilePattern; } template bool NrrdDiffusionImageReader ::CanReadFile(const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/) { // First check the extension if( filename == "" ) { return false; } std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".hdwi" || ext == ".dwi") { itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; typename FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(filename); try { reader->Update(); } catch(itk::ExceptionObject e) { MITK_INFO << e.GetDescription(); } typename ImageType::Pointer img = reader->GetOutput(); itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("modality") != std::string::npos) { if (metaString.find("DWMRI") != std::string::npos) { return true; } } } } if (ext == ".fsl" || ext == ".fslgz") { // itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); // typedef itk::ImageFileReader FileReaderType; // typename FileReaderType::Pointer reader = FileReaderType::New(); // reader->SetImageIO(io2); // reader->SetFileName(filename); // try // { // reader->Update(); // } // catch(itk::ExceptionObject e) // { // MITK_INFO << e.GetDescription(); // } std::string fname = filename; fname += ".bvecs"; std::string fname2 = filename; fname2 += ".bvals"; if( itksys::SystemTools::FileExists(fname.c_str()) && itksys::SystemTools::FileExists(fname2.c_str()) ) { return true; } else { MITK_INFO << ".bvals and .bvals files do not exist properly"; } } return false; } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp b/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp index cf057f06a1..a17e1213d7 100644 --- a/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp +++ b/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp @@ -1,92 +1,99 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include using namespace mitk; /**Documentation * Test for gibbs tracking filter */ int mitkGibbsTrackingTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkGibbsTrackingTest"); MITK_TEST_CONDITION_REQUIRED(argc>5,"check for input data") QBallImage::Pointer mitkQballImage; Image::Pointer mitkMaskImage; mitk::FiberBundleX::Pointer fib1; try{ + + MITK_INFO << "Q-Ball image: " << argv[1]; + MITK_INFO << "Mask image: " << argv[2]; + MITK_INFO << "Parameter file: " << argv[3]; + MITK_INFO << "Lut path: " << argv[4]; + MITK_INFO << "Reference bundle: " << argv[5]; + RegisterDiffusionImagingObjectFactory(); // test if fib1 can be read const std::string s1="", s2=""; std::vector infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[1], s1, s2, false ); mitkQballImage = dynamic_cast(infile.at(0).GetPointer()); MITK_TEST_CONDITION_REQUIRED(mitkQballImage.IsNotNull(),"check qball image") infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[2], s1, s2, false ); mitkMaskImage = dynamic_cast(infile.at(0).GetPointer()); - MITK_TEST_CONDITION_REQUIRED(mitkMaskImage.IsNotNull(),"check qball image") + MITK_TEST_CONDITION_REQUIRED(mitkMaskImage.IsNotNull(),"check mask image") infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[5], s1, s2, false ); fib1 = dynamic_cast(infile.at(0).GetPointer()); MITK_TEST_CONDITION_REQUIRED(fib1.IsNotNull(),"check fiber bundle") typedef itk::Vector OdfVectorType; typedef itk::Image OdfVectorImgType; typedef itk::Image MaskImgType; typedef itk::GibbsTrackingFilter GibbsTrackingFilterType; OdfVectorImgType::Pointer itk_qbi = OdfVectorImgType::New(); mitk::CastToItkImage(mitkQballImage, itk_qbi); MaskImgType::Pointer itk_mask = MaskImgType::New(); mitk::CastToItkImage(mitkMaskImage, itk_mask); GibbsTrackingFilterType::Pointer gibbsTracker = GibbsTrackingFilterType::New(); gibbsTracker->SetQBallImage(itk_qbi.GetPointer()); gibbsTracker->SetMaskImage(itk_mask); gibbsTracker->SetDuplicateImage(false); gibbsTracker->SetRandomSeed(1); - gibbsTracker->SetParameterFile(argv[3]); + gibbsTracker->SetLoadParameterFile(argv[3]); gibbsTracker->SetLutPath(argv[4]); gibbsTracker->Update(); mitk::FiberBundleX::Pointer fib2 = mitk::FiberBundleX::New(gibbsTracker->GetFiberBundle()); MITK_TEST_CONDITION_REQUIRED(fib1->Equals(fib2), "check if gibbs tracking has changed"); gibbsTracker->SetRandomSeed(0); gibbsTracker->Update(); fib2 = mitk::FiberBundleX::New(gibbsTracker->GetFiberBundle()); MITK_TEST_CONDITION_REQUIRED(!fib1->Equals(fib2), "check if gibbs tracking has changed after wrong seed"); } catch(...) { return EXIT_FAILURE; } // always end with this! MITK_TEST_END(); } diff --git a/Modules/ImageExtraction/Testing/files.cmake b/Modules/ImageExtraction/Testing/files.cmake index c983818824..aa6e367684 100644 --- a/Modules/ImageExtraction/Testing/files.cmake +++ b/Modules/ImageExtraction/Testing/files.cmake @@ -1,13 +1,13 @@ set(MODULE_IMAGE_TESTS - + ) set(MODULE_TESTIMAGES US4DCyl.pic.gz Pic3D.pic.gz Pic2DplusT.pic.gz BallBinary30x30x30.pic.gz Png2D-bw.png binary.stl ball.stl ) diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp index 7a910aec58..5829c201cd 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp @@ -1,509 +1,511 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilter.h" #include "mitkAbstractTransformGeometry.h" //#include "mitkImageMapperGL2D.h" #include #include #include #include #include "vtkMitkThickSlicesFilter.h" #include #include #include #include #include #include #include #include "pic2vtk.h" mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(NULL) { + MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead."; + m_Reslicer = vtkImageReslice::New(); m_TargetTimestep = 0; m_InPlaneResampleExtentByGeometry = true; m_ResliceInterpolationProperty = NULL;//VtkResliceInterpolationProperty::New(); //TODO initial with value m_ThickSlicesMode = 0; m_ThickSlicesNum = 1; } mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter() { if(m_ResliceInterpolationProperty!=NULL)m_ResliceInterpolationProperty->Delete(); m_Reslicer->Delete(); } void mitk::ExtractDirectedPlaneImageFilter::GenerateData() { // A world geometry must be set... if ( m_WorldGeometry == NULL ) { itkWarningMacro(<<"No world geometry has been set. Returning."); return; } Image *input = const_cast< ImageToImageFilter::InputImageType* >( this->GetInput() ); input->Update(); if ( input == NULL ) { itkWarningMacro(<<"No input set."); return; } const TimeSlicedGeometry *inputTimeGeometry = input->GetTimeSlicedGeometry(); if ( ( inputTimeGeometry == NULL ) || ( inputTimeGeometry->GetTimeSteps() == 0 ) ) { itkWarningMacro(<<"Error reading input image geometry."); return; } // Get the target timestep; if none is set, use the lowest given. unsigned int timestep = 0; if ( ! m_TargetTimestep ) { ScalarType time = m_WorldGeometry->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { timestep = inputTimeGeometry->MSToTimeStep( time ); } } else timestep = m_TargetTimestep; if ( inputTimeGeometry->IsValidTime( timestep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<IsVolumeSet( timestep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<GetLargestPossibleRegion(); requestedRegion.SetIndex( 3, timestep ); requestedRegion.SetSize( 3, 1 ); requestedRegion.SetSize( 4, 1 ); input->SetRequestedRegion( &requestedRegion ); input->Update(); vtkImageData* inputData = input->GetVtkImageData( timestep ); if ( inputData == NULL ) { itkWarningMacro(<<"Could not extract vtk image data for given timestep"<GetSpacing( spacing ); // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; Vector3D rightInIndex, bottomInIndex; assert( input->GetTimeSlicedGeometry() == inputTimeGeometry ); // take transform of input image into account Geometry3D* inputGeometry = inputTimeGeometry->GetGeometry3D( timestep ); if ( inputGeometry == NULL ) { itkWarningMacro(<<"There is no Geometry3D at given timestep "<( m_WorldGeometry ) != NULL ) { const PlaneGeometry *planeGeometry = static_cast< const PlaneGeometry * >( m_WorldGeometry ); origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. inputGeometry->WorldToIndex( right, rightInIndex ); inputGeometry->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); //origin += right * ( mmPerPixel[0] * 0.5 ); //origin += bottom * ( mmPerPixel[1] * 0.5 ); //widthInMM -= mmPerPixel[0]; //heightInMM -= mmPerPixel[1]; // Use inverse transform of the input geometry for reslicing the 3D image m_Reslicer->SetResliceTransform( inputGeometry->GetVtkTransform()->GetLinearInverse() ); // Set background level to TRANSLUCENT (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -32768 ); // Check if a reference geometry does exist (as would usually be the case for // PlaneGeometry). // Note: this is currently not strictly required, but could facilitate // correct plane clipping. if ( m_WorldGeometry->GetReferenceGeometry() ) { // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. boundsInitialized = this->CalculateClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds ); } } // Do we have an AbstractTransformGeometry? else if ( dynamic_cast< const AbstractTransformGeometry * >( m_WorldGeometry ) ) { const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputGeometry->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see Geometry2DDataVtkMapper3D) m_Reslicer->SetBackgroundLevel( -1023 ); composedResliceTransform->Delete(); } else { itkWarningMacro(<<"World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry."); return; } // Make sure that the image to be resliced has a certain minimum size. if ( (extent[0] <= 2) && (extent[1] <= 2) ) { itkWarningMacro(<<"Image is too small to be resliced..."); return; } vtkImageChangeInformation * unitSpacingImageFilter = vtkImageChangeInformation::New() ; unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInput( inputData ); m_Reslicer->SetInput( unitSpacingImageFilter->GetOutput() ); unitSpacingImageFilter->Delete(); //m_Reslicer->SetInput( inputData ); m_Reslicer->SetOutputDimensionality( 2 ); m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); Vector2D pixelsPerMM; pixelsPerMM[0] = 1.0 / mmPerPixel[0]; pixelsPerMM[1] = 1.0 / mmPerPixel[1]; //calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk( origin, originArray ); m_Reslicer->SetResliceAxesOrigin( originArray ); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk( right.Get_vnl_vector(), cosines ); // direction of the Y-axis of the sampled result vnl2vtk( bottom.Get_vnl_vector(), cosines + 3 ); // normal of the plane vnl2vtk( normal.Get_vnl_vector(), cosines + 6 ); m_Reslicer->SetResliceAxesDirectionCosines( cosines ); int xMin, xMax, yMin, yMax; if ( boundsInitialized ) { xMin = static_cast< int >( bounds[0] / mmPerPixel[0] );//+ 0.5 ); xMax = static_cast< int >( bounds[1] / mmPerPixel[0] );//+ 0.5 ); yMin = static_cast< int >( bounds[2] / mmPerPixel[1] );//+ 0.5); yMax = static_cast< int >( bounds[3] / mmPerPixel[1] );//+ 0.5 ); } else { // If no reference geometry is available, we also don't know about the // maximum plane size; so the overlap is just ignored xMin = yMin = 0; xMax = static_cast< int >( extent[0] - pixelsPerMM[0] );//+ 0.5 ); yMax = static_cast< int >( extent[1] - pixelsPerMM[1] );//+ 0.5 ); } m_Reslicer->SetOutputSpacing( mmPerPixel[0], mmPerPixel[1], 1.0 ); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. m_Reslicer->SetOutputExtent( xMin, xMax-1, yMin, yMax-1, 0, 1 ); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. m_Reslicer->Modified(); m_Reslicer->ReleaseDataFlagOn(); m_Reslicer->Update(); // 1. Check the result vtkImageData* reslicedImage = m_Reslicer->GetOutput(); //mitkIpPicDescriptor *pic = Pic2vtk::convert( reslicedImage ); if((reslicedImage == NULL) || (reslicedImage->GetDataDimension() < 1)) { itkWarningMacro(<<"Reslicer returned empty image"); return; } unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(input->GetPixelType(), 2, dimensions ); //resultImage->Initialize( pic ); resultImage->SetSpacing( spacingVector ); //resultImage->SetPicVolume( pic ); //mitkIpPicFree(pic); /*unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(m_Reslicer->GetOutput()); resultImage->Initialize(inputImage->GetPixelType(), 2, dimensions); resultImage->SetSpacing(spacingVector); resultImage->SetSlice(m_Reslicer->GetOutput());*/ } void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } bool mitk::ExtractDirectedPlaneImageFilter ::CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if(boundingGeometry->GetImageGeometry()) { points->InsertPoint( 0, bbMin[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 1, bbMin[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 2, bbMin[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 3, bbMin[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 4, bbMax[0]-0.5, bbMin[1]-0.5, bbMin[2]-0.5 ); points->InsertPoint( 5, bbMax[0]-0.5, bbMin[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 6, bbMax[0]-0.5, bbMax[1]-0.5, bbMax[2]-0.5 ); points->InsertPoint( 7, bbMax[0]-0.5, bbMax[1]-0.5, bbMin[2]-0.5 ); } else { points->InsertPoint( 0, bbMin[0], bbMin[1], bbMin[2] ); points->InsertPoint( 1, bbMin[0], bbMin[1], bbMax[2] ); points->InsertPoint( 2, bbMin[0], bbMax[1], bbMax[2] ); points->InsertPoint( 3, bbMin[0], bbMax[1], bbMin[2] ); points->InsertPoint( 4, bbMax[0], bbMin[1], bbMin[2] ); points->InsertPoint( 5, bbMax[0], bbMin[1], bbMax[2] ); points->InsertPoint( 6, bbMax[0], bbMax[1], bbMax[2] ); points->InsertPoint( 7, bbMax[0], bbMax[1], bbMin[2] ); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate( planeGeometry->GetVtkTransform()->GetLinearInverse() ); transform->Concatenate( boundingGeometry->GetVtkTransform() ); transform->TransformPoints( points, newPoints ); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero( newPoints, 0, 1, bounds ); this->LineIntersectZero( newPoints, 1, 2, bounds ); this->LineIntersectZero( newPoints, 2, 3, bounds ); this->LineIntersectZero( newPoints, 3, 0, bounds ); this->LineIntersectZero( newPoints, 0, 4, bounds ); this->LineIntersectZero( newPoints, 1, 5, bounds ); this->LineIntersectZero( newPoints, 2, 6, bounds ); this->LineIntersectZero( newPoints, 3, 7, bounds ); this->LineIntersectZero( newPoints, 4, 5, bounds ); this->LineIntersectZero( newPoints, 5, 6, bounds ); this->LineIntersectZero( newPoints, 6, 7, bounds ); this->LineIntersectZero( newPoints, 7, 4, bounds ); // clean up vtk data points->Delete(); newPoints->Delete(); if ( (bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0) ) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const float *planeSpacing = planeGeometry->GetFloatSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractDirectedPlaneImageFilter ::LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ) { vtkFloatingPointType point1[3]; vtkFloatingPointType point2[3]; points->GetPoint( p1, point1 ); points->GetPoint( p2, point2 ); if ( (point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2]) ) { double x, y; x = ( point1[0] * point2[2] - point1[2] * point2[0] ) / ( point2[2] - point1[2] ); y = ( point1[1] * point2[2] - point1[2] * point2[1] ) / ( point2[2] - point1[2] ); if ( x < bounds[0] ) { bounds[0] = x; } if ( x > bounds[1] ) { bounds[1] = x; } if ( y < bounds[2] ) { bounds[2] = y; } if ( y > bounds[3] ) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h index 8161b01819..5afd23ced0 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.h @@ -1,121 +1,124 @@ /*=================================================================== 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 mitkExtractDirectedPlaneImageFilter_h_Included #define mitkExtractDirectedPlaneImageFilter_h_Included #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "vtkImageReslice.h" #include "mitkVtkResliceInterpolationProperty.h" #define setMacro(name,type) \ virtual void Set##name (type _arg) \ { \ if (this->m_##name != _arg) \ { \ this->m_##name = _arg; \ } \ } #define getMacro(name,type) \ virtual type Get##name () \ { \ return m_##name; \ } class vtkPoints; namespace mitk { - /** +/** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief Extracts a 2D slice of arbitrary geometry from a 3D or 4D image. \sa mitkImageMapper2D \ingroup ImageToImageFilter This class takes a 3D or 4D mitk::Image as input and tries to extract one slice from it. This slice can be arbitrary oriented in space. The 2D slice is resliced by a vtk::ResliceImage filter if not perpendicular to the input image. The world geometry of the plane to be extracted image must be given as an input to the filter in order to correctly calculate world coordinates of the extracted slice. Setting a timestep from which the plane should be extracted is optional. Output will not be set if there was a problem extracting the desired slice. Last contributor: $Author: T. Schwarz$ - */ +*/ class ImageExtraction_EXPORT ExtractDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(ExtractDirectedPlaneImageFilter); itkSetMacro( WorldGeometry, Geometry2D* ); // The Reslicer is accessible to configure the desired interpolation; // (See vtk::ImageReslice class for documentation). // Misusage is at your own risk... itkGetMacro( Reslicer, vtkImageReslice* ); // The target timestep in a 4D image from which the 2D plane is supposed // to be extracted. itkSetMacro( TargetTimestep, unsigned int ); itkGetMacro( TargetTimestep, unsigned int ); itkSetMacro( InPlaneResampleExtentByGeometry, bool ); itkGetMacro( InPlaneResampleExtentByGeometry, bool ); setMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); itkGetMacro( ResliceInterpolationProperty, VtkResliceInterpolationProperty* ); setMacro( IsMapperMode, bool ); getMacro( IsMapperMode, bool ); protected: ExtractDirectedPlaneImageFilter(); // purposely hidden virtual ~ExtractDirectedPlaneImageFilter(); virtual void GenerateData(); virtual void GenerateOutputInformation(); bool CalculateClippedPlaneBounds( const Geometry3D *boundingGeometry, const PlaneGeometry *planeGeometry, vtkFloatingPointType *bounds ); bool LineIntersectZero( vtkPoints *points, int p1, int p2, vtkFloatingPointType *bounds ); const Geometry2D* m_WorldGeometry; vtkImageReslice * m_Reslicer; unsigned int m_TargetTimestep; bool m_InPlaneResampleExtentByGeometry; int m_ThickSlicesMode; int m_ThickSlicesNum; bool m_IsMapperMode; VtkResliceInterpolationProperty* m_ResliceInterpolationProperty; }; } // namespace mitk #endif // mitkExtractDirectedPlaneImageFilter_h_Included diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp index ec6ce80698..d58c9cd074 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp @@ -1,296 +1,297 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "itkImageRegionIterator.h" #include mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew() :m_CurrentWorldGeometry2D(NULL), m_ActualInputTimestep(-1) { + MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew() { } void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData(){ mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0); if ( !inputImage ) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!"); return; } m_ImageGeometry = inputImage->GetGeometry(); //If no timestep is set, the lowest given will be selected const mitk::TimeSlicedGeometry* inputTimeGeometry = this->GetInput()->GetTimeSlicedGeometry(); if ( m_ActualInputTimestep == -1) { ScalarType time = m_CurrentWorldGeometry2D->GetTimeBounds()[0]; if ( time > ScalarTypeNumericTraits::NonpositiveMin() ) { m_ActualInputTimestep = inputTimeGeometry->MSToTimeStep( time ); } } if ( inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry."); return; } else if ( inputImage->GetDimension() == 4 ) { mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New(); timeselector->SetInput( inputImage ); timeselector->SetTimeNr( m_ActualInputTimestep ); timeselector->UpdateLargestPossibleRegion(); inputImage = timeselector->GetOutput(); } else if ( inputImage->GetDimension() == 2) { mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast( inputImage.GetPointer() ); ImageToImageFilter::SetNthOutput( 0, resultImage); return; } if ( !m_CurrentWorldGeometry2D ) { MITK_ERROR<< "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldGeometry2D set" << std::endl; return; } AccessFixedDimensionByItk( inputImage, ItkSliceExtraction, 3 ); }//Generate Data void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation () { Superclass::GenerateOutputInformation(); } /* * The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image * Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is divided by two * (similar to the shannon theorem). */ template void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::Image SliceImageType; typedef itk::ImageRegionConstIterator< SliceImageType > SliceIterator; //Creating an itk::Image that represents the sampled slice typename SliceImageType::Pointer resultSlice = SliceImageType::New(); typename SliceImageType::IndexType start; start[0] = 0; start[1] = 0; Point3D origin = m_CurrentWorldGeometry2D->GetOrigin(); Vector3D right = m_CurrentWorldGeometry2D->GetAxisVector(0); Vector3D bottom = m_CurrentWorldGeometry2D->GetAxisVector(1); //Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing(); float minSpacing = newPixelSpacing[0]; for (unsigned int i = 1; i < newPixelSpacing.Size(); i++) { if (newPixelSpacing[i] < minSpacing ) { minSpacing = newPixelSpacing[i]; } } newPixelSpacing[0] = 0.5*minSpacing; newPixelSpacing[1] = 0.5*minSpacing; newPixelSpacing[2] = 0.5*minSpacing; float pixelSpacing[2]; pixelSpacing[0] = newPixelSpacing[0]; pixelSpacing[1] = newPixelSpacing[1]; //Calculating the size of the sampled slice typename SliceImageType::SizeType size; Vector2D extentInMM; extentInMM[0] = m_CurrentWorldGeometry2D->GetExtentInMM(0); extentInMM[1] = m_CurrentWorldGeometry2D->GetExtentInMM(1); //The maximum extent is the lenght of the diagonal of the considered plane double maxExtent = sqrt(extentInMM[0]*extentInMM[0]+extentInMM[1]*extentInMM[1]); unsigned int xTranlation = (maxExtent-extentInMM[0]); unsigned int yTranlation = (maxExtent-extentInMM[1]); size[0] = (maxExtent+xTranlation)/newPixelSpacing[0]; size[1] = (maxExtent+yTranlation)/newPixelSpacing[1]; //Creating an ImageRegion Object typename SliceImageType::RegionType region; region.SetSize( size ); region.SetIndex( start ); //Defining the image`s extent and origin by passing the region to it and allocating memory for it resultSlice->SetRegions( region ); resultSlice->SetSpacing( pixelSpacing ); resultSlice->Allocate(); /* * Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a different bounding box and spacing) * The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which represents the rotation */ right.Normalize(); bottom.Normalize(); //Here we translate the origin to adapt the new geometry to the previous calculated extent origin[0] -= xTranlation*right[0]+yTranlation*bottom[0]; origin[1] -= xTranlation*right[1]+yTranlation*bottom[1]; origin[2] -= xTranlation*right[2]+yTranlation*bottom[2]; //Putting it together for the new geometry mitk::Geometry3D::Pointer newSliceGeometryTest = dynamic_cast(m_CurrentWorldGeometry2D->Clone().GetPointer()); newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true); //Workaround because of BUG (#6505) newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix(m_CurrentWorldGeometry2D->GetIndexToWorldTransform()->GetMatrix()); //Workaround end newSliceGeometryTest->SetOrigin(origin); ScalarType bounds[6]={0, size[0], 0, size[1], 0, 1}; newSliceGeometryTest->SetBounds(bounds); newSliceGeometryTest->SetSpacing(newPixelSpacing); newSliceGeometryTest->Modified(); //Workaround because of BUG (#6505) itk::MatrixOffsetTransformBase::MatrixType tempTransform = newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix(); //Workaround end /* * Now we iterate over the recently created slice. * For each slice - pixel we check whether there is an according * pixel in the input - image which can be set in the slice. * In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry */ Point3D currentSliceIndexPointIn2D; Point3D currentImageWorldPointIn3D; typename InputImageType::IndexType inputIndex; SliceIterator sliceIterator ( resultSlice, resultSlice->GetLargestPossibleRegion() ); sliceIterator.GoToBegin(); while ( !sliceIterator.IsAtEnd() ) { /* * Here we add 0.5 to to assure that the indices are correctly transformed. * (Because of the 0.5er Bug) */ currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0]+0.5; currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1]+0.5; currentSliceIndexPointIn2D[2] = 0; newSliceGeometryTest->IndexToWorld( currentSliceIndexPointIn2D, currentImageWorldPointIn3D ); m_ImageGeometry->WorldToIndex( currentImageWorldPointIn3D, inputIndex); if ( m_ImageGeometry->IsIndexInside( inputIndex )) { resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); } else { resultSlice->SetPixel( sliceIterator.GetIndex(), 0); } ++sliceIterator; } Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(resultSlice, resultImage, NULL, false); resultImage->SetClonedGeometry(newSliceGeometryTest); //Workaround because of BUG (#6505) resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform); //Workaround end } ///**TEST** May ba a little bit more efficient but doesn`t already work/ //right.Normalize(); //bottom.Normalize(); //Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/; //unsigned int columns ( 0 ); /**ENDE**/ /****TEST***/ //SliceImageType::IndexType index = sliceIterator.GetIndex(); //if ( columns == (extentInPixel[0]) ) //{ //If we are at the end of a row, then we have to go to the beginning of the next row //currentImagePointIn3D = origin; //currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1]; //columns = 0; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //else //{ //// //if ( columns != 0 ) //{ //currentImagePointIn3D += newPixelSpacing[0]*right; //} //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //else if (currentImagePointIn3D == origin) //{ //Point3D temp; //temp[0] = bottom[0]*newPixelSpacing[0]*0.5; //temp[1] = bottom[1]*newPixelSpacing[1]*0.5; //temp[2] = bottom[2]*newPixelSpacing[2]*0.5; //origin[0] += temp[0]; //origin[1] += temp[1]; //origin[2] += temp[2]; //currentImagePointIn3D = origin; //m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ //resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //} /****TEST ENDE****/ diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h index 67d0ee37ce..507abc28ea 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.h @@ -1,93 +1,96 @@ /*=================================================================== 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 mitkExtractDirectedPlaneImageFilterNew_h_Included #define mitkExtractDirectedPlaneImageFilterNew_h_Included #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "itkImage.h" #include "mitkITKImageImport.h" namespace mitk { /** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief A filter that can extract a 2D slice from a 3D or 4D image especially if the image`s axes are rotated \sa ContourTool \sa SegTool2D \sa ExtractImageFilter \sa OverwriteSliceImageFilter \sa OverwriteDirectedPlaneImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkSegmentationTechnicalPage This class takes an 3D or 4D mitk::Image as input and extracts a slice from it. If you work with a 4D image as input you have to specify the desired timestep at which the slice shall be extracted, otherwise the lowest given timestep is selected by default. The special feature of this filter is, that the planes of the input image can be rotated in any way. To assure a proper extraction you have to set the currentWorldGeometry2D with you can obtain from the BaseRenderer, respectively the positionEvent send by the renderer. The output will not be set if there was a problem with the input image $Author: fetzer $ */ class ImageExtraction_EXPORT ExtractDirectedPlaneImageFilterNew : public ImageToImageFilter { public: mitkClassMacro(ExtractDirectedPlaneImageFilterNew, ImageToImageFilter); itkNewMacro(Self); /** \brief Set macro for the current worldgeometry \a Parameter The current wordgeometry that describes the position (rotation, translation) of the plane (and therefore the slice to be extracted) in our 3D(+t) image */ itkSetMacro(CurrentWorldGeometry2D, Geometry3D* ); itkSetMacro(ImageGeometry, Geometry3D* ); /** \brief Set macro for the current timestep \a Parameter The timestep of the image from which the slice shall be extracted */ itkSetMacro(ActualInputTimestep, int); protected: ExtractDirectedPlaneImageFilterNew(); virtual ~ExtractDirectedPlaneImageFilterNew(); virtual void GenerateData(); virtual void GenerateOutputInformation(); private: const Geometry3D* m_CurrentWorldGeometry2D; const Geometry3D* m_ImageGeometry; int m_ActualInputTimestep; template void ItkSliceExtraction (itk::Image* inputImage); }; }//namespace #endif diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp index 474dffb6ac..40cb6bc7ff 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp @@ -1,244 +1,245 @@ /*=================================================================== 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 "mitkExtractImageFilter.h" #include "mitkImageCast.h" #include "mitkPlaneGeometry.h" #include "mitkITKImageImport.h" #include "mitkImageTimeSelector.h" #include #include mitk::ExtractImageFilter::ExtractImageFilter() :m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0) { + MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractImageFilter::~ExtractImageFilter() { } void mitk::ExtractImageFilter::GenerateData() { Image::ConstPointer input = ImageToImageFilter::GetInput(0); if ( (input->GetDimension() > 4) || (input->GetDimension() < 2) ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry."); return; } else if (input->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input = timeSelector->GetOutput(); } else if (input->GetDimension() == 2) { Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast(input.GetPointer()); ImageToImageFilter::SetNthOutput( 0, resultImage ); return; } if ( m_SliceDimension >= input->GetDimension() ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } AccessFixedDimensionByItk( input, ItkImageProcessing, 3 ); // set a nice geometry for display and point transformations Geometry3D* inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry(); if (!inputImageGeometry) { MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl; return; } PlaneGeometry::PlaneOrientation orientation = PlaneGeometry::Transversal; switch ( m_SliceDimension ) { default: case 2: orientation = PlaneGeometry::Transversal; break; case 1: orientation = PlaneGeometry::Frontal; break; case 0: orientation = PlaneGeometry::Sagittal; break; } PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false ); Image::Pointer resultImage = ImageToImageFilter::GetOutput(); planeGeometry->ChangeImageGeometryConsideringOriginOffset(true); resultImage->SetGeometry( planeGeometry ); } template void mitk::ExtractImageFilter::ItkImageProcessing( itk::Image* itkImage ) { // use the itk::ExtractImageFilter to get a 2D image typedef itk::Image< TPixel, VImageDimension > ImageType3D; typedef itk::Image< TPixel, VImageDimension-1 > ImageType2D; typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion(); inSliceRegion.SetSize( m_SliceDimension, 0 ); typedef itk::ExtractImageFilter ExtractImageFilterType; typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New(); sliceExtractor->SetInput( itkImage ); inSliceRegion.SetIndex( m_SliceDimension, m_SliceIndex ); sliceExtractor->SetExtractionRegion( inSliceRegion ); // calculate the output sliceExtractor->UpdateLargestPossibleRegion(); typename ImageType2D::Pointer slice = sliceExtractor->GetOutput(); // re-import to MITK Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(slice, resultImage, NULL, false); } /* * What is the input requested region that is required to produce the output * requested region? By default, the largest possible region is always * required but this is overridden in many subclasses. For instance, for an * image processing filter where an output pixel is a simple function of an * input pixel, the input requested region will be set to the output * requested region. For an image processing filter where an output pixel is * a function of the pixels in a neighborhood of an input pixel, then the * input requested region will need to be larger than the output requested * region (to avoid introducing artificial boundary conditions). This * function should never request an input region that is outside the the * input largest possible region (i.e. implementations of this method should * crop the input requested region at the boundaries of the input largest * possible region). */ void mitk::ExtractImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); Image::Pointer output = this->GetOutput(); if (input->GetDimension() == 2) { input->SetRequestedRegionToLargestPossibleRegion(); return; } Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(0, 0); requestedRegion.SetIndex(1, 0); requestedRegion.SetIndex(2, 0); requestedRegion.SetSize(0, input->GetDimension(0)); requestedRegion.SetSize(1, input->GetDimension(1)); requestedRegion.SetSize(2, input->GetDimension(2)); requestedRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // only one slice needed requestedRegion.SetSize( m_SliceDimension, 1 ); input->SetRequestedRegion( &requestedRegion ); } /* * Generate the information decribing the output data. The default * implementation of this method will copy information from the input to the * output. A filter may override this method if its output will have different * information than its input. For instance, a filter that shrinks an image will * need to provide an implementation for this method that changes the spacing of * the pixels. Such filters should call their superclass' implementation of this * method prior to changing the information values they need (i.e. * GenerateOutputInformation() should call * Superclass::GenerateOutputInformation() prior to changing the information. */ void mitk::ExtractImageFilter::GenerateOutputInformation() { Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; if ( m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2 ) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } unsigned int sliceDimension( m_SliceDimension ); if ( input->GetDimension() == 2) { sliceDimension = 2; } unsigned int tmpDimensions[2]; switch ( sliceDimension ) { default: case 2: // orientation = PlaneGeometry::Transversal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(1); break; case 1: // orientation = PlaneGeometry::Frontal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(2); break; case 0: // orientation = PlaneGeometry::Sagittal; tmpDimensions[0] = input->GetDimension(1); tmpDimensions[1] = input->GetDimension(2); break; } output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/); // initialize the spacing of the output /* Vector3D spacing = input->GetSlicedGeometry()->GetSpacing(); if(input->GetDimension()>=2) spacing[2]=spacing[1]; else spacing[2] = 1.0; output->GetSlicedGeometry()->SetSpacing(spacing); */ output->SetPropertyList(input->GetPropertyList()->Clone()); } diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.h b/Modules/ImageExtraction/mitkExtractImageFilter.h index ef707c168c..8196563b7f 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.h +++ b/Modules/ImageExtraction/mitkExtractImageFilter.h @@ -1,98 +1,101 @@ /*=================================================================== 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 mitkExtractImageFilter_h_Included #define mitkExtractImageFilter_h_Included #include "mitkCommon.h" #include "ImageExtractionExports.h" #include "mitkImageToImageFilter.h" #include "itkImage.h" namespace mitk { /** + \deprecated This class is deprecated. Use mitk::ExtractSliceFilter instead. + \sa ExtractSliceFilter + \brief Extracts a 2D slice from a 3D image. \sa SegTool2D \sa OverwriteSliceImageFilter \ingroup Process \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to extract one slice from it. Two parameters determine which slice is extracted: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. Output will not be set if there was a problem extracting the desired slice. Last contributor: $Author$ */ class ImageExtraction_EXPORT ExtractImageFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractImageFilter, ImageToImageFilter); itkNewMacro(ExtractImageFilter); /** \brief Which slice to extract (first one has index 0). */ itkSetMacro(SliceIndex, unsigned int); itkGetConstMacro(SliceIndex, unsigned int); /** \brief The orientation of the slice to be extracted. \a Parameter SliceDimension Number of the dimension which is constant for all pixels of the desired slice (e.g. 2 for transversal) */ itkSetMacro(SliceDimension, unsigned int); itkGetConstMacro(SliceDimension, unsigned int); /** \brief Time step of the image to be extracted. */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); protected: ExtractImageFilter(); // purposely hidden virtual ~ExtractImageFilter(); virtual void GenerateOutputInformation(); virtual void GenerateInputRequestedRegion(); virtual void GenerateData(); template void ItkImageProcessing( itk::Image* image ); unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; }; } // namespace #endif diff --git a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp index 55b3db52db..d07c783a88 100644 --- a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp +++ b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp @@ -1,1008 +1,1007 @@ /*=================================================================== 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 "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkDataNodeFactory.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include #include #include #include #include #include #include #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; actionToSliceDimension[new QAction("Transversal (red window)", 0)] = 2; actionToSliceDimension[new QAction("Sagittal (green window)", 0)] = 0; actionToSliceDimension[new QAction("Coronal (blue window)", 0)] = 1; return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_MultiWidget(NULL), m_ToolManager(NULL), m_Initialized(false), m_LastSliceDimension(2), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false) { m_SurfaceInterpolator = mitk::SurfaceInterpolationController::GetInstance(); QHBoxLayout* layout = new QHBoxLayout(this); m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QGridLayout* grid = new QGridLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_RBtnEnable3DInterpolation = new QRadioButton("3D",this); connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); - connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SIGNAL(Signal3DInterpolationEnabled(bool))); m_RBtnEnable3DInterpolation->setChecked(true); grid->addWidget(m_RBtnEnable3DInterpolation,0,0); m_BtnAccept3DInterpolation = new QPushButton("Accept", this); m_BtnAccept3DInterpolation->setEnabled(false); connect(m_BtnAccept3DInterpolation, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); grid->addWidget(m_BtnAccept3DInterpolation, 0,1); m_CbShowMarkers = new QCheckBox("Show Position Nodes", this); m_CbShowMarkers->setChecked(true); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); grid->addWidget(m_CbShowMarkers,0,2); m_RBtnEnable2DInterpolation = new QRadioButton("2D",this); connect(m_RBtnEnable2DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On2DInterpolationEnabled(bool))); grid->addWidget(m_RBtnEnable2DInterpolation,1,0); m_BtnAcceptInterpolation = new QPushButton("Accept", this); m_BtnAcceptInterpolation->setEnabled( false ); connect( m_BtnAcceptInterpolation, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()) ); grid->addWidget(m_BtnAcceptInterpolation,1,1); m_BtnAcceptAllInterpolations = new QPushButton("... for all slices", this); m_BtnAcceptAllInterpolations->setEnabled( false ); connect( m_BtnAcceptAllInterpolations, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()) ); grid->addWidget(m_BtnAcceptAllInterpolations,1,2); m_RBtnDisableInterpolation = new QRadioButton("Disable", this); connect(m_RBtnDisableInterpolation, SIGNAL(toggled(bool)), this, SLOT(OnInterpolationDisabled(bool))); grid->addWidget(m_RBtnDisableInterpolation, 2,0); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(255.0,255.0,0.0) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(SurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage& storage ) { m_DataStorage = &storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, QmitkStdMultiWidget* multiWidget) { if (m_Initialized) { // remove old observers if (m_ToolManager) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); } if (m_MultiWidget) { disconnect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } //return; } m_MultiWidget = multiWidget; connect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); // connect to the steppers of the three multi widget widgets. after each change, call the interpolator if (m_MultiWidget) { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); m_TimeStep.resize(3); m_TimeStep[2] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalTimeChanged ); TTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalSliceChanged ); TSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); m_TimeStep[0] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalTimeChanged ); STimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalSliceChanged ); SSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); m_TimeStep[1] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalTimeChanged ); FTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalSliceChanged ); FSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } } } m_Initialized = true; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_MultiWidget) { mitk::SliceNavigationController* slicer; if(m_MultiWidget->mitkWidget1 != NULL) { slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); } if(m_MultiWidget->mitkWidget2 != NULL) { slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); } if(m_MultiWidget->mitkWidget3 != NULL) { slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); delete m_Timer; } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { On3DInterpolationActivated( true); } } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } void QmitkSlicesInterpolator::OnTransversalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[2] = event.GetPos(); if (m_LastSliceDimension == 2) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSagittalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[0] = event.GetPos(); if (m_LastSliceDimension == 0) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnFrontalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[1] = event.GetPos(); if (m_LastSliceDimension == 1) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnTransversalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 2 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget1->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnSagittalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 0 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget2->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnFrontalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 1 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget3->GetRenderWindow())->RequestUpdate(); } } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, unsigned int windowID) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); mitk::TimeSlicedGeometry* tsg = event.GetTimeSlicedGeometry(); if (tsg && m_TimeStep.size() > windowID) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[windowID] ); return true; } } } catch(std::bad_cast) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, timeStep ); m_FeedbackNode->SetData( interpolation ); // Workaround for Bug 11318 if ((interpolation.IsNotNull()) && (interpolation->GetGeometry() != NULL)) { if(clickedSliceDimension == 1) { mitk::Point3D orig = interpolation->GetGeometry()->GetOrigin(); orig[0] = orig[0]; orig[1] = orig[1] + 0.5; orig[2] = orig[2]; interpolation->GetGeometry()->SetOrigin(orig); } } // Workaround for Bug 11318 END m_LastSliceDimension = clickedSliceDimension; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::SurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); if(interpolatedSurface.IsNotNull()) { m_BtnAccept3DInterpolation->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); m_InterpolatedSurfaceNode->SetVisibility(true); m_3DContourNode->SetVisibility(true, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) && !m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode); m_DataStorage->Add(m_InterpolatedSurfaceNode); } } else if (interpolatedSurface.IsNull()) { m_BtnAccept3DInterpolation->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } if (m_MultiWidget) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); mitk::OverwriteSliceImageFilter::Pointer slicewriter = mitk::OverwriteSliceImageFilter::New(); slicewriter->SetInput( m_Segmentation ); slicewriter->SetCreateUndoInformation( true ); slicewriter->SetSliceImage( dynamic_cast(m_FeedbackNode->GetData()) ); slicewriter->SetSliceDimension( m_LastSliceDimension ); slicewriter->SetSliceIndex( m_LastSliceIndex ); slicewriter->SetTimeStep( m_TimeStep[m_LastSliceDimension] ); slicewriter->Update(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(unsigned int windowID) { // first creates a 3D diff image, then applies this diff to the segmentation if (m_Segmentation) { int sliceDimension(-1); int dummySliceIndex(-1); if (!GetSliceForWindowsID(windowID, sliceDimension, dummySliceIndex)) { return; // cannot determine slice orientation } //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); // create a diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( m_Segmentation ); mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); diffImage->Initialize( pixelType, 3, m_Segmentation->GetDimensions() ); memset( diffImage->GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); // now the diff image is all 0 unsigned int timeStep( m_TimeStep[windowID] ); // a slicewriter to create the diff image mitk::OverwriteSliceImageFilter::Pointer diffslicewriter = mitk::OverwriteSliceImageFilter::New(); diffslicewriter->SetCreateUndoInformation( false ); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetSliceDimension( sliceDimension ); diffslicewriter->SetTimeStep( timeStep ); unsigned int totalChangedSlices(0); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { diffslicewriter->SetSliceImage( interpolation ); diffslicewriter->SetSliceIndex( sliceIndex ); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Accept all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); // acutally apply the changes here mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(int windowID) { //this redirect is for calling from outside if (windowID < 0) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( (unsigned int)windowID ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); segmentationNode->SetData(s2iFilter->GetOutput()); m_RBtnDisableInterpolation->setChecked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { int windowID = iter->second; AcceptAllInterpolations( windowID ); } } catch(...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } //else //{ // m_DataStorage->Remove( m_FeedbackNode ); //} } } catch(...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); m_BtnAcceptAllInterpolations->setEnabled( on ); m_BtnAcceptInterpolation->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,0.0)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); float yellow[3] = {255.0,255.0,0.0}; if( currentColor[2] == yellow[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,255.0)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { int listID; bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if ((workingNode->IsSelected() && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult) { if (workingNode->GetIntProperty("3DInterpolationListID", listID)) { m_SurfaceInterpolator->SetCurrentListID(listID); if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { listID = m_SurfaceInterpolator->CreateNewContourList(); workingNode->SetIntProperty("3DInterpolationListID", listID); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(false); } mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( m_MultiWidget->GetRenderWindow3()->GetRenderer()->GetTimeStep() )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } else if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetWorkingImage(dynamic_cast(workingNode->GetData())); m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); } } QWidget::setEnabled( workingNode != NULL ); m_CbShowMarkers->setEnabled(m_3DInterpolationEnabled); } else if (!m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = mitk::GlobalInteraction::GetInstance()->GetFocus(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeSlicedGeometry), renderer->GetSlice() ); if ( renderer->GetCurrentWorldGeometry2DNode() ) { if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane1() ) { TranslateAndInterpolateChangedSlice( event, 2 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane2() ) { TranslateAndInterpolateChangedSlice( event, 0 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane3() ) { TranslateAndInterpolateChangedSlice( event, 1 ); } } } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } bool QmitkSlicesInterpolator::GetSliceForWindowsID(unsigned windowID, int& sliceDimension, int& sliceIndex) { mitk::BaseRenderer* renderer(NULL); // find sliceDimension for windowID: // windowID 2: transversal window = renderWindow1 // windowID 1: frontal window = renderWindow3 // windowID 0: sagittal window = renderWindow2 if ( m_MultiWidget ) { switch (windowID) { case 2: default: renderer = m_MultiWidget->mitkWidget1->GetRenderer(); break; case 1: renderer = m_MultiWidget->mitkWidget3->GetRenderer(); break; case 0: renderer = m_MultiWidget->mitkWidget2->GetRenderer(); break; } } if ( m_Segmentation && renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(timeSlicedGeometry->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( renderer->GetSlice() )); Interpolate( plane, m_TimeStep[windowID] ); return mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, sliceDimension, sliceIndex ); } } } return false; } void QmitkSlicesInterpolator::OnMultiWidgetDeleted(QObject*) { if (m_MultiWidget) { m_MultiWidget = NULL; } } diff --git a/Modules/QmitkExt/QmitkSlicesInterpolator.h b/Modules/QmitkExt/QmitkSlicesInterpolator.h index 1fe87695a5..c049dbc48c 100644 --- a/Modules/QmitkExt/QmitkSlicesInterpolator.h +++ b/Modules/QmitkExt/QmitkSlicesInterpolator.h @@ -1,300 +1,299 @@ /*=================================================================== 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 QmitkSlicesInterpolator_h_Included #define QmitkSlicesInterpolator_h_Included #include "mitkSliceNavigationController.h" #include "QmitkExtExports.h" #include "mitkSegmentationInterpolationController.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "mitkSurfaceInterpolationController.h" #include #include #include #include #include #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" //For running 3D interpolation in background #include #include #include #include namespace mitk { class ToolManager; class PlaneGeometry; } class QmitkStdMultiWidget; class QPushButton; //Enhancement for 3D Interpolation //class QRadioButton; //class QGroupBox; //class QCheckBox; /** \brief GUI for slices interpolation. \ingroup ToolManagerEtAl \ingroup Widgets \sa QmitkInteractiveSegmentation \sa mitk::SegmentationInterpolation There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage While mitk::SegmentationInterpolation does the bookkeeping of interpolation (keeping track of which slices contain how much segmentation) and the algorithmic work, QmitkSlicesInterpolator is responsible to watch the GUI, to notice, which slice is currently visible. It triggers generation of interpolation suggestions and also triggers acception of suggestions. \todo show/hide feedback on demand Last contributor: $Author: maleike $ */ class QmitkExt_EXPORT QmitkSlicesInterpolator : public QWidget { Q_OBJECT public: QmitkSlicesInterpolator(QWidget* parent = 0, const char* name = 0); /** To be called once before real use. */ void Initialize(mitk::ToolManager* toolManager, QmitkStdMultiWidget* multiWidget); virtual ~QmitkSlicesInterpolator(); void SetDataStorage( mitk::DataStorage& storage ); mitk::DataStorage* GetDataStorage(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerWorkingDataModified(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerReferenceDataModified(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnTransversalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSagittalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnFrontalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnTransversalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSagittalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnFrontalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnInterpolationInfoChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSurfaceInterpolationInfoChanged(const itk::EventObject&); signals: void SignalRememberContourPositions(bool); void SignalShowMarkerNodes(bool); - void Signal3DInterpolationEnabled(bool); public slots: /** Call this from the outside to enable/disable interpolation */ void EnableInterpolation(bool); void Enable3DInterpolation(bool); /** Call this from the outside to accept all interpolations */ void FinishInterpolation(int windowID = -1); protected slots: /** Reaction to button clicks. */ void OnAcceptInterpolationClicked(); /* Opens popup to ask about which orientation should be interpolated */ void OnAcceptAllInterpolationsClicked(); /* Reaction to button clicks */ void OnAccept3DInterpolationClicked(); /* * Will trigger interpolation for all slices in given orientation (called from popup menu of OnAcceptAllInterpolationsClicked) */ void OnAcceptAllPopupActivated(QAction* action); /** Called on activation/deactivation */ void OnInterpolationActivated(bool); void On3DInterpolationActivated(bool); void OnMultiWidgetDeleted(QObject*); //Enhancement for 3D interpolation void On2DInterpolationEnabled(bool); void On3DInterpolationEnabled(bool); void OnInterpolationDisabled(bool); void OnShowMarkers(bool); void Run3DInterpolation(); void SurfaceInterpolationFinished(); void StartUpdateInterpolationTimer(); void StopUpdateInterpolationTimer(); void ChangeSurfaceColor(); protected: const std::map createActionToSliceDimension(); const std::map ACTION_TO_SLICEDIMENSION; void AcceptAllInterpolations(unsigned int windowID); /** Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a SliceNavigationController and calls Interpolate to further process this PlaneGeometry into an interpolation. \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController \param windowID is 2 for transversal, 1 for frontal, 0 for sagittal (similar to sliceDimension in other methods) */ bool TranslateAndInterpolateChangedSlice(const itk::EventObject& e, unsigned int windowID); /** Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated ToolManager) should be interpolated. The actual work is then done by our SegmentationInterpolation object. */ void Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep ); //void InterpolateSurface(); /** Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an interpolation. */ void UpdateVisibleSuggestion(); /** * Tries to figure out the slice position and orientation for a given render window. * \param windowID is 2 for transversal, 1 for frontal, 0 for sagittal (similar to sliceDimension in other methods) * \return false if orientation could not be determined */ bool GetSliceForWindowsID(unsigned windowID, int& sliceDimension, int& sliceIndex); mitk::SegmentationInterpolationController::Pointer m_Interpolator; mitk::SurfaceInterpolationController::Pointer m_SurfaceInterpolator; QmitkStdMultiWidget* m_MultiWidget; mitk::ToolManager* m_ToolManager; bool m_Initialized; unsigned int TSliceObserverTag; unsigned int SSliceObserverTag; unsigned int FSliceObserverTag; unsigned int TTimeObserverTag; unsigned int STimeObserverTag; unsigned int FTimeObserverTag; unsigned int InterpolationInfoChangedObserverTag; unsigned int SurfaceInterpolationInfoChangedObserverTag; QPushButton* m_BtnAcceptInterpolation; QPushButton* m_BtnAcceptAllInterpolations; //Enhancement for 3D Surface Interpolation QRadioButton* m_RBtnEnable2DInterpolation; QRadioButton* m_RBtnEnable3DInterpolation; QRadioButton* m_RBtnDisableInterpolation; QGroupBox* m_GroupBoxEnableExclusiveInterpolationMode; QPushButton* m_BtnAccept3DInterpolation; QCheckBox* m_CbShowMarkers; mitk::DataNode::Pointer m_FeedbackNode; mitk::DataNode::Pointer m_InterpolatedSurfaceNode; mitk::DataNode::Pointer m_3DContourNode; mitk::Image* m_Segmentation; unsigned int m_LastSliceDimension; unsigned int m_LastSliceIndex; std::vector m_TimeStep; // current time step of the render windows bool m_2DInterpolationEnabled; bool m_3DInterpolationEnabled; //unsigned int m_CurrentListID; mitk::WeakPointer m_DataStorage; QFuture m_Future; QFutureWatcher m_Watcher; QTimer* m_Timer; }; #endif diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp new file mode 100644 index 0000000000..180e493864 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -0,0 +1,97 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision: $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "mitkDiffSliceOperation.h" + +#include + +mitk::DiffSliceOperation::DiffSliceOperation():Operation(1) +{ + m_TimeStep = 0; + m_Slice = NULL; + m_Image = NULL; + m_WorldGeometry = NULL; + m_SliceGeometry = NULL; + m_ImageIsValid = false; +} + + +mitk::DiffSliceOperation::DiffSliceOperation(mitk::Image* imageVolume, + vtkImageData* slice, + AffineGeometryFrame3D* sliceGeometry, + unsigned int timestep, + AffineGeometryFrame3D* currentWorldGeometry):Operation(1) + +{ + m_WorldGeometry = currentWorldGeometry->Clone(); + m_SliceGeometry = sliceGeometry->Clone(); + + m_TimeStep = timestep; + + /*m_zlibSliceContainer = CompressedImageContainer::New(); + m_zlibSliceContainer->SetImage( slice );*/ + m_Slice = vtkSmartPointer::New(); + m_Slice->DeepCopy(slice); + + m_Image = imageVolume; + + if ( m_Image) { + /*add an observer to listen to the delete event of the image, this is necessary because the operation is then invalid*/ + itk::SimpleMemberCommand< DiffSliceOperation >::Pointer command = itk::SimpleMemberCommand< DiffSliceOperation >::New(); + command->SetCallbackFunction( this, &DiffSliceOperation::OnImageDeleted ); + //get the id of the observer, used to remove it later on + m_DeleteObserverTag = imageVolume->AddObserver( itk::DeleteEvent(), command ); + + + m_ImageIsValid = true; + } + else + m_ImageIsValid = false; + +} + +mitk::DiffSliceOperation::~DiffSliceOperation() +{ + + m_Slice = NULL; + m_WorldGeometry = NULL; + //m_zlibSliceContainer = NULL; + + if (m_ImageIsValid) + { + //if the image is still there, we have to remove the observer from it + m_Image->RemoveObserver( m_DeleteObserverTag ); + } + m_Image = NULL; +} + +vtkImageData* mitk::DiffSliceOperation::GetSlice() +{ + //Image::ConstPointer image = m_zlibSliceContainer->GetImage().GetPointer(); + return m_Slice; +} + +bool mitk::DiffSliceOperation::IsValid() +{ + return m_ImageIsValid && (m_Slice.GetPointer() != NULL) && (m_WorldGeometry.IsNotNull());//TODO improve +} + +void mitk::DiffSliceOperation::OnImageDeleted() +{ + //if our imageVolume is removed e.g. from the datastorage the operation is no lnger valid + m_ImageIsValid = false; +} \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h new file mode 100644 index 0000000000..f254211c4e --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.h @@ -0,0 +1,117 @@ +/*=================================================================== + +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 mitkDiffSliceOperation_h_Included +#define mitkDiffSliceOperation_h_Included + +#include "SegmentationExports.h" +#include "mitkCommon.h" +#include +//#include "mitkCompressedImageContainer.h" + +#include +#include +#include + + +namespace mitk +{ + /** \brief An Operation for applying an edited slice to the volume. + \sa DiffSliceOperationApplier + + The information for the operation is specified by properties: + + imageVolume the volume where the slice was extracted from. + slice the slice to be applied. + timestep the timestep in an 4D image. + currentWorldGeometry specifies the axis where the slice has to be applied in the volume. + + This Operation can be used to realize undo-redo functionality for e.g. segmentation purposes. + */ + class Segmentation_EXPORT DiffSliceOperation : public Operation + { + + public: + + mitkClassMacro(DiffSliceOperation, OperationActor); + + //itkNewMacro(DiffSliceOperation); + + //mitkNewMacro4Param(DiffSliceOperation,mitk::Image,mitk::Image,unsigned int, mitk::Geometry2D); + + /** \brief Creates an empty instance. + Note that it is not valid yet. The properties of the object have to be set. + */ + DiffSliceOperation(); + + /** \brief */ + DiffSliceOperation( mitk::Image* imageVolume, vtkImageData* slice, AffineGeometryFrame3D* sliceGeometry, unsigned int timestep, AffineGeometryFrame3D* currentWorldGeometry); + + /** \brief Check if it is a valid operation.*/ + bool IsValid(); + + /** \brief Set the image volume.*/ + void SetImage(mitk::Image* image){ this->m_Image = image;} + /** \brief Get th image volume.*/ + mitk::Image* GetImage(){return this->m_Image;} + + /** \brief Set thee slice to be applied.*/ + void SetImage(vtkImageData* slice){ this->m_Slice = slice;} + /** \brief Get the slice that is applied in the operation.*/ + vtkImageData* GetSlice(); + + /** \brief Get timeStep.*/ + void SetTimeStep(unsigned int timestep){this->m_TimeStep = timestep;} + /** \brief Set timeStep*/ + unsigned int GetTimeStep(){return this->m_TimeStep;} + + /** \brief Set the axis where the slice has to be applied in the volume.*/ + void SetSliceGeometry(AffineGeometryFrame3D* sliceGeometry){this->m_SliceGeometry = sliceGeometry;} + /** \brief Get the axis where the slice has to be applied in the volume.*/ + AffineGeometryFrame3D* GetSliceGeometry(){return this->m_SliceGeometry;} + + /** \brief Set the axis where the slice has to be applied in the volume.*/ + void SetCurrentWorldGeometry(AffineGeometryFrame3D* worldGeometry){this->m_WorldGeometry = worldGeometry;} + /** \brief Get the axis where the slice has to be applied in the volume.*/ + AffineGeometryFrame3D* GetWorldGeometry(){return this->m_WorldGeometry;} + + + protected: + + virtual ~DiffSliceOperation(); + + /** \brief Callback for image observer.*/ + void OnImageDeleted(); + + //CompressedImageContainer::Pointer m_zlibSliceContainer; + + mitk::Image* m_Image; + + vtkSmartPointer m_Slice; + + AffineGeometryFrame3D::Pointer m_SliceGeometry; + + unsigned int m_TimeStep; + + AffineGeometryFrame3D::Pointer m_WorldGeometry; + + bool m_ImageIsValid; + + unsigned long m_DeleteObserverTag; + + }; +} +#endif \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp new file mode 100644 index 0000000000..09e0d21381 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.cpp @@ -0,0 +1,77 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision: $ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "mitkDiffSliceOperationApplier.h" +#include "mitkRenderingManager.h" +#include + +mitk::DiffSliceOperationApplier::DiffSliceOperationApplier() +{ +} + +mitk::DiffSliceOperationApplier::~DiffSliceOperationApplier() +{ +} + +void mitk::DiffSliceOperationApplier::ExecuteOperation( Operation* operation ) +{ + DiffSliceOperation* imageOperation = dynamic_cast( operation ); + + //as we only support DiffSliceOperation return if operation is not type of DiffSliceOperation + if(!imageOperation) + return; + + + //chak if the operation is valid + if(imageOperation->IsValid()) + { + //the actual overwrite filter (vtk) + vtkSmartPointer reslice = vtkSmartPointer::New(); + + //Set the slice as 'input' + reslice->SetInputSlice(imageOperation->GetSlice()); + + //set overwrite mode to true to write back to the image volume + reslice->SetOverwriteMode(true); + reslice->Modified(); + + //a wrapper for vtkImageOverwrite + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( imageOperation->GetImage() ); + extractor->SetTimeStep( imageOperation->GetTimeStep() ); + extractor->SetWorldGeometry( dynamic_cast(imageOperation->GetWorldGeometry()) ); + extractor->SetVtkOutputRequest(true); + extractor->SetResliceTransformByGeometry( imageOperation->GetImage()->GetTimeSlicedGeometry()->GetGeometry3D( imageOperation->GetTimeStep() ) ); + + extractor->Modified(); + extractor->Update(); + + //make sure the modification is rendered + RenderingManager::GetInstance()->RequestUpdateAll(); + imageOperation->GetImage()->Modified(); + } +} + +//mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::s_Instance = NULL; + +mitk::DiffSliceOperationApplier* mitk::DiffSliceOperationApplier::GetInstance() +{ + //if(!s_Instance) + static DiffSliceOperationApplier* s_Instance = new DiffSliceOperationApplier(); + + return s_Instance; +} \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h new file mode 100644 index 0000000000..675aad602b --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperationApplier.h @@ -0,0 +1,63 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkDiffSliceOpertationApplier_h_Included +#define mitkDiffSliceOpertationApplier_h_Included + +#include "SegmentationExports.h" +#include "mitkCommon.h" +#include + +#include "mitkDiffSliceOperation.h" +#include +#include + +namespace mitk +{ + /** \brief Executes a DiffSliceOperation. + \sa DiffSliceOperation + */ + class Segmentation_EXPORT DiffSliceOperationApplier : public OperationActor + { + + public: + + mitkClassMacro(DiffSliceOperationApplier, OperationActor); + + //itkNewMacro(DiffSliceOperationApplier); + + /** \brief Returns an instance of the class */ + static DiffSliceOperationApplier* GetInstance(); + + /** \brief Executes a DiffSliceOperation. + \sa DiffSliceOperation + Note: + Only DiffSliceOperation is supported. + */ + virtual void ExecuteOperation(Operation* op); + + + protected: + + DiffSliceOperationApplier(); + + virtual ~DiffSliceOperationApplier(); + + //static DiffSliceOperationApplier* s_Instance; + + }; +} +#endif \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp index 397fdd10a4..0ef5827bff 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp @@ -1,490 +1,491 @@ /*=================================================================== 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 "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkSegmentationInterpolationController.h" #include "mitkApplyDiffImageOperation.h" #include "mitkOperationEvent.h" #include "mitkInteractionConst.h" #include "mitkUndoController.h" #include "mitkDiffImageApplier.h" #include "mitkImageTimeSelector.h" #include #include mitk::OverwriteDirectedPlaneImageFilter::OverwriteDirectedPlaneImageFilter() :m_PlaneGeometry(0), m_ImageGeometry3D(0), m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), m_CreateUndoInformation(false) { + MITK_WARN << "Class is deprecated! Use mitkVtkImageOverwrite instead."; } mitk::OverwriteDirectedPlaneImageFilter::~OverwriteDirectedPlaneImageFilter() { } void mitk::OverwriteDirectedPlaneImageFilter::GenerateData() { // // this is the place to implement the major part of undo functionality (bug #491) // here we have to create undo/do operations // // WHO is the operation actor? This object may not be destroyed ever (design of undo stack)! // -> some singleton method of this filter? // // neccessary additional objects: // - something that executes the operations // - the operation class (must hold a binary diff or something) // - observer commands to know when the image is deleted (no further action then, perhaps even remove the operations from the undo stack) // //Image::ConstPointer input = ImageToImageFilter::GetInput(0); Image::ConstPointer input3D = ImageToImageFilter::GetInput(0); //Image::ConstPointer slice = m_SliceImage; if ( input3D.IsNull() || m_SliceImage.IsNull() ) return; if ( input3D->GetDimension() == 4 ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input3D ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input3D = timeSelector->GetOutput(); } m_ImageGeometry3D = input3D->GetGeometry(); /* if ( m_SliceDifferenceImage.IsNull() || m_SliceDifferenceImage->GetDimension(0) != m_SliceImage->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_SliceImage->GetDimension(1) ) { m_SliceDifferenceImage = mitk::Image::New(); mitk::PixelType pixelType( typeid(short signed int) ); m_SliceDifferenceImage->Initialize( pixelType, 2, m_SliceImage->GetDimensions() ); } */ //MITK_INFO << "Overwriting slice " << m_SliceIndex << " in dimension " << m_SliceDimension << " at time step " << m_TimeStep << std::endl; // this will do a long long if/else to find out both pixel types /*AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 );*/ AccessFixedDimensionByItk( input3D, ItkSliceOverwriting, 3 ); //SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( input3D ); //if (interpolator) //{ // interpolator->BlockModified(true); // //interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); //} /* if ( m_CreateUndoInformation ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image ApplyDiffImageOperation* doOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); ApplyDiffImageOperation* undoOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); undoOp->SetFactor( -1.0 ); OperationEvent* undoStackItem = new OperationEvent( DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, this->EventDescription(m_SliceDimension, m_SliceIndex, m_TimeStep) ); UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); } */ // this image is modified (good to know for the renderer) input3D->Modified(); /*if (interpolator) { interpolator->BlockModified(false); }*/ } template void mitk::OverwriteDirectedPlaneImageFilter::ItkSliceOverwriting( itk::Image* input3D ) { typedef itk::Image SliceImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; typedef itk::ImageRegionConstIterator< SliceImageType > SliceIteratorType; typename SliceImageType::Pointer sliceImage = SliceImageType::New(); CastToItkImage(m_SliceImage,sliceImage); SliceIteratorType sliceIterator( sliceImage, sliceImage->GetLargestPossibleRegion() ); sliceIterator.GoToBegin(); Point3D currentPointIn2D; Point3D worldPointIn3D; //Here we just iterate over the slice which must be written into the 3D volumen and set the corresponding pixel in our 3D volume while ( !sliceIterator.IsAtEnd() ) { currentPointIn2D[0] = sliceIterator.GetIndex()[0]+0.5; currentPointIn2D[1] = sliceIterator.GetIndex()[1]+0.5; currentPointIn2D[2] = 0; m_PlaneGeometry->IndexToWorld( currentPointIn2D, worldPointIn3D ); typename itk::Image::IndexType outputIndex; m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); // Only access ITK image if it's inside if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) { input3D->SetPixel( outputIndex, (TPixel)sliceIterator.Get() ); } ++sliceIterator; } } /****TEST***/ //Maybe a bit more efficient but doesn`t already work. See also ExtractCirectedPlaneImageFilter //typename itk::Image::IndexType outputIndex; //if ( columns == extent[0] ) //{ ////If we are at the end of a row, then we have to go to the beginning of the next row //currentImagePointIn3D = origin; //currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; //columns = 0; //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} //else //{ //if ( columns != 0 ) //{ //currentImagePointIn3D += spacing[0]*right; //} //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} //if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ //outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} //else if (currentImagePointIn3D == origin) //{ //Point3D temp; //temp[0] = bottom[0]*spacing[0]*0.5; //temp[1] = bottom[1]*spacing[1]*0.5; //temp[2] = bottom[2]*spacing[2]*0.5; //origin[0] += temp[0]; //origin[1] += temp[1]; //origin[2] += temp[2]; //currentImagePointIn3D = origin; //m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ //outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} //} //columns++; /****TEST ENDE****/ //* // // Offset the world coordinate by one pixel to compensate for // // index/world origin differences. // Point3D offsetIndex; // offsetIndex.Fill( 1 ); // Point3D offsetWorld; // offsetWorld.Fill( 0 ); // m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); // // remove origin shift // const Point3D origin = m_PlaneGeometry->GetOrigin(); // offsetWorld[0] -= origin[0]; // offsetWorld[1] -= origin[1]; // offsetWorld[2] -= origin[2]; // // offset world coordinate // worldPointIn3D[ 0 ] += offsetWorld[0]; // worldPointIn3D[ 1 ] += offsetWorld[1]; // worldPointIn3D[ 2 ] += offsetWorld[2]; //*/ // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h /*#define myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ // if ( typeId == typeid(pixeltype) ) \ //{ \ // typedef itk::Image ImageType; \ // typedef mitk::ImageToItk ImageToItkType; \ // itk::SmartPointer imagetoitk = ImageToItkType::New(); \ // imagetoitk->SetInput(mitkImage); \ // imagetoitk->Update(); \ // itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ //} // //#define myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ //{ \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, float, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, char, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned char, dimension, itkimage2) \ //}*/ // // //template //void mitk::OverwriteDirectedPlaneImageFilter::ItkImageSwitch( itk::Image* itkImage ) //{ // const std::type_info& typeId=*(m_SliceImage->GetPixelType().GetTypeId()); // // myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk( m_SliceImage, ItkImageProcessing, 2, itkImage ); //} //template //void mitk::OverwriteDirectedPlaneImageFilter::ItkImageProcessing( itk::Image* inputImage, itk::Image* outputImage ) //{ // typedef itk::Image SliceImageType; // typedef itk::Image DiffImageType; // typedef itk::Image VolumeImageType; // // typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; // typedef itk::ImageRegionConstIterator< SliceImageType > InputSliceIteratorType; // //typedef itk::ImageRegionIterator< DiffImageType > DiffSliceIteratorType; // // InputSliceIteratorType inputIterator( inputImage, inputImage->GetLargestPossibleRegion() ); // // //typename DiffImageType::Pointer diffImage; // //CastToItkImage( m_SliceDifferenceImage, diffImage ); // //DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // // inputIterator.GoToBegin(); // //diffIterator.GoToBegin(); // // //TEST // Point3D origin = m_PlaneGeometry->GetOrigin(); // Vector3D right = m_PlaneGeometry->GetAxisVector(0); // Vector3D bottom = m_PlaneGeometry->GetAxisVector(1); // right.Normalize(); // bottom.Normalize(); // // Vector2D spacing = inputImage->GetSpacing(); // // Vector2D extentInMM; // extentInMM[0] = m_PlaneGeometry->GetExtentInMM(0); // extentInMM[1] = m_PlaneGeometry->GetExtentInMM(1); // // Vector2D extent; // extent[0] = m_PlaneGeometry->GetExtent(0); // extent[1] = m_PlaneGeometry->GetExtent(1); // //TEST ENDE // // Point3D currentPointIn2D, worldPointIn3D; // TPixel2 outputPixel = 0; // // int debugCounter( 0 ); // // std::ofstream geometryFile; // geometryFile.precision(30); // geometryFile.open("C:/Users/fetzer/Desktop/TEST/geometryFileOv.txt"); // // geometryFile<<"Offset: [ "<GetIndexToWorldTransform()->GetOffset()[0]<<", "<GetIndexToWorldTransform()->GetOffset()[1]<<", "<GetIndexToWorldTransform()->GetOffset()[2]<<" ]"<GetIndexToWorldTransform()->GetMatrix()<IndexToWorld( currentPointIn2D, worldPointIn3D ); // //typename itk::Image::IndexType outputIndex; // m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); // // // Only access ITK image if it's inside // if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) // { // //outputPixel = outputImage->GetPixel( outputIndex ); // outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); // /*if( inputIterator.Get() == mitk::paint::addPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 1 ) ); // } // else if( inputIterator.Get() == mitk::paint::subPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 0 ) ); // }*/ // } // ///****TEST***/ // ////typename itk::Image::IndexType outputIndex; // ////if ( columns == extent[0] ) ////{ //////If we are at the end of a row, then we have to go to the beginning of the next row ////currentImagePointIn3D = origin; ////currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; //columns = 0; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} ////else ////{ ////if ( columns != 0 ) ////{ ////currentImagePointIn3D += spacing[0]*right; ////} ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} // ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////else if (currentImagePointIn3D == origin) ////{ ////Point3D temp; ////temp[0] = bottom[0]*spacing[0]*0.5; ////temp[1] = bottom[1]*spacing[1]*0.5; ////temp[2] = bottom[2]*spacing[2]*0.5; ////origin[0] += temp[0]; ////origin[1] += temp[1]; ////origin[2] += temp[2]; ////currentImagePointIn3D = origin; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////} ////columns++; // ///****TEST ENDE****/ // ////* //// // Offset the world coordinate by one pixel to compensate for //// // index/world origin differences. //// Point3D offsetIndex; //// offsetIndex.Fill( 1 ); //// Point3D offsetWorld; //// offsetWorld.Fill( 0 ); //// m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); //// // remove origin shift //// const Point3D origin = m_PlaneGeometry->GetOrigin(); //// offsetWorld[0] -= origin[0]; //// offsetWorld[1] -= origin[1]; //// offsetWorld[2] -= origin[2]; //// // offset world coordinate //// worldPointIn3D[ 0 ] += offsetWorld[0]; //// worldPointIn3D[ 1 ] += offsetWorld[1]; //// worldPointIn3D[ 2 ] += offsetWorld[2]; ////*/ // // Output index // // ////For the purpose of debug ////if( debugCounter%100 == 0) //////{ ////Point3D contIndex; ////m_ImageGeometry3D->WorldToIndex(worldPointIn3D,contIndex); ////overriderFile.precision(10); ////overriderFile<<"2D-Index: [ "<(inputIterator.Get() - outputPixel ) ); // oh oh, not good for bigger values // ++inputIterator; ////++debugCounter; // //++diffIterator; // } // /*overriderFile.close(); // overriderFileIndex.close();*/ // geometryFile.close(); // ///* // typename VolumeImageType::RegionType sliceInVolumeRegion; // // sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); // sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice // sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please // // OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); // outputIterator.SetFirstDirection(m_Dimension0); // outputIterator.SetSecondDirection(m_Dimension1); // // // iterate over output slice (and over input slice simultaneously) // outputIterator.GoToBegin(); // while ( !outputIterator.IsAtEnd() ) // { // while ( !outputIterator.IsAtEndOfSlice() ) // { // while ( !outputIterator.IsAtEndOfLine() ) // { // diffIterator.Set( static_cast(inputIterator.Get() - outputIterator.Get()) ); // oh oh, not good for bigger values // outputIterator.Set( (TPixel2) inputIterator.Get() ); // ++outputIterator; // ++inputIterator; // ++diffIterator; // } // outputIterator.NextLine(); // } // outputIterator.NextSlice(); // } // */ //} /* std::string mitk::OverwriteDirectedPlaneImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) { std::stringstream s; s << "Changed slice ("; switch (sliceDimension) { default: case 2: s << "T"; break; case 1: s << "C"; break; case 0: s << "S"; break; } s << " " << sliceIndex << " " << timeStep << ")"; return s.str(); } */ diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h index 32db491432..5e8aaeefaf 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.h @@ -1,118 +1,121 @@ /*=================================================================== 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 mitkOverwriteDirectedPlaneImageFilter_h_Included #define mitkOverwriteDirectedPlaneImageFilter_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** + \deprecated This class is deprecated. Use mitkVtkImageOverwrite instead. + \sa mitkVtkImageOverwrite + \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup Reliver There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author: maleike $ */ class Segmentation_EXPORT OverwriteDirectedPlaneImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteDirectedPlaneImageFilter, ImageToImageFilter); itkNewMacro(OverwriteDirectedPlaneImageFilter); /** \brief Which plane to overwrite */ const Geometry3D* GetPlaneGeometry3D() const { return m_PlaneGeometry; } void SetPlaneGeometry3D( const Geometry3D *geometry ) { m_PlaneGeometry = geometry; } /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteDirectedPlaneImageFilter(); // purposely hidden virtual ~OverwriteDirectedPlaneImageFilter(); virtual void GenerateData(); template void ItkSliceOverwriting (itk::Image* input3D); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); //std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; const Geometry3D *m_PlaneGeometry; const Geometry3D *m_ImageGeometry3D; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp index 8024b01963..da25152a48 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.cpp @@ -1,246 +1,247 @@ /*=================================================================== 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 "mitkOverwriteSliceImageFilter.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkSegmentationInterpolationController.h" #include "mitkApplyDiffImageOperation.h" #include "mitkOperationEvent.h" #include "mitkInteractionConst.h" #include "mitkUndoController.h" #include "mitkDiffImageApplier.h" #include "mitkImageTimeSelector.h" #include #include mitk::OverwriteSliceImageFilter::OverwriteSliceImageFilter() :m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), m_CreateUndoInformation(false) { + MITK_WARN << "Class is deprecated! Use mitkVtkImageOverwrite instead."; } mitk::OverwriteSliceImageFilter::~OverwriteSliceImageFilter() { } void mitk::OverwriteSliceImageFilter::GenerateData() { // // this is the place to implement the major part of undo functionality (bug #491) // here we have to create undo/do operations // // WHO is the operation actor? This object may not be destroyed ever (design of undo stack)! // -> some singleton method of this filter? // // neccessary additional objects: // - something that executes the operations // - the operation class (must hold a binary diff or something) // - observer commands to know when the image is deleted (no further action then, perhaps even remove the operations from the undo stack) // Image::ConstPointer input = ImageToImageFilter::GetInput(0); Image::ConstPointer input3D = input; Image::ConstPointer slice = m_SliceImage; if ( input.IsNull() || slice.IsNull() ) return; switch (m_SliceDimension) { default: case 2: m_Dimension0 = 0; m_Dimension1 = 1; break; case 1: m_Dimension0 = 0; m_Dimension1 = 2; break; case 0: m_Dimension0 = 1; m_Dimension1 = 2; break; } if ( slice->GetDimension() < 2 || input->GetDimension() > 4 || slice->GetDimension(0) != input->GetDimension(m_Dimension0) || slice->GetDimension(1) != input->GetDimension(m_Dimension1) || m_SliceIndex >= input->GetDimension(m_SliceDimension) ) { itkExceptionMacro("Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this."); return; } if ( input->GetDimension() == 4 ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( input ); timeSelector->SetTimeNr( m_TimeStep ); timeSelector->UpdateLargestPossibleRegion(); input3D = timeSelector->GetOutput(); } if ( m_SliceDifferenceImage.IsNull() || m_SliceDifferenceImage->GetDimension(0) != m_SliceImage->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_SliceImage->GetDimension(1) ) { m_SliceDifferenceImage = mitk::Image::New(); mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); m_SliceDifferenceImage->Initialize( pixelType, 2, m_SliceImage->GetDimensions() ); } //MITK_INFO << "Overwriting slice " << m_SliceIndex << " in dimension " << m_SliceDimension << " at time step " << m_TimeStep << std::endl; // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 ); SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( input ); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); } if ( m_CreateUndoInformation ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image ApplyDiffImageOperation* doOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); ApplyDiffImageOperation* undoOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); undoOp->SetFactor( -1.0 ); OperationEvent* undoStackItem = new OperationEvent( DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, this->EventDescription(m_SliceDimension, m_SliceIndex, m_TimeStep) ); UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); } // this image is modified (good to know for the renderer) input->Modified(); if (interpolator) { interpolator->BlockModified(false); } } // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h #define myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ if ( typeId == typeid(pixeltype) ) \ { \ typedef itk::Image ImageType; \ typedef mitk::ImageToItk ImageToItkType; \ itk::SmartPointer imagetoitk = ImageToItkType::New(); \ imagetoitk->SetInput(mitkImage); \ imagetoitk->Update(); \ itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ } #define myMITKOverwriteSliceImageFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ { \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, float, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned int, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, char, dimension, itkimage2) else \ myMITKOverwriteSliceImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned char, dimension, itkimage2) \ } template void mitk::OverwriteSliceImageFilter::ItkImageSwitch( itk::Image* itkImage ) { const std::type_info& typeId=m_SliceImage->GetPixelType().GetTypeId(); myMITKOverwriteSliceImageFilterAccessAllTypesByItk( m_SliceImage, ItkImageProcessing, 2, itkImage ); } template void mitk::OverwriteSliceImageFilter::ItkImageProcessing( itk::Image* inputImage, itk::Image* outputImage ) { typedef itk::Image SliceImageType; typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; typedef itk::ImageRegionConstIterator< SliceImageType > InputSliceIteratorType; typedef itk::ImageRegionIterator< DiffImageType > DiffSliceIteratorType; typename VolumeImageType::RegionType sliceInVolumeRegion; sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); outputIterator.SetFirstDirection(m_Dimension0); outputIterator.SetSecondDirection(m_Dimension1); InputSliceIteratorType inputIterator( inputImage, inputImage->GetLargestPossibleRegion() ); typename DiffImageType::Pointer diffImage; CastToItkImage( m_SliceDifferenceImage, diffImage ); DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); inputIterator.GoToBegin(); diffIterator.GoToBegin(); while ( !outputIterator.IsAtEnd() ) { while ( !outputIterator.IsAtEndOfSlice() ) { while ( !outputIterator.IsAtEndOfLine() ) { diffIterator.Set( static_cast(inputIterator.Get() - outputIterator.Get()) ); // oh oh, not good for bigger values outputIterator.Set( (TPixel2) inputIterator.Get() ); ++outputIterator; ++inputIterator; ++diffIterator; } outputIterator.NextLine(); } outputIterator.NextSlice(); } } std::string mitk::OverwriteSliceImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) { std::stringstream s; s << "Changed slice ("; switch (sliceDimension) { default: case 2: s << "T"; break; case 1: s << "C"; break; case 0: s << "S"; break; } s << " " << sliceIndex << " " << timeStep << ")"; return s.str(); } diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h index bb165108ae..1c0e2d9624 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h +++ b/Modules/Segmentation/Algorithms/mitkOverwriteSliceImageFilter.h @@ -1,123 +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. ===================================================================*/ #ifndef mitkOverwriteSliceImageFilter_h_Included #define mitkOverwriteSliceImageFilter_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkImageToImageFilter.h" #include namespace mitk { /** + \deprecated This class is deprecated. Use mitkVtkImageOverwrite instead. + \sa mitkVtkImageOverwrite + \brief Writes a 2D slice into a 3D image. \sa SegTool2D \sa ContourTool \sa ExtractImageFilter \ingroup Process \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage This class takes a 3D mitk::Image as input and tries to replace one slice in it with the second input image, which is specified by calling SetSliceImage with a 2D mitk::Image. Two parameters determine which slice is replaced: the "slice dimension" is that one, which is constant for all points in the plane, e.g. transversal would mean 2. The "slice index" is the slice index in the image direction you specified with "affected dimension". Indices count from zero. This class works with all kind of image types, the only restrictions being that the input is 3D, and the slice image is 2D. If requested by SetCreateUndoInformation(true), this class will create instances of ApplyDiffImageOperation for the undo stack. These operations will (on user request) be executed by DiffImageApplier to perform undo. Last contributor: $Author$ */ class Segmentation_EXPORT OverwriteSliceImageFilter : public ImageToImageFilter { public: mitkClassMacro(OverwriteSliceImageFilter, ImageToImageFilter); itkNewMacro(OverwriteSliceImageFilter); /** \brief Which slice to overwrite (first one has index 0). */ itkSetMacro(SliceIndex, unsigned int); itkGetConstMacro(SliceIndex, unsigned int); /** \brief The orientation of the slice to overwrite. \a Parameter \a SliceDimension Number of the dimension which is constant for all pixels of the desired slices (e.g. 0 for transversal) */ itkSetMacro(SliceDimension, unsigned int); itkGetConstMacro(SliceDimension, unsigned int); /** \brief Time step of the slice to overwrite */ itkSetMacro(TimeStep, unsigned int); itkGetConstMacro(TimeStep, unsigned int); /** \brief Whether to create undo operation in the MITK undo stack */ itkSetMacro(CreateUndoInformation, bool); itkGetConstMacro(CreateUndoInformation, bool); itkSetObjectMacro(SliceImage, Image); const Image* GetSliceImage() { return m_SliceImage.GetPointer(); } const Image* GetLastDifferenceImage() { return m_SliceDifferenceImage.GetPointer(); } protected: OverwriteSliceImageFilter(); // purposely hidden virtual ~OverwriteSliceImageFilter(); virtual void GenerateData(); template void ItkImageSwitch( itk::Image* image ); template void ItkImageProcessing( itk::Image* itkImage1, itk::Image* itkImage2 ); std::string EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); Image::ConstPointer m_SliceImage; Image::Pointer m_SliceDifferenceImage; unsigned int m_SliceIndex; unsigned int m_SliceDimension; unsigned int m_TimeStep; unsigned int m_Dimension0; unsigned int m_Dimension1; bool m_CreateUndoInformation; }; } // namespace #endif diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp new file mode 100644 index 0000000000..95a14ff564 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.cpp @@ -0,0 +1,901 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: mitkVtkImageOverwrite.cpp + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "mitkVtkImageOverwrite.h" + +#include "vtkImageData.h" +#include "vtkImageStencilData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMath.h" +#include "vtkObjectFactory.h" +#include "vtkStreamingDemandDrivenPipeline.h" +#include "vtkTransform.h" +#include "vtkDataSetAttributes.h" +#include "vtkGarbageCollector.h" + +#include "vtkTemplateAliasMacro.h" +// turn off 64-bit ints when templating over all types +# undef VTK_USE_INT64 +# define VTK_USE_INT64 0 +# undef VTK_USE_UINT64 +# define VTK_USE_UINT64 0 + +#include +#include +#include + + +vtkStandardNewMacro(mitkVtkImageOverwrite); + + + + +//-------------------------------------------------------------------------- +// The 'floor' function on x86 and mips is many times slower than these +// and is used a lot in this code, optimize for different CPU architectures +template +inline int vtkResliceFloor(double x, F &f) +{ +#if defined mips || defined sparc || defined __ppc__ + x += 2147483648.0; + unsigned int i = static_cast(x); + f = x - i; + return static_cast(i - 2147483648U); +#elif defined i386 || defined _M_IX86 + union { double d; unsigned short s[4]; unsigned int i[2]; } dual; + dual.d = x + 103079215104.0; // (2**(52-16))*1.5 + f = dual.s[0]*0.0000152587890625; // 2**(-16) + return static_cast((dual.i[1]<<16)|((dual.i[0])>>16)); +#elif defined ia64 || defined __ia64__ || defined IA64 + x += 103079215104.0; + long long i = static_cast(x); + f = x - i; + return static_cast(i - 103079215104LL); +#else + double y = floor(x); + f = x - y; + return static_cast(y); +#endif +} + +inline int vtkResliceRound(double x) +{ +#if defined mips || defined sparc || defined __ppc__ + return static_cast(static_cast(x + 2147483648.5) - 2147483648U); +#elif defined i386 || defined _M_IX86 + union { double d; unsigned int i[2]; } dual; + dual.d = x + 103079215104.5; // (2**(52-16))*1.5 + return static_cast((dual.i[1]<<16)|((dual.i[0])>>16)); +#elif defined ia64 || defined __ia64__ || defined IA64 + x += 103079215104.5; + long long i = static_cast(x); + return static_cast(i - 103079215104LL); +#else + return static_cast(floor(x+0.5)); +#endif +} + +//---------------------------------------------------------------------------- +mitkVtkImageOverwrite::mitkVtkImageOverwrite() +{ + m_Overwrite_Mode = false; + this->GetOutput()->SetScalarTypeToUnsignedInt(); +} + +//---------------------------------------------------------------------------- +mitkVtkImageOverwrite::~mitkVtkImageOverwrite() +{ +} + + + + +//---------------------------------------------------------------------------- +// constants for different boundary-handling modes + +#define VTK_RESLICE_BACKGROUND 0 // use background if out-of-bounds +#define VTK_RESLICE_WRAP 1 // wrap to opposite side of image +#define VTK_RESLICE_MIRROR 2 // mirror off of the boundary +#define VTK_RESLICE_BORDER 3 // use a half-voxel border +#define VTK_RESLICE_NULL 4 // do nothing to *outPtr if out-of-bounds + +//---------------------------------------------------------------------------- +// rounding functions for each type, where 'F' is a floating-point type + +#if (VTK_USE_INT8 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt8& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT8 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt8& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_INT16 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt16& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT16 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt16& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_INT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeInt32& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_UINT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeUInt32& rnd) +{ + rnd = vtkResliceRound(val); +} +#endif + +#if (VTK_USE_FLOAT32 != 0) +template +inline void vtkResliceRound(F val, vtkTypeFloat32& rnd) +{ + rnd = val; +} +#endif + +#if (VTK_USE_FLOAT64 != 0) +template +inline void vtkResliceRound(F val, vtkTypeFloat64& rnd) +{ + rnd = val; +} +#endif + +//---------------------------------------------------------------------------- +// clamping functions for each type + +#if (VTK_USE_INT8 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt8& clamp) +{ + if (val < -128.0) + { + val = -128.0; + } + if (val > 127.0) + { + val = 127.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT8 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt8& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 255.0) + { + val = 255.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_INT16 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt16& clamp) +{ + if (val < -32768.0) + { + val = -32768.0; + } + if (val > 32767.0) + { + val = 32767.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT16 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt16& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 65535.0) + { + val = 65535.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_INT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeInt32& clamp) +{ + if (val < -2147483648.0) + { + val = -2147483648.0; + } + if (val > 2147483647.0) + { + val = 2147483647.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_UINT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeUInt32& clamp) +{ + if (val < 0) + { + val = 0; + } + if (val > 4294967295.0) + { + val = 4294967295.0; + } + vtkResliceRound(val,clamp); +} +#endif + +#if (VTK_USE_FLOAT32 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeFloat32& clamp) +{ + clamp = val; +} +#endif + +#if (VTK_USE_FLOAT64 != 0) +template +inline void vtkResliceClamp(F val, vtkTypeFloat64& clamp) +{ + clamp = val; +} +#endif + +//---------------------------------------------------------------------------- +// Perform a wrap to limit an index to [0,range). +// Ensures correct behaviour when the index is negative. + +inline int vtkInterpolateWrap(int num, int range) +{ + if ((num %= range) < 0) + { + num += range; // required for some % implementations + } + return num; +} + +//---------------------------------------------------------------------------- +// Perform a mirror to limit an index to [0,range). + +inline int vtkInterpolateMirror(int num, int range) +{ + if (num < 0) + { + num = -num - 1; + } + int count = num/range; + num %= range; + if (count & 0x1) + { + num = range - num - 1; + } + return num; +} + +//---------------------------------------------------------------------------- +// If the value is within one half voxel of the range [0,inExtX), then +// set it to "0" or "inExtX-1" as appropriate. + +inline int vtkInterpolateBorder(int &inIdX0, int &inIdX1, int inExtX, + double fx) +{ + if (inIdX0 >= 0 && inIdX1 < inExtX) + { + return 0; + } + if (inIdX0 == -1 && fx >= 0.5) + { + inIdX1 = inIdX0 = 0; + return 0; + } + if (inIdX0 == inExtX - 1 && fx < 0.5) + { + inIdX1 = inIdX0; + return 0; + } + + return 1; +} + +inline int vtkInterpolateBorderCheck(int inIdX0, int inIdX1, int inExtX, + double fx) +{ + if ((inIdX0 >= 0 && inIdX1 < inExtX) || + (inIdX0 == -1 && fx >= 0.5) || + (inIdX0 == inExtX - 1 && fx < 0.5)) + { + return 0; + } + + return 1; +} + +//---------------------------------------------------------------------------- +// Do nearest-neighbor interpolation of the input data 'inPtr' of extent +// 'inExt' at the 'point'. The result is placed at 'outPtr'. +// If the lookup data is beyond the extent 'inExt', set 'outPtr' to +// the background color 'background'. +// The number of scalar components in the data is 'numscalars' +template +static int vtkNearestNeighborInterpolation(T *&outPtr, const T *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, const F point[3], + int mode, const T *background, + mitkVtkImageOverwrite *self) +{ + int inIdX0 = vtkResliceRound(point[0]) - inExt[0]; + int inIdY0 = vtkResliceRound(point[1]) - inExt[2]; + int inIdZ0 = vtkResliceRound(point[2]) - inExt[4]; + + int inExtX = inExt[1] - inExt[0] + 1; + int inExtY = inExt[3] - inExt[2] + 1; + int inExtZ = inExt[5] - inExt[4] + 1; + + if (inIdX0 < 0 || inIdX0 >= inExtX || + inIdY0 < 0 || inIdY0 >= inExtY || + inIdZ0 < 0 || inIdZ0 >= inExtZ) + { + if (mode == VTK_RESLICE_WRAP) + { + inIdX0 = vtkInterpolateWrap(inIdX0, inExtX); + inIdY0 = vtkInterpolateWrap(inIdY0, inExtY); + inIdZ0 = vtkInterpolateWrap(inIdZ0, inExtZ); + } + else if (mode == VTK_RESLICE_MIRROR) + { + inIdX0 = vtkInterpolateMirror(inIdX0, inExtX); + inIdY0 = vtkInterpolateMirror(inIdY0, inExtY); + inIdZ0 = vtkInterpolateMirror(inIdZ0, inExtZ); + } + else if (mode == VTK_RESLICE_BACKGROUND || + mode == VTK_RESLICE_BORDER) + { + do + { + *outPtr++ = *background++; + } + while (--numscalars); + return 0; + } + else + { + return 0; + } + } + + inPtr += inIdX0*inInc[0]+inIdY0*inInc[1]+inIdZ0*inInc[2]; + + do + { + + if(!self->IsOverwriteMode()) + { + //just copy from input to output + *outPtr++ = *inPtr++; + } + else + { + //copy from output to input in overwrite mode + *(const_cast(inPtr)) = *outPtr++; + inPtr++; + } + } + while (--numscalars); + + return 1; +} + + +//-------------------------------------------------------------------------- +// get appropriate interpolation function according to scalar type +template +static void vtkGetResliceInterpFunc(mitkVtkImageOverwrite *self, + int (**interpolate)(void *&outPtr, + const void *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, + const F point[3], + int mode, + const void *background, + mitkVtkImageOverwrite *self)) +{ + int dataType = self->GetOutput()->GetScalarType(); + + switch (dataType) + { + vtkTemplateAliasMacro(*((int (**)(VTK_TT *&outPtr, const VTK_TT *inPtr, + const int inExt[6], + const vtkIdType inInc[3], + int numscalars, const F point[3], + int mode, + const VTK_TT *background, + mitkVtkImageOverwrite *self))interpolate) = \ + &vtkNearestNeighborInterpolation); + default: + interpolate = 0; + } +} + + +//---------------------------------------------------------------------------- +// Some helper functions for 'RequestData' +//---------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// pixel copy function, templated for different scalar types +template +struct vtkImageResliceSetPixels +{ +static void Set(void *&outPtrV, const void *inPtrV, int numscalars, int n) +{ + const T* inPtr = static_cast(inPtrV); + T* outPtr = static_cast(outPtrV); + for (int i = 0; i < n; i++) + { + const T *tmpPtr = inPtr; + int m = numscalars; + do + { + *outPtr++ = *tmpPtr++; + } + while (--m); + } + outPtrV = outPtr; +} + +// optimized for 1 scalar components +static void Set1(void *&outPtrV, const void *inPtrV, + int vtkNotUsed(numscalars), int n) +{ + const T* inPtr = static_cast(inPtrV); + T* outPtr = static_cast(outPtrV); + T val = *inPtr; + for (int i = 0; i < n; i++) + { + *outPtr++ = val; + } + outPtrV = outPtr; +} +}; + +// get a pixel copy function that is appropriate for the data type +static void vtkGetSetPixelsFunc(mitkVtkImageOverwrite *self, + void (**setpixels)(void *&out, const void *in, + int numscalars, int n)) +{ + int dataType = self->GetOutput()->GetScalarType(); + int numscalars = self->GetOutput()->GetNumberOfScalarComponents(); + + switch (numscalars) + { + case 1: + switch (dataType) + { + vtkTemplateAliasMacro( + *setpixels = &vtkImageResliceSetPixels::Set1 + ); + default: + setpixels = 0; + } + default: + switch (dataType) + { + vtkTemplateAliasMacro( + *setpixels = &vtkImageResliceSetPixels::Set + ); + default: + setpixels = 0; + } + } +} + +//---------------------------------------------------------------------------- +// Convert background color from float to appropriate type +template +static void vtkAllocBackgroundPixelT(mitkVtkImageOverwrite *self, + T **background_ptr, int numComponents) +{ + *background_ptr = new T[numComponents]; + T *background = *background_ptr; + + for (int i = 0; i < numComponents; i++) + { + if (i < 4) + { + vtkResliceClamp(self->GetBackgroundColor()[i], background[i]); + } + else + { + background[i] = 0; + } + } +} + +static void vtkAllocBackgroundPixel(mitkVtkImageOverwrite *self, void **rval, + int numComponents) +{ + switch (self->GetOutput()->GetScalarType()) + { + vtkTemplateAliasMacro(vtkAllocBackgroundPixelT(self, (VTK_TT **)rval, + numComponents)); + } +} + +static void vtkFreeBackgroundPixel(mitkVtkImageOverwrite *self, void **rval) +{ + switch (self->GetOutput()->GetScalarType()) + { + vtkTemplateAliasMacro(delete [] *((VTK_TT **)rval)); + } + + *rval = 0; +} + +//---------------------------------------------------------------------------- +// helper function for clipping of the output with a stencil +static int vtkResliceGetNextExtent(vtkImageStencilData *stencil, + int &r1, int &r2, int rmin, int rmax, + int yIdx, int zIdx, + void *&outPtr, void *background, + int numscalars, + void (*setpixels)(void *&out, + const void *in, + int numscalars, + int n), + int &iter) +{ + // trivial case if stencil is not set + if (!stencil) + { + if (iter++ == 0) + { + r1 = rmin; + r2 = rmax; + return 1; + } + return 0; + } + + // for clearing, start at last r2 plus 1 + int clear1 = r2 + 1; + if (iter == 0) + { // if no 'last time', start at rmin + clear1 = rmin; + } + + int rval = stencil->GetNextExtent(r1, r2, rmin, rmax, yIdx, zIdx, iter); + int clear2 = r1 - 1; + if (rval == 0) + { + clear2 = rmax; + } + + setpixels(outPtr, background, numscalars, clear2 - clear1 + 1); + + return rval; +} + +//---------------------------------------------------------------------------- +// This function simply clears the entire output to the background color, +// for cases where the transformation places the output extent completely +// outside of the input extent. +static void vtkImageResliceClearExecute(mitkVtkImageOverwrite *self, + vtkImageData *, void *, + vtkImageData *outData, void *outPtr, + int outExt[6], int id) +{ + int numscalars; + int idY, idZ; + vtkIdType outIncX, outIncY, outIncZ; + int scalarSize; + unsigned long count = 0; + unsigned long target; + void *background; + void (*setpixels)(void *&out, const void *in, int numscalars, int n); + + // for the progress meter + target = static_cast + ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); + target++; + + // Get Increments to march through data + outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); + scalarSize = outData->GetScalarSize(); + numscalars = outData->GetNumberOfScalarComponents(); + + // allocate a voxel to copy into the background (out-of-bounds) regions + vtkAllocBackgroundPixel(self, &background, numscalars); + // get the appropriate function for pixel copying + vtkGetSetPixelsFunc(self, &setpixels); + + // Loop through output voxels + for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) + { + for (idY = outExt[2]; idY <= outExt[3]; idY++) + { + if (id == 0) + { // update the progress if this is the main thread + if (!(count%target)) + { + self->UpdateProgress(count/(50.0*target)); + } + count++; + } + // clear the pixels to background color and go to next row + setpixels(outPtr, background, numscalars, outExt[1]-outExt[0]+1); + outPtr = static_cast( + static_cast(outPtr) + outIncY*scalarSize); + } + outPtr = static_cast( + static_cast(outPtr) + outIncZ*scalarSize); + } + + vtkFreeBackgroundPixel(self, &background); +} + +//---------------------------------------------------------------------------- +// This function executes the filter for any type of data. It is much simpler +// in structure than vtkImageResliceOptimizedExecute. +static void vtkImageResliceExecute(mitkVtkImageOverwrite *self, + vtkImageData *inData, void *inPtr, + vtkImageData *outData, void *outPtr, + int outExt[6], int id) +{ + int numscalars; + int idX, idY, idZ; + int idXmin, idXmax, iter; + vtkIdType outIncX, outIncY, outIncZ; + int scalarSize; + int inExt[6]; + vtkIdType inInc[3]; + unsigned long count = 0; + unsigned long target; + double point[4]; + double f; + double *inSpacing, *inOrigin, *outSpacing, *outOrigin, inInvSpacing[3]; + void *background; + int (*interpolate)(void *&outPtr, const void *inPtr, + const int inExt[6], const vtkIdType inInc[3], + int numscalars, const double point[3], + int mode, const void *background, mitkVtkImageOverwrite *self); + void (*setpixels)(void *&out, const void *in, int numscalars, int n); + + // the 'mode' species what to do with the 'pad' (out-of-bounds) area + int mode = VTK_RESLICE_BACKGROUND; + if (self->GetMirror()) + { + mode = VTK_RESLICE_MIRROR; + } + else if (self->GetWrap()) + { + mode = VTK_RESLICE_WRAP; + } + else if (self->GetBorder()) + { + mode = VTK_RESLICE_BORDER; + } + + // the transformation to apply to the data + vtkAbstractTransform *transform = self->GetResliceTransform(); + vtkMatrix4x4 *matrix = self->GetResliceAxes(); + + // for conversion to data coordinates + inOrigin = inData->GetOrigin(); + inSpacing = inData->GetSpacing(); + outOrigin = outData->GetOrigin(); + outSpacing = outData->GetSpacing(); + + // save effor later: invert inSpacing + inInvSpacing[0] = 1.0/inSpacing[0]; + inInvSpacing[1] = 1.0/inSpacing[1]; + inInvSpacing[2] = 1.0/inSpacing[2]; + + // find maximum input range + inData->GetExtent(inExt); + + // for the progress meter + target = static_cast + ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); + target++; + + // Get Increments to march through data + inData->GetIncrements(inInc); + outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); + scalarSize = outData->GetScalarSize(); + numscalars = inData->GetNumberOfScalarComponents(); + + // allocate a voxel to copy into the background (out-of-bounds) regions + vtkAllocBackgroundPixel(self, &background, numscalars); + + // get the appropriate functions for interpolation and pixel copying + vtkGetResliceInterpFunc(self, &interpolate); + vtkGetSetPixelsFunc(self, &setpixels); + + // get the stencil + vtkImageStencilData *stencil = self->GetStencil(); + + // Loop through output voxels + for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) + { + for (idY = outExt[2]; idY <= outExt[3]; idY++) + { + if (id == 0) + { // update the progress if this is the main thread + if (!(count%target)) + { + self->UpdateProgress(count/(50.0*target)); + } + count++; + } + + iter = 0; // if there is a stencil, it is applied here + while (vtkResliceGetNextExtent(stencil, idXmin, idXmax, + outExt[0], outExt[1], idY, idZ, + outPtr, background, numscalars, + setpixels, iter)) + { + for (idX = idXmin; idX <= idXmax; idX++) + { + // convert to data coordinates + point[0] = idX*outSpacing[0] + outOrigin[0]; + point[1] = idY*outSpacing[1] + outOrigin[1]; + point[2] = idZ*outSpacing[2] + outOrigin[2]; + + // apply ResliceAxes matrix + if (matrix) + { + point[3] = 1.0; + matrix->MultiplyPoint(point, point); + f = 1.0/point[3]; + point[0] *= f; + point[1] *= f; + point[2] *= f; + } + + // apply ResliceTransform + if (transform) + { + transform->InternalTransformPoint(point, point); + } + + // convert back to voxel indices + point[0] = (point[0] - inOrigin[0])*inInvSpacing[0]; + point[1] = (point[1] - inOrigin[1])*inInvSpacing[1]; + point[2] = (point[2] - inOrigin[2])*inInvSpacing[2]; + + // interpolate output voxel from input data set + interpolate(outPtr, inPtr, inExt, inInc, numscalars, + point, mode, background, self); + } + } + outPtr = static_cast( + static_cast(outPtr) + outIncY*scalarSize); + } + outPtr = static_cast( + static_cast(outPtr) + outIncZ*scalarSize); + } + + vtkFreeBackgroundPixel(self, &background); +} + + +void mitkVtkImageOverwrite::SetOverwriteMode(bool b){ + m_Overwrite_Mode = b; +} + + +void mitkVtkImageOverwrite::SetInputSlice(vtkImageData* slice){ + //set the output as input + this->SetOutput(slice); +} + + + + +//---------------------------------------------------------------------------- +// This method is passed a input and output region, and executes the filter +// algorithm to fill the output from the input or vice versa. +void mitkVtkImageOverwrite::ThreadedRequestData( + vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *vtkNotUsed(outputVector), + vtkImageData ***inData, + vtkImageData **outData, + int outExt[6], int id) +{ + + vtkDebugMacro(<< "Execute: inData = " << inData[0][0] + << ", outData = " << outData[0]); + // this filter expects that input is the same type as output. + if (inData[0][0]->GetScalarType() != outData[0]->GetScalarType()) + { + vtkErrorMacro(<< "Execute: input ScalarType, " + << inData[0][0]->GetScalarType() + << ", must match out ScalarType " + << outData[0]->GetScalarType()); + return; + } + + int inExt[6]; + inData[0][0]->GetExtent(inExt); + // check for empty input extent + if (inExt[1] < inExt[0] || + inExt[3] < inExt[2] || + inExt[5] < inExt[4]) + { + return; + } + + // Get the output pointer + void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); + + if (this->HitInputExtent == 0) + { + vtkImageResliceClearExecute(this, inData[0][0], 0, outData[0], outPtr, + outExt, id); + return; + } + + // Now that we know that we need the input, get the input pointer + void *inPtr = inData[0][0]->GetScalarPointerForExtent(inExt); + + + vtkImageResliceExecute(this, inData[0][0], inPtr, outData[0], outPtr, + outExt, id); +} + + diff --git a/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h new file mode 100644 index 0000000000..c6a5e40913 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkVtkImageOverwrite.h @@ -0,0 +1,88 @@ +/*=================================================================== + +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 mitkVtkImageOverwrite_h_Included +#define mitkVtkImageOverwrite_h_Included + +#include +#include "SegmentationExports.h" + + /** \brief A vtk Filter based on vtkImageReslice with the aditional feature to write a slice into the given input volume. + All optimizations for e.g. the plane directions or interpolation are stripped away, the algorithm only interpolates nearest + neighbor and uses the non optimized execute function of vtkImageReslice. Note that any interpolation doesn't make sense + for round trip use extract->edit->overwrite, because it is nearly impossible to invert the interolation. + There are two use cases for the Filter which are specified by the overwritemode property: + + 1)Extract slices from a 3D volume. + Overwritemode = false + + In this mode the class can be used like vtkImageReslice. The usual way to do this is: + - Set an image volume as input + - Set the ResliceAxes via SetResliceAxesDirectionCosines and SetResliceAxesOrigin + - Set the the OutputSpacing, OutputOrigin and OutputExtent + - Call Update + + + 2)Overwrite a 3D volume at a given slice. + Overwritemode = true + + The handling in this mode is quite similar to the description above with the addition that the + InputSlice needs to be specified via SetInputSlice(vtkImageData*). + - Set the properties mentioned above (Note that SetInput specifies the volume to write to) + - Set the slice to that has to be overwritten in the volume ( SetInputSlice(vtkImageData*) + + After calling Update() there is no need to retrieve the output as the input volume is modified. + + \sa vtkImageReslice + (Note that the execute and interpolation functions are no members and thus can not be overriden) + */ + class Segmentation_EXPORT mitkVtkImageOverwrite : public vtkImageReslice + { + public: + static mitkVtkImageOverwrite *New(); + vtkTypeMacro(mitkVtkImageOverwrite, vtkImageReslice); + + /** \brief Set the mode either to reslice (false) or to overwrite (true). + Default: false + */ + void SetOverwriteMode(bool b); + bool IsOverwriteMode(){return m_Overwrite_Mode;} + + /** \brief Set the slice for overwrite mode. + Note: + It is recommend not to use this in reslice mode because otherwise the slice will be modified! + */ + void SetInputSlice(vtkImageData* slice); + + + protected: + + mitkVtkImageOverwrite(); + virtual ~mitkVtkImageOverwrite(); + + bool m_Overwrite_Mode; + + /** Overridden from vtkImageReslice. \sa vtkImageReslice::ThreadedRequestData */ + virtual void ThreadedRequestData(vtkInformation *vtkNotUsed(request), + vtkInformationVector **vtkNotUsed(inputVector), + vtkInformationVector *vtkNotUsed(outputVector), + vtkImageData ***inData, + vtkImageData **outData, + int outExt[6], int id); + }; + + +#endif //mitkVtkImageOverwrite_h_Included diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp index 79e24d9edb..c891fe9765 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdTool.cpp @@ -1,320 +1,320 @@ /*=================================================================== 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 "mitkBinaryThresholdTool.h" #include "mitkBinaryThresholdTool.xpm" #include "mitkToolManager.h" #include "mitkBoundingObjectToSegmentationFilter.h" #include #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkOrganTypeProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include #include #include "mitkPadImageFilter.h" #include "mitkMaskAndCutRoiImageFilter.h" namespace mitk { MITK_TOOL_MACRO(Segmentation_EXPORT, BinaryThresholdTool, "Thresholding tool"); } mitk::BinaryThresholdTool::BinaryThresholdTool() :m_SensibleMinimumThresholdValue(-100), m_SensibleMaximumThresholdValue(+100), m_CurrentThresholdValue(0.0), m_IsFloatImage(false) { this->SupportRoiOn(); m_ThresholdFeedbackNode = DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_ThresholdFeedbackNode ); m_ThresholdFeedbackNode->SetProperty( "color", ColorProperty::New(1.0, 0.0, 0.0) ); m_ThresholdFeedbackNode->SetProperty( "texture interpolation", BoolProperty::New(false) ); m_ThresholdFeedbackNode->SetProperty( "layer", IntProperty::New( 100 ) ); m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(100, 1) ) ); m_ThresholdFeedbackNode->SetProperty( "name", StringProperty::New("Thresholding feedback") ); m_ThresholdFeedbackNode->SetProperty( "opacity", FloatProperty::New(0.3) ); m_ThresholdFeedbackNode->SetProperty( "helper object", BoolProperty::New(true) ); } mitk::BinaryThresholdTool::~BinaryThresholdTool() { } const char** mitk::BinaryThresholdTool::GetXPM() const { return mitkBinaryThresholdTool_xpm; } const char* mitk::BinaryThresholdTool::GetName() const { return "Thresholding"; } void mitk::BinaryThresholdTool::Activated() { m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); m_OriginalImageNode = m_ToolManager->GetReferenceData(0); m_NodeForThresholding = m_OriginalImageNode; if ( m_NodeForThresholding.IsNotNull() ) { SetupPreviewNodeFor( m_NodeForThresholding ); } else { m_ToolManager->ActivateTool(-1); } } void mitk::BinaryThresholdTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::BinaryThresholdTool::OnRoiDataChanged); m_NodeForThresholding = NULL; m_OriginalImageNode = NULL; try { if (DataStorage* storage = m_ToolManager->GetDataStorage()) { storage->Remove( m_ThresholdFeedbackNode ); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch(...) { // don't care } m_ThresholdFeedbackNode->SetData(NULL); } void mitk::BinaryThresholdTool::SetThresholdValue(double value) { if (m_ThresholdFeedbackNode.IsNotNull()) { m_CurrentThresholdValue = value; - m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(m_CurrentThresholdValue, 1) ) ); + m_ThresholdFeedbackNode->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow(m_CurrentThresholdValue, 0.001) ) ); RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::BinaryThresholdTool::AcceptCurrentThresholdValue(const std::string& organName, const Color& color) { CreateNewSegmentationFromThreshold(m_NodeForThresholding, organName, color ); RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdTool::CancelThresholding() { m_ToolManager->ActivateTool(-1); } void mitk::BinaryThresholdTool::SetupPreviewNodeFor( DataNode* nodeForThresholding ) { if (nodeForThresholding) { Image::Pointer image = dynamic_cast( nodeForThresholding->GetData() ); Image::Pointer originalImage = dynamic_cast (m_OriginalImageNode->GetData()); if (image.IsNotNull()) { // initialize and a new node with the same image as our reference image // use the level window property of this image copy to display the result of a thresholding operation m_ThresholdFeedbackNode->SetData( image ); int layer(50); nodeForThresholding->GetIntProperty("layer", layer); m_ThresholdFeedbackNode->SetIntProperty("layer", layer+1); if (DataStorage* storage = m_ToolManager->GetDataStorage()) { if (storage->Exists(m_ThresholdFeedbackNode)) storage->Remove(m_ThresholdFeedbackNode); storage->Add( m_ThresholdFeedbackNode, m_OriginalImageNode ); } if (image.GetPointer() == originalImage.GetPointer()) { if (originalImage->GetPixelType().GetPixelTypeId() == typeid(float)) m_IsFloatImage = true; else m_IsFloatImage = false; m_SensibleMinimumThresholdValue = static_cast( originalImage->GetScalarValueMin() ); m_SensibleMaximumThresholdValue = static_cast( originalImage->GetScalarValueMax() ); } LevelWindowProperty::Pointer lwp = dynamic_cast( m_ThresholdFeedbackNode->GetProperty( "levelwindow" )); if (lwp && !m_IsFloatImage ) { m_CurrentThresholdValue = static_cast( lwp->GetLevelWindow().GetLevel() ); } else { m_CurrentThresholdValue = (m_SensibleMaximumThresholdValue + m_SensibleMinimumThresholdValue)/2; } IntervalBordersChanged.Send(m_SensibleMinimumThresholdValue, m_SensibleMaximumThresholdValue, m_IsFloatImage); ThresholdingValueChanged.Send(m_CurrentThresholdValue); } } } void mitk::BinaryThresholdTool::CreateNewSegmentationFromThreshold(DataNode* node, const std::string& organName, const Color& color) { if (node) { Image::Pointer image = dynamic_cast( m_NodeForThresholding->GetData() ); if (image.IsNotNull()) { // create a new image of the same dimensions and smallest possible pixel type DataNode::Pointer emptySegmentation = Tool::CreateEmptySegmentationNode( image, organName, color ); if (emptySegmentation) { // actually perform a thresholding and ask for an organ type for (unsigned int timeStep = 0; timeStep < image->GetTimeSteps(); ++timeStep) { try { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( image ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer image3D = timeSelector->GetOutput(); if (image3D->GetDimension() == 2) { AccessFixedDimensionByItk_2( image3D, ITKThresholding, 2, dynamic_cast(emptySegmentation->GetData()), timeStep ); } else { AccessFixedDimensionByItk_2( image3D, ITKThresholding, 3, dynamic_cast(emptySegmentation->GetData()), timeStep ); } } catch(...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); } } if (m_OriginalImageNode.GetPointer() != m_NodeForThresholding.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, dynamic_cast (emptySegmentation->GetData())); padFilter->SetInput(1, dynamic_cast (m_OriginalImageNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); emptySegmentation->SetData(padFilter->GetOutput()); } if (DataStorage::Pointer storage = m_ToolManager->GetDataStorage()) { storage->Add( emptySegmentation, m_OriginalImageNode ); // add as a child, because the segmentation "derives" from the original } m_ToolManager->SetWorkingData( emptySegmentation ); } } } } template void mitk::BinaryThresholdTool::ITKThresholding( itk::Image* originalImage, Image* segmentation, unsigned int timeStep ) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput( segmentation ); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer segmentation3D = timeSelector->GetOutput(); typedef itk::Image< Tool::DefaultSegmentationDataType, 3> SegmentationType; // this is sure for new segmentations SegmentationType::Pointer itkSegmentation; CastToItkImage( segmentation3D, itkSegmentation ); // iterate over original and segmentation typedef itk::ImageRegionConstIterator< itk::Image > InputIteratorType; typedef itk::ImageRegionIterator< SegmentationType > SegmentationIteratorType; InputIteratorType inputIterator( originalImage, originalImage->GetLargestPossibleRegion() ); SegmentationIteratorType outputIterator( itkSegmentation, itkSegmentation->GetLargestPossibleRegion() ); inputIterator.GoToBegin(); outputIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { if ( inputIterator.Get() >= m_CurrentThresholdValue ) outputIterator.Set( 1 ); else outputIterator.Set( 0 ); ++inputIterator; ++outputIterator; } } void mitk::BinaryThresholdTool::OnRoiDataChanged() { mitk::DataNode::Pointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast (m_NodeForThresholding->GetData()); if (image.IsNull()) return; mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); mitk::Image::Pointer tmpImage = roiFilter->GetOutput(); tmpNode->SetData(tmpImage); m_SensibleMaximumThresholdValue = static_cast (roiFilter->GetMaxValue()); m_SensibleMinimumThresholdValue = static_cast (roiFilter->GetMinValue()); SetupPreviewNodeFor( tmpNode ); m_NodeForThresholding = tmpNode; return; } else { this->SetupPreviewNodeFor(m_OriginalImageNode); m_NodeForThresholding = m_OriginalImageNode; return; } } diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index ba15b2ad07..f3c344bcbd 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,379 +1,377 @@ /*=================================================================== 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 "mitkPaintbrushTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" #include "mitkLevelWindowProperty.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { m_MasterContour = Contour::New(); m_MasterContour->Initialize(); m_CurrentPlane = NULL; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); Superclass::Deactivated(); m_WorkingSlice = NULL; m_CurrentPlane = NULL; } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } void mitk::PaintbrushTool::UpdateContour(const StateEvent* stateEvent) { //MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // create a copy of this slice (at least match the pixel sizes/spacings), // then draw the desired mask on it and create a contour from it // Convert to ipMITKSegmentationTYPE (because getting pixels relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingSlice/*dynamic_cast(m_WorkingNode->GetData())*/, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); CastToMitkImage( correctPixelTypeImage, temporarySlice ); //mitkIpPicDescriptor* stupidClone = mitkIpPicClone( temporarySlice->GetSliceData()->GetPicDescriptor() ); mitkIpPicDescriptor* stupidClone = mitkIpPicNew(); CastToIpPicDescriptor( temporarySlice->GetSliceData(), stupidClone ); unsigned int pixelWidth = m_Size + 1; unsigned int pixelHeight = m_Size + 1; if ( stupidClone->n[0] <= pixelWidth || stupidClone->n[1] <= pixelHeight ) { MITK_INFO << "Brush size is bigger than your working image. Reconsider this...\n" "(Or tell your progammer until (s)he fixes this message.)" << std::endl; mitkIpPicFree( stupidClone ); return; } unsigned int lineLength( stupidClone->n[0] ); unsigned int oneContourOffset(0); float circleCenterX = (float)m_Size / 2.0; float circleCenterY = (float)m_Size / 2.0; for (unsigned int x = 0; x <= pixelWidth; ++x) { for (unsigned int y = 0; y <= pixelHeight; ++y) { unsigned int offset = lineLength * y + x; ipMITKSegmentationTYPE* current = (ipMITKSegmentationTYPE*)stupidClone->data + offset; float pixelCenterX = x + 0.5; float pixelCenterY = y + 0.5; float xoff = pixelCenterX - circleCenterX; float yoff = pixelCenterY - circleCenterY; bool inside = xoff * xoff + yoff * yoff < (m_Size * m_Size) / 4.0; // no idea, if this would work for ellipses if (inside) { *current = 1; oneContourOffset = offset; } else { *current = 0; } } } int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( stupidClone, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (!contourPoints) { mitkIpPicFree( stupidClone ); return; } // copy point from float* to mitk::Contour Contour::Pointer contourInImageIndexCoordinates = Contour::New(); contourInImageIndexCoordinates->Initialize(); Point3D newPoint; //ipMITKSegmentationGetContour8N returns all points, which causes vtk warnings, since the first and the last points are coincident. //leaving the last point out, the contour is still drawn correctly for (int index = 0; index < numberOfContourPoints-1; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - circleCenterX; // master contour should be centered around (0,0) newPoint[1] = contourPoints[ 2 * index + 1] - circleCenterY; newPoint[2] = 0.0; MITK_DEBUG << "Point [" << index << "] (" << newPoint[0] << ", " << newPoint[1] << ")" << std::endl; contourInImageIndexCoordinates->AddVertex( newPoint ); } free(contourPoints); m_MasterContour = contourInImageIndexCoordinates; // The PicDescriptor is only REFERENCING(!) the data, the temporarySlice takes care of deleting the data also the descriptor is pointing on // because they got allocated by the ImageDataItem, not the descriptor. stupidClone->data = NULL; mitkIpPicFree( stupidClone ); } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ bool mitk::PaintbrushTool::OnMousePressed (Action* action, const StateEvent* stateEvent) { return this->OnMouseMoved(action, stateEvent); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ bool mitk::PaintbrushTool::OnMouseMoved (Action* itkNotUsed(action), const StateEvent* stateEvent) { - const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); + const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; CheckIfCurrentSliceHasChanged(positionEvent); if ( m_LastContourSize != m_Size ) { UpdateContour( stateEvent ); m_LastContourSize = m_Size; } bool leftMouseButtonPressed( stateEvent->GetId() == 530 || stateEvent->GetId() == 534 || stateEvent->GetId() == 1 || stateEvent->GetId() == 5 ); Point3D worldCoordinates = positionEvent->GetWorldPosition(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates ); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if ( m_Size % 2 == 0 ) // even { indexCoordinates[0] = ROUND( indexCoordinates[0] /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND( indexCoordinates[1] /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND( indexCoordinates[0] ) ; indexCoordinates[1] = ROUND( indexCoordinates[1] ) ; } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed ) { lastPos = indexCoordinates; } else { MITK_DEBUG << "." << std::flush; return false; } MITK_DEBUG << "Mouse at C " << indexCoordinates; Contour::Pointer contour = Contour::New(); contour->Initialize(); for (unsigned int index = 0; index < m_MasterContour->GetNumberOfPoints(); ++index) { Point3D point = m_MasterContour->GetPoints()->ElementAt(index); point[0] += indexCoordinates[ 0 ]; point[1] += indexCoordinates[ 1 ]; contour->AddVertex( point ); } if (leftMouseButtonPressed) { FeedbackContourTool::FillContourInSlice( contour, m_WorkingSlice, m_PaintingPixelValue ); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour Contour::Pointer displayContour = Contour::New(); displayContour->Initialize(); //for (unsigned int index = 0; index < contour->GetNumberOfPoints(); ++index) //{ // Point3D point = contour->GetPoints()->ElementAt(index); // if ( m_Size % 2 == 0 ) // even // { // point[0] += 0.5; // point[1] += 0.5; // } // displayContour->AddVertex( point ); //} displayContour = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); SetFeedbackContour( *displayContour ); + assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::PaintbrushTool::OnMouseReleased(Action* /*action*/, const StateEvent* stateEvent) { //When mouse is released write segmentationresult back into image const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); return true; } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ -bool mitk::PaintbrushTool::OnInvertLogic(Action* action, const StateEvent* stateEvent) +bool mitk::PaintbrushTool::OnInvertLogic(Action* itkNotUsed(action), const StateEvent* stateEvent) { - if (!FeedbackContourTool::OnInvertLogic(action, stateEvent)) return false; - - // Inversion only for 0 and 1 as painting values - if (m_PaintingPixelValue == 1) - { - m_PaintingPixelValue = 0; - FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); - } - else if (m_PaintingPixelValue == 0) - { - m_PaintingPixelValue = 1; - FeedbackContourTool::SetFeedbackContourColorDefault(); - } - - return true; + // Inversion only for 0 and 1 as painting values + if (m_PaintingPixelValue == 1) + { + m_PaintingPixelValue = 0; + FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); + } + else if (m_PaintingPixelValue == 0) + { + m_PaintingPixelValue = 1; + FeedbackContourTool::SetFeedbackContourColorDefault(); + } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + return true; } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const PositionEvent *event) { const PlaneGeometry* planeGeometry( dynamic_cast (event->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if ( !image || !planeGeometry ) return; if(m_CurrentPlane.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); - m_WorkingNode->ReplaceProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice (false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = NULL; m_WorkingSlice = NULL; m_WorkingNode = NULL; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetData(m_WorkingSlice); //So that the paintbrush contour vanished in the previous render window RenderingManager::GetInstance()->RequestUpdateAll(); } } if(!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetProperty( "name", mitk::StringProperty::New("Paintbrush_Node") ); m_WorkingNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_WorkingNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_WorkingNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 53084af156..ad0db3d800 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,403 +1,384 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" +//includes for resling and overwriting +#include +#include +#include +#include + +#include +#include "mitkOperationEvent.h" +#include "mitkUndoController.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::SegTool2D::SegTool2D(const char* type) :Tool(type), m_LastEventSender(NULL), m_LastEventSlice(0), m_Contourmarkername ("Position"), - m_ShowMarkerNodes (true), - m_3DInterpolationEnabled (true) + m_ShowMarkerNodes (true) { // great magic numbers CONNECT_ACTION( 80, OnMousePressed ); CONNECT_ACTION( 90, OnMouseMoved ); CONNECT_ACTION( 42, OnMouseReleased ); CONNECT_ACTION( 49014, OnInvertLogic ); + + } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::OnMousePressed (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return false; // we don't want anything but 2D m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); return true; } bool mitk::SegTool2D::OnMouseMoved (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnMouseReleased(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnInvertLogic(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal0.Get_vnl_vector()) ); imageNormal1.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal1.Get_vnl_vector()) ); imageNormal2.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal2.Get_vnl_vector()) ); double eps( 0.00001 ); // transversal if ( imageNormal2.GetNorm() <= eps ) { affectedDimension = 2; } // sagittal else if ( imageNormal1.GetNorm() <= eps ) { affectedDimension = 1; } // frontal else if ( imageNormal0.GetNorm() <= eps ) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image Geometry3D* imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project( testPoint, projectedPoint ); Point3D indexPoint; imageGeometry->WorldToIndex( projectedPoint, indexPoint ); affectedSlice = ROUND( indexPoint[affectedDimension] ); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if ( affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension)) ) return false; return true; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PositionEvent* positionEvent, const Image* image) { if (!positionEvent) return NULL; assert( positionEvent->GetSender() ); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); // get the timestep of the visible part (time-wise) of the image // first, we determine, which slice is affected const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return NULL; - int affectedDimension( -1 ); - int affectedSlice( -1 ); - //DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ); - if ( DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ) ) - { - try - { - // now we extract the correct slice from the volume, resulting in a 2D image - ExtractImageFilter::Pointer extractor= ExtractImageFilter::New(); - extractor->SetInput( image ); - extractor->SetSliceDimension( affectedDimension ); - extractor->SetSliceIndex( affectedSlice ); - extractor->SetTimeStep( timeStep ); - extractor->Update(); - - // here we have a single slice that can be modified - Image::Pointer slice = extractor->GetOutput(); - - //Workaround because of bug #7079 - Point3D origin = slice->GetGeometry()->GetOrigin(); - - int affectedDimension(-1); - - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")) - { - affectedDimension = 2; - } - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2")) - { - affectedDimension = 0; - } - if(positionEvent->GetSender()->GetRenderWindow() == mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")) - { - affectedDimension = 1; - } - - if (affectedDimension != -1) - { - origin[affectedDimension] = planeGeometry->GetOrigin()[affectedDimension]; - slice->GetGeometry()->SetOrigin(origin); - } - //Workaround end - - return slice; - } - catch(...) - { - // not working - return NULL; - } - } - else - { - ExtractDirectedPlaneImageFilterNew::Pointer newExtractor = ExtractDirectedPlaneImageFilterNew::New(); - newExtractor->SetInput( image ); - newExtractor->SetActualInputTimestep( timeStep ); - newExtractor->SetCurrentWorldGeometry2D( planeGeometry ); - newExtractor->Update(); - Image::Pointer slice = newExtractor->GetOutput(); - return slice; - } + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer reslice = vtkSmartPointer::New(); + //set to false to extract a slice + reslice->SetOverwriteMode(false); + reslice->Modified(); + + //use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( image ); + extractor->SetTimeStep( timeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(false); + extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( timeStep ) ); + + extractor->Modified(); + extractor->Update(); + + Image::Pointer slice = extractor->GetOutput(); + + /*============= BEGIN undo feature block ========================*/ + //specify the undo operation with the non edited slice + m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); + /*============= END undo feature block ========================*/ + + return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const PositionEvent* positionEvent) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const PositionEvent* positionEvent, Image* slice) { const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); - int affectedDimension( -1 ); - int affectedSlice( -1 ); - DetermineAffectedImageSlice( image, planeGeometry, affectedDimension, affectedSlice ); - - if (affectedDimension != -1) { - OverwriteSliceImageFilter::Pointer slicewriter = OverwriteSliceImageFilter::New(); - slicewriter->SetInput( image ); - slicewriter->SetCreateUndoInformation( true ); - slicewriter->SetSliceImage( slice ); - slicewriter->SetSliceDimension( affectedDimension ); - slicewriter->SetSliceIndex( affectedSlice ); - slicewriter->SetTimeStep( positionEvent->GetSender()->GetTimeStep( image ) ); - slicewriter->Update(); - } - else { - OverwriteDirectedPlaneImageFilter::Pointer slicewriter = OverwriteDirectedPlaneImageFilter::New(); - slicewriter->SetInput( image ); - slicewriter->SetCreateUndoInformation( false ); - slicewriter->SetSliceImage( slice ); - slicewriter->SetPlaneGeometry3D( slice->GetGeometry() ); - slicewriter->SetTimeStep( positionEvent->GetSender()->GetTimeStep( image ) ); - slicewriter->Update(); - } - if ( m_3DInterpolationEnabled ) + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer reslice = vtkSmartPointer::New(); + + //Set the slice as 'input' + reslice->SetInputSlice(slice->GetVtkImageData(this->m_TimeStep)); + + //set overwrite mode to true to write back to the image volume + reslice->SetOverwriteMode(true); + reslice->Modified(); + + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); + extractor->SetInput( image ); + extractor->SetTimeStep( this->m_TimeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(true); + extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( this->m_TimeStep ) ); + + extractor->Modified(); + extractor->Update(); + + //the image was modified within the pipeline, but not marked so + image->Modified(); + + /*============= BEGIN undo feature block ========================*/ + //specify the undo operation with the edited slice + m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),slice->GetGeometry(), this->m_TimeStep, const_cast(planeGeometry)); + + //create an operation event for the undo stack + OperationEvent* undoStackItem = new OperationEvent( DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Segmentation" ); + + //add it to the undo controller + UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); + + //clear the pointers as the operation are stored in the undocontroller and also deleted from there + m_undoOperation = NULL; + m_doOperation = NULL; + /*============= END undo feature block ========================*/ + + slice->DisconnectPipeline(); + ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); + contourExtractor->SetInput(slice); + contourExtractor->Update(); + mitk::Surface::Pointer contour = contourExtractor->GetOutput(); + + if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) { - slice->DisconnectPipeline(); - ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); - contourExtractor->SetInput(slice); - contourExtractor->Update(); - mitk::Surface::Pointer contour = contourExtractor->GetOutput(); - - if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) - { - unsigned int pos = this->AddContourmarker(positionEvent); - mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); - PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); - mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); - contour->DisconnectPipeline(); - } - + unsigned int pos = this->AddContourmarker(positionEvent); + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); + mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); + contour->DisconnectPipeline(); } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } -void mitk::SegTool2D::Enable3DInterpolation(bool status) -{ - m_3DInterpolationEnabled = status; -} - unsigned int mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, positionEvent->GetSender()->GetSliceNavigationController()->GetSlice()->GetPos()); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); contourMarker->SetGeometry2D( const_cast(plane)); std::stringstream markerStream; mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername ; markerStream << " "; markerStream << id+1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); rotatedContourNode->SetProperty( "isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty( "helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); if (plane) { if ( id == size ) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode,isMarker); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t+1).c_str())-1; if(id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage( const std::string& message ) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " http://bugs.mitk.org" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h index 236e7f58fc..7a39fff44c 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,135 +1,138 @@ /*=================================================================== 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 mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkTool.h" #include "mitkImage.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.h" +#include + + namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class Segmentation_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Transversal --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ); void SetShowMarkerNodes(bool); - void Enable3DInterpolation(bool); - protected: SegTool2D(); // purposely hidden SegTool2D(const char*); // purposely hidden virtual ~SegTool2D(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** \brief Extract the slice of an image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position. */ Image::Pointer GetAffectedImageSliceAs2DImage(const PositionEvent*, const Image* image); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const PositionEvent*); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const PositionEvent*); void WriteBackSegmentationResult (const PositionEvent*, Image*); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ unsigned int AddContourmarker ( const PositionEvent* ); void InteractiveSegmentationBugMessage( const std::string& message ); - private: BaseRenderer* m_LastEventSender; unsigned int m_LastEventSlice; //The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes; bool m_3DInterpolationEnabled; + + DiffSliceOperation* m_doOperation; + DiffSliceOperation* m_undoOperation; }; } // namespace #endif diff --git a/Modules/Segmentation/Testing/CMakeLists.txt b/Modules/Segmentation/Testing/CMakeLists.txt index e69de29bb2..d45daf111d 100644 --- a/Modules/Segmentation/Testing/CMakeLists.txt +++ b/Modules/Segmentation/Testing/CMakeLists.txt @@ -0,0 +1 @@ +MITK_CREATE_MODULE_TESTS() \ No newline at end of file diff --git a/Modules/Segmentation/Testing/files.cmake b/Modules/Segmentation/Testing/files.cmake index 2f9a9aa8a0..ce4ad96b02 100644 --- a/Modules/Segmentation/Testing/files.cmake +++ b/Modules/Segmentation/Testing/files.cmake @@ -1,22 +1,23 @@ set(MODULE_TESTS mitkContourMapper2DTest.cpp mitkContourTest.cpp mitkDataNodeSegmentationTest.cpp # mitkSegmentationInterpolationTest.cpp ) set(MODULE_IMAGE_TESTS mitkManualSegmentationToSurfaceFilterTest.cpp mitkOverwriteSliceImageFilterTest.cpp + mitkOverwriteSliceFilterTest.cpp ) set(MODULE_CUSTOM_TESTS ) set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png binary.stl ball.stl ) diff --git a/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp index 3c7cb5ec21..5a4570cc9c 100644 --- a/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp +++ b/Modules/Segmentation/Testing/mitkDataNodeSegmentationTest.cpp @@ -1,164 +1,164 @@ /*=================================================================== 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 "mitkDataNode.h" #include #include "mitkVtkPropRenderer.h" #include "mitkTestingMacros.h" #include "mitkGlobalInteraction.h" #include //Basedata Test #include #include #include //Mapper Test #include #include #include #include #include #include #include //Interactors #include #include #include //Propertylist Test #include #include #include #include #include #include #include #include #include #include #include /** * Extended test for mitk::DataNode. A number of tests from the core test * mitkDataNodeTest are assumed to pass! */ class mitkDataNodeSegmentationTestClass { public: static void TestDataSetting(mitk::DataNode::Pointer dataNode) { mitk::BaseData::Pointer baseData; //NULL pointer Test dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a NULL pointer was set correctly" ) baseData = mitk::Contour::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a Contour object was set correctly" ) baseData = mitk::ContourSet::New(); dataNode->SetData(baseData); MITK_TEST_CONDITION( baseData == dataNode->GetData(), "Testing if a ContourSet object was set correctly" ) } static void TestMapperSetting(mitk::DataNode::Pointer dataNode) { //tests the SetMapper() method //in dataNode is a mapper vector which can be accessed by index //in this test method we use only slot 0 (filled with null) and slot 1 //so we also test the destructor of the mapper classes mitk::Mapper::Pointer mapper; dataNode->SetMapper(0,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(0), "Testing if a NULL pointer was set correctly" ) mapper = mitk::ContourMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourSetMapper2D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetMapper2D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) //3D Mappers mapper = mitk::ContourSetVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourSetVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) mapper = mitk::ContourVtkMapper3D::New(); dataNode->SetMapper(1,mapper); MITK_TEST_CONDITION( mapper == dataNode->GetMapper(1), "Testing if a ContourVtkMapper3D was set correctly" ) MITK_TEST_CONDITION( dataNode == mapper->GetDataNode(), "Testing if the mapper returns the right DataNode" ) } static void TestInteractorSetting(mitk::DataNode::Pointer dataNode) { //this method tests the SetInteractor() and GetInteractor methods //the Interactor base class calls the DataNode->SetInteractor method mitk::Interactor::Pointer interactor; MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a NULL pointer was set correctly (Interactor)" ) interactor = mitk::ContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ContourInteractor was set correctly" ) interactor = mitk::ExtrudedContourInteractor::New("AffineInteractions click to select", dataNode); MITK_TEST_CONDITION( interactor == dataNode->GetInteractor(), "Testing if a ExtrudedContourInteractor was set correctly" ) } }; -int mitkDataNodeExtTest(int /* argc */, char* /*argv*/[]) +int mitkDataNodeSegmentationTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("DataNode") // Global interaction must(!) be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); // let's create an object of our class mitk::DataNode::Pointer myDataNode = mitk::DataNode::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myDataNode.IsNotNull(),"Testing instantiation") //test setData() Method - mitkDataNodeExtTestClass::TestDataSetting(myDataNode); - mitkDataNodeExtTestClass::TestMapperSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestDataSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestMapperSetting(myDataNode); //note, that no data is set to the dataNode - mitkDataNodeExtTestClass::TestInteractorSetting(myDataNode); + mitkDataNodeSegmentationTestClass::TestInteractorSetting(myDataNode); // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // always end with this! MITK_TEST_END() } diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp new file mode 100644 index 0000000000..2d60f29ed7 --- /dev/null +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp @@ -0,0 +1,391 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + + + +int VolumeSize = 128; + + +static void OverwriteByPointerTest(mitk::Image* workingImage, mitk::Image* refImg) +{ + + /* ============= setup plane ============*/ + int sliceindex = 55;//rand() % 32; + bool isFrontside = true; + bool isRotated = false; + + mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); + plane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Transversal, sliceindex, isFrontside, isRotated); + mitk::Point3D origin = plane->GetOrigin(); + mitk::Vector3D normal; + normal = plane->GetNormal(); + normal.Normalize(); + origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 + plane->SetOrigin(origin); + + + + /* ============= extract slice ============*/ + vtkSmartPointer resliceIdx = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(resliceIdx); + slicer->SetInput(workingImage); + slicer->SetWorldGeometry(plane); + slicer->SetVtkOutputRequest(true); + slicer->Modified(); + slicer->Update(); + + vtkSmartPointer slice = vtkSmartPointer::New(); + slice = slicer->GetVtkOutput(); + + + + /* ============= overwrite slice ============*/ + resliceIdx->SetOverwriteMode(true); + resliceIdx->Modified(); + slicer->Modified(); + slicer->Update();//implicit overwrite + + + + /* ============= check ref == working ============*/ + bool areSame = true; + mitk::Index3D id; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop; + } + } + } +stop: + MITK_TEST_CONDITION(areSame,"comparing images [orthogonal]"); + + + + /* ============= edit slice ============*/ + int idX = std::abs(VolumeSize-59); + int idY = std::abs(VolumeSize-23); + int idZ = 0; + int component = 0; + double val = 33.0; + + slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); + + + + /* ============= overwrite slice ============*/ + + vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); + resliceIdx2->SetOverwriteMode(true); + resliceIdx2->SetInputSlice(slice); + mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(resliceIdx2); + slicer2->SetInput(workingImage); + slicer2->SetWorldGeometry(plane); + slicer2->SetVtkOutputRequest(true); + slicer2->Modified(); + slicer2->Update(); + + + + + /* ============= check ============*/ + areSame = true; + + int x,y,z; + + for ( x = 0; x < VolumeSize; ++x){ + id[0] = x; + for ( y = 0; y < VolumeSize; ++y){ + id[1] = y; + for ( z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop2; + } + } + } +stop2: + //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; + MITK_TEST_CONDITION(x==idX && y==idY && z==sliceindex,"overwrited the right index [orthogonal]"); +} + + + +static void OverwriteByPointerForObliquePlaneTest(mitk::Image* workingImage, mitk::Image* refImg) +{ + +/*==============TEST WITHOUT MITK CONVERTION=============================*/ + + /* ============= setup plane ============*/ + int sliceindex = (int)(VolumeSize/2);//rand() % 32; + bool isFrontside = true; + bool isRotated = false; + + mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); + obliquePlane->InitializeStandardPlane(workingImage->GetGeometry(), mitk::PlaneGeometry::Transversal, sliceindex, isFrontside, isRotated); + mitk::Point3D origin = obliquePlane->GetOrigin(); + mitk::Vector3D normal; + normal = obliquePlane->GetNormal(); + normal.Normalize(); + origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 + obliquePlane->SetOrigin(origin); + + mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0); + rotationVector.Normalize(); + + float degree = 45.0; + + mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); + obliquePlane->ExecuteOperation(op); + delete op; + + + /* ============= extract slice ============*/ + mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); + slicer->SetInput(workingImage); + slicer->SetWorldGeometry(obliquePlane); + slicer->SetVtkOutputRequest(true); + slicer->Modified(); + slicer->Update(); + + vtkSmartPointer slice = vtkSmartPointer::New(); + slice = slicer->GetVtkOutput(); + + + + /* ============= overwrite slice ============*/ + vtkSmartPointer resliceIdx = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx); + resliceIdx->SetOverwriteMode(true); + resliceIdx->SetInputSlice(slice); + resliceIdx->Modified(); + overwriter->SetInput(workingImage); + overwriter->SetWorldGeometry(obliquePlane); + overwriter->SetVtkOutputRequest(true); + overwriter->Modified(); + overwriter->Update(); + + + + /* ============= check ref == working ============*/ + bool areSame = true; + mitk::Index3D id; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop; + } + } + } +stop: + MITK_TEST_CONDITION(areSame,"comparing images (no mitk convertion) [oblique]"); + + + + + +/*==============TEST WITH MITK CONVERTION=============================*/ + + /* ============= extract slice ============*/ + mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(); + slicer2->SetInput(workingImage); + slicer2->SetWorldGeometry(obliquePlane); + slicer2->Modified(); + slicer2->Update(); + + + mitk::Image::Pointer sliceInMitk = slicer2->GetOutput(); + vtkSmartPointer slice2 = vtkSmartPointer::New(); + slice2 = sliceInMitk->GetVtkImageData(); + + + /* ============= overwrite slice ============*/ + vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); + mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2); + resliceIdx2->SetOverwriteMode(true); + resliceIdx2->SetInputSlice(slice2); + resliceIdx2->Modified(); + overwriter2->SetInput(workingImage); + overwriter2->SetWorldGeometry(obliquePlane); + overwriter2->SetVtkOutputRequest(true); + overwriter2->Modified(); + overwriter2->Update(); + + + + /* ============= check ref == working ============*/ + areSame = true; + id[0] = id[1] = id[2] = 0; + for (int x = 0; x < VolumeSize; ++x){ + id[0] = x; + for (int y = 0; y < VolumeSize; ++y){ + id[1] = y; + for (int z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop2; + } + } + } +stop2: + MITK_TEST_CONDITION(areSame,"comparing images (with mitk convertion) [oblique]"); + + +/*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/ + + /* ============= edit slice ============*/ + int idX = std::abs(VolumeSize-59); + int idY = std::abs(VolumeSize-23); + int idZ = 0; + int component = 0; + double val = 33.0; + + slice->SetScalarComponentFromDouble(idX,idY,idZ,component,val); + + mitk::Vector3D indx; + indx[0] = idX; indx[1] = idY; indx[2] = idZ; + sliceInMitk->GetGeometry()->IndexToWorld(indx, indx); + + /* ============= overwrite slice ============*/ + + vtkSmartPointer resliceIdx3 = vtkSmartPointer::New(); + resliceIdx3->SetOverwriteMode(true); + resliceIdx3->SetInputSlice(slice); + mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3); + overwriter3->SetInput(workingImage); + overwriter3->SetWorldGeometry(obliquePlane); + overwriter3->SetVtkOutputRequest(true); + overwriter3->Modified(); + overwriter3->Update(); + + + + + /* ============= check ============*/ + areSame = true; + + int x,y,z; + + for ( x = 0; x < VolumeSize; ++x){ + id[0] = x; + for ( y = 0; y < VolumeSize; ++y){ + id[1] = y; + for ( z = 0; z < VolumeSize; ++z){ + id[2] = z; + areSame = refImg->GetPixelValueByIndex(id) == workingImage->GetPixelValueByIndex(id); + if(!areSame) + goto stop3; + } + } + } +stop3: + //MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; + //MITK_INFO << indx; + MITK_TEST_CONDITION(x==idX && y==z,"overwrited the right index [oblique]"); +} + + + + +/*================ #BEGIN test main ================*/ +int mitkOverwriteSliceFilterTest(int argc, char* argv[]) +{ + + MITK_TEST_BEGIN("mitkOverwriteSliceFilterTest") + + + + + typedef itk::Image ImageType; + + typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; + + ImageType::Pointer image = ImageType::New(); + + ImageType::IndexType start; + start[0] = start[1] = start[2] = 0; + + ImageType::SizeType size; + size[0] = size[1] = size[2] = VolumeSize; + + ImageType::RegionType imgRegion; + imgRegion.SetSize(size); + imgRegion.SetIndex(start); + + image->SetRegions(imgRegion); + image->SetSpacing(1.0); + image->Allocate(); + + + ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); + imageIterator.GoToBegin(); + + + unsigned short pixelValue = 0; + + //fill the image with distinct values + while ( !imageIterator.IsAtEnd() ) + { + image->SetPixel(imageIterator.GetIndex(), pixelValue); + ++imageIterator; + ++pixelValue; + } + /* end setup itk image */ + + + + mitk::Image::Pointer refImage; + CastToMitkImage(image, refImage); + mitk::Image::Pointer workingImg; + CastToMitkImage(image, workingImg); + OverwriteByPointerTest(workingImg, refImage); + + + mitk::Image::Pointer workingImg3; + CastToMitkImage(image, workingImg3); + OverwriteByPointerForObliquePlaneTest(workingImg3, refImage); + + MITK_TEST_END() +} diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 9911599184..fe671afe8a 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,53 +1,56 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkComputeContourSetNormalsFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkCreateDistanceImageFromSurfaceFilter.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkImageToContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkReduceContourSetFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkSegmentationSink.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp +Algorithms/mitkVtkImageOverwrite.cpp +Algorithms/mitkDiffSliceOperation.cpp +Algorithms/mitkDiffSliceOperationApplier.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkSurfaceInterpolationController.cpp # DataManagement/mitkApplyDiffImageOperation.cpp DataManagement/mitkContour.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourInteractor.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkExtrudedContourInteractor.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkRegionGrow3DTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp ) diff --git a/Modules/US/CMakeLists.txt b/Modules/US/CMakeLists.txt index 9318ab6d39..7c957fcf23 100644 --- a/Modules/US/CMakeLists.txt +++ b/Modules/US/CMakeLists.txt @@ -1,11 +1,11 @@ MITK_CREATE_MODULE(MitkUS SUBPROJECTS - INCLUDE_DIRS USFilters + INCLUDE_DIRS USFilters USModel USService INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} DEPENDS Mitk mitkOpenCVVideoSupport ) ## create US config CONFIGURE_FILE(mitkUSConfig.h.in ${PROJECT_BINARY_DIR}/mitkUSConfig.h @ONLY) -ADD_SUBDIRECTORY(Testing) \ No newline at end of file +ADD_SUBDIRECTORY(Testing) diff --git a/Modules/US/Testing/mitkUSDeviceTest.cpp b/Modules/US/Testing/mitkUSDeviceTest.cpp index 62ca54f7e4..667fb3bb50 100644 --- a/Modules/US/Testing/mitkUSDeviceTest.cpp +++ b/Modules/US/Testing/mitkUSDeviceTest.cpp @@ -1,109 +1,113 @@ /*=================================================================== 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 "mitkUSVideoDevice.h" #include "mitkUSProbe.h" #include "mitkTestingMacros.h" -#include class mitkUSDeviceTestClass { public: static void TestInstantiation() { // let's create an object of our class - mitk::USDevice::Pointer device = mitk::USDevice::New("Manufacturer", "Model", true); - MITK_TEST_CONDITION_REQUIRED(device.IsNotNull(), "USDevice should not be null after instantiation"); + mitk::USVideoDevice::Pointer tempPointer = mitk::USVideoDevice::New("IllegalPath", "Manufacturer", "Model"); + mitk::USDevice* device = tempPointer.GetPointer(); + MITK_TEST_CONDITION_REQUIRED(device, "USDevice should not be null after instantiation"); MITK_TEST_CONDITION_REQUIRED((device->GetDeviceManufacturer().compare("Manufacturer") == 0), "Manufacturer should be set correctly"); MITK_TEST_CONDITION_REQUIRED((device->GetDeviceModel().compare("Model") == 0), "Model should be set correctly"); - MITK_TEST_CONDITION_REQUIRED((device->GetIsVideoOnly() == true), "Device should be VideoOnly"); } static void TestAddProbe() { - mitk::USDevice::Pointer device = mitk::USDevice::New("Manufacturer", "Model", true); + //mitk::USDevice::Pointer device = mitk::USVideoDevice::New("IllegalPath", "Manufacturer", "Model"); + mitk::USVideoDevice::Pointer tempPointer = mitk::USVideoDevice::New("IllegalPath", "Manufacturer", "Model"); + mitk::USDevice* device = tempPointer.GetPointer(); + // create probes mitk::USProbe::Pointer usSource = mitk::USProbe::New(); mitk::USProbe::Pointer probeA = mitk::USProbe::New(); mitk::USProbe::Pointer probeB = mitk::USProbe::New(); mitk::USProbe::Pointer identicalProbe = mitk::USProbe::New(); // only this one should be identical // give my babys some names probeA->SetName("ProbeA"); probeB->SetName("ProbeB"); identicalProbe->SetName("ProbeA"); // I'm gonna be a bad father... //right now, list of devices should be empty MITK_TEST_CONDITION_REQUIRED(device->GetConnectedProbes().size() == 0, "Newly created device should have no probes connected"); // Connect Probe A device->AddProbe(probeA); MITK_TEST_CONDITION_REQUIRED(device->GetConnectedProbes().size() == 1, "Device should add one new probe"); // Connect Probe B device->AddProbe(probeB); MITK_TEST_CONDITION_REQUIRED(device->GetConnectedProbes().size() == 2, "Device should add another probe"); // Connect identical Probe device->AddProbe(identicalProbe); MITK_TEST_CONDITION_REQUIRED(device->GetConnectedProbes().size() == 2, "Device should not have added identical probe"); } static void TestActivateProbe() { - mitk::USDevice::Pointer device = mitk::USDevice::New("Manufacturer", "Model", true); + //mitk::USDevice::Pointer device = mitk::USVideoDevice::New("IllegalVideoDevice", "Manufacturer", "Model"); + mitk::USVideoDevice::Pointer tempPointer = mitk::USVideoDevice::New("IllegalPath", "Manufacturer", "Model"); + mitk::USDevice* device = tempPointer.GetPointer(); // create probes mitk::USProbe::Pointer usSource = mitk::USProbe::New(); mitk::USProbe::Pointer probeA = mitk::USProbe::New(); mitk::USProbe::Pointer probeB = mitk::USProbe::New(); mitk::USProbe::Pointer identicalProbe = mitk::USProbe::New(); // only this one should be identical // names probeA->SetName("ProbeA"); probeB->SetName("ProbeB"); identicalProbe->SetName("ProbeA"); device->AddProbe(probeA); device->AddProbe(probeB); // We after activation, we expect the device to activate probeA, which is the first-connected identical version. device->ActivateProbe(identicalProbe); MITK_TEST_CONDITION_REQUIRED(device->GetActiveProbe() == probeA, "probe A should be active"); // And we deactivate it again... device->DeactivateProbe(); MITK_TEST_CONDITION_REQUIRED(device->GetActiveProbe().IsNull(), "After deactivation, no probe should be active"); } }; /** * This function is testing methods of the class USDevice. */ int mitkUSDeviceTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("mitkUSDeviceTest"); mitkUSDeviceTestClass::TestInstantiation(); mitkUSDeviceTestClass::TestAddProbe(); mitkUSDeviceTestClass::TestActivateProbe(); MITK_TEST_END(); } diff --git a/Modules/US/Testing/mitkUSPipelineTest.cpp b/Modules/US/Testing/mitkUSPipelineTest.cpp index 7d2613358a..5bba1ef99a 100644 --- a/Modules/US/Testing/mitkUSPipelineTest.cpp +++ b/Modules/US/Testing/mitkUSPipelineTest.cpp @@ -1,102 +1,98 @@ /*=================================================================== 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 "mitkUSVideoDevice.h" #include "mitkTestingMacros.h" #include "mitkUSImageToUSImageFilter.h" #include "mitkPadImageFilter.h" // START TESTFILER // This is an specialization of the USImageToUSImageFIlter class TestUSFilter : public mitk::USImageToUSImageFilter { protected: TestUSFilter() : mitk::USImageToUSImageFilter(){}; virtual ~TestUSFilter(){}; public: mitkClassMacro(TestUSFilter, mitk::USImageToUSImageFilter); itkNewMacro(Self); virtual void GenerateOutputInformation() { mitk::Image::Pointer inputImage = (mitk::Image*) this->GetInput(0); mitk::Image::Pointer output = (mitk::Image*) this->GetOutput(0); if(inputImage.IsNull()) return; }; void GenerateData() - { - MITK_INFO << "GenerateData called in Testfilter!"; - //mitk::Image::Pointer ni = const_cast(this->GetInput(0)); + { mitk::USImage::Pointer ni = this->GetInput(0); mitk::USImage::Pointer result = mitk::USImage::New(); result->Initialize(ni); result->SetImportVolume(ni->GetData()); mitk::USImageMetadata::Pointer meta = ni->GetMetadata(); meta->SetDeviceComment("Test"); result->SetMetadata(meta); SetNthOutput(0, result); - MITK_INFO << "GenerateData completed in Testfilter!"; }; }; // END TESTFILTER class mitkUSPipelineTestClass { public: static void TestPipelineUS(std::string videoFilePath) { // Set up a pipeline mitk::USVideoDevice::Pointer videoDevice = mitk::USVideoDevice::New("C:\\Users\\maerz\\Videos\\Debut\\us.avi", "Manufacturer", "Model"); TestUSFilter::Pointer filter = TestUSFilter::New(); videoDevice->Update(); filter->SetInput(videoDevice->GetOutput()); filter->Update(); MITK_TEST_CONDITION_REQUIRED(videoDevice.IsNotNull(), "videoDevice should not be null after instantiation"); - //mitk::USImage::Pointer result = dynamic_cast (filter->GetOutput(0)); mitk::USImage::Pointer result = filter->GetOutput(0); MITK_TEST_CONDITION_REQUIRED(result.IsNotNull(), "resulting images should not be null"); std::string model = result->GetMetadata()->GetDeviceModel(); MITK_TEST_CONDITION_REQUIRED(model.compare("Model") == 0 , "Resulting images should have their metadata set correctly"); - } }; /** * This function is setting up a pipeline and checks the output for validity. */ int mitkUSPipelineTest(int argc , char* argv[]) { MITK_TEST_BEGIN("mitkUSPipelineTest"); #ifdef WIN32 // Video file compression is currently only supported under windows. - mitkUSPipelineTestClass::TestPipelineUS(argv[1]); + // US Pipelines need to be reworked :( + // mitkUSPipelineTestClass::TestPipelineUS(argv[1]); #endif MITK_TEST_END(); } diff --git a/Modules/US/USFilters/mitkUSImageVideoSource.cpp b/Modules/US/USFilters/mitkUSImageVideoSource.cpp index 4bc5abc0af..4ae1a6d51f 100644 --- a/Modules/US/USFilters/mitkUSImageVideoSource.cpp +++ b/Modules/US/USFilters/mitkUSImageVideoSource.cpp @@ -1,83 +1,112 @@ /*=================================================================== 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() : itk::Object() { m_IsVideoReady = false; m_IsMetadataReady = false; m_IsGeometryReady = false; this->m_OpenCVToMitkFilter = mitk::OpenCVToMitkImageFilter::New(); } mitk::USImageVideoSource::~USImageVideoSource() { } void mitk::USImageVideoSource::SetVideoFileInput(std::string path) { m_OpenCVVideoSource = mitk::OpenCVVideoSource::New(); // Example: "C:\\Users\\maerz\\Videos\\Debut\\us.avi" m_OpenCVVideoSource->SetVideoFileInput(path.c_str(),true,false); m_OpenCVVideoSource->StartCapturing(); m_OpenCVVideoSource->FetchFrame(); // Let's see if we have been successful m_IsVideoReady = m_OpenCVVideoSource->IsCapturingEnabled(); } void mitk::USImageVideoSource::SetCameraInput(int deviceID) { + + + + // Old Code, this may not work + m_OpenCVVideoSource = mitk::OpenCVVideoSource::New(); + m_OpenCVVideoSource->SetVideoCameraInput(deviceID); m_OpenCVVideoSource->StartCapturing(); m_OpenCVVideoSource->FetchFrame(); // Let's see if we have been successful m_IsVideoReady = m_OpenCVVideoSource->IsCapturingEnabled(); } mitk::USImage::Pointer mitk::USImageVideoSource::GetNextImage() { - m_OpenCVVideoSource->FetchFrame(); - // This is a bit of a workaround: We only need to initialize an OpenCV Image. Actual - // Initialization happens inside the OpenCVVideoSource - IplImage* iplImage = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3); - m_OpenCVVideoSource->GetCurrentFrameAsOpenCVImage(iplImage); - - this->m_OpenCVToMitkFilter->SetOpenCVImage(iplImage); +// The following code utilizes open CV directly do access images + //IplImage *m_cvCurrentVideoFrame = NULL; + //CvCapture* capture = cvCaptureFromCAM( 1000 ); + // if ( !capture ) { + // fprintf( stderr, "ERROR: capture is NULL \n" ); + // getchar(); + // return NULL; + // } + // // Show the image captured from the camera in the window and repeat + // + // // Get one frame + // m_cvCurrentVideoFrame = cvQueryFrame( capture ); + // +/// WORKING CODE + + + IplImage *m_cvCurrentVideoFrame = NULL; + int height = m_OpenCVVideoSource->GetImageHeight(); + int width = m_OpenCVVideoSource->GetImageWidth(); + m_cvCurrentVideoFrame = cvCreateImage(cvSize(width,height),8,3); + m_OpenCVVideoSource->GetCurrentFrameAsOpenCVImage(m_cvCurrentVideoFrame); + m_OpenCVVideoSource->FetchFrame(); + this->m_OpenCVToMitkFilter->SetOpenCVImage(m_cvCurrentVideoFrame); this->m_OpenCVToMitkFilter->Update(); // OpenCVToMitkImageFilter returns a standard mit::image. We then transform it into an USImage mitk::USImage::Pointer result = mitk::USImage::New(this->m_OpenCVToMitkFilter->GetOutput(0)); - cvReleaseImage (&iplImage); + cvReleaseImage (&m_cvCurrentVideoFrame); return result; } diff --git a/Modules/US/USFilters/mitkUSVideoDevice.cpp b/Modules/US/USFilters/mitkUSVideoDevice.cpp deleted file mode 100644 index b37f8f8dfb..0000000000 --- a/Modules/US/USFilters/mitkUSVideoDevice.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/*=================================================================== - -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 "mitkUSVideoDevice.h" -#include - - -mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model, true) -{ - this->SetNumberOfInputs(1); - this->SetNumberOfOutputs(1); - m_Source = mitk::USImageVideoSource::New(); - m_Source->SetCameraInput(videoDeviceNumber); -} - -mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model, true) -{ - this->SetNumberOfInputs(1); - this->SetNumberOfOutputs(1); - m_Source = mitk::USImageVideoSource::New(); - m_Source->SetVideoFileInput(videoFilePath); -} - -mitk::USVideoDevice::~USVideoDevice() -{ - -} - -void mitk::USVideoDevice::GenerateData() -{ - mitk::USImage::Pointer result; - result = m_Source->GetNextImage(); - - // Set Metadata - result->SetMetadata(this->m_Metadata); - // Set Output - this->SetNthOutput(0, result); -} diff --git a/Modules/US/USFilters/mitkUSVideoDevice.h b/Modules/US/USFilters/mitkUSVideoDevice.h deleted file mode 100644 index 3de4e71894..0000000000 --- a/Modules/US/USFilters/mitkUSVideoDevice.h +++ /dev/null @@ -1,64 +0,0 @@ -/*=================================================================== - -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 -#include "mitkUSImageVideoSource.h" - -namespace mitk { - - /**Documentation - * \brief A VideoDevcie is the common class for video only devcies. They capture Video Input either from - * a file or from a devcie, and transform the output into an mitkUSImage with attached Metadata. - * This simple implementation does only capture and display 2D Images without cropping or registration. - * One can simply inherit from this class and overwrite the handle2D and handle 3Dmethods to get full access to the data - * \ingroup US - */ - class MitkUS_EXPORT USVideoDevice : public mitk::USDevice - { - public: - mitkClassMacro(USVideoDevice, mitk::USDevice); - // To open a devcie (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); - - void GenerateData(); - - 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); - virtual ~USVideoDevice(); - - mitk::USImageVideoSource::Pointer m_Source; - - }; -} // namespace mitk -#endif diff --git a/Modules/US/USFilters/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp similarity index 58% rename from Modules/US/USFilters/mitkUSDevice.cpp rename to Modules/US/USModel/mitkUSDevice.cpp index dca8cf7fde..03b601dc80 100644 --- a/Modules/US/USFilters/mitkUSDevice.cpp +++ b/Modules/US/USModel/mitkUSDevice.cpp @@ -1,147 +1,271 @@ /*=================================================================== 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 "mitkUSImageMetadata.h" +//Microservices +#include +#include +#include +#include "mitkModuleContext.h" -mitk::USDevice::USDevice(std::string manufacturer, std::string model, bool isVideoOnly) : mitk::ImageSource() + +mitk::USDevice::USDevice(std::string manufacturer, std::string model) : mitk::ImageSource() { // Initialize Members m_Metadata = mitk::USImageMetadata::New(); m_Metadata->SetDeviceManufacturer(manufacturer); m_Metadata->SetDeviceModel(model); - m_Metadata->SetDeviceIsVideoOnly(isVideoOnly); + m_IsActive = false; //set number of outputs this->SetNumberOfOutputs(1); //create a new output mitk::USImage::Pointer newOutput = mitk::USImage::New(); this->SetNthOutput(0,newOutput); } +mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata) : mitk::ImageSource() +{ + m_Metadata = metadata; + m_IsActive = false; + + //set number of outputs + this->SetNumberOfOutputs(1); + + //create a new output + mitk::USImage::Pointer newOutput = mitk::USImage::New(); + this->SetNthOutput(0,newOutput); +} + + mitk::USDevice::~USDevice() { } +bool mitk::USDevice::Connect() +{ + //TODO Throw Exception is already activated before connection + + // Prepare connection, fail if this fails. + if (! this->OnConnection()) return false; + + // Get Context and Module + mitk::ModuleContext* context = GetModuleContext(); + + // Define ServiceProps + ServiceProperties props; + props["DeviceClass"] = this->GetDeviceClass(); + std::string no = "false"; + props["IsActive"] = no; + m_ServiceRegistration = context->RegisterService(this, props); + return true; +} + + + +bool mitk::USDevice::Disconnect() +{ + // Prepare connection, fail if this fails. + if (! this->OnDisconnection()) return false; + + // Unregister + m_ServiceRegistration.Unregister(); + m_ServiceRegistration = 0; + return true; +} + +//bool mitk::USDevice::OnConnection() +//{ +// return true; +// // TODO: Make Abstract +//} +// +//bool mitk::USDevice::OnDisconnection() +//{ +// return true; +// // TODO Make Abstract +//} + + +bool mitk::USDevice::Activate() +{ + if (! this->GetIsConnected()) return false; + + m_IsActive = OnActivation(); + + ServiceProperties props; + props["DeviceClass"] = this->GetDeviceClass(); + std::string yes = "true"; + props["IsActive"] = yes; + this->m_ServiceRegistration.SetProperties(props); + return m_IsActive; +} + + +void mitk::USDevice::Deactivate() +{ + m_IsActive= false; + + ServiceProperties props; + props["DeviceClass"] = this->GetDeviceClass(); + std::string no = "false"; + props["IsActive"] = no; + this->m_ServiceRegistration.SetProperties(props); + OnDeactivation(); +} + +// +//bool mitk::USDevice::OnActivation() +//{ +// return true; +// // TODO Make Abstract +//} +// +// +//void mitk::USDevice::OnDeactivation() +//{ +// // TODO Make Abstract +//} +// +// +//std::string mitk::USDevice::GetDeviceClass() +//{ +// return "org.mitk.Ultrasound.GenericDevice"; +//} + + + void mitk::USDevice::AddProbe(mitk::USProbe::Pointer probe) { for(int i = 0; i < m_ConnectedProbes.size(); i++) { if (m_ConnectedProbes[i]->IsEqualToProbe(probe)) return; } this->m_ConnectedProbes.push_back(probe); } + void mitk::USDevice::ActivateProbe(mitk::USProbe::Pointer probe){ // currently, we may just add the probe. This behaviour must be changed, should more complicated SDK applications emerge AddProbe(probe); int index = -1; for(int i = 0; i < m_ConnectedProbes.size(); i++) { if (m_ConnectedProbes[i]->IsEqualToProbe(probe)) index = i; } // index now contains the position of the original instance of this probe m_ActiveProbe = m_ConnectedProbes[index]; } + void mitk::USDevice::DeactivateProbe(){ m_ActiveProbe = 0; } void mitk::USDevice::GenerateData() { } mitk::USImage* mitk::USDevice::GetOutput() { if (this->GetNumberOfOutputs() < 1) return NULL; return static_cast(this->ProcessObject::GetOutput(0)); } mitk::USImage* mitk::USDevice::GetOutput(unsigned int idx) { if (this->GetNumberOfOutputs() < 1) return NULL; 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 NULL pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a NULL pointer" ); } // Call Graft on USImage to copy member data output->Graft( graft ); } itk::ProcessObject::DataObjectPointer mitk::USDevice::MakeOutput( unsigned int /*idx */) { mitk::USImage::Pointer p = mitk::USImage::New(); return static_cast(p.GetPointer()); } //########### GETTER & SETTER ##################// +bool mitk::USDevice::GetIsActive() +{ + return m_IsActive; +} + + +bool mitk::USDevice::GetIsConnected() +{ + // a device is connected if it is registered with the service + return (m_ServiceRegistration != 0); +} + + std::string mitk::USDevice::GetDeviceManufacturer(){ return this->m_Metadata->GetDeviceManufacturer(); } std::string mitk::USDevice::GetDeviceModel(){ return this->m_Metadata->GetDeviceModel(); } std::string mitk::USDevice::GetDeviceComment(){ return this->m_Metadata->GetDeviceComment(); } -bool mitk::USDevice::GetIsVideoOnly(){ - return this->m_Metadata->GetDeviceIsVideoOnly(); -} - std::vector mitk::USDevice::GetConnectedProbes() { return m_ConnectedProbes; } diff --git a/Modules/US/USFilters/mitkUSDevice.h b/Modules/US/USModel/mitkUSDevice.h similarity index 53% rename from Modules/US/USFilters/mitkUSDevice.h rename to Modules/US/USModel/mitkUSDevice.h index 5fb28e7dc1..32330d2dc4 100644 --- a/Modules/US/USFilters/mitkUSDevice.h +++ b/Modules/US/USModel/mitkUSDevice.h @@ -1,156 +1,249 @@ /*=================================================================== 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_ #include #include "mitkUSProbe.h" #include "mitkUSImageMetadata.h" #include "mitkUSImage.h" #include #include #include #include +// Microservices +#include +#include + + namespace mitk { /**Documentation * \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. * \ingroup US */ class MitkUS_EXPORT USDevice : public mitk::ImageSource { public: mitkClassMacro(USDevice, mitk::ImageSource); + /** - * \brief Enforces minimal Metadata to be set. The isVideoOnly flag indicates that this class - * only handles a videostream and does not receive Metadata from the physical device itself. + * \brief Enforces minimal Metadata to be set. */ - mitkNewMacro3Param(Self, std::string, std::string, bool); - + // mitkNewMacro3Param(Self, std::string, std::string, bool); + /** + * \brief Constructs a device with the given Metadata. Make sure the Metadata contains meaningful content! + * + */ + // mitkNewMacro2Param(Self, mitk::USImageMetadata::Pointer, bool); + + /** + * \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(); + + /** + * \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 Add a probe to the device without connecting to it. * This should usually be done before connecting to the probe. */ virtual void AddProbe(mitk::USProbe::Pointer probe); /** * \brief Connect to a probe and activate it. The probe should be added first. * Usually, a VideoDevice will simply add a probe it wants to connect to, * but an SDK Device might require adding a probe first. */ virtual void ActivateProbe(mitk::USProbe::Pointer probe); /** * \brief Deactivates the currently active probe. */ virtual void DeactivateProbe(); /** * \brief Removes a probe from the ist of currently added probes. */ //virtual void removeProbe(mitk::USProbe::Pointer probe); /** * \brief Returns a vector containing all connected probes. */ std::vector GetConnectedProbes(); /** *\brief return the output (output with id 0) of the filter */ USImage* GetOutput(void); /** *\brief return the output with id idx of the filter */ USImage* GetOutput(unsigned int idx); /** *\brief Graft the specified DataObject onto this ProcessObject's output. * * See itk::ImageSource::GraftNthOutput for details */ virtual void GraftNthOutput(unsigned int idx, itk::DataObject *graft); /** * \brief Graft the specified DataObject onto this ProcessObject's output. * * See itk::ImageSource::Graft Output for details */ virtual void GraftOutput(itk::DataObject *graft); /** * \brief Make a DataObject of the correct type to used as the specified output. * * This method is automatically called when DataObject::DisconnectPipeline() * is called. DataObject::DisconnectPipeline, disconnects a data object * from being an output of its current source. When the data object * is disconnected, the ProcessObject needs to construct a replacement * output data object so that the ProcessObject is in a valid state. * Subclasses of USImageVideoSource that have outputs of different * data types must overwrite this method so that proper output objects * are created. */ virtual DataObjectPointer MakeOutput(unsigned int idx); //########### 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 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(); + /** * \brief Returns the currently active probe or null, if none is active */ itkGetMacro(ActiveProbe, mitk::USProbe::Pointer); std::string GetDeviceManufacturer(); std::string GetDeviceModel(); std::string GetDeviceComment(); - bool GetIsVideoOnly(); protected: mitk::USProbe::Pointer m_ActiveProbe; std::vector m_ConnectedProbes; + bool m_IsActive; + + /** + * \brief Is called during the connection process. Override this method in your subclass to handle the actual connection. + * Return true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. + */ + virtual bool OnConnection() = 0; + + /** + * \brief Is called during the disconnection process. Override this method in your subclass to handle the actual disconnection. + * Return true if successful and false if unsuccessful. Additionally, you may throw an exception to clarify what went wrong. + */ + virtual bool OnDisconnection() = 0; + + /** + * \brief Is called during the activation process. After this method is finsihed, the device should be generating images + */ + 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. + */ + virtual void OnDeactivation() = 0; + + /** * \brief This metadata set is privately used to imprint USImages with Metadata later. * At instantiation time, it only contains Information about the Device, * At scan time, it integrates this data with the probe information and imprints it on * the produced images. This field is intentionally hidden from outside interference. */ mitk::USImageMetadata::Pointer m_Metadata; /** - * \brief Enforces minimal Metadata to be set. The isVideoOnly flag indicates that this class - * only handles a videostream and does not recieve Metadata from the physical device itself. + * \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! */ - USDevice(std::string manufacturer, std::string model, bool isVideoOnly); + 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. */ void GenerateData(); + + + private: + + mitk::ServiceRegistration m_ServiceRegistration; + }; } // namespace mitk -#endif + +// This is the microservice declaration. Do not meddle! +US_DECLARE_SERVICE_INTERFACE(mitk::USDevice, "org.mitk.services.UltrasoundDevice") + +#endif \ No newline at end of file diff --git a/Modules/US/USFilters/mitkUSImage.cpp b/Modules/US/USModel/mitkUSImage.cpp similarity index 98% rename from Modules/US/USFilters/mitkUSImage.cpp rename to Modules/US/USModel/mitkUSImage.cpp index 214acd2cdf..713eb2e99a 100644 --- a/Modules/US/USFilters/mitkUSImage.cpp +++ b/Modules/US/USModel/mitkUSImage.cpp @@ -1,63 +1,62 @@ /*=================================================================== 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 "mitkUSImage.h" #include #include mitk::USImage::USImage() : mitk::Image() { this->SetMetadata(mitk::USImageMetadata::New()); } mitk::USImage::USImage(mitk::Image::Pointer image) : mitk::Image() { this->Initialize(image); this->SetVolume(image->GetData()); - //this->SetMetadata(mitk::USImageMetadata::New()); } mitk::USImage::~USImage() { } mitk::USImageMetadata::Pointer mitk::USImage::GetMetadata(){ mitk::USImageMetadata::Pointer result = mitk::USImageMetadata::New(); result->SetDeviceManufacturer(this->GetProperty(mitk::USImageMetadata::PROP_DEV_MANUFACTURER)->GetValueAsString()); result->SetDeviceModel( this->GetProperty(mitk::USImageMetadata::PROP_DEV_MODEL)->GetValueAsString()); result->SetDeviceComment( this->GetProperty(mitk::USImageMetadata::PROP_DEV_COMMENT)->GetValueAsString()); result->SetDeviceIsVideoOnly( this->GetProperty(mitk::USImageMetadata::PROP_DEV_ISVIDEOONLY)); result->SetProbeName( this->GetProperty(mitk::USImageMetadata::PROP_PROBE_NAME)->GetValueAsString()); result->SetProbeFrequency( this->GetProperty(mitk::USImageMetadata::PROP_PROBE_FREQUENCY)->GetValueAsString()); result->SetZoom( this->GetProperty(mitk::USImageMetadata::PROP_ZOOM)->GetValueAsString()); return result; } void mitk::USImage::SetMetadata(mitk::USImageMetadata::Pointer metadata){ this->SetProperty(mitk::USImageMetadata::PROP_DEV_MANUFACTURER, mitk::StringProperty::New(metadata->GetDeviceManufacturer())); this->SetProperty(mitk::USImageMetadata::PROP_DEV_MODEL, mitk::StringProperty::New(metadata->GetDeviceModel())); this->SetProperty(mitk::USImageMetadata::PROP_DEV_COMMENT, mitk::StringProperty::New(metadata->GetDeviceComment())); this->SetProperty(mitk::USImageMetadata::PROP_DEV_ISVIDEOONLY, mitk::BoolProperty::New(metadata->GetDeviceIsVideoOnly())); this->SetProperty(mitk::USImageMetadata::PROP_PROBE_NAME, mitk::StringProperty::New(metadata->GetProbeName())); this->SetProperty(mitk::USImageMetadata::PROP_PROBE_FREQUENCY, mitk::StringProperty::New(metadata->GetProbeFrequency())); this->SetProperty(mitk::USImageMetadata::PROP_ZOOM, mitk::StringProperty::New(metadata->GetZoom())); } diff --git a/Modules/US/USFilters/mitkUSImage.h b/Modules/US/USModel/mitkUSImage.h similarity index 100% rename from Modules/US/USFilters/mitkUSImage.h rename to Modules/US/USModel/mitkUSImage.h diff --git a/Modules/US/USFilters/mitkUSImageMetadata.cpp b/Modules/US/USModel/mitkUSImageMetadata.cpp similarity index 100% rename from Modules/US/USFilters/mitkUSImageMetadata.cpp rename to Modules/US/USModel/mitkUSImageMetadata.cpp diff --git a/Modules/US/USFilters/mitkUSImageMetadata.h b/Modules/US/USModel/mitkUSImageMetadata.h similarity index 100% rename from Modules/US/USFilters/mitkUSImageMetadata.h rename to Modules/US/USModel/mitkUSImageMetadata.h diff --git a/Modules/US/USFilters/mitkUSProbe.cpp b/Modules/US/USModel/mitkUSProbe.cpp similarity index 100% rename from Modules/US/USFilters/mitkUSProbe.cpp rename to Modules/US/USModel/mitkUSProbe.cpp diff --git a/Modules/US/USFilters/mitkUSProbe.h b/Modules/US/USModel/mitkUSProbe.h similarity index 100% rename from Modules/US/USFilters/mitkUSProbe.h rename to Modules/US/USModel/mitkUSProbe.h diff --git a/Modules/US/USModel/mitkUSVideoDevice.cpp b/Modules/US/USModel/mitkUSVideoDevice.cpp new file mode 100644 index 0000000000..caf08f928c --- /dev/null +++ b/Modules/US/USModel/mitkUSVideoDevice.cpp @@ -0,0 +1,111 @@ +/*=================================================================== + +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 "mitkUSVideoDevice.h" + + +mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) +{ + this->SetNumberOfInputs(1); + this->SetNumberOfOutputs(1); + m_SourceIsFile = false; + m_DeviceID = videoDeviceNumber; +} + +mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) +{ + this->SetNumberOfInputs(1); + this->SetNumberOfOutputs(1); + m_SourceIsFile = true; + m_FilePath = videoFilePath; +} + +mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) +{ + this->SetNumberOfInputs(1); + this->SetNumberOfOutputs(1); + m_SourceIsFile = false; + m_DeviceID = videoDeviceNumber; +} + +mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) +{ + this->SetNumberOfInputs(1); + this->SetNumberOfOutputs(1); + m_SourceIsFile = true; + m_FilePath = videoFilePath; +} + +mitk::USVideoDevice::~USVideoDevice() +{ + +} + +std::string mitk::USVideoDevice::GetDeviceClass(){ + return "org.mitk.modules.us.USVideoDevice"; +} + + +bool mitk::USVideoDevice::OnConnection() +{ + m_Source = mitk::USImageVideoSource::New(); + if (m_SourceIsFile){ + m_Source->SetVideoFileInput(m_FilePath); + } else { + m_Source->SetCameraInput(m_DeviceID); + } + return true; +} + +/** +* \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. +*/ +bool mitk::USVideoDevice::OnDisconnection() +{ + // TODO Implement Disconnection Behaviour + return true; +} + +/** +* \brief Is called during the activation process. After this method is finsihed, the device should be generating images +*/ +bool mitk::USVideoDevice::OnActivation() +{ + // TODO Implement Activation Behaviour + return true; +} + + +/** +* \brief Is called during the deactivation process. After a call to this method the device should still be connected, but not producing images anymore. +*/ +void mitk::USVideoDevice::OnDeactivation() +{ + // TODO Implement Deactivation Behaviour + +} + +void mitk::USVideoDevice::GenerateData() +{ + mitk::USImage::Pointer result; + result = m_Source->GetNextImage(); + + // Set Metadata + result->SetMetadata(this->m_Metadata); + // Set Output + this->SetNthOutput(0, result); +} diff --git a/Modules/US/USModel/mitkUSVideoDevice.h b/Modules/US/USModel/mitkUSVideoDevice.h new file mode 100644 index 0000000000..6008bacaef --- /dev/null +++ b/Modules/US/USModel/mitkUSVideoDevice.h @@ -0,0 +1,127 @@ +/*=================================================================== + +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 +#include "mitkUSImageVideoSource.h" + +namespace mitk { + + /**Documentation + * \brief A VideoDevice 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 mitkUSImage with attached Metadata. + * This simple implementation does only capture and display 2D Images without cropping or registration. + * One can simply inherit from this class and overwrite the handle2D and handle 3Dmethods to get full access to the data + * \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); + + + /** + * \brief Returns the qualified name of this class. Be sure to override this when inheriting from VideoDevice! + */ + virtual std::string GetDeviceClass(); + + void GenerateData(); + + 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 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(); + + /** + * \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(); + + /** + * \brief Is called during the activation process. After this method is finsihed, the device should be generating images + */ + virtual bool OnActivation(); + + + /** + * \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 void OnDeactivation(); + + /** + * \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; + + }; +} // namespace mitk +#endif diff --git a/Modules/US/USService/mitkUSDeviceService.cpp b/Modules/US/USService/mitkUSDeviceService.cpp new file mode 100644 index 0000000000..04fb71245c --- /dev/null +++ b/Modules/US/USService/mitkUSDeviceService.cpp @@ -0,0 +1,49 @@ +/*=================================================================== + +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 "mitkUSDeviceService.h" +#include + + +mitk::USDeviceService::USDeviceService() : itk::Object() +{ + +} + +mitk::USDeviceService::~USDeviceService() +{ + +} + +void mitk::USDeviceService::ActivateDevice (mitk::USDevice::Pointer device){ + // Check if device is already active + for(std::vector::iterator it = m_ActiveDevices.begin(); it != m_ActiveDevices.end(); ++it) { + if (it->GetPointer() == device.GetPointer()) return; + } + + // add device + m_ActiveDevices.push_back(device); +} + + +void mitk::USDeviceService::DeactivateDevice (int index){ + // m_ActiveDevices.erase(index); + // Not yet supported +} + +std::vector mitk::USDeviceService::GetActiveDevices(){ + return m_ActiveDevices; +} \ No newline at end of file diff --git a/Modules/US/USService/mitkUSDeviceService.h b/Modules/US/USService/mitkUSDeviceService.h new file mode 100644 index 0000000000..c40494ed5d --- /dev/null +++ b/Modules/US/USService/mitkUSDeviceService.h @@ -0,0 +1,78 @@ +/*=================================================================== + +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 MITKUSDeviceService_H_HEADER_INCLUDED_ +#define MITKUSDeviceService_H_HEADER_INCLUDED_ + +#include +#include +#include +#include +#include +#include "mitkUSDevice.h" + +namespace mitk { + + /**Documentation + * \brief TODO + * + * \ingroup US + */ + + class MitkUS_EXPORT USDeviceService : public itk::Object + { + public: + mitkClassMacro(USDeviceService,itk::Object); + itkNewMacro(Self); + + + + + //## getter and setter ## + + /** + * \brief Sets that given device as active in the service (i.e. ready to produce images) + */ + void ActivateDevice (mitk::USDevice::Pointer device); + /** + * \brief Removes the device with index i from the List of currently active devices + */ + void DeactivateDevice (int index); + + std::vector GetActiveDevices(); + + + protected: + USDeviceService(); + virtual ~USDeviceService(); + + // This Vector contains all devices that are connected and active (i.e. ready to produce images) + std::vector m_ActiveDevices; + + // This Vector contains all known devices. Might later function as a catalogue. Not yet implemented. + //std::vector m_AllDevices; + + + + + + + + + }; +} // namespace mitk +#endif \ No newline at end of file diff --git a/Modules/US/files.cmake b/Modules/US/files.cmake index 27b34cab17..7a7c4721fc 100644 --- a/Modules/US/files.cmake +++ b/Modules/US/files.cmake @@ -1,9 +1,15 @@ SET(CPP_FILES -USFilters/mitkUSImage.cpp +## Model classes +USModel/mitkUSImage.cpp +USModel/mitkUSImageMetadata.cpp +USModel/mitkUSDevice.cpp +USModel/mitkUSVideoDevice.cpp +USModel/mitkUSProbe.cpp + +## Services +USService/mitkUSDeviceService.cpp + +## Filters and sources USFilters/mitkUSImageVideoSource.cpp -USFilters/mitkUSImageMetadata.cpp -USFilters/mitkUSDevice.cpp -USFilters/mitkUSVideoDevice.cpp -USFilters/mitkUSProbe.cpp USFilters/mitkUSImageToUSImageFilter.cpp ) diff --git a/Modules/USUI/CMakeLists.txt b/Modules/USUI/CMakeLists.txt new file mode 100644 index 0000000000..c0ac18187a --- /dev/null +++ b/Modules/USUI/CMakeLists.txt @@ -0,0 +1,8 @@ +MITK_CREATE_MODULE(MitkUSUI + #SUBPROJECTS MITK-US + INCLUDE_DIRS Qmitk + DEPENDS MitkUS Qmitk QmitkExt + QT_MODULE + GENERATED_CPP ${TOOL_GUI_CPPS} ${TOOL_CPPS} +) + diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.cpp b/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.cpp new file mode 100644 index 0000000000..75acd872b2 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.cpp @@ -0,0 +1,167 @@ +/*=================================================================== + +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. + +===================================================================*/ + +//#define _USE_MATH_DEFINES +#include +#include + +//QT headers +#include + +//mitk headers + + +//itk headers + +//microservices +#include +#include +#include + + +const std::string QmitkUSDeviceListWidget::VIEW_ID = "org.mitk.views.QmitkUSDeviceListWidget"; + +QmitkUSDeviceListWidget::QmitkUSDeviceListWidget(QWidget* parent, Qt::WindowFlags f): QWidget(parent, f) +{ + m_Controls = NULL; + CreateQtPartControl(this); + + // get ModuleContext + mitk::Module* mitkUS = mitk::ModuleRegistry::GetModule("MitkUS"); + m_MitkUSContext = mitkUS->GetModuleContext(); + +} + +QmitkUSDeviceListWidget::~QmitkUSDeviceListWidget() +{ + +} + +//////////////////// INITIALIZATION ///////////////////// + +void QmitkUSDeviceListWidget::CreateQtPartControl(QWidget *parent) +{ + if (!m_Controls) + { + // create GUI widgets + m_Controls = new Ui::QmitkUSDeviceListWidgetControls; + m_Controls->setupUi(parent); + this->CreateConnections(); + } +} + +void QmitkUSDeviceListWidget::CreateConnections() +{ + if ( m_Controls ) + { + connect( m_Controls->m_DeviceList, SIGNAL(currentItemChanged( QListWidgetItem *, QListWidgetItem *)), this, SLOT(OnDeviceSelectionChanged()) ); + } +} + +void QmitkUSDeviceListWidget::Initialize(std::string filter) +{ + m_Filter = filter; + m_MitkUSContext->AddServiceListener(this, &QmitkUSDeviceListWidget::OnServiceEvent, m_Filter); +} + + +///////////////////////// Getter & Setter ///////////////////////////////// + +mitk::USDevice::Pointer QmitkUSDeviceListWidget::GetSelectedDevice() +{ + return this->GetDeviceForListItem(this->m_Controls->m_DeviceList->currentItem()); +} + +///////////// Methods & Slots Handling Direct Interaction ///////////////// + + +void QmitkUSDeviceListWidget::OnDeviceSelectionChanged(){ + mitk::USDevice::Pointer device = this->GetDeviceForListItem(this->m_Controls->m_DeviceList->currentItem()); + if (device.IsNull()) return; + emit (DeviceSelected(device)); +} + + +///////////////// Methods & Slots Handling Logic ////////////////////////// + +void QmitkUSDeviceListWidget::OnServiceEvent(const mitk::ServiceEvent event){ + // Empty ListWidget + this->m_ListContent.clear(); + m_Controls->m_DeviceList->clear(); + + + + // get Active Devices + std::vector devices = this->GetAllRegisteredDevices(); + // Transfer them to the List + for(std::vector::iterator it = devices.begin(); it != devices.end(); ++it) + { + QListWidgetItem *newItem = ConstructItemFromDevice(it->GetPointer()); + //Add new item to QListWidget + m_Controls->m_DeviceList->addItem(newItem); + // Construct link and add to internal List for reference + QmitkUSDeviceListWidget::DeviceListLink link; + link.device = it->GetPointer(); + link.item = newItem; + m_ListContent.push_back(link); + } +} + + +/////////////////////// HOUSEHOLDING CODE ///////////////////////////////// + +QListWidgetItem* QmitkUSDeviceListWidget::ConstructItemFromDevice(mitk::USDevice::Pointer device){ + QListWidgetItem *result = new QListWidgetItem; + std::string text = device->GetDeviceManufacturer() + "|" + device->GetDeviceModel(); + + if (device->GetIsActive()) + { + result->foreground().setColor(Qt::blue); + text += "|(ON)"; + } else text += "|(OFF)"; + + result->setText(text.c_str()); + + return result; +} + + +mitk::USDevice::Pointer QmitkUSDeviceListWidget::GetDeviceForListItem(QListWidgetItem* item) +{ + for(std::vector::iterator it = m_ListContent.begin(); it != m_ListContent.end(); ++it) + { + if (item == it->item) return it->device; + } + return 0; +} + + +std::vector QmitkUSDeviceListWidget::GetAllRegisteredDevices(){ + + //Get Service References + std::list serviceRefs = m_MitkUSContext->GetServiceReferences(m_Filter); + + // Convert Service References to US Devices + std::vector* result = new std::vector; + std::list::const_iterator iterator; + for (iterator = serviceRefs.begin(); iterator != serviceRefs.end(); ++iterator) + { + mitk::USDevice::Pointer device = m_MitkUSContext->GetService(*iterator); + if (device) result->push_back(device); + } + + return *result; +} \ No newline at end of file diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.h b/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.h new file mode 100644 index 0000000000..4cdbd671c4 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceListWidget.h @@ -0,0 +1,158 @@ +/*=================================================================== + +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 _QmitkUSDeviceListWidget_H_INCLUDED +#define _QmitkUSDeviceListWidget_H_INCLUDED + +#include "MitkUSUIExports.h" +#include "ui_QmitkUSDeviceListWidgetControls.h" +#include "mitkUSDevice.h" +#include + +//QT headers +#include +#include + +//mitk header + +//Microservices +#include "usServiceReference.h" +#include "usModuleContext.h" +#include "usServiceEvent.h" +#include "usServiceTrackerCustomizer.h" + +/** +* @brief TODO +* +* @ingroup USUI +*/ +class MitkUSUI_EXPORT QmitkUSDeviceListWidget :public QWidget //, public mitk::ServiceTrackerCustomizer<> // this extension is necessary if one wants to use ServiceTracking instead of filtering +{ + + //this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) + Q_OBJECT + + public: + + static const std::string VIEW_ID; + + QmitkUSDeviceListWidget(QWidget* p = 0, Qt::WindowFlags f1 = 0); + virtual ~QmitkUSDeviceListWidget(); + + /* @brief This method is part of the widget an needs not to be called seperately. */ + virtual void CreateQtPartControl(QWidget *parent); + /* @brief This method is part of the widget an needs not to be called seperately. (Creation of the connections of main and control widget.)*/ + virtual void CreateConnections(); + + /* + * \brief Initializes the connection to the registry. The string filter is an LDAP parsable String, compare mitk::ModuleContext for examples on filtering. + */ + void Initialize(std::string filter); + + /* + * \brief Returns the currently selected device, or null if none is selected. + */ + mitk::USDevice::Pointer GetSelectedDevice(); + + /* + *\brief This Function listens to ServiceRegistry changes and updates the + * list of devices accordingly. + */ + void OnServiceEvent(const mitk::ServiceEvent event); + + + + signals: + + /* + *\brief Emitted when a new device mathing the filter connects + */ + void DeviceConnected(mitk::USDevice::Pointer); + /* + *\brief Emitted directly before device matching the filter disconnects + */ + void DeviceDisconnected(mitk::USDevice::Pointer); + /* + *\brief Emitted when a new device mathing the filter changes it's state. This does of now only compromise changes to activity. + */ + void DeviceChanged(mitk::USDevice::Pointer); + + /* + *\brief Emitted the user selects a device from the list + */ + void DeviceSelected(mitk::USDevice::Pointer); + + + + public slots: + + protected slots: + + /* + \brief Called, when the selection in the devicelist changes + */ + void OnDeviceSelectionChanged(); + + + protected: + + Ui::QmitkUSDeviceListWidgetControls* m_Controls; ///< member holding the UI elements of this widget + + /* + * \brief Internal Structure used to link devices to their QListWidget Items + */ + struct DeviceListLink { + mitk::USDevice::Pointer device; + QListWidgetItem* item; + }; + + /* + * \brief Contains a list of currently active devices and their entires in the list. This is wiped with every ServiceRegistryEvent. + */ + std::vector m_ListContent; + + /* + * \brief Constructs a ListItem from the given device for display in the list of active devices. + */ + QListWidgetItem* ConstructItemFromDevice(mitk::USDevice::Pointer device); + + /* + * \brief Returns the device corresponding to the given ListEntry or null if none was found (which really shouldnt happen). + */ + mitk::USDevice::Pointer GetDeviceForListItem(QListWidgetItem* item); + + //mitk::ServiceTracker ConstructServiceTracker(); + + /* + * \brief Returns a List of US Devices that are currently connected by querying the service registry. + */ + std::vector GetAllRegisteredDevices(); + + + + + private: + + mitk::ModuleContext* m_MitkUSContext; + std::string m_Filter; + + + + + +}; + +#endif // _QmitkUSDeviceListWidget_H_INCLUDED diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceListWidgetControls.ui b/Modules/USUI/Qmitk/QmitkUSDeviceListWidgetControls.ui new file mode 100644 index 0000000000..4d36c2bb5e --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceListWidgetControls.ui @@ -0,0 +1,31 @@ + + + QmitkUSDeviceListWidgetControls + + + + 0 + 0 + 323 + 231 + + + + + 0 + 0 + + + + QmitkUSDeviceListWidget + + + + + + + + + + + diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.cpp b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.cpp new file mode 100644 index 0000000000..4117083a44 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.cpp @@ -0,0 +1,183 @@ +/*=================================================================== + +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. + +===================================================================*/ + +//#define _USE_MATH_DEFINES +#include +#include + +//QT headers +#include + +//mitk headers + + + +//itk headers + +//microservices +#include +#include +#include + + +const std::string QmitkUSDeviceManagerWidget::VIEW_ID = "org.mitk.views.QmitkUSDeviceManagerWidget"; + +QmitkUSDeviceManagerWidget::QmitkUSDeviceManagerWidget(QWidget* parent, Qt::WindowFlags f): QWidget(parent, f) +{ + m_Controls = NULL; + CreateQtPartControl(this); + + // get ModuleContext + mitk::Module* mitkUS = mitk::ModuleRegistry::GetModule("MitkUS"); + m_MitkUSContext = mitkUS->GetModuleContext(); + + //ServiceTracker* tracker = new ServiceTracker(m_MitkUSContext, this); + + // Register this Widget as a listener for Registry changes. + // If devices are registered, unregistered or changed, notifications will go there + std::string filter = "(&("; + filter += mitk::ServiceConstants::OBJECTCLASS(); + filter += "="; + //filter += us_service_interface_iid(); + filter += "org.mitk.services.UltrasoundDevice)(IsActive=false))"; + m_MitkUSContext->AddServiceListener(this, &QmitkUSDeviceManagerWidget::OnServiceEvent, filter); +} + +QmitkUSDeviceManagerWidget::~QmitkUSDeviceManagerWidget() +{ +} + +//////////////////// INITIALIZATION ///////////////////// + +void QmitkUSDeviceManagerWidget::CreateQtPartControl(QWidget *parent) +{ + if (!m_Controls) + { + // create GUI widgets + m_Controls = new Ui::QmitkUSDeviceManagerWidgetControls; + m_Controls->setupUi(parent); + this->CreateConnections(); + } +} + +void QmitkUSDeviceManagerWidget::CreateConnections() +{ + if ( m_Controls ) + { + connect( m_Controls->m_BtnActivate, SIGNAL(clicked()), this, SLOT(OnClickedActivateDevice()) ); + connect( m_Controls->m_BtnDisconnect, SIGNAL(clicked()), this, SLOT(OnClickedDisconnectDevice()) ); + connect( m_Controls->m_ConnectedDevices, SIGNAL(currentItemChanged( QListWidgetItem *, QListWidgetItem *)), this, SLOT(OnDeviceSelectionChanged()) ); + } +} + + + + +///////////// Methods & Slots Handling Direct Interaction ///////////////// + +void QmitkUSDeviceManagerWidget::OnClickedActivateDevice() +{ + MITK_INFO << "Activated Device"; + mitk::USDevice::Pointer device = this->GetDeviceForListItem(this->m_Controls->m_ConnectedDevices->currentItem()); + if (device.IsNull()) return; + if (device->GetIsActive()) device->Deactivate(); + else device->Activate(); +} + +void QmitkUSDeviceManagerWidget::OnClickedDisconnectDevice(){ + MITK_INFO << "Disconnected Device"; + mitk::USDevice::Pointer device = this->GetDeviceForListItem(this->m_Controls->m_ConnectedDevices->currentItem()); + if (device.IsNull()) return; + device->Disconnect(); +} + +void QmitkUSDeviceManagerWidget::OnDeviceSelectionChanged(){ + mitk::USDevice::Pointer device = this->GetDeviceForListItem(this->m_Controls->m_ConnectedDevices->currentItem()); + if (device.IsNull()) return; + if (device->GetIsActive()) m_Controls->m_BtnActivate->setText("Deactivate"); + else m_Controls->m_BtnActivate->setText("Activate"); +} + + +///////////////// Methods & Slots Handling Logic ////////////////////////// + +void QmitkUSDeviceManagerWidget::OnServiceEvent(const mitk::ServiceEvent event){ + // Empty ListWidget + this->m_ListContent.clear(); + m_Controls->m_ConnectedDevices->clear(); + + + + // get Active Devices + std::vector devices = this->GetAllRegisteredDevices(); + // Transfer them to the List + for(std::vector::iterator it = devices.begin(); it != devices.end(); ++it) + { + QListWidgetItem *newItem = ConstructItemFromDevice(it->GetPointer()); + //Add new item to QListWidget + m_Controls->m_ConnectedDevices->addItem(newItem); + // Construct Link and add to internal List for reference + QmitkUSDeviceManagerWidget::DeviceListLink link; + link.device = it->GetPointer(); + link.item = newItem; + m_ListContent.push_back(link); + } +} + + +/////////////////////// HOUSEHOLDING CODE ///////////////////////////////// + +QListWidgetItem* QmitkUSDeviceManagerWidget::ConstructItemFromDevice(mitk::USDevice::Pointer device){ + QListWidgetItem *result = new QListWidgetItem; + std::string text = device->GetDeviceManufacturer() + "|" + device->GetDeviceModel(); + + if (device->GetIsActive()) + { + result->foreground().setColor(Qt::blue); + text += "|(ON)"; + } else text += "|(OFF)"; + + result->setText(text.c_str()); + + return result; +} + + +mitk::USDevice::Pointer QmitkUSDeviceManagerWidget::GetDeviceForListItem(QListWidgetItem* item) +{ + for(std::vector::iterator it = m_ListContent.begin(); it != m_ListContent.end(); ++it) + { + if (item == it->item) return it->device; + } + return 0; +} + +std::vector QmitkUSDeviceManagerWidget::GetAllRegisteredDevices(){ + + //Get Service References + std::list serviceRefs = m_MitkUSContext->GetServiceReferences(); + + // Convert Service References to US Devices + std::vector* result = new std::vector; + std::list::const_iterator iterator; + for (iterator = serviceRefs.begin(); iterator != serviceRefs.end(); ++iterator) + { + mitk::USDevice::Pointer device = m_MitkUSContext->GetService(*iterator); + if (device) result->push_back(device); + } + + return *result; +} \ No newline at end of file diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.h b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.h new file mode 100644 index 0000000000..2e8f70d03c --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidget.h @@ -0,0 +1,135 @@ +/*=================================================================== + +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 _QmitkUSDeviceManagerWidget_H_INCLUDED +#define _QmitkUSDeviceManagerWidget_H_INCLUDED + +#include "MitkUSUIExports.h" +#include "ui_QmitkUSDeviceManagerWidgetControls.h" +#include "mitkUSDevice.h" +#include + +//QT headers +#include +#include + +//mitk header + +//Microservices +#include "usServiceReference.h" +#include "usModuleContext.h" +#include "usServiceEvent.h" + +/** +* @brief TODO +* +* @ingroup USUI +*/ +class MitkUSUI_EXPORT QmitkUSDeviceManagerWidget :public QWidget //, public mitk::ServiceTrackerCustomizer<> // this extension is necessary if one wants to use ServiceTracking instead of filtering +{ + + //this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) + Q_OBJECT + + public: + + static const std::string VIEW_ID; + + QmitkUSDeviceManagerWidget(QWidget* p = 0, Qt::WindowFlags f1 = 0); + virtual ~QmitkUSDeviceManagerWidget(); + + /* @brief This method is part of the widget an needs not to be called seperately. */ + virtual void CreateQtPartControl(QWidget *parent); + /* @brief This method is part of the widget an needs not to be called seperately. (Creation of the connections of main and control widget.)*/ + virtual void CreateConnections(); + + /* + *\brief This Function listens to ServiceRegistry changes and updates the + * list of devices accordingly. + */ + void OnServiceEvent(const mitk::ServiceEvent event); + + + + signals: + + /* + \brief Sent, when the user clicks "Activate Device" + */ + void USDeviceActivated(); + + public slots: + + protected slots: + + /* + \brief Called, when the button "Activate Device" was clicked + */ + void OnClickedActivateDevice(); + + /* + \brief Called, when the button "Disconnect Device" was clicked + */ + void OnClickedDisconnectDevice(); + + /* + \brief Called, when the selection in the devicelist changes + */ + void OnDeviceSelectionChanged(); + + + protected: + + Ui::QmitkUSDeviceManagerWidgetControls* m_Controls; ///< member holding the UI elements of this widget + + /* + * \brief Internal Structure used to link devices to their QListWidget Items + */ + struct DeviceListLink { + mitk::USDevice::Pointer device; + QListWidgetItem* item; + }; + + /* + * \brief Contains a list of currently active devices and their entires in the list. This is wiped with every ServiceRegistryEvent. + */ + std::vector m_ListContent; + + /* + * \brief Constructs a ListItem from the given device for display in the list of active devices. + */ + QListWidgetItem* ConstructItemFromDevice(mitk::USDevice::Pointer device); + + /* + * \brief Returns the device corresponding to the given ListEntry or null if none was found (which really shouldnt happen). + */ + mitk::USDevice::Pointer GetDeviceForListItem(QListWidgetItem* item); + + //mitk::ServiceTracker ConstructServiceTracker(); + + /* + * \brief Returns a List of US Devices that are currently connected by querying the service registry. + */ + std::vector GetAllRegisteredDevices(); + + private: + + mitk::ModuleContext* m_MitkUSContext; + + +}; + +#endif // _QmitkUSDeviceManagerWidget_H_INCLUDED diff --git a/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidgetControls.ui b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidgetControls.ui new file mode 100644 index 0000000000..12e83cb191 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSDeviceManagerWidgetControls.ui @@ -0,0 +1,57 @@ + + + QmitkUSDeviceManagerWidgetControls + + + + 0 + 0 + 405 + 231 + + + + + 0 + 0 + + + + QmitkUSDeviceManagerWidget + + + + + + + 11 + + + + Connected Devices: + + + + + + + Activate Device + + + + + + + Disconnect Device + + + + + + + + + + + + diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp new file mode 100644 index 0000000000..d365b0b674 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.cpp @@ -0,0 +1,155 @@ +/*=================================================================== + +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. + +===================================================================*/ + +//#define _USE_MATH_DEFINES +#include + +//QT headers + + +//mitk headers + + +//itk headers + + +const std::string QmitkUSNewVideoDeviceWidget::VIEW_ID = "org.mitk.views.QmitkUSNewVideoDeviceWidget"; + +QmitkUSNewVideoDeviceWidget::QmitkUSNewVideoDeviceWidget(QWidget* parent, Qt::WindowFlags f): QWidget(parent, f) +{ + m_Controls = NULL; + CreateQtPartControl(this); +} + +QmitkUSNewVideoDeviceWidget::~QmitkUSNewVideoDeviceWidget() +{ +} + +//////////////////// INITIALIZATION ///////////////////// + + +void QmitkUSNewVideoDeviceWidget::CreateQtPartControl(QWidget *parent) +{ + if (!m_Controls) + { + // create GUI widgets + m_Controls = new Ui::QmitkUSNewVideoDeviceWidgetControls; + m_Controls->setupUi(parent); + this->CreateConnections(); + } +} + +void QmitkUSNewVideoDeviceWidget::CreateConnections() +{ + if ( m_Controls ) + { + connect( m_Controls->m_BtnDone, SIGNAL(clicked()), this, SLOT(OnClickedDone()) ); + connect( m_Controls->m_BtnCancel, SIGNAL(clicked()), this, SLOT(OnClickedCancel()) ); + connect( m_Controls->m_RadioDeviceSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection()) ); + connect( m_Controls->m_RadioFileSource, SIGNAL(clicked()), this, SLOT(OnDeviceTypeSelection()) ); + + } + // Hide & show stuff + m_Controls->m_FilePathSelector->setVisible(false); +} + + +///////////// Methods & Slots Handling Direct Interaction ///////////////// + +void QmitkUSNewVideoDeviceWidget::OnClickedDone(){ + m_Active = false; + MITK_INFO << "NewDeviceWidget: ClickedDone()"; + + // Assemble Metadata + mitk::USImageMetadata::Pointer metadata = mitk::USImageMetadata::New(); + metadata->SetDeviceComment(m_Controls->m_Comment->text().toStdString()); + metadata->SetDeviceModel(m_Controls->m_Model->text().toStdString()); + metadata->SetDeviceManufacturer(m_Controls->m_Manufacturer->text().toStdString()); + metadata->SetProbeName(m_Controls->m_Probe->text().toStdString()); + metadata->SetZoom(m_Controls->m_Zoom->text().toStdString()); + + + // Create Device + mitk::USVideoDevice::Pointer newDevice; + if (m_Controls->m_RadioDeviceSource->isChecked()){ + int deviceID = m_Controls->m_DeviceSelector->value(); + newDevice = mitk::USVideoDevice::New(deviceID, metadata); + } else { + std::string filepath = m_Controls->m_FilePathSelector->text().toStdString(); + newDevice = mitk::USVideoDevice::New(filepath, metadata); + } + + newDevice->Connect(); + + emit Finished(); +} + +void QmitkUSNewVideoDeviceWidget::OnClickedCancel(){ + m_TargetDevice = 0; + m_Active = false; + MITK_INFO << "NewDeviceWidget: OnClickedCancel()"; + emit Finished(); + +} + +void QmitkUSNewVideoDeviceWidget::OnDeviceTypeSelection(){ + m_Controls->m_FilePathSelector->setVisible(m_Controls->m_RadioFileSource->isChecked()); + m_Controls->m_DeviceSelector->setVisible(m_Controls->m_RadioDeviceSource->isChecked()); +} + + + +///////////////// Methods & Slots Handling Logic ////////////////////////// + + +void QmitkUSNewVideoDeviceWidget::EditDevice(mitk::USDevice::Pointer device) +{ + // If no VideoDevice is given, throw an exception + if (device->GetDeviceClass().compare("org.mitk.modules.us.USVideoDevice") != 0){ + // TODO Alert if bad path + mitkThrow() << "NewVideoDevcieWidget recieved an incompatible Device Type to edit. Devicetype was: " << device->GetDeviceClass(); + } + MITK_INFO << "NewDeviceWidget: EditDevice()"; + m_TargetDevice = static_cast (device.GetPointer()); + m_Active = true; +} + + +void QmitkUSNewVideoDeviceWidget::CreateNewDevice() +{ + MITK_INFO << "NewDeviceWidget: CreateNewDevice()"; + m_TargetDevice = 0; + InitFields(mitk::USImageMetadata::New()); + m_Active = true; +} + + +/////////////////////// HOUSEHOLDING CODE /////////////////////////////// + +QListWidgetItem* QmitkUSNewVideoDeviceWidget::ConstructItemFromDevice(mitk::USDevice::Pointer device){ + QListWidgetItem *result = new QListWidgetItem; + std::string text = device->GetDeviceManufacturer() + "|" + device->GetDeviceModel(); + result->setText(text.c_str()); + return result; +} + +void QmitkUSNewVideoDeviceWidget::InitFields(mitk::USImageMetadata::Pointer metadata){ + this->m_Controls->m_Manufacturer->setText (metadata->GetDeviceManufacturer().c_str()); + this->m_Controls->m_Model->setText (metadata->GetDeviceModel().c_str()); + this->m_Controls->m_Comment->setText (metadata->GetDeviceComment().c_str()); + this->m_Controls->m_Probe->setText (metadata->GetProbeName().c_str()); + this->m_Controls->m_Zoom->setText (metadata->GetZoom().c_str()); +} diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h new file mode 100644 index 0000000000..233129d4c3 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidget.h @@ -0,0 +1,122 @@ +/*=================================================================== + +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 _QmitkUSNewVideoDeviceWidget_H_INCLUDED +#define _QmitkUSNewVideoDeviceWidget_H_INCLUDED + +#include "MitkUSUIExports.h" +#include "ui_QmitkUSNewVideoDeviceWidgetControls.h" +#include "mitkUSVideoDevice.h" +#include "mitkUSImageMetadata.h" + +//QT headers +#include +#include + +//mitk header + +/** +* @brief TODO +* +* @ingroup USUI +*/ +class MitkUSUI_EXPORT QmitkUSNewVideoDeviceWidget :public QWidget +{ + + //this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) + Q_OBJECT + + public: + + static const std::string VIEW_ID; + + QmitkUSNewVideoDeviceWidget(QWidget* p = 0, Qt::WindowFlags f1 = 0); + virtual ~QmitkUSNewVideoDeviceWidget(); + + /* @brief This method is part of the widget an needs not to be called seperately. */ + virtual void CreateQtPartControl(QWidget *parent); + /* @brief This method is part of the widget an needs not to be called seperately. (Creation of the connections of main and control widget.)*/ + virtual void CreateConnections(); + + signals: + + void Finished(); + + public slots: + + /* + \brief Activates the widget and displays the given device's Data to edit. + */ + void EditDevice(mitk::USDevice::Pointer device); + + /* + \brief Activates the widget with fields empty. + */ + void CreateNewDevice(); + + protected slots: + + /* + \brief Called, when the the user clicks the "Done" button (Labeled either "Add Device" or "Edit Device", depending on the situation. + */ + void OnClickedDone(); + + /* + \brief Called, when the button "Cancel" was clicked + */ + void OnClickedCancel(); + + /* + \brief Called, when the Use selects one of the Radiobuttons + */ + void OnDeviceTypeSelection(); + + + + + + protected: + + Ui::QmitkUSNewVideoDeviceWidgetControls* m_Controls; ///< member holding the UI elements of this widget + + /* + \brief Constructs a ListItem from the given devie for display in the list of active devices + */ + QListWidgetItem* ConstructItemFromDevice(mitk::USDevice::Pointer device); + + /* + \brief Initializes the Widget's ListItems with the given Metadata Object. + */ + void InitFields(mitk::USImageMetadata::Pointer); + + /* + \brief Displays whether this widget is active or not. It gets activated by either sending a Signal to + * the "CreateNewDevice" Slot or to the "EditDevice" Slot. If the user finishes editing the device, a + * "EditingComplete" Signal is sent, and the widget is set to inactive again. Clicking Cancel also + * deactivates it. + */ + bool m_Active; + + /** + * \brief This is the device to edit. It is either the device transmitted in the "EditDevice" signal, or a new one + * if the "CreateNewDevice slot was called. + */ + mitk::USVideoDevice::Pointer m_TargetDevice; + + +}; + +#endif // _QmitkUSNewVideoDeviceWidget_H_INCLUDED diff --git a/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui new file mode 100644 index 0000000000..08f540f348 --- /dev/null +++ b/Modules/USUI/Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui @@ -0,0 +1,173 @@ + + + QmitkUSNewVideoDeviceWidgetControls + + + + 0 + 0 + 405 + 404 + + + + + 0 + 0 + + + + QmitkUSNewVideoDeviceWidget + + + + + + Metadata + + + + + + + 50 + false + true + + + + Device Information: + + + + + + + Manufacturer + + + + + + + + + + Model + + + + + + + + + + Comment + + + + + + + + + + + true + + + + Probe Information: + + + + + + + Probe + + + + + + + + + + Zoom + + + + + + + + + + + + + GroupBox + + + + + + C://Builds//MITK-superbuild//CMakeExternals//Source//MITK-Data//CommonTestData//bunny_320x240.avi + + + + + + + -1 + + + 10 + + + -1 + + + + + + + From Device: + + + true + + + + + + + From File: + + + + + + + + + + Add Device + + + + + + + Cancel + + + + + + + + + diff --git a/Modules/USUI/files.cmake b/Modules/USUI/files.cmake new file mode 100644 index 0000000000..fb6be5c93c --- /dev/null +++ b/Modules/USUI/files.cmake @@ -0,0 +1,22 @@ + +set(CPP_FILES + Qmitk/QmitkUSDeviceManagerWidget.cpp + Qmitk/QmitkUSNewVideoDeviceWidget.cpp + Qmitk/QmitkUSDeviceListWidget.cpp +) +set(UI_FILES + Qmitk/QmitkUSDeviceListWidgetControls.ui + Qmitk/QmitkUSDeviceManagerWidgetControls.ui + Qmitk/QmitkUSNewVideoDeviceWidgetControls.ui +) + +set(MOC_H_FILES + Qmitk/QmitkUSDeviceManagerWidget.h + Qmitk/QmitkUSDeviceListWidget.h + Qmitk/QmitkUSNewVideoDeviceWidget.h +) + +# uncomment the following line if you want to use Qt resources +set(QRC_FILES +# resources/QmitkToFUtilWidget.qrc +) diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index 67b5e41db5..4d396b4932 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,41 +1,42 @@ # Plug-ins must be ordered according to their dependencies set(MITK_EXT_PLUGINS org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.diffusionimaging:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.dtiatlasapp:OFF org.mitk.gui.qt.examples:OFF org.mitk.gui.qt.examplesopencv:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.meshdecimation:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.python.console:OFF org.mitk.gui.qt.registration:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.ugvisualization:OFF + org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF ) diff --git a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml index 3229b590a3..11d4f7d5df 100644 --- a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml +++ b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml @@ -1,3838 +1,3838 @@ - - - - - + + + + + + - + - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp index 5da3f432f6..03c92ef076 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp @@ -1,1830 +1,1828 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkFiberProcessingView.h" #include // Qt #include // MITK #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include #include #include #include #include #include #include const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace mitk; QmitkFiberProcessingView::QmitkFiberProcessingView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_EllipseCounter(0) , m_PolygonCounter(0) , m_UpsamplingFactor(5) { } // Destructor QmitkFiberProcessingView::~QmitkFiberProcessingView() { } void QmitkFiberProcessingView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberProcessingViewControls; m_Controls->setupUi( parent ); m_Controls->doExtractFibersButton->setDisabled(true); m_Controls->PFCompoANDButton->setDisabled(true); m_Controls->PFCompoORButton->setDisabled(true); m_Controls->PFCompoNOTButton->setDisabled(true); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_RectangleButton->setVisible(false); connect( m_Controls->doExtractFibersButton, SIGNAL(clicked()), this, SLOT(DoFiberExtraction()) ); connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); connect(m_Controls->m_Extract3dButton, SIGNAL(clicked()), this, SLOT(Extract3d())); connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); connect( m_Controls->m_ResampleFibersButton, SIGNAL(clicked()), this, SLOT(ResampleSelectedBundles()) ); connect(m_Controls->m_FaColorFibersButton, SIGNAL(clicked()), this, SLOT(DoFaColorCoding())); connect( m_Controls->m_PruneFibersButton, SIGNAL(clicked()), this, SLOT(PruneBundle()) ); connect( m_Controls->m_MirrorFibersButton, SIGNAL(clicked()), this, SLOT(MirrorFibers()) ); } } void QmitkFiberProcessingView::Extract3d() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(); mitk::Surface::Pointer roi = mitk::Surface::New(); bool fibB = false; bool roiB = false; for (int i=0; i(nodes.at(i)->GetData())) { fib = dynamic_cast(nodes.at(i)->GetData()); fibB = true; } else if (dynamic_cast(nodes.at(i)->GetData())) { roi = dynamic_cast(nodes.at(i)->GetData()); roiB = true; } } if (!fibB) return; if (!roiB) return; vtkSmartPointer polyRoi = roi->GetVtkPolyData(); vtkSmartPointer polyFib = fib->GetFiberPolyData(); vtkSmartPointer selectEnclosedPoints = vtkSmartPointer::New(); selectEnclosedPoints->SetInput(polyFib); selectEnclosedPoints->SetSurface(polyRoi); selectEnclosedPoints->Update(); vtkSmartPointer newPoly = vtkSmartPointer::New(); vtkSmartPointer newCellArray = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer newPolyComplement = vtkSmartPointer::New(); vtkSmartPointer newCellArrayComplement = vtkSmartPointer::New(); vtkSmartPointer newPointsComplement = vtkSmartPointer::New(); vtkSmartPointer vLines = polyFib->GetLines(); vLines->InitTraversal(); int numberOfLines = vLines->GetNumberOfCells(); // each line for (int j=0; jGetNextCell ( numPoints, points ); bool isPassing = false; // each point of this line for (int k=0; kIsInside(points[k])) { isPassing = true; // fill new polydata vtkSmartPointer container = vtkSmartPointer::New(); for (int k=0; kGetPoint(points[k]); vtkIdType pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); } newCellArray->InsertNextCell(container); break; } } if (!isPassing) { vtkSmartPointer container = vtkSmartPointer::New(); for (int k=0; kGetPoint(points[k]); vtkIdType pointId = newPointsComplement->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); } newCellArrayComplement->InsertNextCell(container); } } newPoly->SetPoints(newPoints); newPoly->SetLines(newCellArray); mitk::FiberBundleX::Pointer fb = mitk::FiberBundleX::New(newPoly); DataNode::Pointer newNode = DataNode::New(); newNode->SetData(fb); newNode->SetName("passing surface"); GetDefaultDataStorage()->Add(newNode); newPolyComplement->SetPoints(newPointsComplement); newPolyComplement->SetLines(newCellArrayComplement); mitk::FiberBundleX::Pointer fbComplement = mitk::FiberBundleX::New(newPolyComplement); DataNode::Pointer newNodeComplement = DataNode::New(); newNodeComplement->SetData(fbComplement); newNodeComplement->SetName("not passing surface"); GetDefaultDataStorage()->Add(newNodeComplement); } void QmitkFiberProcessingView::GenerateRoiImage(){ if (m_SelectedPF.empty()) return; mitk::Geometry3D::Pointer geometry; if (!m_SelectedFB.empty()) { mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); geometry = fib->GetGeometry(); } else return; mitk::Vector3D spacing = geometry->GetSpacing(); spacing /= m_UpsamplingFactor; mitk::Point3D newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); itk::Matrix direction; itk::ImageRegion<3> imageRegion; for (int i=0; i<3; i++) for (int j=0; j<3; j++) direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); m_PlanarFigureImage = itkUCharImageType::New(); m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin m_PlanarFigureImage->SetDirection( direction ); // Set the image direction m_PlanarFigureImage->SetRegions( imageRegion ); m_PlanarFigureImage->Allocate(); m_PlanarFigureImage->FillBuffer( 0 ); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); for (int i=0; iInitializeByItk(m_PlanarFigureImage.GetPointer()); tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); node->SetData(tmpImage); node->SetName("ROI Image"); this->GetDefaultDataStorage()->Add(node); } void QmitkFiberProcessingView::CompositeExtraction(mitk::DataNode::Pointer node, mitk::Image* image) { if (dynamic_cast(node.GetPointer()->GetData()) && !dynamic_cast(node.GetPointer()->GetData())) { m_PlanarFigure = dynamic_cast(node.GetPointer()->GetData()); AccessFixedDimensionByItk_2( image, InternalReorientImagePlane, 3, m_PlanarFigure->GetGeometry(), -1); // itk::Image< unsigned char, 3 >::Pointer outimage = itk::Image< unsigned char, 3 >::New(); // outimage->SetSpacing( m_PlanarFigure->GetGeometry()->GetSpacing()/m_UpsamplingFactor ); // Set the image spacing // mitk::Point3D origin = m_PlanarFigure->GetGeometry()->GetOrigin(); // mitk::Point3D indexOrigin; // m_PlanarFigure->GetGeometry()->WorldToIndex(origin, indexOrigin); // indexOrigin[0] = indexOrigin[0] - .5 * (1.0-1.0/m_UpsamplingFactor); // indexOrigin[1] = indexOrigin[1] - .5 * (1.0-1.0/m_UpsamplingFactor); // indexOrigin[2] = indexOrigin[2] - .5 * (1.0-1.0/m_UpsamplingFactor); // mitk::Point3D newOrigin; // m_PlanarFigure->GetGeometry()->IndexToWorld(indexOrigin, newOrigin); // outimage->SetOrigin( newOrigin ); // Set the image origin // itk::Matrix matrix; // for (int i=0; i<3; i++) // for (int j=0; j<3; j++) // matrix[j][i] = m_PlanarFigure->GetGeometry()->GetMatrixColumn(i)[j]/m_PlanarFigure->GetGeometry()->GetSpacing().GetElement(i); // outimage->SetDirection( matrix ); // Set the image direction // itk::ImageRegion<3> upsampledRegion; // upsampledRegion.SetSize(0, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(0)/m_PlanarFigure->GetGeometry()->GetSpacing()[0]); // upsampledRegion.SetSize(1, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(1)/m_PlanarFigure->GetGeometry()->GetSpacing()[1]); // upsampledRegion.SetSize(2, 1); // typename itk::Image< unsigned char, 3 >::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); // for (unsigned int n = 0; n < 2; n++) // { // upsampledSize[n] = upsampledSize[n] * m_UpsamplingFactor; // } // upsampledRegion.SetSize( upsampledSize ); // outimage->SetRegions( upsampledRegion ); // outimage->Allocate(); // this->m_InternalImage = mitk::Image::New(); // this->m_InternalImage->InitializeByItk( outimage.GetPointer() ); // this->m_InternalImage->SetVolume( outimage->GetBufferPointer() ); AccessFixedDimensionByItk_2( m_InternalImage, InternalCalculateMaskFromPlanarFigure, 3, 2, node->GetName() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::Geometry3D* planegeo3D, int additionalIndex ) { MITK_INFO << "InternalReorientImagePlane() start"; typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< float, VImageDimension > FloatImageType; typedef itk::ResampleImageFilter ResamplerType; typename ResamplerType::Pointer resampler = ResamplerType::New(); mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); float upsamp = m_UpsamplingFactor; float gausssigma = 0.5; // Spacing typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); spacing[0] = image->GetSpacing()[0] / upsamp; spacing[1] = image->GetSpacing()[1] / upsamp; spacing[2] = image->GetSpacing()[2]; resampler->SetOutputSpacing( spacing ); // Size typename ResamplerType::SizeType size; size[0] = planegeo->GetParametricExtentInMM(0) / spacing[0]; size[1] = planegeo->GetParametricExtentInMM(1) / spacing[1]; size[2] = 1; resampler->SetSize( size ); // Origin typename mitk::Point3D orig = planegeo->GetOrigin(); typename mitk::Point3D corrorig; planegeo3D->WorldToIndex(orig,corrorig); corrorig[0] += 0.5/upsamp; corrorig[1] += 0.5/upsamp; corrorig[2] += 0; planegeo3D->IndexToWorld(corrorig,corrorig); resampler->SetOutputOrigin(corrorig ); // Direction typename ResamplerType::DirectionType direction; typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); for(int c=0; cSetOutputDirection( direction ); // Gaussian interpolation if(gausssigma != 0) { double sigma[3]; for( unsigned int d = 0; d < 3; d++ ) { sigma[d] = gausssigma * image->GetSpacing()[d]; } double alpha = 2.0; typedef itk::GaussianInterpolateImageFunction GaussianInterpolatorType; typename GaussianInterpolatorType::Pointer interpolator = GaussianInterpolatorType::New(); interpolator->SetInputImage( image ); interpolator->SetParameters( sigma, alpha ); resampler->SetInterpolator( interpolator ); } else { // typedef typename itk::BSplineInterpolateImageFunction // InterpolatorType; typedef typename itk::LinearInterpolateImageFunction InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); interpolator->SetInputImage( image ); resampler->SetInterpolator( interpolator ); } // Other resampling options resampler->SetInput( image ); resampler->SetDefaultPixelValue(0); MITK_INFO << "Resampling requested image plane ... "; resampler->Update(); MITK_INFO << " ... done"; if(additionalIndex < 0) { this->m_InternalImage = mitk::Image::New(); this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string nodeName ) { MITK_INFO << "InternalCalculateMaskFromPlanarFigure() start"; typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::CastImageFilter< ImageType, itkUCharImageType > CastFilterType; // Generate mask image as new image with same header as input image and // initialize with "1". itkUCharImageType::Pointer newMaskImage = itkUCharImageType::New(); newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); newMaskImage->Allocate(); newMaskImage->FillBuffer( 1 ); // Generate VTK polygon from (closed) PlanarFigure polyline // (The polyline points are shifted by -0.5 in z-direction to make sure // that the extrusion filter, which afterwards elevates all points by +0.5 // in z-direction, creates a 3D object which is cut by the the plane z=0) const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const Geometry3D *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); vtkPolyData *polyline = vtkPolyData::New(); polyline->Allocate( 1, 1 ); // Determine x- and y-dimensions depending on principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // Create VTK polydata object of polyline contour vtkPoints *points = vtkPoints::New(); PlanarFigure::PolyLineType::const_iterator it; std::vector indices; unsigned int numberOfPoints = 0; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image Point2D point2D = it->Point; planarFigureGeometry2D->WorldToIndex(point2D, point2D); point2D[0] -= 0.5/m_UpsamplingFactor; point2D[1] -= 0.5/m_UpsamplingFactor; planarFigureGeometry2D->IndexToWorld(point2D, point2D); planarFigureGeometry2D->Map( point2D, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { float bounds[2] = {0,0}; bounds[0] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); bounds[1] = this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); imageGeometry3D->WorldToIndex( point3D, point3D ); // if (point3D[i0]<0) // point3D[i0] = 0.5; // else if (point3D[i0]>bounds[0]) // point3D[i0] = bounds[0]-0.5; // if (point3D[i1]<0) // point3D[i1] = 0.5; // else if (point3D[i1]>bounds[1]) // point3D[i1] = bounds[1]-0.5; if (point3D[i0]<0) point3D[i0] = 0.0; else if (point3D[i0]>bounds[0]) point3D[i0] = bounds[0]-0.001; if (point3D[i1]<0) point3D[i1] = 0.0; else if (point3D[i1]>bounds[1]) point3D[i1] = bounds[1]-0.001; points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } else { imageGeometry3D->WorldToIndex( point3D, point3D ); // Add point to polyline array points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); numberOfPoints++; } } polyline->SetPoints( points ); points->Delete(); vtkIdType *ptIds = new vtkIdType[numberOfPoints]; for ( vtkIdType i = 0; i < numberOfPoints; ++i ) { ptIds[i] = i; } polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); // Extrude the generated contour polygon vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); extrudeFilter->SetInput( polyline ); extrudeFilter->SetScaleFactor( 1 ); extrudeFilter->SetExtrusionTypeToNormalExtrusion(); extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); // Make a stencil from the extruded polygon vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); polyDataToImageStencil->SetInput( extrudeFilter->GetOutput() ); // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< itkUCharImageType > ImageImportType; typedef itk::VTKImageExport< itkUCharImageType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( newMaskImage ); vtkImageImport *vtkImporter = vtkImageImport::New(); this->ConnectPipelines( itkExporter, vtkImporter ); vtkImporter->Update(); // Apply the generated image stencil to the input image vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencil( polyDataToImageStencil->GetOutput() ); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); // Export from VTK back to ITK vtkImageExport *vtkExporter = vtkImageExport::New(); vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); // calculate cropping bounding box m_InternalImageMask3D = itkImporter->GetOutput(); m_InternalImageMask3D->SetDirection(image->GetDirection()); itk::ImageRegionConstIterator itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); itk::ImageRegionIterator itimage(image, image->GetLargestPossibleRegion()); itmask = itmask.Begin(); itimage = itimage.Begin(); typename ImageType::SizeType lowersize = {{9999999999,9999999999,9999999999}}; typename ImageType::SizeType uppersize = {{0,0,0}}; while( !itmask.IsAtEnd() ) { if(itmask.Get() == 0) { itimage.Set(0); } else { typename ImageType::IndexType index = itimage.GetIndex(); typename ImageType::SizeType signedindex; signedindex[0] = index[0]; signedindex[1] = index[1]; signedindex[2] = index[2]; lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; } ++itmask; ++itimage; } typename ImageType::IndexType index; index[0] = lowersize[0]; index[1] = lowersize[1]; index[2] = lowersize[2]; typename ImageType::SizeType size; size[0] = uppersize[0] - lowersize[0] + 1; size[1] = uppersize[1] - lowersize[1] + 1; size[2] = uppersize[2] - lowersize[2] + 1; itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); // crop internal mask typedef itk::RegionOfInterestImageFilter< itkUCharImageType, itkUCharImageType > ROIMaskFilterType; typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); roi2->SetRegionOfInterest(cropRegion); roi2->SetInput(m_InternalImageMask3D); roi2->Update(); m_InternalImageMask3D = roi2->GetOutput(); Image::Pointer tmpImage = Image::New(); tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); Image::Pointer tmpImage2 = Image::New(); tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); const Geometry3D *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); const Geometry3D *intImageGeometry3D = tmpImage->GetGeometry( 0 ); typedef itk::ImageRegionIteratorWithIndex IteratorType; IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); imageIterator.GoToBegin(); while ( !imageIterator.IsAtEnd() ) { unsigned char val = imageIterator.Value(); if (val>0) { itk::Index<3> index = imageIterator.GetIndex(); Point3D point; point[0] = index[0]; point[1] = index[1]; point[2] = index[2]; intImageGeometry3D->IndexToWorld(point, point); pfImageGeometry3D->WorldToIndex(point, point); point[i0] += 0.5; point[i1] += 0.5; index[0] = point[0]; index[1] = point[1]; index[2] = point[2]; if (pfImageGeometry3D->IsIndexInside(index)) m_PlanarFigureImage->SetPixel(index, 1); } ++imageIterator; } // Clean up VTK objects polyline->Delete(); extrudeFilter->Delete(); polyDataToImageStencil->Delete(); vtkImporter->Delete(); imageStencilFilter->Delete(); //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? delete[] ptIds; } void QmitkFiberProcessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkFiberProcessingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkFiberProcessingView::UpdateGui() { // are fiber bundles selected? if ( m_SelectedFB.empty() ) { m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); m_Controls->m_ProcessFiberBundleButton->setEnabled(false); m_Controls->doExtractFibersButton->setEnabled(false); m_Controls->m_Extract3dButton->setEnabled(false); m_Controls->m_ResampleFibersButton->setEnabled(false); m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); m_Controls->m_FaColorFibersButton->setEnabled(false); m_Controls->m_PruneFibersButton->setEnabled(false); if (m_Surfaces.size()>0) m_Controls->m_MirrorFibersButton->setEnabled(true); else m_Controls->m_MirrorFibersButton->setEnabled(false); } else { m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); m_Controls->m_ProcessFiberBundleButton->setEnabled(true); m_Controls->m_ResampleFibersButton->setEnabled(true); m_Controls->m_PruneFibersButton->setEnabled(true); + m_Controls->m_MirrorFibersButton->setEnabled(true); if (m_Surfaces.size()>0) - { m_Controls->m_Extract3dButton->setEnabled(true); - m_Controls->m_MirrorFibersButton->setEnabled(true); - } // one bundle and one planar figure needed to extract fibers if (!m_SelectedPF.empty()) m_Controls->doExtractFibersButton->setEnabled(true); // more than two bundles needed to join/subtract if (m_SelectedFB.size() > 1) { m_Controls->m_JoinBundles->setEnabled(true); m_Controls->m_SubstractBundles->setEnabled(true); } else { m_Controls->m_JoinBundles->setEnabled(false); m_Controls->m_SubstractBundles->setEnabled(false); } if (m_SelectedImage.IsNotNull()) m_Controls->m_FaColorFibersButton->setEnabled(true); } // are planar figures selected? if ( m_SelectedPF.empty() ) { m_Controls->doExtractFibersButton->setEnabled(false); m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(false); m_Controls->m_GenerateRoiImage->setEnabled(false); } else { if ( !m_SelectedFB.empty() ) m_Controls->m_GenerateRoiImage->setEnabled(true); else m_Controls->m_GenerateRoiImage->setEnabled(false); if (m_SelectedPF.size() > 1) { m_Controls->PFCompoANDButton->setEnabled(true); m_Controls->PFCompoORButton->setEnabled(true); m_Controls->PFCompoNOTButton->setEnabled(false); } else { m_Controls->PFCompoANDButton->setEnabled(false); m_Controls->PFCompoORButton->setEnabled(false); m_Controls->PFCompoNOTButton->setEnabled(true); } } } void QmitkFiberProcessingView::OnSelectionChanged( std::vector nodes ) { if ( !this->IsVisible() ) return; //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection m_SelectedFB.clear(); m_SelectedPF.clear(); m_Surfaces.clear(); m_SelectedImage = NULL; for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if ( dynamic_cast(node->GetData()) ) m_SelectedFB.push_back(node); else if (dynamic_cast(node->GetData())) m_SelectedPF.push_back(node); else if (dynamic_cast(node->GetData())) m_SelectedImage = dynamic_cast(node->GetData()); else if (dynamic_cast(node->GetData())) m_Surfaces.push_back(dynamic_cast(node->GetData())); } UpdateGui(); GenerateStats(); } void QmitkFiberProcessingView::OnDrawPolygon() { // bool checked = m_Controls->m_PolygonButton->isChecked(); // if(!this->AssertDrawingIsPossible(checked)) // return; mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); MITK_INFO << "PlanarPolygon created ..."; mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figureP = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figureP = dynamic_cast(node->GetData()); if(figureP) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } } void QmitkFiberProcessingView::OnDrawCircle() { //bool checked = m_Controls->m_CircleButton->isChecked(); //if(!this->AssertDrawingIsPossible(checked)) // return; mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_EllipseCounter)); this->GetDataStorage()->Modified(); MITK_INFO << "PlanarCircle created ..."; //call mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figureP = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figureP = dynamic_cast(node->GetData()); if(figureP) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } } void QmitkFiberProcessingView::Activated() { MITK_INFO << "FB OPerations ACTIVATED()"; /* mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figure = 0; for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); figure = dynamic_cast(node->GetData()); if(figure) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } */ } void QmitkFiberProcessingView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey, mitk::BaseProperty *property ) { // initialize figure's geometry with empty geometry mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); figure->SetGeometry2D( emptygeometry ); //set desired data to DataNode where Planarfigure is stored mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetName(name.toStdString()); newNode->SetData(figure); newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,0.0,0.0)); newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(2.0)); newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true)); newNode->AddProperty( "selected", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(false) ); newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true) ); newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(3.0) ); newNode->AddProperty( "planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.outline.width", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.helperline.width", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); newNode->AddProperty( "planarfigure.default.line.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.outline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.helperline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(0.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.default.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); newNode->AddProperty( "planarfigure.default.marker.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.line.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.outline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.hover.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.hover.marker.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.line.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.outline.opacity", mitk::FloatProperty::New(2.0)); newNode->AddProperty( "planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.helperline.opacity",mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(2.0) ); newNode->AddProperty( "planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); newNode->AddProperty( "planarfigure.selected.marker.opacity",mitk::FloatProperty::New(2.0)); // figure drawn on the topmost layer / image newNode->SetColor(1.0,1.0,1.0); newNode->SetOpacity(0.8); GetDataStorage()->Add(newNode ); std::vector selectedNodes = GetDataManagerSelection(); for(unsigned int i = 0; i < selectedNodes.size(); i++) { selectedNodes[i]->SetSelected(false); } newNode->SetSelected(true); } void QmitkFiberProcessingView::DoFiberExtraction() { if ( m_SelectedFB.empty() ){ QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; return; } for (int i=0; i(m_SelectedFB.at(i)->GetData()); mitk::PlanarFigure::Pointer roi = dynamic_cast (m_SelectedPF.at(0)->GetData()); mitk::FiberBundleX::Pointer extFB = fib->ExtractFiberSubset(roi); if (extFB->GetNumFibers()<=0) continue; mitk::DataNode::Pointer node; node = mitk::DataNode::New(); node->SetData(extFB); QString name(m_SelectedFB.at(i)->GetName().c_str()); name += "_"; name += m_SelectedPF.at(0)->GetName().c_str(); node->SetName(name.toStdString()); GetDataStorage()->Add(node); m_SelectedFB.at(i)->SetVisibility(false); } } void QmitkFiberProcessingView::GenerateAndComposite() { mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCAnd->SetGeometry2D(currentGeometry2D); PFCAnd->setOperationType(mitk::PFCOMPOSITION_AND_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCAnd->addPlanarFigure( tmpPF ); PFCAnd->addDataNode( nodePF ); PFCAnd->setDisplayName("AND_COMPO"); } AddCompositeToDatastorage(PFCAnd, NULL); } void QmitkFiberProcessingView::GenerateOrComposite() { mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCOr->SetGeometry2D(currentGeometry2D); PFCOr->setOperationType(mitk::PFCOMPOSITION_OR_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCOr->addPlanarFigure( tmpPF ); PFCOr->addDataNode( nodePF ); PFCOr->setDisplayName("OR_COMPO"); } AddCompositeToDatastorage(PFCOr, NULL); } void QmitkFiberProcessingView::GenerateNotComposite() { mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); PFCNot->SetGeometry2D(currentGeometry2D); PFCNot->setOperationType(mitk::PFCOMPOSITION_NOT_OPERATION); for( std::vector::iterator it = m_SelectedPF.begin(); it != m_SelectedPF.end(); ++it ) { mitk::DataNode::Pointer nodePF = *it; mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); PFCNot->addPlanarFigure( tmpPF ); PFCNot->addDataNode( nodePF ); PFCNot->setDisplayName("NOT_COMPO"); } AddCompositeToDatastorage(PFCNot, NULL); } /* CLEANUP NEEDED */ void QmitkFiberProcessingView::AddCompositeToDatastorage(mitk::PlanarFigureComposite::Pointer pfcomp, mitk::DataNode::Pointer parentDataNode ) { mitk::DataNode::Pointer newPFCNode; newPFCNode = mitk::DataNode::New(); newPFCNode->SetName( pfcomp->getDisplayName() ); newPFCNode->SetData(pfcomp); newPFCNode->SetVisibility(true); switch (pfcomp->getOperationType()) { case 0: { if (!parentDataNode.IsNull()) { GetDataStorage()->Add(newPFCNode, parentDataNode); } else { GetDataStorage()->Add(newPFCNode); } //iterate through its childs for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // make new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; } else { MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } case 1: { if (!parentDataNode.IsNull()) { MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; GetDataStorage()->Add(newPFCNode, parentDataNode); } else { MITK_INFO << "adding " << newPFCNode->GetName(); GetDataStorage()->Add(newPFCNode); } for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // make new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } case 2: { if (!parentDataNode.IsNull()) { MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; GetDataStorage()->Add(newPFCNode, parentDataNode); } else { MITK_INFO << "adding " << newPFCNode->GetName(); GetDataStorage()->Add(newPFCNode); } //iterate through its childs for(int i=0; igetNumberOfChildren(); ++i) { mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); if ( !pfcompcast.IsNull() ) { // child is of type planar Figure composite // makeRemoveBundle new node of the child, cuz later the child has to be removed of its old position in datamanager // feed new dataNode with information of the savedDataNode, which is gonna be removed soon mitk::DataNode::Pointer newChildPFCNode; newChildPFCNode = mitk::DataNode::New(); newChildPFCNode->SetData(tmpPFchild); newChildPFCNode->SetName( savedPFchildNode->GetName() ); pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user //update inside vector the dataNodePointer pfcomp->replaceDataNodeAt(i, newChildPFCNode); AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager // without having its parent anymore //GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } } else { // child is not of type PlanarFigureComposite, so its one of the planarFigures // create new dataNode containing the data of the old dataNode, but position in dataManager will be // modified cuz we re setting a (new) parent. mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); newPFchildNode->SetName(savedPFchildNode->GetName() ); newPFchildNode->SetData(tmpPFchild); newPFchildNode->SetVisibility(true); // replace the dataNode in PFComp DataNodeVector pfcomp->replaceDataNodeAt(i, newPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; }else{ MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); } // remove old child position in dataStorage GetDataStorage()->Remove(savedPFchildNode); if ( GetDataStorage()->Exists(savedPFchildNode)) { MITK_INFO << savedPFchildNode->GetName() << " still exists"; } MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); //add new child to datamanager with its new position as child of newPFCNode parent GetDataStorage()->Add(newPFchildNode, newPFCNode); } } GetDataStorage()->Modified(); break; } default: MITK_INFO << "we have an UNDEFINED composition... ERROR" ; break; } } void QmitkFiberProcessingView::JoinBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } std::vector::const_iterator it = m_SelectedFB.begin(); mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); QString name(""); name += QString((*it)->GetName().c_str()); ++it; for (it; it!=m_SelectedFB.end(); ++it) { newBundle = newBundle->AddBundle(dynamic_cast((*it)->GetData())); name += "+"+QString((*it)->GetName().c_str()); } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } void QmitkFiberProcessingView::SubstractBundles() { if ( m_SelectedFB.size()<2 ){ QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; return; } std::vector::const_iterator it = m_SelectedFB.begin(); mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); QString name(""); name += QString((*it)->GetName().c_str()); ++it; for (it; it!=m_SelectedFB.end(); ++it) { newBundle = newBundle->SubtractBundle(dynamic_cast((*it)->GetData())); if (newBundle.IsNull()) break; name += "-"+QString((*it)->GetName().c_str()); } if (newBundle.IsNull()) { QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); return; } mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); fbNode->SetData(newBundle); fbNode->SetName(name.toStdString()); fbNode->SetVisibility(true); GetDataStorage()->Add(fbNode); } void QmitkFiberProcessingView::PruneBundle() { int minLength = this->m_Controls->m_PruneFibersSpinBox->value(); bool doneSomething = false; for (int i=0; i(m_SelectedFB.at(i)->GetData()); if (!fib->RemoveShortFibers(minLength)) QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers."); else doneSomething = true; } if (doneSomething) { GenerateStats(); RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkFiberProcessingView::GenerateStats() { if ( m_SelectedFB.empty() ) return; QString stats(""); for( int i=0; i(node->GetData())) { if (i>0) stats += "\n-----------------------------\n"; stats += QString(node->GetName().c_str()) + "\n"; mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numberOfLines = vLines->GetNumberOfCells(); stats += "Number of fibers: "+ QString::number(numberOfLines) + "\n"; float length = 0; std::vector lengths; for (int i=0; iGetNextCell ( numPoints, points ); float l=0; for (unsigned int j=0; j p1; itk::Point p2; fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); float dist = p1.EuclideanDistanceTo(p2); length += dist; l += dist; } itk::Point p2; fiberPolyData->GetPoint(points[numPoints-1], p2.GetDataPointer()); lengths.push_back(l); } std::sort(lengths.begin(), lengths.end()); if (numberOfLines>0) length /= numberOfLines; float dev=0; int count = 0; vLines->InitTraversal(); for (int i=0; iGetNextCell ( numPoints, points ); float l=0; for (unsigned int j=0; j p1; itk::Point p2; fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); float dist = p1.EuclideanDistanceTo(p2); l += dist; } dev += (length-l)*(length-l); count++; } if (numberOfLines>1) dev /= (numberOfLines-1); else dev = 0; stats += "Min. length: "+ QString::number(lengths.front(),'f',1) + " mm\n"; stats += "Max. length: "+ QString::number(lengths.back(),'f',1) + " mm\n"; stats += "Mean length: "+ QString::number(length,'f',1) + " mm\n"; stats += "Median length: "+ QString::number(lengths.at(lengths.size()/2),'f',1) + " mm\n"; stats += "Standard deviation: "+ QString::number(sqrt(dev),'f',1) + " mm\n"; } } this->m_Controls->m_StatsTextEdit->setText(stats); } void QmitkFiberProcessingView::ProcessSelectedBundles() { if ( m_SelectedFB.empty() ){ QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; return; } int generationMethod = m_Controls->m_GenerationBox->currentIndex(); for( int i=0; i(node->GetData())) { mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); QString name(node->GetName().c_str()); DataNode::Pointer newNode = NULL; switch(generationMethod){ case 0: newNode = GenerateTractDensityImage(fib, false, true); name += "_TDI"; break; case 1: newNode = GenerateTractDensityImage(fib, false, false); name += "_TDI"; break; case 2: newNode = GenerateTractDensityImage(fib, true, false); name += "_envelope"; break; case 3: newNode = GenerateColorHeatmap(fib); break; case 4: newNode = GenerateFiberEndingsImage(fib); name += "_fiber_endings"; break; case 5: newNode = GenerateFiberEndingsPointSet(fib); name += "_fiber_endings"; break; } if (newNode.IsNotNull()) { newNode->SetName(name.toStdString()); GetDataStorage()->Add(newNode); } } } } // generate pointset displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsPointSet(mitk::FiberBundleX::Pointer fib) { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int count = 0; int numFibers = fib->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); if (numPoints>0) { double* point = fiberPolyData->GetPoint(points[0]); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } if (numPoints>2) { double* point = fiberPolyData->GetPoint(points[numPoints-1]); itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; pointSet->InsertPoint(count, itkPoint); count++; } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( pointSet ); return node; } // generate image displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsImage(mitk::FiberBundleX::Pointer fib) { typedef unsigned char OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image OutImageType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate rgba heatmap from fiber bundle mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateColorHeatmap(mitk::FiberBundleX::Pointer fib) { typedef itk::RGBAPixel OutPixType; typedef itk::Image OutImageType; typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); generator->SetFiberBundle(fib); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { itk::Image::Pointer itkImage = itk::Image::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } // generate tract density image from fiber bundle mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary, bool absolute) { typedef float OutPixType; typedef itk::Image OutImageType; itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(fib); generator->SetBinaryOutput(binary); generator->SetOutputAbsoluteValues(absolute); generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); if (m_SelectedImage.IsNotNull()) { OutImageType::Pointer itkImage = OutImageType::New(); CastToItkImage(m_SelectedImage, itkImage); generator->SetInputImage(itkImage); generator->SetUseImageGeometry(true); } generator->Update(); // get output image typedef itk::Image OutType; OutType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // init data node mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); return node; } void QmitkFiberProcessingView::ResampleSelectedBundles() { int factor = this->m_Controls->m_ResampleFibersSpinBox->value(); for (int i=0; i(m_SelectedFB.at(i)->GetData()); fib->DoFiberSmoothing(factor); } GenerateStats(); RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::MirrorFibers() { unsigned int axis = this->m_Controls->m_AxisSelectionBox->currentIndex(); for (int i=0; i(m_SelectedFB.at(i)->GetData()); fib->MirrorFibers(axis); } if (m_SelectedFB.size()>0) GenerateStats(); if (m_Surfaces.size()>0) { for (int i=0; i poly = surf->GetVtkPolyData(); vtkSmartPointer vtkNewPoints = vtkPoints::New(); for (int i=0; iGetNumberOfPoints(); i++) { double* point = poly->GetPoint(i); point[axis] *= -1; vtkNewPoints->InsertNextPoint(point); } poly->SetPoints(vtkNewPoints); surf->CalculateBoundingBox(); } } RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkFiberProcessingView::DoFaColorCoding() { if (m_SelectedImage.IsNull()) return; for( int i=0; i(m_SelectedFB.at(i)->GetData()); fib->SetFAMap(m_SelectedImage); fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_FA_BASED); fib->DoColorCodingFaBased(); } if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index d868e4a21a..d80cc65ffa 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,559 +1,573 @@ /*=================================================================== 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 "QmitkImageStatisticsView.h" // Qt includes #include // berry includes #include // mitk includes #include "mitkNodePredicateDataType.h" #include "mitkPlanarFigureInteractor.h" // itk includes #include "itksys/SystemTools.hxx" const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( NULL ), m_TimeStepperAdapter( NULL ), m_SelectedImage( NULL ), m_SelectedImageMask( NULL ), m_SelectedPlanarFigure( NULL ), m_ImageObserverTag( -1 ), m_ImageMaskObserverTag( -1 ), m_PlanarFigureObserverTag( -1 ), m_CurrentStatisticsValid( false ), m_StatisticsUpdatePending( false ), m_DataNodeSelectionChanged ( false ), m_Visible(false) { this->m_CalculationThread = new QmitkImageStatisticsCalculationThread; this->m_SelectedDataNodes = SelectedDataNodeVectorType(2); // maximum number of selected nodes is exactly two! } QmitkImageStatisticsView::~QmitkImageStatisticsView() { if ( m_SelectedImage != NULL ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask != NULL ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure != NULL ) m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); while(this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculationThread; - - //if(m_QThreadMutex != 0) - //{ - // delete m_QThreadMutex; - //} } void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { if (m_Controls == NULL) { m_Controls = new Ui::QmitkImageStatisticsViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_LineProfileWidget->SetPathModeToPlanarFigure(); } } void QmitkImageStatisticsView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(this->m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardHistogramButtonClicked()) ); connect( (QObject*)(this->m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardStatisticsButtonClicked()) ); connect( (QObject*)(this->m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(OnIgnoreZerosCheckboxClicked()) ); connect( (QObject*) this->m_CalculationThread, SIGNAL(finished()),this, SLOT( OnThreadedStatisticsCalculationEnds()),Qt::QueuedConnection); connect( (QObject*) this, SIGNAL(StatisticsUpdate()),this, SLOT( RequestStatisticsUpdate()), Qt::QueuedConnection); } } void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked() { emit StatisticsUpdate(); } void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked() { if ( m_CurrentStatisticsValid ) { typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram().GetPointer(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); it != histogram->End(); ++it ) { clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) .arg( it.GetFrequency() ); } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::OnClipboardStatisticsButtonClicked() { if ( this->m_CurrentStatisticsValid ) { const mitk::ImageStatisticsCalculator::Statistics &statistics = this->m_CalculationThread->GetStatisticsData(); // Copy statistics to clipboard ("%Ln" will use the default locale for // number formatting) QString clipboard( "Mean \t StdDev \t RMS \t Max \t Min \t N \t V (mm³)\n" ); clipboard = clipboard.append( "%L1 \t %L2 \t %L3 \t %L4 \t %L5 \t %L6 \t %L7" ) .arg( statistics.Mean, 0, 'f', 10 ) .arg( statistics.Sigma, 0, 'f', 10 ) .arg( statistics.RMS, 0, 'f', 10 ) .arg( statistics.Max, 0, 'f', 10 ) .arg( statistics.Min, 0, 'f', 10 ) .arg( statistics.N ) .arg( m_Controls->m_StatisticsTable->item( 0, 6 )->text() ); QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, const QList &selectedNodes ) { this->SelectionChanged( selectedNodes ); } void QmitkImageStatisticsView::SelectionChanged(const QList &selectedNodes) { if( this->m_StatisticsUpdatePending ) { this->m_DataNodeSelectionChanged = true; return; // not ready for new data now! } this->ReinitData(); if(selectedNodes.size() == 1 || selectedNodes.size() == 2) { for (int i= 0; i< selectedNodes.size(); ++i) { this->m_SelectedDataNodes.push_back(selectedNodes.at(i)); } this->m_DataNodeSelectionChanged = false; this->m_Controls->m_ErrorMessageLabel->setText( "" ); this->m_Controls->m_ErrorMessageLabel->hide(); emit StatisticsUpdate(); } else { this->m_DataNodeSelectionChanged = false; } } void QmitkImageStatisticsView::ReinitData() { while( this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } if(this->m_SelectedImage != NULL) { this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag); this->m_SelectedImage = NULL; } if(this->m_SelectedImageMask != NULL) { this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag); this->m_SelectedImageMask = NULL; } if(this->m_SelectedPlanarFigure != NULL) { this->m_SelectedPlanarFigure->RemoveObserver( this->m_PlanarFigureObserverTag); this->m_SelectedPlanarFigure = NULL; } this->m_SelectedDataNodes.clear(); this->m_StatisticsUpdatePending = false; m_Controls->m_ErrorMessageLabel->setText( "" ); m_Controls->m_ErrorMessageLabel->hide(); this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); } void QmitkImageStatisticsView::OnThreadedStatisticsCalculationEnds() { std::stringstream message; message << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->hide(); this->WriteStatisticsToGUI(); } void QmitkImageStatisticsView::UpdateStatistics() { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if ( renderPart == NULL ) { this->m_StatisticsUpdatePending = false; return; } // classify selected nodes mitk::NodePredicateDataType::Pointer imagePredicate = mitk::NodePredicateDataType::New("Image"); std::string maskName = std::string(); std::string maskType = std::string(); unsigned int maskDimension = 0; // reset data from last run ITKCommandType::Pointer changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::SelectedDataModified ); mitk::Image::Pointer selectedImage = mitk::Image::New(); for( int i= 0 ; i < this->m_SelectedDataNodes.size(); ++i) { mitk::PlanarFigure::Pointer planarFig = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); if( imagePredicate->CheckNode(this->m_SelectedDataNodes.at(i)) ) { bool isMask = false; this->m_SelectedDataNodes.at(i)->GetPropertyValue("binary", isMask); if( this->m_SelectedImageMask == NULL && isMask) { this->m_SelectedImageMask = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageMaskObserverTag = this->m_SelectedImageMask->AddObserver(itk::ModifiedEvent(), changeListener); maskName = this->m_SelectedDataNodes.at(i)->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; } else if( !isMask ) { if(this->m_SelectedImage == NULL) { this->m_SelectedImage = static_cast(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } else if (planarFig.IsNotNull()) { if(this->m_SelectedPlanarFigure == NULL) { this->m_SelectedPlanarFigure = planarFig; this->m_PlanarFigureObserverTag = this->m_SelectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener); maskName = this->m_SelectedDataNodes.at(i)->GetName(); maskType = this->m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; } } else { std::stringstream message; message << "" << "Invalid data node type!" << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); } } if(maskName == "") { maskName = "None"; maskType = ""; maskDimension = 0; } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); if ( m_SelectedImage != NULL && m_SelectedImage->IsInitialized()) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { std::stringstream message; message << "Multi-component images not supported."; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; this->m_StatisticsUpdatePending = false; return; } std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); // check time step validity if(m_SelectedImage->GetDimension() <= 3 && timeStep > m_SelectedImage->GetDimension(3)-1) { timeStep = m_SelectedImage->GetDimension(3)-1; } //// initialize thread and trigger it this->m_CalculationThread->SetIgnoreZeroValueVoxel( m_Controls->m_IgnoreZerosCheckbox->isChecked() ); this->m_CalculationThread->Initialize( m_SelectedImage, m_SelectedImageMask, m_SelectedPlanarFigure ); this->m_CalculationThread->SetTimeStep( timeStep ); std::stringstream message; message << "Calculating statistics..."; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); try { // Compute statistics this->m_CalculationThread->start(); } catch ( const std::runtime_error &e ) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } } else { this->m_StatisticsUpdatePending = false; } } void QmitkImageStatisticsView::SelectedDataModified() { if( !m_StatisticsUpdatePending ) { emit StatisticsUpdate(); } } +void QmitkImageStatisticsView::NodeRemoved(const mitk::DataNode *node) +{ + while(this->m_CalculationThread->isRunning()) // wait until thread has finished + { + itksys::SystemTools::Delay(100); + } + + if (node->GetData() == m_SelectedImage) + { + m_SelectedImage = NULL; + } +} + void QmitkImageStatisticsView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { if(this->m_DataNodeSelectionChanged) { this->SelectionChanged( this->GetDataManagerSelection()); } else { this->m_StatisticsUpdatePending = true; this->UpdateStatistics(); } } - this->GetRenderWindowPart()->RequestUpdate(); + if (this->GetRenderWindowPart()) + this->GetRenderWindowPart()->RequestUpdate(); } void QmitkImageStatisticsView::WriteStatisticsToGUI() { if(m_DataNodeSelectionChanged) { this->m_StatisticsUpdatePending = false; this->RequestStatisticsUpdate(); return; // stop visualization of results and calculate statistics of new selection } if ( this->m_CalculationThread->GetStatisticsUpdateSuccessFlag()) { if ( this->m_CalculationThread->GetStatisticsChangedFlag() ) { // Do not show any error messages m_Controls->m_ErrorMessageLabel->hide(); m_CurrentStatisticsValid = true; } m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); - m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); m_Controls->m_HistogramWidget->SetHistogram( this->m_CalculationThread->GetTimeStepHistogram().GetPointer() ); + m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); int timeStep = this->m_CalculationThread->GetTimeStep(); this->FillStatisticsTableView( this->m_CalculationThread->GetStatisticsData(), this->m_CalculationThread->GetStatisticsImage()); } else { m_Controls->m_SelectedMaskLabel->setText( "None" ); std::stringstream message; message << "Error calculating statistics!"; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); // Clear statistics and histogram this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure != NULL ) { // check whether PlanarFigure is initialized const mitk::Geometry2D *planarFigureGeometry2D = m_SelectedPlanarFigure->GetGeometry2D(); if ( planarFigureGeometry2D == NULL ) { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); this->m_StatisticsUpdatePending = false; return; } // TODO: enable line profile widget m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); m_Controls->m_LineProfileWidget->SetImage( this->m_CalculationThread->GetStatisticsImage() ); m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); } } this->m_StatisticsUpdatePending = false; } void QmitkImageStatisticsView::ComputeIntensityProfile( mitk::PlanarLine* line ) { double sampling = 300; QmitkVtkHistogramWidget::HistogramType::Pointer histogram = QmitkVtkHistogramWidget::HistogramType::New(); itk::Size<1> siz; siz[0] = sampling; itk::FixedArray lower, higher; lower.Fill(0); mitk::Point3D begin = line->GetWorldControlPoint(0); mitk::Point3D end = line->GetWorldControlPoint(1); itk::Vector direction = (end - begin); higher.Fill(direction.GetNorm()); histogram->Initialize(siz, lower, higher); for(int i = 0; i < sampling; i++) { double d = m_SelectedImage->GetPixelValueByWorldCoordinate(begin + double(i)/sampling * direction); histogram->SetFrequency(i,d); } m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( histogram ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); } void QmitkImageStatisticsView::FillStatisticsTableView( const mitk::ImageStatisticsCalculator::Statistics &s, const mitk::Image *image ) { this->m_Controls->m_StatisticsTable->setItem( 0, 0, new QTableWidgetItem( QString("%1").arg(s.Mean, 0, 'f', 2) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 1, new QTableWidgetItem( QString("%1").arg(s.Sigma, 0, 'f', 2) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 2, new QTableWidgetItem( QString("%1").arg(s.RMS, 0, 'f', 2) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 3, new QTableWidgetItem( QString("%1").arg(s.Max, 0, 'f', 2) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 4, new QTableWidgetItem( QString("%1").arg(s.Min, 0, 'f', 2) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 5, new QTableWidgetItem( QString("%1").arg(s.N) ) ); const mitk::Geometry3D *geometry = image->GetGeometry(); if ( geometry != NULL ) { const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); double volume = spacing[0] * spacing[1] * spacing[2] * (double) s.N; this->m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( QString("%1").arg(volume, 0, 'f', 2) ) ); } else { this->m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( "NA" ) ); } } void QmitkImageStatisticsView::InvalidateStatisticsTableView() { for ( unsigned int i = 0; i < 7; ++i ) { this->m_Controls->m_StatisticsTable->setItem( 0, i, new QTableWidgetItem( "NA" ) ); } } void QmitkImageStatisticsView::Activated() { } void QmitkImageStatisticsView::Deactivated() { } void QmitkImageStatisticsView::Visible() { m_Visible = true; this->OnSelectionChanged(this->GetSite()->GetPage()->FindView("org.mitk.views.datamanager"), this->GetDataManagerSelection()); } void QmitkImageStatisticsView::Hidden() { m_Visible = false; } void QmitkImageStatisticsView::SetFocus() { } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index 54c44444bd..00f45517e0 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -1,152 +1,155 @@ /*=================================================================== 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 QmitkImageStatisticsView_H__INCLUDED #define QmitkImageStatisticsView_H__INCLUDED #include "ui_QmitkImageStatisticsViewControls.h" // Qmitk includes #include #include "QmitkStepperAdapter.h" #include "QmitkImageStatisticsCalculationThread.h" // mitk includes #include "mitkImageStatisticsCalculator.h" #include "mitkILifecycleAwarePart.h" #include "mitkPlanarLine.h" /*! \brief QmitkImageStatisticsView is a bundle that allows statistics calculation from images. Three modes are supported: 1. Statistics of one image, 2. Statistics of an image and a segmentation, 3. Statistics of an image and a Planar Figure. The statistics calculation is realized in a seperate thread to keep the gui accessable during calculation. \ingroup Plugins/org.mitk.gui.qt.measurementtoolbox */ class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { Q_OBJECT public: /*! \ Convenient typedefs */ typedef mitk::DataStorage::SetOfObjects ConstVector; typedef ConstVector::ConstPointer ConstVectorPointer; typedef ConstVector::ConstIterator ConstVectorIterator; typedef std::map< mitk::Image *, mitk::ImageStatisticsCalculator::Pointer > ImageStatisticsMapType; typedef std::vector SelectedDataNodeVectorType; typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; /*! \brief default constructor */ QmitkImageStatisticsView(QObject *parent=0, const char *name=0); /*! \brief default destructor */ virtual ~QmitkImageStatisticsView(); /*! \brief method for creating the widget containing the application controls, like sliders, buttons etc. */ virtual void CreateQtPartControl(QWidget *parent); /*! \brief method for creating the connections of main and control widget */ virtual void CreateConnections(); /*! \brief not implemented*/ //bool IsExclusiveFunctionality() const; /*! \brief Is called from the selection mechanism once the data manager selection has changed*/ void OnSelectionChanged( berry::IWorkbenchPart::Pointer part, const QList &nodes ); static const std::string VIEW_ID; public slots: - /** \brief */ + /** \brief Called when the statistics update is finished, sets the results to GUI.*/ void OnThreadedStatisticsCalculationEnds(); protected slots: - /** \brief */ + /** \brief Saves the histogram to the clipboard */ void OnClipboardHistogramButtonClicked(); - /** \brief */ + /** \brief Saves the statistics to the clipboard */ void OnClipboardStatisticsButtonClicked(); - /** \brief */ + /** \brief Indicates if zeros should be excluded from statistics calculation */ void OnIgnoreZerosCheckboxClicked( ); - /** \brief */ + /** \brief Checks if update is possible and calls StatisticsUpdate() possible */ void RequestStatisticsUpdate(); signals: - /** \brief */ + /** \brief Method to set the data to the member and start the threaded statistics update */ void StatisticsUpdate(); protected: - /** \brief */ + /** \brief Writes the calculated statistics to the GUI */ void FillStatisticsTableView( const mitk::ImageStatisticsCalculator::Statistics &s, const mitk::Image *image ); - /** \brief */ + /** \brief Removes statistics from the GUI */ void InvalidateStatisticsTableView(); /** \brief Recalculate statistics for currently selected image and mask and * update the GUI. */ void UpdateStatistics(); /** \brief Listener for progress events to update progress bar. */ void UpdateProgressBar(); /** \brief Removes any cached images which are no longer referenced elsewhere. */ void RemoveOrphanImages(); /** \brief Computes an Intensity Profile along line and updates the histogram widget with it. */ void ComputeIntensityProfile( mitk::PlanarLine* line ); void Activated(); void Deactivated(); void Visible(); void Hidden(); - void SetFocus(); + /** \brief Method called when itkModifiedEvent is called by selected data. */ void SelectedDataModified(); /** \brief Method called when the data manager selection changes */ void SelectionChanged(const QList &selectedNodes); /** \brief Method called to remove old selection when a new selection is present */ void ReinitData(); /** \brief writes the statistics to the gui*/ void WriteStatisticsToGUI(); + void NodeRemoved(const mitk::DataNode *node); + + // member variables Ui::QmitkImageStatisticsViewControls *m_Controls; QmitkImageStatisticsCalculationThread* m_CalculationThread; QmitkStepperAdapter* m_TimeStepperAdapter; unsigned int m_CurrentTime; QString m_Clipboard; // Image and mask data mitk::Image* m_SelectedImage; mitk::Image* m_SelectedImageMask; mitk::PlanarFigure* m_SelectedPlanarFigure; // observer tags long m_ImageObserverTag; long m_ImageMaskObserverTag; long m_PlanarFigureObserverTag; SelectedDataNodeVectorType m_SelectedDataNodes; bool m_CurrentStatisticsValid; bool m_StatisticsUpdatePending; bool m_StatisticsIntegrationPending; bool m_DataNodeSelectionChanged; bool m_Visible; }; #endif // QmitkImageStatisticsView_H__INCLUDED diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui index 112a9396fb..1d00f48e24 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,349 +1,339 @@ QmitkImageStatisticsViewControls true 0 0 465 800 Form 0 0 Mask: None 2 Qt::Horizontal 40 20 Error Message Qt::AutoText Ignore zero-valued voxels Statistics 0 0 0 0 0 100 - 144 + 175 16777215 - 144 + 170 Qt::ScrollBarAlwaysOff Qt::ScrollBarAsNeeded true true true Qt::DotLine true false false 80 true 80 false true true false - 20 + 25 - 20 + 25 false false Mean StdDev RMS Max Min N V (mm³) Component 1 0 0 Copy to Clipboard Qt::Horizontal 40 20 150 160 Histogram false - - - - 1 - 23 - 445 - 163 - - - - 0 - - - - - - - - 1 - 192 - 134 - 23 - - - - - 0 - - - 0 - - - - - Copy to Clipboard - - - - - - - Qt::Horizontal + + + + + 0 + + + + + + + + + + 0 - - - 40 - 20 - + + 0 - - - - + + + + Copy to Clipboard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical 20 40 QmitkVtkHistogramWidget QWidget
QmitkVtkHistogramWidget.h
1
QmitkVtkLineProfileWidget QWidget
QmitkVtkLineProfileWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index ef850ce1f0..f15f2ad731 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1239 +1,1221 @@ /*=================================================================== 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 "mitkDataNodeObject.h" #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkGlobalInteraction.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include //For Segmentation in rotated slices //TODO clean up includes #include "mitkVtkResliceInterpolationProperty.h" #include "mitkPlanarCircle.h" #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkSegmentationObjectFactory.h" const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_RenderingManagerObserverTag(0) { RegisterSegmentationObjectFactory(); } QmitkSegmentationView::~QmitkSegmentationView() { // delete m_PostProcessing; delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { // ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox->setEnabled( true ); m_Controls->m_OrganToolSelectionBox->setEnabled( true ); m_Controls->m_LesionToolSelectionBox->setEnabled( true ); m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); //TODO Remove Observer itk::ReceptorMemberCommand::Pointer command1 = itk::ReceptorMemberCommand::New(); command1->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command1 ); //Adding observers for node visibility to existing segmentations mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isSegmentation = mitk::NodePredicateAnd::New( isImage, isBinary ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( isSegmentation ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if(segmentations->Size() > 0) { FireNodeSelected(segmentations->ElementAt(0)); segmentations->ElementAt(0)->GetProperty("visible")->Modified(); } } } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { mitk::RenderingManager::GetInstance()->RemoveObserver( m_RenderingManagerObserverTag ); m_Controls->m_ManualToolSelectionBox->setEnabled( false ); //deactivate all tools m_Controls->m_ManualToolSelectionBox->GetToolManager()->ActivateTool(-1); m_Controls->m_OrganToolSelectionBox->setEnabled( false ); m_Controls->m_LesionToolSelectionBox->setEnabled( false ); m_Controls->m_SlicesInterpolator->EnableInterpolation( false ); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); if (m_MultiWidget) { mitk::SlicesCoordinator *coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag2); } // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); service->RemoveAllPlanePositions(); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag1 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag2 ); } } // save the current multiwidget as the working widget m_MultiWidget = multiWidget; //TODO Remove Observers if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } //TODO End Remove Observers if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( *(this->GetDefaultDataStorage())); m_Controls->m_SlicesInterpolator->Initialize( toolManager, m_MultiWidget ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences*) { ForceDisplayPreferencesUponAllImages(); } //TODO remove function void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } //TODO remove function void QmitkSegmentationView::SliceRotation(const itk::EventObject&) { CheckImageAlignment(); } // protected slots void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, dialog->GetSegmentationName().toStdString(), dialog->GetColor() ); //Here we change the reslice interpolation mode for a segmentation, so that contours in rotated slice can be shown correctly - emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); + emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_NEAREST) ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); this->SetToolManagerSelection(node, emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged(/*const itk::Object* caller, const itk::EventObject& e*/) { if (!m_Parent || !m_Parent->isVisible()) return; // The new selection behaviour is: // // When clicking on the checkbox of a segmentation the node will e selected and its reference node either // The previous selected segmentation (if there is one) will be deselected. Additionally a reinit on the // selected segmenation will be performed. // If more than one segmentation is selected the tools will be disabled. if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceDataNew = mitk::DataNode::New(); mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); unsigned int numberOfSelectedSegmentations (0); // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); bool isSegmentation(false); node->GetBoolProperty("binary", isSegmentation); if (node->IsSelected() && isSegmentation) { workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; if (this->GetDefaultDataStorage()->GetSources(node)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); } bool isBinary(false); //Find topmost source or first source which is no binary image while (referenceDataNew && this->GetDefaultDataStorage()->GetSources(referenceDataNew)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(referenceDataNew)->ElementAt(0); referenceDataNew->GetBoolProperty("binary",isBinary); if (!isBinary) break; } if (workingNodeIsVisible && referenceDataNew) { //Since the binary property of a segmentation can be set to false and afterwards you can create a new segmentation out of it //->could lead to a deadloop NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( referenceDataNew ); if ( searchIter != m_WorkingDataObserverTags.end()) { referenceDataNew->GetProperty("visible")->RemoveObserver( (*searchIter).second ); } referenceDataNew->SetVisibility(true); } //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceDataNew) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); continue; } if (workingData.IsNull() || (workingNodeIsVisible && node != referenceDataNew)) { node->SetVisibility((false)); } } if(numberOfSelectedSegmentations == 1) SetToolManagerSelection(referenceDataNew, workingData); mitk::DataStorage::SetOfObjects::Pointer temp = mitk::DataStorage::SetOfObjects::New(); temp->InsertElement(0,workingData); mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(temp); // initialize the views to the bounding geometry /*mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll();*/ } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); if(isSeg && !isHelperObject) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } mitk::DataNode* tempNode = const_cast(node); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); this->SetToolManagerSelection(NULL, NULL); } } void QmitkSegmentationView::CreateSegmentationFromSurface() { mitk::DataNode::Pointer surfaceNode = m_Controls->MaskSurfaces->GetSelectedNode(); mitk::Surface::Pointer surface(0); if(surfaceNode.IsNotNull()) surface = dynamic_cast ( surfaceNode->GetData() ); if(surface.IsNull()) { this->HandleException( "No surface selected.", m_Parent, true); return; } mitk::DataNode::Pointer imageNode = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::Image::Pointer image(0); if (imageNode.IsNotNull()) image = dynamic_cast( imageNode->GetData() ); if(image.IsNull()) { this->HandleException( "No image selected.", m_Parent, true); return; } mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(surface); s2iFilter->SetImage(image); s2iFilter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = imageNode->GetName(); nameOfResultImage.append(surfaceNode->GetName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( s2iFilter->GetOutput() ); this->GetDataStorage()->Add(resultNode, imageNode); } void QmitkSegmentationView::ManualToolSelected(int id) { // disable crosshair movement when a manual drawing tool is active (otherwise too much visual noise) if (m_MultiWidget) { if (id >= 0) { m_MultiWidget->DisableNavigationControllerEventListening(); } else { m_MultiWidget->EnableNavigationControllerEventListening(); } } } void QmitkSegmentationView::ToolboxStackPageChanged(int id) { // interpolation only with manual tools visible m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); if( id == 0 ) { mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); if( workingData.IsNotNull() ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegImage->show(); m_Controls->lblSegmentation->show(); } } else { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } // this is just a workaround, should be removed when all tools support 3D+t if (id==2) // lesions { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>3) { m_Controls->widgetStack->setCurrentIndex(0); QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); } } } } } // protected void QmitkSegmentationView::OnComboBoxSelectionChanged( const mitk::DataNode* node ) { mitk::DataNode* selectedNode = const_cast(node); if( selectedNode != NULL ) { m_Controls->refImageSelector->show(); m_Controls->lblReferenceImageSelectionWarning->hide(); bool isBinary(false); selectedNode->GetBoolProperty("binary", isBinary); if ( isBinary ) { FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); } else if (node != m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) { if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)->SetVisibility(false); if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetVisibility(false); } FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); SetToolManagerSelection(selectedNode, NULL); } } else { m_Controls->refImageSelector->hide(); m_Controls->lblReferenceImageSelectionWarning->show(); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } -void QmitkSegmentationView::On3DInterpolationEnabled (bool state) -{ - mitk::SegTool2D::Pointer manualSegmentationTool; - - unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); - - for(unsigned int i = 0; i < numberOfExistingTools; i++) -{ - manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); - - if (manualSegmentationTool) - { - manualSegmentationTool->Enable3DInterpolation( state ); - } - } -} - void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSurfaceSelectionChanged() { // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { // if the selected node is a contourmarker if ( !nodes.empty() ) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); } } // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); if (!m_Parent || !m_Parent->isVisible()) return; // reaction to BlueBerry selection events // this method will try to figure out if a relevant segmentation and its corresponding original image were selected // a warning is issued if the selection is invalid // appropriate reactions are triggered otherwise mitk::DataNode::Pointer referenceData = FindFirstRegularImage( nodes ); //m_Controls->refImageSelector->GetSelectedNode(); //FindFirstRegularImage( nodes ); mitk::DataNode::Pointer workingData = FindFirstSegmentation( nodes ); if(referenceData.IsNull() && workingData.IsNull()) return; bool invalidSelection( !nodes.empty() && ( nodes.size() > 2 || // maximum 2 selected nodes (nodes.size() == 2 && (workingData.IsNull() || referenceData.IsNull()) ) || // with two nodes, one must be the original image, one the segmentation ( workingData.GetPointer() == referenceData.GetPointer() ) //one node is selected as reference and working image // one item is always ok (might be working or reference or nothing ) ); if (invalidSelection) { // TODO visible warning when two images are selected MITK_ERROR << "WARNING: No image, too many (>2) or two equal images were selected."; workingData = NULL; if( m_Controls->refImageSelector->GetSelectedNode().IsNull() ) referenceData = NULL; } if ( workingData.IsNotNull() && referenceData.IsNull() ) { // find the DataStorage parent of workingData // try to find a "normal image" parent, select this as reference image mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( workingData, isNormalImage ); if (possibleParents->size() > 0) { if (possibleParents->size() > 1) { // TODO visible warning for this rare case MITK_ERROR << "Selected binary image has multiple parents. Using arbitrary first one for segmentation."; } referenceData = (*possibleParents)[0]; } NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( workingData ); if ( searchIter == m_WorkingDataObserverTags.end() ) { //MITK_INFO<<"Creating new observer"; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( workingData, workingData->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); workingData->GetProperty("visible")->Modified(); return; } if(workingData->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(workingData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(workingData); } else { SetToolManagerSelection(NULL, NULL); FireNodeSelected(workingData); } } else { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(referenceData); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { //TODO renderWindow anders bestimmen, siehe CheckAlignment QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstRegularImage( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { //mitk::DataNode::Pointer node = i.value() bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } // make sure this is not a binary image bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper mitk::Image if (isImage && !isSegmentation) return nodes.at(i); } return NULL; } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstSegmentation( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper binary mitk::Image if (isImage && isSegmentation) { return nodes.at(i); } } return NULL; } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { m_Controls->lblReferenceImageSelectionWarning->hide(); } else { m_Controls->lblReferenceImageSelectionWarning->show(); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } //TODO remove statement // check, wheter reference image is aligned like render windows. Otherwise display a visible warning (because 2D tools will probably not work) CheckImageAlignment(); // check segmentation if (referenceData) { if (!workingData) { m_Controls->lblWorkingImageSelectionWarning->show(); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } } else { m_Controls->lblWorkingImageSelectionWarning->hide(); this->FireNodeSelected(const_cast(workingData)); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegmentation->show(); m_Controls->lblSegImage->show(); } } } } //TODO remove function void QmitkSegmentationView::CheckImageAlignment() { bool wrongAlignment(true); mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull() && m_MultiWidget) { wrongAlignment = !( IsRenderWindowAligned(m_MultiWidget->GetRenderWindow1(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow2(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow3(), image ) ); } } m_Controls->lblAlignmentWarning->setVisible(wrongAlignment); } //TODO remove function bool QmitkSegmentationView::IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image) { if (!renderWindow) return false; // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( renderWindow->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; int affectedDimension(-1); int affectedSlice(-1); return mitk::SegTool2D::DetermineAffectedImageSlice( image, displayPlane, affectedDimension, affectedSlice ); } //TODO remove function void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); //mitk::DataStorage::SetOfObjects::ConstPointer allSegmentationChilds = this->GetDefaultDataStorage()->GetDerivations(referenceData, isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility if(!node->IsSelected() || (node->IsSelected() && !node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))))) node->SetVisibility((node == referenceData) || node->IsSelected() ); } } // 2. //if (workingData.IsNotNull() && !workingData->IsSelected()) //{ // workingData->SetVisibility(true); //} mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblAlignmentWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); m_Controls->refImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->refImageSelector->SetPredicate(mitk::NodePredicateDataType::New("Image")); if( m_Controls->refImageSelector->GetSelectedNode().IsNotNull() ) m_Controls->lblReferenceImageSelectionWarning->hide(); else m_Controls->refImageSelector->hide(); mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); assert ( toolManager ); // all part of open source MITK m_Controls->m_ManualToolSelectionBox->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer ); m_Controls->m_ManualToolSelectionBox->SetDisplayedToolGroups("Add Subtract Paint Wipe 'Region Growing' Correction Fill Erase"); m_Controls->m_ManualToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingData ); // available only in the 3M application if ( !m_Controls->m_OrganToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 1, false ); } m_Controls->m_OrganToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_OrganToolSelectionBox->SetToolGUIArea( m_Controls->m_OrganToolGUIContainer ); m_Controls->m_OrganToolSelectionBox->SetDisplayedToolGroups("'Hippocampus left' 'Hippocampus right' 'Lung left' 'Lung right' 'Liver' 'Heart LV' 'Endocard LV' 'Epicard LV' 'Prostate'"); m_Controls->m_OrganToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); // available only in the 3M application if ( !m_Controls->m_LesionToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 2, false ); } m_Controls->m_LesionToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_LesionToolSelectionBox->SetToolGUIArea( m_Controls->m_LesionToolGUIContainer ); m_Controls->m_LesionToolSelectionBox->SetDisplayedToolGroups("'Lymph Node'"); m_Controls->m_LesionToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); connect( m_Controls->m_ManualToolSelectionBox, SIGNAL(ToolSelected(int)), this, SLOT(ManualToolSelected(int)) ); connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); - connect(m_Controls->m_SlicesInterpolator, SIGNAL(Signal3DInterpolationEnabled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); //// create helper class to provide context menus for segmentations in data manager // m_PostProcessing = new QmitkSegmentationPostProcessing(this->GetDefaultDataStorage(), this, m_Parent); } //void QmitkSegmentationView::OnPlaneModeChanged(int i) //{ // //if plane mode changes, disable all tools // if (m_MultiWidget) // { // mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); // // if (toolManager) // { // if (toolManager->GetActiveToolID() >= 0) // { // toolManager->ActivateTool(-1); // } // else // { // m_MultiWidget->EnableNavigationControllerEventListening(); // } // } // } //} // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h index ee3296d2ac..86802542e1 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h @@ -1,168 +1,166 @@ /*=================================================================== 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 QmitkSegmentationView_h #define QmitkSegmentationView_h #include "QmitkFunctionality.h" #include #include "ui_QmitkSegmentationControls.h" class QmitkRenderWindow; // class QmitkSegmentationPostProcessing; /** * \ingroup ToolManagerEtAl * \ingroup org_mitk_gui_qt_segmentation_internal * \warning Implementation of this class is split up into two .cpp files to make things more compact. Check both this file and QmitkSegmentationOrganNamesHandling.cpp */ class QmitkSegmentationView : public QmitkFunctionality { Q_OBJECT public: QmitkSegmentationView(); virtual ~QmitkSegmentationView(); typedef std::map NodeTagMapType; /*! \brief Invoked when the DataManager selection changed */ virtual void OnSelectionChanged(mitk::DataNode* node); virtual void OnSelectionChanged(std::vector nodes); // reaction to new segmentations being created by segmentation tools void NewNodesGenerated(); void NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType*); // QmitkFunctionality's activate/deactivate virtual void Activated(); virtual void Deactivated(); // QmitkFunctionality's changes regarding THE QmitkStdMultiWidget virtual void StdMultiWidgetAvailable(QmitkStdMultiWidget& stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); virtual void StdMultiWidgetClosed(QmitkStdMultiWidget& stdMultiWidget); // BlueBerry's notification about preference changes (e.g. from a dialog) virtual void OnPreferencesChanged(const berry::IBerryPreferences*); // observer to mitk::RenderingManager's RenderingManagerViewsInitializedEvent event void RenderingManagerReinitialized(const itk::EventObject&); // observer to mitk::SliceController's SliceRotation event void SliceRotation(const itk::EventObject&); static const std::string VIEW_ID; protected slots: void OnComboBoxSelectionChanged(const mitk::DataNode* node); // reaction to the button "New segmentation" void CreateNewSegmentation(); // reaction to the button "New segmentation" void CreateSegmentationFromSurface(); // called when a segmentation tool is activated void ManualToolSelected(int id); // called when one of "Manual", "Organ", "Lesion" pages of the QToolbox is selected void ToolboxStackPageChanged(int id); void OnSurfaceSelectionChanged(); //called when the checkbox Remember Contour Positions is selected/deselected void OnWorkingNodeVisibilityChanged(); void OnShowMarkerNodes(bool); - void On3DInterpolationEnabled(bool); - protected: // a type for handling lists of DataNodes typedef std::vector NodeList; // set available multiwidget void SetMultiWidget(QmitkStdMultiWidget* multiWidget); // actively query the current selection of data manager //void PullCurrentDataManagerSelection(); // reactions to selection events from data manager (and potential other senders) //void BlueBerrySelectionChanged(berry::IWorkbenchPart::Pointer sourcepart, berry::ISelection::ConstPointer selection); mitk::DataNode::Pointer FindFirstRegularImage( std::vector nodes ); mitk::DataNode::Pointer FindFirstSegmentation( std::vector nodes ); // propagate BlueBerry selection to ToolManager for manual segmentation void SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData); // checks if selected reference image is aligned with the slices stack orientation of the StdMultiWidget void CheckImageAlignment(); // checks if given render window aligns with the slices of given image bool IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image); // make sure all images/segmentations look as selected by the users in this view's preferences void ForceDisplayPreferencesUponAllImages(); // decorates a DataNode according to the user preference settings void ApplyDisplayOptions(mitk::DataNode* node); // GUI setup void CreateQtPartControl(QWidget* parent); // handling of a list of known (organ name, organ color) combination // ATTENTION these methods are defined in QmitkSegmentationOrganNamesHandling.cpp QStringList GetDefaultOrganColorString(); void UpdateOrganList(QStringList& organColors, const QString& organname, mitk::Color colorname); void AppendToOrganList(QStringList& organColors, const QString& organname, int r, int g, int b); // If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry void OnContourMarkerSelected (const mitk::DataNode* node); void NodeRemoved(const mitk::DataNode* node); // the Qt parent of our GUI (NOT of this object) QWidget* m_Parent; // our GUI Ui::QmitkSegmentationControls * m_Controls; // THE currently existing QmitkStdMultiWidget QmitkStdMultiWidget * m_MultiWidget; // QmitkSegmentationPostProcessing* m_PostProcessing; unsigned long m_RenderingManagerObserverTag; unsigned long m_SlicesRotationObserverTag1; unsigned long m_SlicesRotationObserverTag2; unsigned long m_VisibilityChangedObserverTag; NodeTagMapType m_WorkingDataObserverTags; }; #endif /*QMITKsegmentationVIEW_H_*/ diff --git a/Plugins/org.mitk.gui.qt.ultrasound/CMakeLists.txt b/Plugins/org.mitk.gui.qt.ultrasound/CMakeLists.txt new file mode 100644 index 0000000000..45ce803469 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/CMakeLists.txt @@ -0,0 +1,7 @@ +project(org_mitk_gui_qt_ultrasound) + +MACRO_CREATE_MITK_CTK_PLUGIN( + EXPORT_DIRECTIVE ULTRASOUND_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDENCIES QmitkExt MitkUS MitkUSUI +) diff --git a/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/Manual.dox new file mode 100644 index 0000000000..42934f7778 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/Manual.dox @@ -0,0 +1,19 @@ +/** +\bundlemainpage{org.mitk.gui.qt.ultrasound} Ultrasound + +\image html icon.xpm "Icon of Ultrasound" + +Available sections: + - \ref org.mitk.gui.qt.ultrasoundOverview + +\section org.mitk.gui.qt.ultrasoundOverview +Describe the features of your awesome plugin here +
    +
  • Increases productivity +
  • Creates beautiful images +
  • Generates PhD thesis +
  • Brings world peace +
+ +*/ + diff --git a/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/icon.xpm b/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/icon.xpm new file mode 100644 index 0000000000..9057c20bc6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/documentation/UserManual/icon.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static const char * icon_xpm[] = { +"16 16 2 1", +" c #FF0000", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.ultrasound/documentation/doxygen/modules.dox b/Plugins/org.mitk.gui.qt.ultrasound/documentation/doxygen/modules.dox new file mode 100644 index 0000000000..22a80e24b6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/documentation/doxygen/modules.dox @@ -0,0 +1,16 @@ +/** + \defgroup org_mitk_gui_qt_ultrasound org.mitk.gui.qt.ultrasound + \ingroup MITKPlugins + + \brief Describe your plugin here. + +*/ + +/** + \defgroup org_mitk_gui_qt_ultrasound_internal Internal + \ingroup org_mitk_gui_qt_ultrasound + + \brief This subcategory includes the internal classes of the org.mitk.gui.qt.ultrasound plugin. Other + plugins must not rely on these classes. They contain implementation details and their interface + may change at any time. We mean it. +*/ diff --git a/Plugins/org.mitk.gui.qt.ultrasound/files.cmake b/Plugins/org.mitk.gui.qt.ultrasound/files.cmake new file mode 100644 index 0000000000..d39df0669c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/files.cmake @@ -0,0 +1,43 @@ +set(SRC_CPP_FILES + +) + +set(INTERNAL_CPP_FILES + org_mitk_gui_qt_ultrasound_Activator.cpp + UltrasoundSupport.cpp +) + +set(UI_FILES + src/internal/UltrasoundSupportControls.ui +) + +set(MOC_H_FILES + src/internal/org_mitk_gui_qt_ultrasound_Activator.h + src/internal/UltrasoundSupport.h +) + +# list of resource files which can be used by the plug-in +# system without loading the plug-ins shared library, +# for example the icon used in the menu and tabs for the +# plug-in views in the workbench +set(CACHED_RESOURCE_FILES + resources/icon.xpm + plugin.xml +) + +# list of Qt .qrc files which contain additional resources +# specific to this plugin +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.ultrasound/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.ultrasound/manifest_headers.cmake new file mode 100644 index 0000000000..182113f62b --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "Ultrasound") +set(Plugin-Version "0.1") +set(Plugin-Vendor "DKFZ, Medical and Biological Informatics") +set(Plugin-ContactAddress "") +set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.ultrasound/plugin.xml b/Plugins/org.mitk.gui.qt.ultrasound/plugin.xml new file mode 100644 index 0000000000..12e452b3d5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.ultrasound/resources/icon.xpm b/Plugins/org.mitk.gui.qt.ultrasound/resources/icon.xpm new file mode 100644 index 0000000000..54a1bcb8b9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/resources/icon.xpm @@ -0,0 +1,105 @@ +/* XPM */ +static char * C:\Builds\MITK\Plugins\org_mitk_gui_qt_ultrasound\resources\icon_xpm[] = { +"100 100 2 1", +" c None", +". c #000000", +" .......................................................................................... ", +" .......................................................................................... ", +" .......................................................................................... ", +" ... .. ", +" ... .. ", +" ... .. ", +" ... .. ", +" ... .. ", +" ... .. .. .. ", +" ... .... ..... .. ", +" ... ................... .. ", +" ... ..................... .. ", +" ... ....................... .. ", +" ... ......................... .. ", +" ... ........................... .. ", +" ... ............................. .. ", +" ... ............................... .. ", +" ... ............ .................. .. ", +" ... ............ ........ .... .. ", +" ... ............. ....... .... .. ", +" ... .............. ...... .... .. ", +" ... ............... ..... .... .. ", +" ... ................ ..... .... .. ", +" ... ................ ..... ..... .. ", +" ... ................. .... ...... .. ", +" ... .................. .... ....... .. ", +" ... ................... .... ........ .. ", +" ... .......... ...... ... .......... .. ", +" ... .. ...... ... ........... .. ", +" ... .. ...... .. ............. .. ", +" ... ... ...... ... ................ .. ", +" ... .... ....... ......................... .. ", +" ... ..... ...... .......................... .. ", +" ... ....... ..... .......................... .. ", +" ... ................. ... .......................... .. ", +" ... .................. .. ........................... .. ", +" ... .................. .......................... .. ", +" ... ................. ......................... .. ", +" ... ................ ......................... .. ", +" ... ............... ........................ .. ", +" ... ............. ......................... .. ", +" ... ............ ........................ .. ", +" ... ........... ......................... .. ", +" ... .......... ........................ .. ", +" ... ........ ......................... .. ", +" ... ....... ....................... .. ", +" ... ....... ....................... .. ", +" ... ...... ....................... .. ", +" ... ........................... .. .. ", +" ... ................... ... .. ", +" ... ..... .. ", +" ... ...... .. ", +" ... ....... .. ", +" ... ....... .. ", +" ... ........ .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... .......... .. ", +" ... ......... .. ", +" ... ......... .. ", +" ... .......... .. ", +" ... .......... ... ", +" .......................................................................................... ", +" .......................................................................................... ", +" ................................. ", +" ................................. ", +" .................................. ", +" ................................. ", +" .................................. ", +" .................................. ", +" ................................. ", +" ................................. ", +" ................................ ", +" ................................ ", +" ................................ ", +" ............................... ", +" .............................. ", +" .............................. ", +" ............................ ", +" ............................ ", +" ........................... ", +" ........................... ", +" .......................... ", +" .......................... ", +" ......................... ", +" ....................... ", +" ..................... ", +" ..................... ", +" .................... ", +" ..................... ", +" ..................... ", +" .................... ", +" ..................... ", +" ..................... "}; diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp new file mode 100644 index 0000000000..8afd157b3b --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.cpp @@ -0,0 +1,108 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +// Blueberry +#include +#include + +//Mitk +#include "mitkDataNode.h" + + +// Qmitk +#include "UltrasoundSupport.h" +#include + +// Qt +#include + +// Ultrasound +#include "mitkUSDevice.h" + +const std::string UltrasoundSupport::VIEW_ID = "org.mitk.views.ultrasoundsupport"; + +void UltrasoundSupport::SetFocus() +{ + m_Controls.m_AddDevice->setFocus(); +} + +void UltrasoundSupport::CreateQtPartControl( QWidget *parent ) +{ + m_Timer = new QTimer(this); + + // create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi( parent ); + connect( m_Controls.m_AddDevice, SIGNAL(clicked()), this, SLOT(OnClickedAddNewDevice()) ); // Change Widget Visibilities + connect( m_Controls.m_AddDevice, SIGNAL(clicked()), this->m_Controls.m_NewVideoDeviceWidget, SLOT(CreateNewDevice()) ); // Init NewDeviceWidget + connect( m_Controls.m_NewVideoDeviceWidget, SIGNAL(Finished()), this, SLOT(OnNewDeviceWidgetDone()) ); // After NewDeviceWidget finished editing + connect( m_Controls.m_BtnView, SIGNAL(clicked()), this, SLOT(OnClickedViewDevice()) ); + connect( m_Timer, SIGNAL(timeout()), this, SLOT(DisplayImage())); + //connect (m_Controls.m_ActiveVideoDevices, SIGNAL()) + + // Initializations + m_Controls.m_NewVideoDeviceWidget->setVisible(false); + std::string filter = "(&(" + mitk::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.UltrasoundDevice)(IsActive=true))"; + m_Controls.m_ActiveVideoDevices->Initialize(filter); + + m_Node = mitk::DataNode::New(); + m_Node->SetName("US Image Stream"); + this->GetDataStorage()->Add(m_Node); +} + +void UltrasoundSupport::OnClickedAddNewDevice() +{ + MITK_INFO << "USSUPPORT: OnClickedAddNewDevice()"; + m_Controls.m_NewVideoDeviceWidget->setVisible(true); + m_Controls.m_DeviceManagerWidget->setVisible(false); + m_Controls.m_AddDevice->setVisible(false); + m_Controls.m_Headline->setText("Add New Device:"); +} + +void UltrasoundSupport::DisplayImage() +{ + MITK_INFO << "USSUPPORT: DisplayImage()"; + //QList nodes = this->GetDataManagerSelection(); + // if (nodes.empty()) return; + + m_Device->UpdateOutputData(0); + mitk::USImage::Pointer image = m_Device->GetOutput(); + m_Node->SetData(image); + this->RequestRenderWindowUpdate(); +} + +void UltrasoundSupport::OnClickedViewDevice() +{ + + MITK_INFO << "USSUPPORT: OnClickedViewDevice()"; + m_Device = m_Controls.m_ActiveVideoDevices->GetSelectedDevice(); + if (m_Device.IsNull()){ + m_Timer->stop(); + return; + } + + m_Timer->start(50); +} + +void UltrasoundSupport::OnNewDeviceWidgetDone() +{ + MITK_INFO << "USSUPPORT: OnNewDeviceWidgetDone()"; + m_Controls.m_NewVideoDeviceWidget->setVisible(false); + m_Controls.m_DeviceManagerWidget->setVisible(true); + m_Controls.m_AddDevice->setVisible(true); + m_Controls.m_Headline->setText("Connected Devices:"); +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.h b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.h new file mode 100644 index 0000000000..2ffb46bc06 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupport.h @@ -0,0 +1,91 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +#ifndef UltrasoundSupport_h +#define UltrasoundSupport_h + +#include + +#include + +#include "ui_UltrasoundSupportControls.h" + + +/*! + \brief UltrasoundSupport + This plugin provides functionality to manage Ultrasound devices, create video devices and to view device images. + + \sa QmitkFunctionality + \ingroup ${plugin_target}_internal +*/ +class UltrasoundSupport : 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(); + + static const std::string VIEW_ID; + + virtual void CreateQtPartControl(QWidget *parent); + + signals: + + public slots: + /* + * \brief This is called when the newDeviceWidget is closed + */ + void OnNewDeviceWidgetDone(); + + protected slots: + + void OnClickedAddNewDevice(); + + void OnClickedViewDevice(); + + /* + * \brief This is the main imaging loop that is called regularily during the imaging process + */ + void DisplayImage(); + + protected: + + /* + * \brief This timer triggers periodic updates to the pipeline + */ + QTimer *m_Timer; + + /* + * \brief The device that is currently used to quire images + */ + mitk::USDevice::Pointer m_Device; + + /* + * \brief The node that we feed images into + */ + mitk::DataNode::Pointer m_Node; + + Ui::UltrasoundSupportControls m_Controls; + +}; + +#endif // UltrasoundSupport_h + diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupportControls.ui b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupportControls.ui new file mode 100644 index 0000000000..7a96cde657 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/UltrasoundSupportControls.ui @@ -0,0 +1,143 @@ + + + UltrasoundSupportControls + + + + 0 + 0 + 467 + 461 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + 1 + + + + Device Management + + + + + + + 12 + 75 + true + + + + Connected Devices: + + + + + + + + + + New Device + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + US Imaging + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + 12 + 75 + true + + + + Active Video Devices: + + + + + + + + 0 + 0 + + + + + + + + View + + + + + + + + + + + + + QmitkUSDeviceManagerWidget + QWidget +
QmitkUSDeviceManagerWidget.h
+ 1 +
+ + QmitkUSNewVideoDeviceWidget + QWidget +
QmitkUSNewVideoDeviceWidget.h
+ 1 +
+ + QmitkUSDeviceListWidget + QWidget +
QmitkUSDeviceListWidget.h
+ 1 +
+
+ + +
diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.cpp b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.cpp new file mode 100644 index 0000000000..b77473cc4d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.cpp @@ -0,0 +1,39 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +#include "org_mitk_gui_qt_ultrasound_Activator.h" + +#include + +#include "UltrasoundSupport.h" + +namespace mitk { + +void org_mitk_gui_qt_ultrasound_Activator::start(ctkPluginContext* context) +{ + BERRY_REGISTER_EXTENSION_CLASS(UltrasoundSupport, context) +} + +void org_mitk_gui_qt_ultrasound_Activator::stop(ctkPluginContext* context) +{ + Q_UNUSED(context) +} + +} + +Q_EXPORT_PLUGIN2(org_mitk_gui_qt_ultrasound, mitk::org_mitk_gui_qt_ultrasound_Activator) diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.h b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.h new file mode 100644 index 0000000000..2bd3b8741e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/org_mitk_gui_qt_ultrasound_Activator.h @@ -0,0 +1,41 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + + +#ifndef org_mitk_gui_qt_ultrasound_Activator_h +#define org_mitk_gui_qt_ultrasound_Activator_h + +#include + +namespace mitk { + +class org_mitk_gui_qt_ultrasound_Activator : + public QObject, public ctkPluginActivator +{ + Q_OBJECT + Q_INTERFACES(ctkPluginActivator) + +public: + + void start(ctkPluginContext* context); + void stop(ctkPluginContext* context); + +}; // org_mitk_gui_qt_ultrasound_Activator + +} + +#endif // org_mitk_gui_qt_ultrasound_Activator_h