From: David Sarrut Date: Wed, 17 Oct 2012 10:55:39 +0000 (+0200) Subject: Merge branch 'master' of git.creatis.insa-lyon.fr:clitk X-Git-Tag: v1.4.0~289 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=f1cebc71809c5218578a58486f829980eeb29ab3;hp=921642d767beba2442dacc8fdb40dc36396e1b7d;p=clitk.git Merge branch 'master' of git.creatis.insa-lyon.fr:clitk Conflicts: vv/vv.cxx vv/vvSlicerManager.cxx vv/vvSlicerManagerCommand.cxx --- diff --git a/common/clitkImageUtilities.h b/common/clitkImageUtilities.h deleted file mode 100644 index ab12d21..0000000 --- a/common/clitkImageUtilities.h +++ /dev/null @@ -1,52 +0,0 @@ -/*========================================================================= - Program: vv http://www.creatis.insa-lyon.fr/rio/vv - - Authors belong to: - - University of LYON http://www.universite-lyon.fr/ - - Léon Bérard cancer center http://www.centreleonberard.fr - - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the copyright notices for more information. - - It is distributed under dual licence - - - BSD See included LICENSE.txt file - - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html -===========================================================================**/ -#ifndef CLITKIMAGEUTILITIES_H -#define CLITKIMAGEUTILITIES_H -/** - =================================================================== - * @file clitkImageUtilities.h - * @author David Sarrut - * @date 22 Sep 2006 10:38:36 - - * @brief - - ===================================================================*/ - -// clitk -#include "clitkCommon.h" -#include "clitkImageCommon.h" - -// std -#include -#include -#include - -// itk -#include "itkImageRegionConstIterator.h" - -namespace clitk { - - template - int ComputeHowManyDifferentIntensity(const typename ImageType::Pointer & image, - std::vector & listOfIntensities); - #include "clitkImageUtilities.txx" - -} // end namespace - -#endif /* end #define CLITKIMAGEUTILITIES_H */ - diff --git a/common/clitkImageUtilities.txx b/common/clitkImageUtilities.txx deleted file mode 100644 index 8f42551..0000000 --- a/common/clitkImageUtilities.txx +++ /dev/null @@ -1,122 +0,0 @@ -/*========================================================================= - Program: vv http://www.creatis.insa-lyon.fr/rio/vv - - Authors belong to: - - University of LYON http://www.universite-lyon.fr/ - - Léon Bérard cancer center http://www.centreleonberard.fr - - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the copyright notices for more information. - - It is distributed under dual licence - - - BSD See included LICENSE.txt file - - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html -===========================================================================**/ -#ifndef CLITKIMAGEUTILITIES_TXX -#define CLITKIMAGEUTILITIES_TXX -/** - ================================================= - * @file clitkImageUtilities.txx - * @author David Sarrut - * @date 22 Sep 2006 10:39:48 - * - * @brief - * - * - =================================================*/ - -//==================================================================== -// Compute the number of different intensities in an image -template -int ComputeHowManyDifferentIntensity(const typename ImageType::Pointer & image, - std::vector & l) -{ - //std::set listOfIntensities; - std::map listOfIntensities; - // listOfIntensities.resize(0); - typedef itk::ImageRegionConstIterator ConstIteratorType; - ConstIteratorType pi(image, image->GetLargestPossibleRegion()); - pi.Begin(); - while (!pi.IsAtEnd()) { - if (!listOfIntensities[pi.Get()]) listOfIntensities[pi.Get()] = true; - // if (std::find(listOfIntensities.begin(), -// listOfIntensities.end(), -// pi.Get()) == listOfIntensities.end()) { -// listOfIntensities.insert(pi.Get()); -// } - ++pi; - } - - //typename std::set::const_iterator ppi = listOfIntensities.begin(); - typename std::map::const_iterator ppi = listOfIntensities.begin(); - while (ppi != listOfIntensities.end()) { - l.push_back(ppi->first); - ++ppi; - } - - return listOfIntensities.size(); -} -//==================================================================== - -//==================================================================== -template -void ComputeWeightsOfEachClasses(const typename InputImageType::Pointer & input, - const typename MaskImageType::Pointer & mask, - const std::vector & listOfIntensities, - std::map > & mapOfLabelsAndWeights) -{ - // Check size - if (input->GetLargestPossibleRegion() != mask->GetLargestPossibleRegion()) { - itkGenericExceptionMacro(<< "Input and mask images have not the same size" - << std::endl - << "Input = " << input->GetLargestPossibleRegion() - << std::endl - << "Mask = " << mask->GetLargestPossibleRegion()); - } - - // reset weights list - mapOfLabelsAndWeights.clear(); - - // loop - typedef itk::ImageRegionConstIterator ConstInputIteratorType; - ConstInputIteratorType pi(input, input->GetLargestPossibleRegion()); - typedef itk::ImageRegionConstIterator ConstMaskIteratorType; - ConstMaskIteratorType pm(mask, mask->GetLargestPossibleRegion()); - pi.Begin(); - pm.Begin(); - while (!pi.IsAtEnd()) { - mapOfLabelsAndWeights[pm.Get()][pi.Get()]++; - ++pi; - ++pm; - } -} -//==================================================================== - -// //==================================================================== -// template -// typename ImageType::Pointer NewImage3D(int x, int y, int z, float dx, float dy, float dz) { -// typename ImageType::Pointer output = ImageType::New(); -// typename ImageType::RegionType region; -// typename ImageType::SizeType size; -// size[0] = x; -// size[1] = y; -// size[2] = z; -// region.SetSize(size); -// output->SetRegions(region); -// output->Allocate(); -// typename ImageType::SpacingType spacing; -// spacing[0] = dx; -// spacing[1] = dy; -// spacing[2] = dz; -// output->SetSpacing(spacing); -// return output; -// } -// //==================================================================== - - -#endif /* end #define CLITKIMAGEUTILITIES_TXX */ - diff --git a/itk/clitkCropLikeImageFilter.txx b/itk/clitkCropLikeImageFilter.txx index 519c5fd..6399ec6 100644 --- a/itk/clitkCropLikeImageFilter.txx +++ b/itk/clitkCropLikeImageFilter.txx @@ -269,7 +269,10 @@ clitk::ResizeImageLike(const ImageType * input, { typename ImageType::Pointer output = ImageType::New(); output->CopyInformation(input); - output->SetRegions(region); + typename ImageType::RegionType reg; + reg.SetIndex(region->GetIndex()); + reg.SetSize(region->GetSize()); + output->SetRegions(reg); output->Allocate(); return clitk::ResizeImageLike(input, output, backgroundValue); } diff --git a/itk/clitkLabelImageOverlapMeasureFilter.h b/itk/clitkLabelImageOverlapMeasureFilter.h index 17581f6..17b7c21 100644 --- a/itk/clitkLabelImageOverlapMeasureFilter.h +++ b/itk/clitkLabelImageOverlapMeasureFilter.h @@ -29,6 +29,8 @@ #include #include +#include + namespace clitk { //-------------------------------------------------------------------- diff --git a/itk/clitkLabelImageOverlapMeasureFilter.txx b/itk/clitkLabelImageOverlapMeasureFilter.txx index 57516bb..a29aa6a 100644 --- a/itk/clitkLabelImageOverlapMeasureFilter.txx +++ b/itk/clitkLabelImageOverlapMeasureFilter.txx @@ -121,8 +121,13 @@ GenerateData() statFilter->Update(); int in2 = statFilter->GetCount(GetLabel1()); - std::cout << in1 << " " << in2 << " " << inter << " " << u << " " - << 2.0*(double)inter/(double)(in1+in2) << std::endl; + double dice = 2.0*(double)inter/(double)(in1+in2); + int width = 6; + std::cout << std::setw(width) << in1 << " " + << std::setw(width) << in2 << " " + << std::setw(width) << inter << " " + << std::setw(width) << u << " " + << std::setw(width) << dice << " "; //std::endl; } //-------------------------------------------------------------------- diff --git a/itk/clitkSegmentationUtils.txx b/itk/clitkSegmentationUtils.txx index b7a5de5..a57d813 100644 --- a/itk/clitkSegmentationUtils.txx +++ b/itk/clitkSegmentationUtils.txx @@ -1321,11 +1321,11 @@ namespace clitk { // Compute dmap for S1 *TO PUT IN FONCTION* dmap = clitk::DistanceMap(slices_s1[i], BG); dmaps1.push_back(dmap); - writeImage(dmap, "dmap1.mha"); + //writeImage(dmap, "dmap1.mha"); // Compute dmap for S2 dmap = clitk::DistanceMap(slices_s2[i], BG); dmaps2.push_back(dmap); - writeImage(dmap, "dmap2.mha"); + //writeImage(dmap, "dmap2.mha"); // Look in S2 for the point the closest to S1 typename SliceType::PointType p = ComputeClosestPoint(slices_s1[i], dmaps2[i], BG); diff --git a/itk/clitkSliceBySliceRelativePositionFilter.txx b/itk/clitkSliceBySliceRelativePositionFilter.txx index db3c677..e68514d 100644 --- a/itk/clitkSliceBySliceRelativePositionFilter.txx +++ b/itk/clitkSliceBySliceRelativePositionFilter.txx @@ -78,7 +78,7 @@ clitk::SliceBySliceRelativePositionFilter:: PrintOptions(std::ostream & os) { os << "Slice direction = " << this->GetDirection() << std::endl - << "BG value = " << this->GetBackgroundValue() << std::endl; + << "BG value = " << (int)this->GetBackgroundValue() << std::endl; for(int i=0; iGetNumberOfAngles(); i++) { os << "Orientation = " << this->GetOrientationTypeString()[i] << std::endl; os << "Angles = " << clitk::rad2deg(this->GetAngle1InRad(i)) diff --git a/segmentation/CMakeLists.txt b/segmentation/CMakeLists.txt index e5ec8d4..f26ec74 100644 --- a/segmentation/CMakeLists.txt +++ b/segmentation/CMakeLists.txt @@ -1,7 +1,7 @@ #========================================================= # Add Libraries used in vv and clitk to avoid recompilation -FOREACH(clitkTool clitkExtractLung clitkExtractPatient) +FOREACH(clitkTool clitkExtractLung clitkExtractPatient clitkConnectedComponentLabeling clitkRegionGrowing) WRAP_GGO(${clitkTool}_GGO_C ${clitkTool}.ggo) SET(GGO_C_FILES ${GGO_C_FILES} ${${clitkTool}_GGO_C}) ENDFOREACH(clitkTool) @@ -46,6 +46,10 @@ IF(CLITK_BUILD_SEGMENTATION) TARGET_LINK_LIBRARIES(clitkMotionMask clitkCommon ${ITK_LIBRARIES}) SET(SEGMENTATION_INSTALL ${SEGMENTATION_INSTALL} clitkMotionMask) + WRAP_GGO(clitkRegionGrowing_GGO_C clitkRegionGrowing.ggo) + ADD_EXECUTABLE(clitkRegionGrowing clitkRegionGrowing.cxx ${clitkRegionGrowing_GGO_C} ${clitkRelativePosition_GGO_C}) + TARGET_LINK_LIBRARIES(clitkRegionGrowing clitkCommon ${ITK_LIBRARIES}) + SET_TARGET_PROPERTIES(${SEGMENTATION_INSTALL} PROPERTIES INSTALL_RPATH "${VTK_DIR}:${ITK_DIR}" ) INSTALL (TARGETS ${SEGMENTATION_INSTALL} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) diff --git a/segmentation/clitkExtractLungFilter.txx b/segmentation/clitkExtractLungFilter.txx index ffca1df..f84a095 100644 --- a/segmentation/clitkExtractLungFilter.txx +++ b/segmentation/clitkExtractLungFilter.txx @@ -47,6 +47,8 @@ #include "itkOrientImageFilter.h" #include "itkSpatialOrientationAdapter.h" #include "itkImageDuplicator.h" +#include "itkRelabelComponentImageFilter.h" + #include //-------------------------------------------------------------------- @@ -67,6 +69,7 @@ ExtractLungFilter(): SetForegroundValue(1); SetMinimalComponentSize(100); VerboseRegionGrowingFlagOff(); + RemoveSmallLabelBeforeSeparationFlagOn(); // Step 1 default values SetUpperThreshold(-300); @@ -436,7 +439,7 @@ GenerateOutputInformation() //-------------------------------------------------------------------- //-------------------------------------------------------------------- StartNewStep("Separate Left/Right lungs"); - PrintMemory(GetVerboseMemoryFlag(), "Before Separate"); + PrintMemory(GetVerboseMemoryFlag(), "Before Separate"); // Initial label working_mask = Labelize(working_mask, GetBackgroundValue(), @@ -451,9 +454,33 @@ GenerateOutputInformation() statisticsImageFilter->SetInput(working_mask); statisticsImageFilter->Update(); unsigned int initialNumberOfLabels = statisticsImageFilter->GetMaximum(); - working_mask = statisticsImageFilter->GetOutput(); - + working_mask = statisticsImageFilter->GetOutput(); PrintMemory(GetVerboseMemoryFlag(), "After count label"); + + // If already 2 labels, but a too big differences, remove the + // smalest one (sometimes appends with the stomach + if (initialNumberOfLabels >1) { + if (GetRemoveSmallLabelBeforeSeparationFlag()) { + DD(GetRemoveSmallLabelBeforeSeparationFlag()); + typedef itk::RelabelComponentImageFilter RelabelFilterType; + typename RelabelFilterType::Pointer relabelFilter = RelabelFilterType::New(); + relabelFilter->SetInput(working_mask); + relabelFilter->SetMinimumObjectSize(10); + relabelFilter->Update(); + const std::vector & a = relabelFilter->GetSizeOfObjectsInPhysicalUnits(); + std::vector remove_label; + for(unsigned int i=1; i(working_mask, GetBackgroundValue(), remove_label); + statisticsImageFilter->SetInput(working_mask); + statisticsImageFilter->Update(); + initialNumberOfLabels = statisticsImageFilter->GetMaximum(); + } + } // Decompose the first label if (initialNumberOfLabels<2) { diff --git a/common/clitkImageUtilities.cxx b/segmentation/clitkRegionGrowing.cxx similarity index 55% rename from common/clitkImageUtilities.cxx rename to segmentation/clitkRegionGrowing.cxx index eb3d1bc..d24d2f7 100644 --- a/common/clitkImageUtilities.cxx +++ b/segmentation/clitkRegionGrowing.cxx @@ -1,7 +1,7 @@ /*========================================================================= Program: vv http://www.creatis.insa-lyon.fr/rio/vv - Authors belong to: + Authors belong to: - University of LYON http://www.universite-lyon.fr/ - Léon Bérard cancer center http://www.centreleonberard.fr - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr @@ -15,20 +15,27 @@ - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ===========================================================================**/ -#ifndef CLITKIMAGEUTILITIES_CXX -#define CLITKIMAGEUTILITIES_CXX -/** - ================================================= - * @file clitkImageUtilities.cxx - * @author David Sarrut - * @date 22 Sep 2006 10:39:40 - * - * @brief - * - * - =================================================*/ - -#include "clitkImageUtilities.h" - -#endif /* end #define CLITKIMAGEUTILITIES_CXX */ +// clitk +#include "clitkRegionGrowing_ggo.h" +#include "clitkIO.h" +#include "clitkRegionGrowingGenericFilter.h" + +//-------------------------------------------------------------------- +int main(int argc, char * argv[]) { + + // Init command line + GGO(clitkRegionGrowing, args_info); + CLITK_INIT; + + // Filter + typedef clitk::RegionGrowingGenericFilter FilterType; + FilterType::Pointer genericFilter = FilterType::New(); + + genericFilter->SetArgsInfo(args_info); + genericFilter->Update(); + + return EXIT_SUCCESS; +}// end main + +//-------------------------------------------------------------------- diff --git a/segmentation/clitkRegionGrowing.ggo b/segmentation/clitkRegionGrowing.ggo new file mode 100644 index 0000000..cd2d03e --- /dev/null +++ b/segmentation/clitkRegionGrowing.ggo @@ -0,0 +1,34 @@ +#File clitkRegionGrowing.ggo +package "clitkRegionGrowing" +version "1.0" +purpose "Region growing from a seed point using various types of conditions to control the growing" + +option "config" - "Config file" string no +option "verbose" v "Verbose" flag off + +section "I/O" + +option "input" i "Input image filename" string yes +option "output" o "Output image filename" string yes + + +section "Parameters" + +option "type" t "Region growing filter type: 0=threshold , 1=neighborhood-threshold , 2=confidence , 3= locally-adaptive-threshold, 4= explosion-controlled-threshold" int no default="0" +option "lower" l "1,2,3,4: Lower threshold value" double no default="310" +option "upper" u "1,2,3,4: Upper threshold value" double no default="4000" +option "maxUpper" - "4: Maximum upper threshold value" double no default="2000" +option "minLower" - "4: Minimum lower threshold value" double no default="-1000" +option "step" - "4: Threshold step size" double no default="64.0" +option "minStep" - "4: Minimum threshold step size" double no default="1" +option "adaptLower" - "3,4: (locally) adapt lower thresholding" flag off +option "adaptUpper" - "3,4: (locally) adapt upper thresholding" flag off +option "multiplier" m "2-4: (2-3) accept if within mean+-mutiplier*SD, (4) explosion if size increases multiplier times" double no default="2.0" +option "seed" s "Seed index postion (in voxels)" int multiple no default="0" +option "seedRadius" - "Radius used for seed dilatation(in voxel)" int multiple no default="0" +option "pad" p "The replace padding value" double no default="1.0" +option "radius" r "1-3: The radius of the neighborhood" int no multiple default="1" +option "maxSD" - "3: Limit to SD" double no +option "full" - "4: use full connectivity (not implemented yet)" flag off +option "iter" - "2: Iterations" int no default="5" + diff --git a/segmentation/clitkRegionGrowingGenericFilter.cxx b/segmentation/clitkRegionGrowingGenericFilter.cxx new file mode 100644 index 0000000..f5092ba --- /dev/null +++ b/segmentation/clitkRegionGrowingGenericFilter.cxx @@ -0,0 +1,72 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://www.centreleonberard.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +===========================================================================**/ +#ifndef clitkRegionGrowingGenericFilter_cxx +#define clitkRegionGrowingGenericFilter_cxx + +/* ================================================= + * @file clitkRegionGrowingGenericFilter.cxx + * @author + * @date + * + * @brief + * + ===================================================*/ + +#include "clitkRegionGrowingGenericFilter.h" + + +namespace clitk +{ + + + //----------------------------------------------------------- + // Constructor + //----------------------------------------------------------- + RegionGrowingGenericFilter::RegionGrowingGenericFilter() + { + m_Verbose=false; + m_InputFileName=""; + } + + + //----------------------------------------------------------- + // Update + //----------------------------------------------------------- + void RegionGrowingGenericFilter::Update() + { + // Read the Dimension and PixelType + int Dimension; + std::string PixelType; + ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType); + + + // Call UpdateWithDim + if(Dimension==2) UpdateWithDim<2>(PixelType); + else if(Dimension==3) UpdateWithDim<3>(PixelType); + // else if (Dimension==4)UpdateWithDim<4>(PixelType); + else + { + std::cout<<"Error, Only for 2 or 3 Dimensions!!!"< + class ITK_EXPORT RegionGrowingGenericFilter: + public ImageToImageGenericFilter > + { + + public: + //---------------------------------------- + RegionGrowingGenericFilter(); + + //---------------------------------------- + typedef RegionGrowingGenericFilter Self; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + //---------------------------------------- + itkNewMacro(Self); + itkTypeMacro( RegionGrowingGenericFilter, LightObject ); + + //-------------------------------------------------------------------- + void SetArgsInfo(const ArgsInfoType & a); + + //-------------------------------------------------------------------- + // Main function called each time the filter is updated + template + void UpdateWithInputImageType(); + + protected: + void Modified() {} // Need for using itkMacros + template void InitializeImageType(); + ArgsInfoType mArgsInfo; + + }; // end class +} // end namespace clitk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkRegionGrowingGenericFilter.txx" +#endif + +#endif // #define CLITKREGIONGROWINGGENERICFILTER_H diff --git a/segmentation/clitkRegionGrowingGenericFilter.txx b/segmentation/clitkRegionGrowingGenericFilter.txx new file mode 100644 index 0000000..e8f3653 --- /dev/null +++ b/segmentation/clitkRegionGrowingGenericFilter.txx @@ -0,0 +1,300 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://www.centreleonberard.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html + ===========================================================================**/ + +#ifndef CLITKREGIONGROWINGGENERICFILTER_TXX +#define CLITKREGIONGROWINGGENERICFILTER_TXX + +#include +#include + + +//-------------------------------------------------------------------- +template +clitk::RegionGrowingGenericFilter::RegionGrowingGenericFilter(): + ImageToImageGenericFilter("RegionGrowing") +{ + InitializeImageType<2>(); + InitializeImageType<3>(); + //InitializeImageType<4>(); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +template +void clitk::RegionGrowingGenericFilter::InitializeImageType() +{ + ADD_IMAGE_TYPE(Dim, uchar); + ADD_IMAGE_TYPE(Dim, short); + // ADD_IMAGE_TYPE(Dim, int); + ADD_IMAGE_TYPE(Dim, float); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void clitk::RegionGrowingGenericFilter::SetArgsInfo(const ArgsInfoType & a) +{ + mArgsInfo=a; + this->SetIOVerbose(mArgsInfo.verbose_flag); + if (mArgsInfo.input_given) this->AddInputFilename(mArgsInfo.input_arg); + if (mArgsInfo.output_given) this->SetOutputFilename(mArgsInfo.output_arg); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +// Update with the number of dimensions and the pixeltype +//-------------------------------------------------------------------- +template +template +void clitk::RegionGrowingGenericFilter::UpdateWithInputImageType() +{ + DD("UpdateWithInputImageType"); + const int Dimension = ImageType::ImageDimension; + + // ImageTypes + typedef ImageType InputImageType; + typedef ImageType OutputImageType; + typedef typename ImageType::PixelType PixelType; + + // Reading input + typename ImageType::Pointer input = this->template GetInput(0); + + // Seed + typedef typename std::vector SeedsType; + SeedsType seeds(1); + if (mArgsInfo.seed_given==Dimension) + for (unsigned int i=0; i BallType; + typename BallType::RadiusType r; + + if (mArgsInfo.seedRadius_given == Dimension) + for (unsigned i = 0; i < Dimension; i++) + r[i] = mArgsInfo.seedRadius_arg[i]; + else + r.Fill(mArgsInfo.seedRadius_arg[0]); + + BallType ball; + ball.SetRadius(r); + ball.CreateStructuringElement(); + + typedef itk::ConstShapedNeighborhoodIterator IteratorType; + IteratorType it(ball.GetRadius(), + input, + input->GetLargestPossibleRegion()); +#if ITK_VERSION_MAJOR < 4 + typename BallType::ConstIterator nit; + unsigned idx = 0; + for (nit = ball.Begin(); nit != ball.End(); ++nit, ++idx) + { + if (*nit) + { + it.ActivateOffset(it.GetOffset(idx)); + } + else + { + it.DeactivateOffset(it.GetOffset(idx)); + } + } +#else + it.CreateActiveListFromNeighborhood(ball); + it.NeedToUseBoundaryConditionOff(); +#endif + + it.SetLocation(seeds[0]); + for (typename IteratorType::ConstIterator i = it.Begin(); !i.IsAtEnd(); ++i) + { + typename InputImageType::IndexType id = seeds[0] + i.GetNeighborhoodOffset(); + if (id != seeds[0] && input->GetLargestPossibleRegion().IsInside(id)) + seeds.push_back(id); + } + } + + // Filter + typedef itk::ImageToImageFilter ImageToImageFilterType; + typename ImageToImageFilterType::Pointer filter; + + switch (mArgsInfo.type_arg) + { + case 0: { + + typedef itk::ConnectedThresholdImageFilter ImageFilterType; + typename ImageFilterType::Pointer f= ImageFilterType::New(); + + f->SetLower(mArgsInfo.lower_arg); + f->SetUpper(mArgsInfo.upper_arg); + f->SetReplaceValue(static_cast(mArgsInfo.pad_arg)); + for (typename SeedsType::const_iterator it = seeds.begin(); it != seeds.end(); ++it) + f->AddSeed(*it); + filter=f; + if (mArgsInfo.verbose_flag) std::cout<<"Using the connected threshold image filter..."< ImageFilterType; + typename ImageFilterType::Pointer f= ImageFilterType::New(); + + // Radius + typename InputImageType::SizeType size; + if (mArgsInfo.radius_given==Dimension) + for (unsigned int i=0; iSetLower(mArgsInfo.lower_arg); + f->SetUpper(mArgsInfo.upper_arg); + f->SetReplaceValue(static_cast(mArgsInfo.pad_arg)); + for (typename SeedsType::const_iterator it = seeds.begin(); it != seeds.end(); ++it) + f->AddSeed(*it); + f->SetRadius(size); + filter=f; + if (mArgsInfo.verbose_flag) std::cout<<"Using the neighborhood threshold connected image filter..."< ImageFilterType; + typename ImageFilterType::Pointer f= ImageFilterType::New(); + + // Radius + typename InputImageType::SizeType size; + if (mArgsInfo.radius_given==Dimension) + for (unsigned int i=0; iSetMultiplier( mArgsInfo.multiplier_arg ); + f->SetNumberOfIterations( mArgsInfo.multiplier_arg ); + for (typename SeedsType::const_iterator it = seeds.begin(); it != seeds.end(); ++it) + f->AddSeed(*it); + f->SetNumberOfIterations( mArgsInfo.iter_arg); + f->SetReplaceValue(static_cast(mArgsInfo.pad_arg)); + f->SetInitialNeighborhoodRadius(size[0]); + filter=f; + if (mArgsInfo.verbose_flag) std::cout<<"Using the confidence threshold connected image filter..."< ImageFilterType; + typename ImageFilterType::Pointer f= ImageFilterType::New(); + + // Radius + typename InputImageType::SizeType size; + if (mArgsInfo.radius_given==Dimension) + for (unsigned int i=0; iSetLower(mArgsInfo.lower_arg); + f->SetUpper(mArgsInfo.upper_arg); + f->SetLowerBorderIsGiven(mArgsInfo.adaptLower_flag); + f->SetLowerBorderIsGiven(mArgsInfo.adaptUpper_flag); + f->SetReplaceValue(static_cast(mArgsInfo.pad_arg)); + f->SetMultiplier(mArgsInfo.multiplier_arg); + f->SetMaximumSDIsGiven(mArgsInfo.maxSD_given); + if (mArgsInfo.maxSD_given) f->SetMaximumSD(mArgsInfo.maxSD_arg); + for (typename SeedsType::const_iterator it = seeds.begin(); it != seeds.end(); ++it) + f->AddSeed(*it); + f->SetRadius(size); + filter=f; + if (mArgsInfo.verbose_flag) std::cout<<"Using the locally adaptive threshold connected image filter..."< ImageFilterType; + typename ImageFilterType::Pointer f= ImageFilterType::New(); + + // // Radius + // typename InputImageType::SizeType size; + // if (mArgsInfo.radius_given==Dimension) + // for (unsigned int i=0; iSetVerbose(mArgsInfo.verbose_flag); + f->SetLower(mArgsInfo.lower_arg); + f->SetUpper(mArgsInfo.upper_arg); + f->SetMinimumLowerThreshold(mArgsInfo.minLower_arg); + f->SetMaximumUpperThreshold(mArgsInfo.maxUpper_arg); + f->SetAdaptLowerBorder(mArgsInfo.adaptLower_flag); + f->SetAdaptUpperBorder(mArgsInfo.adaptUpper_flag); + f->SetReplaceValue(static_cast(mArgsInfo.pad_arg)); + f->SetMultiplier(mArgsInfo.multiplier_arg); + f->SetThresholdStepSize(mArgsInfo.step_arg); + f->SetMinimumThresholdStepSize(mArgsInfo.minStep_arg); + f->SetFullyConnected(mArgsInfo.full_flag); + for (typename SeedsType::const_iterator it = seeds.begin(); it != seeds.end(); ++it) + f->AddSeed(*it); + filter=f; + if (mArgsInfo.verbose_flag) std::cout<<"Using the explosion controlled threshold connected image filter..."<SetInput(input); + filter->Update(); + typename OutputImageType::Pointer output=filter->GetOutput(); + + // Write/Save results + this->template SetNextOutput(output); +} +//-------------------------------------------------------------------- + + +#endif //#define CLITKREGIONGROWINGGENERICFILTER_TXX diff --git a/tools/clitkCropImageGenericFilter.cxx b/tools/clitkCropImageGenericFilter.cxx index d46f20e..ede4007 100644 --- a/tools/clitkCropImageGenericFilter.cxx +++ b/tools/clitkCropImageGenericFilter.cxx @@ -18,18 +18,8 @@ #ifndef clitkCropImageGenericFilter_cxx #define clitkCropImageGenericFilter_cxx -/* ================================================= - * @file clitkCropImageGenericFilter.cxx - * @author - * @date - * - * @brief - * - ===================================================*/ - #include "clitkCropImageGenericFilter.h" - //----------------------------------------------------------- // Constructor //----------------------------------------------------------- diff --git a/tools/clitkImageStatistics.cxx b/tools/clitkImageStatistics.cxx index 265871f..818a88c 100644 --- a/tools/clitkImageStatistics.cxx +++ b/tools/clitkImageStatistics.cxx @@ -16,16 +16,6 @@ - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ===========================================================================**/ -/* ================================================= - * @file clitkImageStatistics.cxx - * @author - * @date - * - * @brief - * - ===================================================*/ - - // clitk #include "clitkImageStatistics_ggo.h" #include "clitkIO.h" @@ -43,7 +33,7 @@ int main(int argc, char * argv[]) { clitk::ImageStatisticsGenericFilter::Pointer genericFilter=clitk::ImageStatisticsGenericFilter::New(); genericFilter->SetArgsInfo(args_info); - genericFilter->Update(); + CLITK_TRY_CATCH_EXIT(genericFilter->Update()); return EXIT_SUCCESS; }// end main diff --git a/tools/clitkImageStatistics.ggo b/tools/clitkImageStatistics.ggo index bbb127b..d2e55f3 100644 --- a/tools/clitkImageStatistics.ggo +++ b/tools/clitkImageStatistics.ggo @@ -15,3 +15,5 @@ option "histogram" - "Compute histogram, allows median calculation" string no option "bins" - "Number of histogram bins" int no default="100" option "lower" - "Lower histogram bound" double no default="-1000" option "upper" - "Upper histogram bound" double no default="1000" + +option "allow_resize" r "Resize mask if different from input" flag off diff --git a/tools/clitkImageStatisticsGenericFilter.cxx b/tools/clitkImageStatisticsGenericFilter.cxx index ddfe5e0..931e1bf 100644 --- a/tools/clitkImageStatisticsGenericFilter.cxx +++ b/tools/clitkImageStatisticsGenericFilter.cxx @@ -18,18 +18,8 @@ #ifndef clitkImageStatisticsGenericFilter_cxx #define clitkImageStatisticsGenericFilter_cxx -/* ================================================= - * @file clitkImageStatisticsGenericFilter.cxx - * @author - * @date - * - * @brief - * - ===================================================*/ - #include "clitkImageStatisticsGenericFilter.h" - namespace clitk { @@ -42,7 +32,8 @@ namespace clitk m_Verbose=false; m_InputFileName=""; } - + //----------------------------------------------------------- + //----------------------------------------------------------- // Update diff --git a/tools/clitkImageStatisticsGenericFilter.h b/tools/clitkImageStatisticsGenericFilter.h index 6511c0e..00cfff8 100644 --- a/tools/clitkImageStatisticsGenericFilter.h +++ b/tools/clitkImageStatisticsGenericFilter.h @@ -18,16 +18,6 @@ #ifndef clitkImageStatisticsGenericFilter_h #define clitkImageStatisticsGenericFilter_h -/* ================================================= - * @file clitkImageStatisticsGenericFilter.h - * @author - * @date - * - * @brief - * - ===================================================*/ - - // clitk include #include "clitkIO.h" #include "clitkCommon.h" @@ -41,7 +31,6 @@ namespace clitk { - class ITK_EXPORT ImageStatisticsGenericFilter : public itk::LightObject { public: diff --git a/tools/clitkImageStatisticsGenericFilter.txx b/tools/clitkImageStatisticsGenericFilter.txx index a0d2abf..7b9c78b 100644 --- a/tools/clitkImageStatisticsGenericFilter.txx +++ b/tools/clitkImageStatisticsGenericFilter.txx @@ -21,16 +21,9 @@ #include "itkNthElementImageAdaptor.h" #include "itkJoinSeriesImageFilter.h" -/* ================================================= - * @file clitkImageStatisticsGenericFilter.txx - * @author - * @date - * - * @brief - * - ===================================================*/ #include "clitkImageStatisticsGenericFilter.h" - +#include "clitkCropLikeImageFilter.h" +#include "clitkResampleImageWithOptionsFilter.h" namespace clitk { @@ -114,6 +107,7 @@ namespace clitk int maskDimension, maskComponents; std::string maskPixelType; ReadImageDimensionAndPixelType(m_ArgsInfo.mask_arg, maskDimension, maskPixelType, maskComponents); + if (maskDimension == Dimension - 1) { // Due to a limitation of filter itk::LabelStatisticsImageFilter, InputImageType and LabelImageType // must have the same image dimension. However, we want to support label images with Dl = Di - 1, @@ -143,6 +137,30 @@ namespace clitk labelImageReader->SetFileName(m_ArgsInfo.mask_arg); labelImageReader->Update(); labelImage= labelImageReader->GetOutput(); + + // Check mask sampling/size + if (!HaveSameSizeAndSpacing(labelImage, input)) { + if (m_ArgsInfo.allow_resize_flag) { + if (m_ArgsInfo.verbose_flag) { + std::cout << "Resize mask image like input" << std::endl; + } + typedef clitk::ResampleImageWithOptionsFilter ResamplerType; + typename ResamplerType::Pointer resampler = ResamplerType::New(); + resampler->SetInput(labelImage); + resampler->SetOutputSpacing(input->GetSpacing()); + resampler->Update(); + labelImage = resampler->GetOutput(); + + typename itk::ImageBase::RegionType reg + = input->GetLargestPossibleRegion(); + labelImage = ResizeImageLike(labelImage, ®, 0); + } + else { + std::cerr << "Mask image has a different size/spacing than input. Abort" << std::endl; + exit(-1); + } + } + } } diff --git a/vv/CMakeLists.txt b/vv/CMakeLists.txt index b77a52c..bfb3bcb 100644 --- a/vv/CMakeLists.txt +++ b/vv/CMakeLists.txt @@ -23,6 +23,7 @@ SET(vv_TOOLS vvToolMIP vvToolConvert ## with dummy vvToolConvert.ui vvToolROIManager + vvToolSegmentation ## these ones are for tests (not working) # vvToolFoo diff --git a/vv/qt_ui/vvToolSegmentation.ui b/vv/qt_ui/vvToolSegmentation.ui index 4931841..625b4fa 100644 --- a/vv/qt_ui/vvToolSegmentation.ui +++ b/vv/qt_ui/vvToolSegmentation.ui @@ -6,26 +6,196 @@ 0 0 - 408 - 285 + 401 + 489 Binarize image - - - - 140 - 120 - 86 - 27 - - - - PushButton - - + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Ref mask size + + + + + + + 12303 voxels (65464 mm3) + + + + + + + Current mask size + + + + + + + 12303 voxels (65464 mm3) + + + + + + + Label mask sizes + + + + + + + + 16777215 + 50 + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + 12 56 32 + + + + + + + Current pos (mm) + + + + + + + 12.3 56.6 32.9 + + + + + + + Image value + + + + + + + 236 + + + + + + + Ref mask + + + + + + + Inside + + + + + + + Current mask + + + + + + + Outside + + + + + + + Label mask + + + + + + + Outside + + + + + + + Current pos (pix) + + + + + + + + + + Log + + + + + + + true + + + + + + + Save current mask + + + + + + + Quit + + + + diff --git a/vv/vvROIActor.cxx b/vv/vvROIActor.cxx index e7f82f6..6e79456 100644 --- a/vv/vvROIActor.cxx +++ b/vv/vvROIActor.cxx @@ -166,10 +166,10 @@ void vvROIActor::Initialize(double depth, bool IsVisible) { mImageContour.push_back(vvImageContour::New()); mImageContour[i]->SetSlicer(mSlicerManager->GetSlicer(i)); mImageContour[i]->SetImage(mROI->GetImage()); - // Color of the contour is "complement" of roi color - mContourColor[0] = 1-mROI->GetDisplayColor()[0]; - mContourColor[1] = 1-mROI->GetDisplayColor()[1]; - mContourColor[2] = 1-mROI->GetDisplayColor()[2]; + // Color of the contour is same of roi color + mContourColor[0] = mROI->GetDisplayColor()[0]; + mContourColor[1] = mROI->GetDisplayColor()[1]; + mContourColor[2] = mROI->GetDisplayColor()[2]; mImageContour[i]->SetColor(mContourColor[0], mContourColor[1], mContourColor[2]); mImageContour[i]->SetLineWidth(mContourWidth); mImageContour[i]->SetPreserveMemoryModeEnabled(true); diff --git a/vv/vvSlicerManager.cxx b/vv/vvSlicerManager.cxx index 9331bf1..244a7f6 100644 --- a/vv/vvSlicerManager.cxx +++ b/vv/vvSlicerManager.cxx @@ -418,6 +418,23 @@ void vvSlicerManager::LeftButtonReleaseEvent(int slicer) } //---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +void vvSlicerManager::EmitMousePositionUpdated(int slicer) +{ + emit MousePositionUpdatedSignal(slicer); +} +//---------------------------------------------------------------------------- + + +//---------------------------------------------------------------------------- +void vvSlicerManager::EmitKeyPressed(std::string KeyPress) +{ + emit KeyPressedSignal(KeyPress); +} +//---------------------------------------------------------------------------- + + //---------------------------------------------------------------------------- void vvSlicerManager::SetSliceOrientation(int slicer, int orientation) { @@ -642,18 +659,18 @@ void vvSlicerManager::UpdateViews(int current,int slicer) } switch (mSlicers[i]->GetSliceOrientation()) { case vtkImageViewer2::SLICE_ORIENTATION_XY: - if (mSlicers[i]->GetSlice() != (int)floor(z)) - mSlicers[i]->SetSlice((int)floor(z)); + if (mSlicers[i]->GetSlice() != (int)lrint(z)) + mSlicers[i]->SetSlice((int)lrint(z)); break; case vtkImageViewer2::SLICE_ORIENTATION_XZ: - if (mSlicers[i]->GetSlice() != (int)floor(y)) - mSlicers[i]->SetSlice((int)floor(y)); + if (mSlicers[i]->GetSlice() != (int)lrint(y)) + mSlicers[i]->SetSlice((int)lrint(y)); break; case vtkImageViewer2::SLICE_ORIENTATION_YZ: - if (mSlicers[i]->GetSlice() != (int)floor(x)) - mSlicers[i]->SetSlice((int)floor(x)); + if (mSlicers[i]->GetSlice() != (int)lrint(x)) + mSlicers[i]->SetSlice((int)lrint(x)); break; } diff --git a/vv/vvSlicerManager.h b/vv/vvSlicerManager.h index 1824524..940b107 100644 --- a/vv/vvSlicerManager.h +++ b/vv/vvSlicerManager.h @@ -208,6 +208,8 @@ class vvSlicerManager : public QObject { void Activated(); void Picked(); void UpdateInfoOnCursorPosition(int slicer); + void EmitMousePositionUpdated(int slicer); + void EmitKeyPressed(std::string keyPress); void UpdateWindowLevel(); void UpdateSlice(int slicer); void UpdateTSlice(int slicer); @@ -230,6 +232,8 @@ signals : void UpdateVector(int display, double x, double y, double z, double value); void UpdateOverlay(int display, double valueOver, double valueRef); void UpdateFusion(int display, double valueFus); + void MousePositionUpdatedSignal(int slicer); + void KeyPressedSignal(std::string KeyPressed); void UpdateOrientation(int slicer, int orientation); void UpdateSlice(int slicer, int slice); void UpdateTSlice(int slicer, int slice); diff --git a/vv/vvSlicerManagerCommand.cxx b/vv/vvSlicerManagerCommand.cxx index 73a0ab5..ad80b92 100644 --- a/vv/vvSlicerManagerCommand.cxx +++ b/vv/vvSlicerManagerCommand.cxx @@ -266,6 +266,7 @@ void vvSlicerManagerCommand::Execute(vtkObject *caller, this->SM->UpdateSliceRange(VisibleInWindow); } + this->SM->EmitKeyPressed(KeyPress); } //All type of mouse events @@ -276,7 +277,6 @@ void vvSlicerManagerCommand::Execute(vtkObject *caller, return; } - //DD(event); // Mouse release HERE if (event == vtkCommand::EndPickEvent) { // DD(VisibleInWindow); @@ -363,12 +363,24 @@ void vvSlicerManagerCommand::Execute(vtkObject *caller, break; } + // <<<<<<< HEAD + // this->SM->GetSlicer(VisibleInWindow)->SetCurrentPosition(xWorld,yWorld,zWorld, + // this->SM->GetSlicer(VisibleInWindow)->GetTSlice()); + // // We propagate the mouse position + // this->SM->EmitMousePositionUpdated(VisibleInWindow); + // ======= double p[3]; p[0] = xWorld; p[1] = yWorld; p[2] = zWorld; double pt[3]; this->SM->GetSlicer(VisibleInWindow)->GetSlicingTransform()->TransformPoint(p, pt); this->SM->GetSlicer(VisibleInWindow)->SetCurrentPosition(pt[0],pt[1],pt[2], this->SM->GetSlicer(VisibleInWindow)->GetMaxCurrentTSlice()); + + // We propagate the mouse position + this->SM->EmitMousePositionUpdated(VisibleInWindow); + + //>>>>>>> 921642d767beba2442dacc8fdb40dc36396e1b7d + if (newLandmark) { this->SM->AddLandmark(xWorld,yWorld,zWorld, this->SM->GetSlicer(VisibleInWindow)->GetTSlice()); diff --git a/vv/vvToolROIManager.cxx b/vv/vvToolROIManager.cxx index 8b0a487..e1d2e5c 100644 --- a/vv/vvToolROIManager.cxx +++ b/vv/vvToolROIManager.cxx @@ -491,9 +491,7 @@ void vvToolROIManager::UpdateAllContours() for(unsigned int i=0; iUpdate(); } - for(int i=0; iGetNumberOfSlicers(); i++) { - mCurrentSlicerManager->GetSlicer(i)->Render(); - } + mCurrentSlicerManager->Render(); } //------------------------------------------------------------------------------ @@ -679,6 +677,8 @@ void vvToolROIManager::ChangeColor() { mCurrentROIActor->GetROI()->GetDisplayColor()[1], mCurrentROIActor->GetROI()->GetDisplayColor()[2]); QColor c = QColorDialog::getColor(color, this, "Choose the ROI color"); + if (!c.isValid()) return;// User cancel + mCurrentROIActor->GetROI()->SetDisplayColor(c.redF(), c.greenF(), c.blueF()); mCurrentROIActor->UpdateColor(); diff --git a/vv/vvToolSegmentation.cxx b/vv/vvToolSegmentation.cxx index dea3513..341084d 100644 --- a/vv/vvToolSegmentation.cxx +++ b/vv/vvToolSegmentation.cxx @@ -14,7 +14,7 @@ - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html -===========================================================================**/ + ===========================================================================**/ // vv #include "vvToolSegmentation.h" @@ -23,6 +23,12 @@ #include "vvToolInputSelectorWidget.h" #include "vvImageWriter.h" +// clitk +#include "clitkConnectedComponentLabeling_ggo.h" +#include "clitkConnectedComponentLabelingGenericFilter.h" +#include "clitkRegionGrowing_ggo.h" +#include "clitkRegionGrowingGenericFilter.h" + // Qt #include #include @@ -30,7 +36,8 @@ // vtk #include "vtkImageContinuousErode3D.h" #include "vtkImageContinuousDilate3D.h" - +#include "vtkRenderWindow.h" + //------------------------------------------------------------------------------ // Create the tool and automagically (I like this word) insert it in // the main window menu. @@ -64,7 +71,16 @@ vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlag AddInputSelector("Select one image"); // Init + mCurrentLabelUnderMousePointer = 0.0; + mCurrentMousePositionInMM.resize(3); + // mCurrentMousePositionInPixel.resize(3); + mRefMaskImage = NULL; + mCurrentState = State_Default; mKernelValue = 3; // FIXME must be odd. If even -> not symmetrical + mDefaultLUTColor = vtkSmartPointer::New(); + mDefaultLUTColor->SetNumberOfTableValues(256); +#include "vvDefaultLut.h" + } //------------------------------------------------------------------------------ @@ -72,6 +88,7 @@ vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlag //------------------------------------------------------------------------------ vvToolSegmentation::~vvToolSegmentation() { + DD("destructor"); } //------------------------------------------------------------------------------ @@ -79,7 +96,14 @@ vvToolSegmentation::~vvToolSegmentation() //------------------------------------------------------------------------------ bool vvToolSegmentation::close() { - mRefMaskActor->RemoveActors(); + DD("remo ref"); + if (mRefMaskActor) mRefMaskActor->RemoveActors(); + DD("remo mask"); + if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors(); + for(int i=0; iRemoveActors(); + } + DD("wclose"); QWidget::close(); mCurrentSlicerManager->Render(); return true; @@ -106,6 +130,21 @@ void vvToolSegmentation::InputIsSelected(vvSlicerManager * m) // Open mask OpenBinaryImage(); + + // If cancel: stop + if (mRefMaskActor == NULL) { + close(); + return; + } + + // Update gui + mToolInputSelectionWidget->hide(); + + // Connect mouse position + connect(mCurrentSlicerManager, SIGNAL(MousePositionUpdatedSignal(int)), + this, SLOT(MousePositionChanged(int))); + connect(mCurrentSlicerManager, SIGNAL(KeyPressedSignal(std::string)), + this, SLOT(KeyPressed(std::string))); } //------------------------------------------------------------------------------ @@ -149,69 +188,222 @@ void vvToolSegmentation::OpenBinaryImage() return; } - mMaskImage = reader->GetOutput(); - int dim = mMaskImage->GetNumberOfDimensions(); + mRefMaskImage = reader->GetOutput(); + int dim = mRefMaskImage->GetNumberOfDimensions(); if (dim != 3 ) { QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet")); close(); return; } - // Add a new roi actor - mRefMaskActor = QSharedPointer(new vvROIActor); - std::vector color; - color.push_back(1); - color.push_back(0); - color.push_back(0); - clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New(); - roi->SetFromBinaryImage(mMaskImage, 1, std::string("toto"), color, filename.toStdString()); - mRefMaskActor->SetBGMode(true); - mRefMaskActor->SetROI(roi); - mRefMaskActor->SetSlicerManager(mCurrentSlicerManager); - mRefMaskActor->Initialize(10, true); + reader = vvImageReader::New(); + reader->SetInputFilenames(filenames); + reader->Update(vvImageReader::IMAGE); + mCurrentMaskImage = reader->GetOutput(); + + // Add a new roi actor for the current mask + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); + mCurrentMaskActor->Update(); // default color is red + UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC); + + // Add a mask actor for the reference + mRefMaskActor = CreateMaskActor(mRefMaskImage, 0, 1, true); + mRefMaskActor->SetContourVisible(true); + mRefMaskActor->SetVisible(false); + mRefMaskActor->SetContourColor(0,1,0); // green contour + mRefMaskActor->UpdateColor(); mRefMaskActor->Update(); + UpdateMaskSize(mRefMaskImage, mRefMaskSizeInPixels, mRefMaskSizeInCC); - // Prepare widget to get keyboard event - grabKeyboard(); - //connect(this, SIGNAL(keyPressEvent(QKeyEvent*)), this, SLOT(keyPressed(QKeyEvent*))); + // Update GUI + UpdateMaskSizeLabels(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -void vvToolSegmentation::keyPressEvent(QKeyEvent * event) +void vvToolSegmentation::UpdateMaskSizeLabels() { - vvToolWidgetBase::keyPressEvent(event); - //DD("key"); - - if (event->text() == "e") { + QString s("%1 pix (%2 cm3)"); + s = s.arg(mRefMaskSizeInPixels).arg(mRefMaskSizeInCC); + mRefMaskSizeLabel->setText(s); + QString s2("%1 pix (%2 cm3)"); + s2 = s2.arg(mCurrentMaskSizeInPixels).arg(mCurrentMaskSizeInCC); + mCurrentMaskSizeLabel->setText(s2); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc) +{ + pix = ComputeNumberOfPixels(image, GetForegroundValue()); + double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2]; + cc = pix * vol / (10*10*10); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value) +{ + int n=0; + vtkImageData * im = image->GetFirstVTKImageData(); + char * pPix = (char*)im->GetScalarPointer(); // FIXME char ? + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pPix[i] == value) n++; + } + DD(n); + return n; +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::KeyPressed(std::string KeyPress) +{ + if (KeyPress == "G") { + RegionGrowing(); + } + if (KeyPress == "e") { Erode(); } - if (event->text() == "d") { + if (KeyPress == "d") { Dilate(); // FIXME -> extend image BB !! } - if (event->text() == "s") { + if (KeyPress == "L") { + Labelize(); + } + if (KeyPress == "m") { + Merge(); + UpdateAndRenderNewMask(); + } + if (KeyPress == "s") { // Supress "Remove" one label + if (mCurrentState == State_CCL) RemoveLabel(); + } + if (KeyPress == "t") { // display remove ref contour + mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible()); + mRefMaskActor->UpdateColor(); + mCurrentSlicerManager->Render(); + } + if (KeyPress == "w") { vvImageWriter::Pointer writer = vvImageWriter::New(); writer->SetOutputFileName("a.mha"); - writer->SetInput(mMaskImage); + writer->SetInput(mCurrentMaskImage); writer->Update(); } } //------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void vvToolSegmentation::RegionGrowing() +{ + DD("RegionGrowing"); + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get mouse location + DD(mCurrentLabelUnderMousePointer); + DDV(mCurrentMousePositionInMM, 3); + // DDV(mCurrentMousePositionInPixel, 3); + + // Build RG filter parameters + typedef args_info_clitkRegionGrowing ArgsInfoType; + ArgsInfoType a; + cmdline_parser_clitkRegionGrowing_init(&a); + // FIXME parameters + a.type_arg = 4; // FIXME set by gui + DD(a.lower_arg); + a.lower_arg = 200; + a.upper_arg = 3000; + a.seed_given = 3; + a.seed_arg = new int[3]; + + DDV(mCurrentMousePositionInMM, 3); + vtkImageData * image = mCurrentImage->GetFirstVTKImageData(); + double x = (mCurrentMousePositionInMM[0] - image->GetOrigin()[0]) / image->GetSpacing()[0]; + double y = (mCurrentMousePositionInMM[1] - image->GetOrigin()[1]) / image->GetSpacing()[1]; + double z = (mCurrentMousePositionInMM[2] - image->GetOrigin()[2]) / image->GetSpacing()[2]; + a.seed_arg[0] = x; + a.seed_arg[1] = y; + a.seed_arg[2] = z; + a.verbose_flag = 1; + + // Build RG filter parameters + typedef clitk::RegionGrowingGenericFilter FilterType; + FilterType::Pointer filter = FilterType::New(); + filter->SetArgsInfo(a); + filter->SetInputVVImage(mCurrentImage); + filter->SetIOVerbose(true); + filter->Update(); + mCurrentMaskImage = filter->GetOutputVVImage(); + DD("filter done"); + + mCurrentMaskActor->RemoveActors(); + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); + mCurrentMaskActor->Update(); // default color is red + UpdateAndRenderNewMask(); + UpdateMaskSizeLabels(); + DD("end"); + + // mouse pointer + QApplication::restoreOverrideCursor(); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::Merge() +{ + if (mCurrentState != State_CCL) return; + + DD("Merge"); + // Remove actors + for(int i=0; iSetVisible(false); + mCurrentCCLActors[i]->RemoveActors(); + } + } + mCurrentCCLActors.clear(); + + // Compute new mask + vtkImageData * ccl = mCurrentCCLImage->GetFirstVTKImageData(); + vtkImageData * mask = mCurrentMaskImage->GetFirstVTKImageData(); + int * pCCL = (int*)ccl->GetScalarPointer(); + char * pPix = (char*)mask->GetScalarPointer(); + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pCCL[i] == 0) pPix[i] = GetBackgroundValue(); // copy BG. In CCL BG is always 0 + } + + // Display new mask and remove ccl + mCurrentCCLImage->Reset(); + mCurrentMaskActor->RemoveActors(); // kill it + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); // renew + mCurrentMaskActor->Update(); + mCurrentMaskActor->SetVisible(true); + // mCurrentSlicerManager->Render(); + mCurrentState = State_Default; +} +//------------------------------------------------------------------------------ + + //------------------------------------------------------------------------------ void vvToolSegmentation::Erode() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get image and start erosion vtkImageContinuousErode3D* erode = vtkImageContinuousErode3D::New(); erode->SetKernelSize(mKernelValue,mKernelValue,mKernelValue); - vtkImageData* image = mMaskImage->GetVTKImages()[0]; + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; erode->SetInput(image); erode->Update(); image->DeepCopy(erode->GetOutput()); image->Update(); - UpdateAndRender(); + UpdateAndRenderNewMask(); erode->Delete(); QApplication::restoreOverrideCursor(); } @@ -222,14 +414,17 @@ void vvToolSegmentation::Erode() void vvToolSegmentation::Dilate() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get image and start dilatation vtkImageContinuousDilate3D* dilate = vtkImageContinuousDilate3D::New(); dilate->SetKernelSize(mKernelValue,mKernelValue,mKernelValue); - vtkImageData* image = mMaskImage->GetVTKImages()[0]; + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; dilate->SetInput(image); dilate->Update(); image->DeepCopy(dilate->GetOutput()); image->Update(); - UpdateAndRender(); + UpdateAndRenderNewMask(); dilate->Delete(); QApplication::restoreOverrideCursor(); } @@ -237,18 +432,177 @@ void vvToolSegmentation::Dilate() //------------------------------------------------------------------------------ -void vvToolSegmentation::UpdateAndRender() +void vvToolSegmentation::UpdateAndRenderNewMask() { - bool visible = mRefMaskActor->IsVisible(); - bool cvisible = mRefMaskActor->IsContourVisible(); - mRefMaskActor->SetVisible(false); - mRefMaskActor->SetContourVisible(false); + bool visible = mCurrentMaskActor->IsVisible(); + bool cvisible = mCurrentMaskActor->IsContourVisible(); + mCurrentMaskActor->SetVisible(false); + mCurrentMaskActor->SetContourVisible(false); + mCurrentMaskActor->UpdateImage(); + mCurrentMaskActor->SetVisible(visible); + mCurrentMaskActor->SetContourVisible(cvisible); + mCurrentSlicerManager->Render(); + UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC); + UpdateMaskSizeLabels(); +} +//------------------------------------------------------------------------------ - //mRefMaskActor->RemoveActors(); - mRefMaskActor->UpdateImage(); - mRefMaskActor->SetVisible(visible); - mRefMaskActor->SetContourVisible(cvisible); +//------------------------------------------------------------------------------ +void vvToolSegmentation::Labelize() +{ + if (mCurrentState == State_CCL) return; // Do nothing in this case + DD("Labelize"); + // Waiting cursos + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mCurrentMaskActor->SetVisible(false); + + // Build CCL filter + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; + typedef args_info_clitkConnectedComponentLabeling ArgsInfoType; + ArgsInfoType a; + cmdline_parser_clitkConnectedComponentLabeling_init(&a); + a.inputBG_arg = GetBackgroundValue(); + a.full_flag = false; // FIXME set by gui + a.minSize_arg = 100; // FIXME set by gui + typedef clitk::ConnectedComponentLabelingGenericFilter FilterType; + FilterType::Pointer filter = FilterType::New(); + filter->SetArgsInfo(a); + filter->SetInputVVImage(mCurrentMaskImage); // FIXME Check type is ok ? convert float ? + filter->SetIOVerbose(true); + filter->Update(); + DD(filter->GetOriginalNumberOfObjects()); + DD(filter->GetSizeOfObjectsInPixels().size()); + mCurrentCCLImage = filter->GetOutputVVImage(); + DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size()); + DD("filter done"); + + /* + // DEBUG + vvImageWriter::Pointer writer = vvImageWriter::New(); + writer->SetInput(mCurrentCCLImage); + writer->SetOutputFileName("bidon-ccl.mha"); + writer->Update(); + DD(mCurrentCCLImage->IsScalarTypeInteger()); + */ + + // Create actors + int n = filter->GetSizeOfObjectsInPixels().size(); + mCurrentCCLActors.clear(); + for(int i=1; i<=std::min(n,10); i++) { // Start at 1 because 0 is BG. FIXME max by gui + QSharedPointer actor = CreateMaskActor(mCurrentCCLImage, i, i+1, false); + mCurrentCCLActors.push_back( actor ); + actor->Update(); + } + // mCurrentMaskActor->Update(); mCurrentSlicerManager->Render(); + + // UpdateAndRender(); + mCurrentState = State_CCL; + QApplication::restoreOverrideCursor(); +} +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +QSharedPointer vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode) +{ + static int depth = 1; + depth += 1; + QSharedPointer actor = QSharedPointer(new vvROIActor); + double * color = mDefaultLUTColor->GetTableValue(colorID % mDefaultLUTColor->GetNumberOfTableValues ()); + std::vector c; + c.push_back(color[0]); + c.push_back(color[1]); + c.push_back(color[2]); + clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New(); + roi->SetFromBinaryImage(image, i, std::string("toto"), c, std::string("titi")); + if (BGMode) { + actor->SetBGMode(true); + } + else { + roi->SetForegroundValueLabelImage(i); // FG mode + actor->SetBGMode(false); // FG mode + } + actor->SetROI(roi); + actor->SetSlicerManager(mCurrentSlicerManager); + actor->Initialize(depth+i, true); // +1 to start at 1 not 0 + actor->SetContourVisible(false); + actor->SetVisible(true); + return actor; +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::MousePositionChanged(int slicer) +{ + // DD("MousePositionChanged "); + // DD(slicer); + double x = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[0]; + double y = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[1]; + double z = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[2]; + mCurrentMousePositionInMM[0] = x; + mCurrentMousePositionInMM[1] = y; + mCurrentMousePositionInMM[2] = z; + // DDV(mCurrentMousePositionInMM, 3); + + // vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + vtkImageData * image = mCurrentMaskImage->GetFirstVTKImageData(); + double Xover = (x - image->GetOrigin()[0]) / image->GetSpacing()[0]; + double Yover = (y - image->GetOrigin()[1]) / image->GetSpacing()[1]; + double Zover = (z - image->GetOrigin()[2]) / image->GetSpacing()[2]; + int ix, iy, iz; + + // mCurrentMousePositionInPixel[0] = Xover; + // mCurrentMousePositionInPixel[1] = Yover; + // mCurrentMousePositionInPixel[2] = Zover; + // DDV(mCurrentMousePositionInPixel, 3); + + if (Xover >= image->GetWholeExtent()[0] && + Xover <= image->GetWholeExtent()[1] && + Yover >= image->GetWholeExtent()[2] && + Yover <= image->GetWholeExtent()[3] && + Zover >= image->GetWholeExtent()[4] && + Zover <= image->GetWholeExtent()[5]) { + if (mCurrentState == State_Default) { // inside the mask + mCurrentLabelUnderMousePointer = 1; + return; + } + else { // inside the label image + vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + mCurrentLabelUnderMousePointer = + mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0); + return; + } + } + else { + // DD("out of mask"); + mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL + } +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::RemoveLabel() { + DD("RemoveLabel"); + if (mCurrentLabelUnderMousePointer == 0) return; + // First actor=0 and is label 1. Label 0 is not an actor, it is BG + int actorNumber = mCurrentLabelUnderMousePointer-1; + // Set actor invisible + mCurrentCCLActors[actorNumber]->SetVisible(false); + mCurrentSlicerManager->Render(); + // Set image label + vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + int * pPix = (int*)image->GetScalarPointer(); + int n = 0; + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pPix[i] == mCurrentLabelUnderMousePointer) pPix[i] = 0; + if (pPix[i] != 0) n++; // count the number of pixels in the foreground + } + // Update mask size + mCurrentMaskSizeInPixels = n; + mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10); + UpdateMaskSizeLabels(); } //------------------------------------------------------------------------------ diff --git a/vv/vvToolSegmentation.h b/vv/vvToolSegmentation.h index 7b8bb8c..5cd6e40 100644 --- a/vv/vvToolSegmentation.h +++ b/vv/vvToolSegmentation.h @@ -25,6 +25,8 @@ #include "vvROIActor.h" #include "ui_vvToolSegmentation.h" +#include "vtkLookupTable.h" + //------------------------------------------------------------------------------ class vvToolSegmentation: public vvToolWidgetBase, @@ -40,23 +42,50 @@ class vvToolSegmentation: static void Initialize(); virtual void InputIsSelected(vvSlicerManager * m); void OpenBinaryImage(); + void RegionGrowing(); void Erode(); void Dilate(); - void UpdateAndRender(); + void Labelize(); + void Merge(); + void RemoveLabel(); + void UpdateAndRenderNewMask(); //----------------------------------------------------- public slots: virtual void apply(); - virtual void keyPressEvent(QKeyEvent * event); + void KeyPressed(std::string KeyPress); virtual bool close(); - // virtual void reject(); + virtual void MousePositionChanged(int slicer); protected: - // virtual void closeEvent(QCloseEvent *event); Ui::vvToolSegmentation ui; QSharedPointer mRefMaskActor; - vvImage::Pointer mMaskImage; + QSharedPointer mCurrentMaskActor; + std::vector > mCurrentCCLActors; + vvImage::Pointer mRefMaskImage; + vvImage::Pointer mCurrentMaskImage; + vvImage::Pointer mCurrentCCLImage; int mKernelValue; + vtkSmartPointer mDefaultLUTColor; + enum { State_Default, State_CCL}; + int mCurrentState; + + QSharedPointer CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode=false); + + double mCurrentLabelUnderMousePointer; + std::vector mCurrentMousePositionInMM; + //std::vector mCurrentMousePositionInPixel; + double GetBackgroundValue() { return 0; } + double GetForegroundValue() { return 1; } + long ComputeNumberOfPixels(vvImage::Pointer image, double value); + + // Compute and store sizes of mask Foreground + void UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc); + void UpdateMaskSizeLabels(); + long mRefMaskSizeInPixels; + double mRefMaskSizeInCC; + long mCurrentMaskSizeInPixels; + double mCurrentMaskSizeInCC; }; // end class vvToolSegmentation //------------------------------------------------------------------------------