ITK: 连通区域检测和分析

    最近做实验需要对二维图像进行连通区域分析,在翻阅资料后发现ITk提供了相应的功能。十分好用。在这里总结一下。

 实现连通区域分析细分为两个步骤 1、寻找连通区域   2、连通区域分析。

ITK中使用下面的类寻找标记连通区域

#include "itkConnectedComponentImageFilter.h"
该类输出一个对各连通区域标记过的图像,称之为LabelImage.

连通区域分析使用到的类:

#include "itkLabelImageToShapeLabelMapFilter.h"

看名字就大概知道意思这是一个映射:把LabelImage映射到ShapeLabelImage.它的输出类型为

itk::LabelMap< ShapeLabelObjectType >

ITK文档里找到一副图很好解释LabelMap:

ITK: 连通区域检测和分析_第1张图片

ShapeLabelObject这个类很厉害,ITK中的描述

A Label object to store the common attributes related to the shape of the object.

提供了很多形状相关的属性描述,例如面积 周长 质心 等效圆半径等等。下面给出一个ITK WiKI提供的例子:

连通区域分析

由于ITK没有提供可视化功能,我们可以使用VTK将寻找到的连通区域画出来

ShapeLabelObject提供了一个函数接口GetBoundingBox, 可以获取连通区域的包围盒。该函数的返回类型是

InputImageType::RegionType region
结合VTK中的vtkPolyData类画出一个包围盒

主要代码如下:

CreateBoundingBox(InputImageType::RegionType region)
{
    InputImageType::IndexType start=region.GetIndex();
    InputImageType::SizeType size=region.GetSize();

    vtkSmartPointer points=
            vtkSmartPointer::New();
    points->InsertNextPoint(start[0],start[1],0.0);//points of left bottom
    points->InsertNextPoint(start[0]+size[0],start[1],0.0);//points of right bottom
    points->InsertNextPoint(start[0]+size[0],start[1]+size[1],0.0);//points of right up
    points->InsertNextPoint(start[0],start[1]+size[1],0.0);//points of left up

    vtkSmartPointer Bottomline=
            vtkSmartPointer::New();  //Bottom
    Bottomline->GetPointIds()->SetId(0,0);
    Bottomline->GetPointIds()->SetId(1,1);

    vtkSmartPointer Rightline=
            vtkSmartPointer::New();  //Right
    Rightline->GetPointIds()->SetId(0,1);
    Rightline->GetPointIds()->SetId(1,2);

    vtkSmartPointer Upline=
            vtkSmartPointer::New();  //Up
    Upline->GetPointIds()->SetId(0,2);
    Upline->GetPointIds()->SetId(1,3);

    vtkSmartPointer Leftline=
            vtkSmartPointer::New();  //Left
    Leftline->GetPointIds()->SetId(0,3);
    Leftline->GetPointIds()->SetId(1,0);

    vtkSmartPointer Maindiagonal=
            vtkSmartPointer::New();  //主对角线
    Maindiagonal->GetPointIds()->SetId(0,3);
    Maindiagonal->GetPointIds()->SetId(1,1);

    vtkSmartPointer Deputydiagonal=
            vtkSmartPointer::New();  //副对角线
    Deputydiagonal->GetPointIds()->SetId(0,2);
    Deputydiagonal->GetPointIds()->SetId(1,0);

    //draw bounding box
    vtkSmartPointer Lines=
            vtkSmartPointer::New();
    Lines->InsertNextCell(Bottomline);
    Lines->InsertNextCell(Rightline);
    Lines->InsertNextCell(Upline);
    Lines->InsertNextCell(Leftline);
    Lines->InsertNextCell(Maindiagonal);
    Lines->InsertNextCell(Deputydiagonal);

    polyData->SetPoints(points);
    polyData->SetLines(Lines);

    vtkSmartPointer mapper=
            vtkSmartPointer::New();
    mapper->SetInputData(polyData);

    actor->SetMapper(mapper);
    actor->PickableOff();
    actor->SetVisibility(false);
    actor->GetProperty()->SetColor(1.0,0.0,0.0);
    actor->GetProperty()->SetLineWidth(2);

}

      结果:

                       ITK: 连通区域检测和分析_第2张图片      


补充: 

    在获取连通区域的包围盒的时候,其返回类型为InputImageType::RegionType region  被称为图像区域类

    ITK文档中这样描述这个region:

 A region is defined by two classes: the itk::Indexand itk::Size classes. The origin of the region within the image is defined by theIndex. The extent, or size, of the region is defined by the Size. When an image is created manually, the user is responsible for defining the image size and the index at which the image grid starts. These two parameters make it possible to process selected regions.
The
Index is represented by a n-dimensional array where each component is an integer indicating—in topological image coordinates—the initial pixel of the image.

The region size is represented by an array of the same dimension as the image (using theitk::Size class). The components of the array are unsigned integers indicating the extent in pixels of the image along every dimension.

 index:表示图像区域起始位置

 size:表示图像区域的大小

 但没有留意到他们的单位是什么。原文中说:Index——the initial pixel of the image  图像的初始像素。size——the extent in pixels of the image along every dimension 每个维度上像素个数的扩展。

 因为不考虑像素间隔(spacing)这样一来其反映的不是实际的物理尺寸size,而只是表示当前维度下像素的个数。

 在实际操作时发现,当spacing小于1的时候获得的包围盒位置都向左上角偏移。如图:

     ITK: 连通区域检测和分析_第3张图片

spacing=0.3134108

所以在实际计算包围盒的start和size参数时候要乘以一个spacing:

InputImageType::IndexType start1=region.GetIndex();
    InputImageType::SizeType size1=region.GetSize();
  double start[2],size[2];
  for(int i=0;i<2;i++)
  {
      start[i]=start1[i]*spacing[i];
      size[i]=size1[i]*spacing[i];
  }

补充 2016-12-5

   ShapeLabelObject这个类中有一个成员函数 GetPrincipalAxes();返回值类型是一个变换矩阵(Matrix)。itk中有一个很粗略的解释大概意思是说:获得主轴到物理轴坐标的仿射变换。对仿射变换的概念了解不清。这里做一个小结:

  

仿射变换

仿射变换(Affine Transformation或 Affine Map),又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间的过程。它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。

一个任意的仿射变换都能表示为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。

那么, 我们能够用仿射变换来表示如下三种常见的变换形式:

  • 旋转,rotation (线性变换)
  • 平移,translation(向量加)
  • 缩放,scale(线性变换)

如果进行更深层次的理解,仿射变换代表的是两幅图之间的一种映射关系。

而我们通常使用2 x 3的矩阵来表示仿射变换。


 

 

考虑到我们要使用矩阵 A 和 B 对二维向量 做变换, 所以也能表示为下列形式:


  或者     


 

即:       


    回归正题,    对于一副二维图像,GetPrincipalAxes()返回值是Matrix类型,是一个2*2的矩阵。也就是对应于前面的A矩阵。其只能表示旋转变换而不能表示平移变换。在实际使用过程本人用该函数再加上求质心点的函数(GetCentroid())来获取连通区域等效椭圆的长短轴的端点坐标。代码如下:

/*!
 * \brief ConnectedRegion::GetShortAxesPoints
 *        获取等效椭圆长轴的两个端点坐标
 *        itk中短轴方向与连通区域坐标系(主轴坐标)的x轴平行
 * \param i 连通区域索引号
 * \param pt QPointF数组 保存了短轴的两个端点坐标
 */
void ConnectedRegion::GetShortAxesPoints(const int i, QPointF pt[]) const
{
    //主轴坐标系==》物理坐标系的变换矩阵
    const ShapeLabelObjectType::MatrixType matrix=GetPrincipalAxes(i);
    //短轴的一半长度
    const double halfLenngth=GetEquivalentEllipsoidDiameter(i)[0]/2.0;
    //连通区域质心
    const ShapeLabelObjectType::CentroidType center=GetCentroid(i);

    pt[0].setX(center[0]-halfLenngth*matrix[0][0]);
    pt[0].setY(center[1]-halfLenngth*matrix[0][1]);
    pt[1].setX(center[0]+halfLenngth*matrix[0][0]);
    pt[1].setY(center[1]+halfLenngth*matrix[0][1]);
}


   



你可能感兴趣的:(VTK,ITK)