ITK图像分割三个示例

最近刚刚配置好ITK环境,兴奋之余赶紧跑了图像分割的三个示例,分别是BinaryThresholdImageFilter,WatershedSegmentation和ThresholdSegmentationLevelSetImageFilter,也即是双阈值法,分水岭以及水平集方法,这些cxx源文件在ITK的example下面均可以找到。

双阈值分割法

#include "itkBinaryThresholdImageFilter.h"
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
int main( int argc, char * argv[] )
{
  if( argc < 7 )
    {
    std::cerr << "Usage: " << argv[0];
    std::cerr << " inputImageFile outputImageFile ";
    std::cerr << " lowerThreshold upperThreshold ";
    std::cerr << " outsideValue insideValue   "  << std::endl;
    return EXIT_FAILURE;
    }
typedef  unsigned char  InputPixelType;
typedef  unsigned char  OutputPixelType;
typedef itk::Image< InputPixelType,  2 >   InputImageType;
typedef itk::Image< OutputPixelType, 2 >   OutputImageType;
typedef itk::BinaryThresholdImageFilter<
InputImageType, OutputImageType >  FilterType;
typedef itk::ImageFileReader< InputImageType >  ReaderType;
typedef itk::ImageFileWriter< OutputImageType >  WriterType;
ReaderType::Pointer reader = ReaderType::New();
FilterType::Pointer filter = FilterType::New();
WriterType::Pointer writer = WriterType::New();</span></p><p><span style="white-space: pre;">
writer->SetInput( filter->GetOutput() );
reader->SetFileName( argv[1] );
filter->SetInput( reader->GetOutput() );
const OutputPixelType outsideValue = atoi( argv[5] );
const OutputPixelType insideValue  = atoi( argv[6] );

filter->SetOutsideValue( outsideValue );
filter->SetInsideValue(  insideValue  );

const InputPixelType lowerThreshold = atoi( argv[3] );
const InputPixelType upperThreshold = atoi( argv[4] );

filter->SetLowerThreshold( lowerThreshold );
filter->SetUpperThreshold( upperThreshold );
filter->Update();
writer->SetFileName( argv[2] );
writer->Update();


  return EXIT_SUCCESS;
}
下面来一行行分析代码:
包含文件
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"


 
 
这里面有ITK图像的模板,ITK的图像类型定义与OpenCV略有不同,它采用所谓的数据处理管道结构,如下:<span style="font-family: Arial, Helvetica, sans-serif;">ITK中的数据处理对象分为3种类型</span>
1 Source类型对象的输出为数据对象,包括各种图像文件读取类
2 Filter类型对象以一个或多个数据对象作为输入、然后输出一个或多个数据对象,包括数据处理算法类
3 Mapper类型对象是管道处理的最后一步,它将最终处理结果存到硬盘
<span style="font-family: Arial, Helvetica, sans-serif;">首先要定义输入输出图像的类型,</span>
typedef  unsigned char  InputPixelType;
typedef  unsigned char  OutputPixelType;
typedef itk::Image< InputPixelType,  2 >   InputImageType;
typedef itk::Image< OutputPixelType, 2 >   OutputImageType;</p><p>ImageType可以是任何类型(uchar,float......)任意维度的图像</p><p>typedef itk::BinaryThresholdImageFilter<InputImageType, OutputImageType >  FilterType;</p><p>定义了数据处理算法类。</p><p>所有的类型都要通过new()操作符进行显示初始化。</p><p><span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">ReaderType::Pointer reader = ReaderType::New();</span><br style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);" /><span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">FilterType::Pointer filter = FilterType::New();</span><br style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);" /><span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">WriterType::Pointer writer = WriterType::New();</span>
</p>
使用set函数类来设置各个数据处理对象的输入与输出。
 
 
<span style="font-family:Arial, Helvetica, sans-serif;"></span><pre name="code" class="cpp"><p>writer->SetInput( filter->GetOutput() );
reader->SetFileName( argv[1] );
</p><p>filter->SetInput( reader->GetOutput() );</p>
本双阈值函数包括低阈值和高阈值,处于之间的阈值化为255,之外的阈值化为0。
 
 
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="font-family: monospace; white-space: pre; background-color: rgb(240, 240, 240);">最后使用Update</span><span style="white-space: pre; background-color: rgb(240, 240, 240);">方法写入硬盘。
</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);">最终获得的效果如下:</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);"><img src="https://img-blog.csdn.net/20141108194941533" alt="" /><img src="https://img-blog.csdn.net/20141108195042484" alt="" />
</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);">总结:这是简单的阈值法,所以对于有噪声,以及由于偏移场造成空间异构的MRI医学图像,无法获得较好的分割结果。</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);">
</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);">二<span style="white-space:pre">	</span>分水岭分割法
</span></span>
<span style="font-family:Arial, Helvetica, sans-serif;"><span style="white-space: pre; background-color: rgb(240, 240, 240);"></span></span><pre name="code" class="cpp">#include "itkVectorGradientAnisotropicDiffusionImageFilter.h"
#include "itkVectorGradientMagnitudeImageFilter.h"
#include "itkWatershedImageFilter.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVectorCastImageFilter.h"
#include "itkScalarToRGBPixelFunctor.h"
int main( int argc, char *argv[] )
{
  if (argc < 8 )
    {
    std::cerr << "Missing Parameters " << std::endl;
    std::cerr << "Usage: " << argv[0];
    std::cerr << " inputImage outputImage conductanceTerm diffusionIterations lowerThreshold outputScaleLevel gradientMode " << std::endl;
    return 1;
    }
  typedef itk::RGBPixel< unsigned char >       RGBPixelType;
  typedef itk::Image< RGBPixelType, 2 >        RGBImageType;
  typedef itk::Vector< float, 3 >              VectorPixelType;
  typedef itk::Image< VectorPixelType, 2 >     VectorImageType;
  typedef itk::Image< itk::IdentifierType, 2 > LabeledImageType;
  typedef itk::Image< float, 2 >               ScalarImageType;
  typedef itk::ImageFileReader< RGBImageType >   FileReaderType;
  typedef itk::VectorCastImageFilter< RGBImageType, VectorImageType >
                                                 CastFilterType;
  typedef itk::VectorGradientAnisotropicDiffusionImageFilter<
                        VectorImageType, VectorImageType >
                                                 DiffusionFilterType;
  typedef itk::VectorGradientMagnitudeImageFilter< VectorImageType >
                                                 GradientMagnitudeFilterType;
  typedef itk::WatershedImageFilter< ScalarImageType >
                                                 WatershedFilterType;
  typedef itk::ImageFileWriter<RGBImageType> FileWriterType;

  FileReaderType::Pointer reader = FileReaderType::New();
  reader->SetFileName(argv[1]);

  CastFilterType::Pointer caster = CastFilterType::New();
  DiffusionFilterType::Pointer diffusion = DiffusionFilterType::New();
  diffusion->SetNumberOfIterations( atoi(argv[4]) );
  diffusion->SetConductanceParameter( atof(argv[3]) );
  diffusion->SetTimeStep(0.125);
  GradientMagnitudeFilterType::Pointer
    gradient = GradientMagnitudeFilterType::New();
  gradient->SetUsePrincipleComponents(atoi(argv[7]));
  WatershedFilterType::Pointer watershed = WatershedFilterType::New();
  watershed->SetLevel( atof(argv[6]) );
  watershed->SetThreshold( atof(argv[5]) );
  typedef itk::Functor::ScalarToRGBPixelFunctor<unsigned long>
    ColorMapFunctorType;
  typedef itk::UnaryFunctorImageFilter<LabeledImageType,
    RGBImageType, ColorMapFunctorType> ColorMapFilterType;
  ColorMapFilterType::Pointer colormapper = ColorMapFilterType::New();
  FileWriterType::Pointer writer = FileWriterType::New();
  writer->SetFileName(argv[2]);
  caster->SetInput(reader->GetOutput());
  diffusion->SetInput(caster->GetOutput());
  gradient->SetInput(diffusion->GetOutput());
  watershed->SetInput(gradient->GetOutput());
  colormapper->SetInput(watershed->GetOutput());
  writer->SetInput(colormapper->GetOutput());
  try
    {
    writer->Update();
    }
  catch (itk::ExceptionObject &e)
    {
    std::cerr << e << std::endl;
    }

  return 0;
}
包含文件
#include "itkVectorGradientAnisotropicDiffusionImageFilter.h"
#include "itkVectorGradientMagnitudeImageFilter.h"
#include "itkWatershedImageFilter.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkVectorCastImageFilter.h"
#include "itkScalarToRGBPixelFunctor.h"
VectorCastImageFilter包括了从RGB像素类型至数值像素类型的转换,ITK中存在<span style="font-family: Arial, Helvetica, sans-serif;">RGBPixel这样的结构,而在OpenCV中RGB像素类型与灰度像素类型的区别只是通过通道数的不同来区分。</span><pre name="code" class="cpp">CastFilterType这样的一个算法类似于与OpenCV中的convert函数,整个分水岭算法的流程是这样的;
<pre name="code" class="cpp">caster->SetInput(reader->GetOutput()); //输入图像转换成浮点数实值类型,输入为uchar型图像
diffusion->SetInput(caster->GetOutput()); //扩散滤波,输入为实值类型图像,输出为经过扩散滤波的图像
gradient->SetInput(diffusion->GetOutput());//梯度场滤波,输入为经过扩散滤波的图像,输出为梯度图像
watershed->SetInput(gradient->GetOutput());//输出梯度图像,其实好的分水岭算法,输出不是简单的梯度图像,往往是带标记的梯度极值图像,输出则是标签图像
//标签图像是什么呢,就是经过分水岭浸没之后,图像会形成一个个的区域,不同的区域采用不同的整数值(1,2,3...)来标记,而区域与区域之间的边界只有一个像素宽,我
//初学图像处理时,自己动手写过带标记的分水岭的源程序,这个一个像素宽的边界就没有写出来
colormapper->SetInput(watershed->GetOutput());//将标签图像转换成彩色图像,以便显示
writer->SetInput(colormapper->GetOutput());
使用默认的参数2 10 0.001 0.15 0,所得到的结果如下,<span style="color:#ff0000;">效果简直没法忍啊!</span>不过今天的目的只是观看下原始的ITK算法,以后将会来仔细研究分水岭算法用于彩色图像分割。
<img src="https://img-blog.csdn.net/20141109134204563" alt="" /><img src="https://img-blog.csdn.net/20141109134218042" alt="" />
<pre name="code" class="cpp"><span style="font-family:Arial, Helvetica, sans-serif;">三<span>	</span>水平集分割法</span>
跟上面的分水岭算法一样,我假设读者是已经熟悉至少是了解这个算法的,不然下面的有些术语就会听得稀里糊涂,不过不懂也没关系,问度娘吧,言归正传。
 
 
水平集方法相对以上的方法来说就要复杂很多,此处只是演示,具体的方法以后再说。
ITK里面的水平集方法的步骤是;
参数如下:60 116 5 150 180
分别对应seedX seedY InitialDistance LowerThreshold UpperThreshold",它们都是什么呢,首先我们要解释一个函数,快速步进法<span style="font-family: Arial, Helvetica, sans-serif;">FastMarchingImageFilter。</span>
<span style="font-family: Arial, Helvetica, sans-serif;">快速步进法是由Sethian和Tsitsiklis独立提出的基于模板变形的演变方法。它通过将问题放到高一维的情景中进行求精,比如要跟踪曲线的变形,转化为跟踪曲面的等高线的方法,因为在图像中,曲面的演化非常容易(就是灰度值的变化),而曲线的演化,很难用数学的公司来表述,站在更高维度空间进行思考,这就是水平集思想的精髓。那么它的演化就通过几个变量进行控制:(1)初始演化曲面(对于二类分割,就是正负区域,此处通过seedX和seedY来进行双阈值化来提供初始演化曲面)(2)曲面演化方向,也就是朝着梯度方向进行演化(3)迭代次数,步长以及等等一些控制参数。水平集方法的流程就是:初始演化曲面(种子区域)-> 按照一定的速度演化,迭代直至收敛-> 阈值化得到零水平集,即分割线。得到的结果如下:</span>
 <img src="https://img-blog.csdn.net/20141109143012534" alt="" /><img src="https://img-blog.csdn.net/20141109143024842" alt="" />
<span style="color:#ff0000;">换一个图片,就极有可能要换一种参数,这就是水平集最大的软肋。具体的细节,咱们留待以后慢慢讨论。</span>
可能看完这篇博客你什么都没学习,依旧稀里糊涂;我也是,因为毕竟自己也是刚学,而且主要目标是分享很具体的实例。但是万里长城第一步嘛,以后会一步接一步。
想进一步了解ITK文件的读写:请进http://blog.163.com/jacky_ling0/blog/static/13739257120108335625694
</pre><pre>
 
 
 
 
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
<span style="font-family:Arial, Helvetica, sans-serif;">
</span>

猜你喜欢

转载自blog.csdn.net/hacker_long/article/details/40922811