Learning Opencv 3 —— 十四章 轮廓匹配

Learning Opencv 3 —— 十四章 轮廓匹配

关于 Opencv 轮廓的介绍可以参考:https://blog.csdn.net/a40850273/article/details/88063478

Moments

Moments 是一种轮廓、图像和点的高层次特征,计算方式如下

其能够理解为对图像中每个像素点的加权和,如果 x = 0, y = 0,即 m_{00},则每个像素点的权重都是 1。如果对于一幅二值图像(像素值不是 1 就是 0),那么 m_{00} 就是非零像素值的面积。如果一个轮廓,那么 m_{00} 就是轮廓的长度。同理,如果用 m_{10} 和 m_{01} 除以 m_{00},则表示图像在 x 和 y 方向的均值。

cv::moments() 用于计算一幅图像的 Moments

cv::Moments cv::moments(             // Return structure contains moments
  cv::InputArray points,             // 2-dimensional points or an "image"
  bool           binaryImage = false // false='interpret image values as "mass"'
)

参数介绍:

  • points:可以是一个二维数组(一幅图像),也可以是一堆点(一个轮廓)
  • binaryImage:如果为 True,所有的非零像素值将被视为 1

不过以上求得的 Moments 会因为对轮廓进行放缩和旋转而发生改变。

满足平移不变性的中心 Moments

对于一幅图像或轮廓,m_{00} 具有平移不变性。但是更高阶的 Moments 将不再具有该特征。中心 Moments 定义如下

,其中

通过对每个像素点对于其中心进行解决成功满足了平移不变性。其中,也可以很显然地得到 mu_{00} = m_{00}, mu_{10} = mu_{01} = 0

同时满足缩放不变性的归一化中心 Moments

为了进一步引入缩放不变性,引入归一化中心 Moments,定义如下

 

满足旋转不变性的 Hu invariant moments

Hu invariant moments 通过归一化中心 Moments 进行线性组合,从而实现对于缩放,旋转和反射(反射 h_1 并不满足)的不变性。

下面给出一个计算范例

void cv::HuMoments(
  const cv::Moments& moments, // Input is result from cv::moments() function
  double*            hu       // Return is C-style array of 7 Hu moments
);

cv::HuMoments() 通过传入一个 cv::Moments 的对象计算出上面给出的 7 个 hu moments。

使用 Hu Moments 进行匹配

cv::matchShapes() 基于提供的两个目标,自动计算他们的 Moments,最后基于用户给出的标准进行比较。

double  cv::MatchShapes(
  cv::InputArray object1,      // First array of 2D points or cv:U8C1 image
  cv::InputArray object2,      // Second array of 2D points or cv:U8C1 image
  int            method,       // Comparison method (Table 14-4)
  double         parameter = 0 // Method-specific parameter
);

参数说明:

  • object1,object2:输入的两个目标,必须为灰度图像或者轮廓
  • method:匹配方法包括一下三种,并且不同的 method 将影响最后返回的匹配度

其中,\eta_i^x = sign(h_i^x) \cdot log(h_i^x)

  • parameter:当前的算法并没有使用,可以简单地使用初值。这个参数主要是为了适配将来新增的 method 可能使用到自定义参数

使用形状上下文比较形状

使用 Moments 进行形状匹配可以追溯到 80 年代。同时最新的算法不断出现,不过由于当前的 Shape 模块还在开发中,因此这里将只简要介绍一下高层接口。

Shape 模块的结构

Shape 的构建基于一个抽象类 cv::ShapeDistanceExtractor。其返回一个非负数,如果两个形状完全相同,将返回 0。

class ShapeContextDistanceExtractor : public ShapeDistanceExtractor {
  public:
  ...
  virtual float computeDistance( InputArray contour1, InputArray contour2 ) = 0;
};

而具体的形状距离提取类将派生自这个基类 cv::ShapeDistanceExtractor。这里简要介绍其中的两个 cv::ShapeTransformer 和 cv::HistogramCostExtractor。

class ShapeTransformer : public Algorithm {

public:
  virtual void estimateTransformation(
    cv::InputArray      transformingShape,
    cv::InputArray      targetShape,
    vector<cv::DMatch>& matches
  ) = 0;

  virtual float applyTransformation(
    cv::InputArray      input,
    cv::OutputArray     output      = noArray()
  ) = 0;

  virtual void warpImage(
    cv::InputArray      transformingImage,
    cv::OutputArray     output,
    int                 flags       = INTER_LINEAR,
    int                 borderMode  = BORDER_CONSTANT,
    const cv::Scalar&   borderValue = cv::Scalar()
  ) const = 0;
};

class HistogramCostExtractor : public Algorithm {

public:
  virtual void  buildCostMatrix(
    cv::InputArray      descriptors1,
    cv::InputArray      descriptors2,
    cv::OutputArray     costMatrix
  )        
                                         = 0;
  virtual void  setNDummies( int nDummies )         = 0;
  virtual int   getNDummies() const                 = 0;

  virtual void  setDefaultCost( float defaultCost ) = 0;
  virtual float getDefaultCost() const              = 0;
};

shape transformer 表示了从一堆点到另一堆点再映射算法的类,其中仿射变换和透视变换都可以用形状变换实现(在 Opencv 中 cv::ThinPlateSplineShapeTransformer)。

histogram cost extractor 映射直方图中一个格到另一个格的 shoveling dirt 为一个代价。常用的派生类如下

对于每一个 extractors 和 transformers 都存在一个工厂方法(createX()),比如 cv::createChiHistogramCostExtractor()。

shape context distance extractor

namespace cv {

  class ShapeContextDistanceExtractor : public ShapeDistanceExtractor {

    public:
    ...
    virtual float computeDistance( 
      InputArray contour1, 
      InputArray contour2 
      ) = 0;
  };

  Ptr<ShapeContextDistanceExtractor> createShapeContextDistanceExtractor(
    int   nAngularBins                          = 12,
    int   nRadialBins                           = 4,
    float innerRadius                           = 0.2f,
    float outerRadius                           = 2,
    int   iterations                            = 3,
    const Ptr<HistogramCostExtractor> &comparer 
                                        = createChiHistogramCostExtractor(),
    const Ptr<ShapeTransformer>       &transformer
                                        = createThinPlateSplineShapeTransformer()
  );
}

本质上 Shape Context algorithm 计算两个或更多的待比较物体的表征。每一个表征基于形状边缘的一系列子级点,同时对于每一个采样点,它构造一个确定的直方图来反映从该点角度在极坐标系下的形状。所有的直方图具有相同的大小 nAngularBins * nRadialBins。两个待匹配物体上的点基于 chi-squared 计算距离。然后算法计算两个待匹配物体最优的 1:1 点匹配从而得到最小的 chi-squared 距离总和。这个算法不算快,不过能够提供一个相对不错的结果。

#include "opencv2/opencv.hpp"
#include <algorithm>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

static vector<Point> sampleContour( const Mat& image, int n=300 ) {

  vector<vector<Point> > _contours;
  vector<Point> all_points;
  findContours(image, _contours, RETR_LIST, CHAIN_APPROX_NONE);
  for (size_t i=0; i <_contours.size(); i++) {
    for (size_t j=0; j <_contours[i].size(); j++)
      all_points.push_back( _contours[i][j] );

  // If too little points, replicate them
//
  int dummy=0;
  for (int add=(int)all_points.size(); add<n; add++)
    all_points.push_back(all_points[dummy++]);

  // Sample uniformly
  random_shuffle(all_points.begin(), all_points.end());
  vector<Point> sampled;
  for (int i=0; i<n; i++)
    sampled.push_back(all_points[i]);
  return sampled;
}

int main(int argc, char** argv) {

  string path    = "../data/shape_sample/";
  int indexQuery = 1;

  Ptr<ShapeContextDistanceExtractor> mysc = createShapeContextDistanceExtractor();

  Size sz2Sh(300,300);
  Mat img1=imread(argv[1], IMREAD_GRAYSCALE);
  Mat img2=imread(argv[2], IMREAD_GRAYSCALE);
  vector<Point> c1 = sampleContour(img1);
  vector<Point> c2 = sampleContour(img2);
  float dis = mysc->computeDistance( c1, c2 );
  cout << "shape context distance between " <<
     argv[1] << " and " << argv[2] << " is: " << dis << endl;

  return 0;
}

Hausdorff distance extractor

类似于 Shape Context distance,Hausdorff distance 基于 cv::ShapeDistanceExtractor 接口给出了另一种形状不相似度的度量。

Hausdorff distance 首先对于一张图像中的每个点找出另一幅图像上的最近点,而其中最大距离就是 Hausdorff distance。不过 Hausdorff distance 不是对称的(不过可以通过某些操作实现对称)。

H(A,B) = max(h(A,B), h(B,A)),其中 

Hausdorff distance extractor 可以通过工厂方法 cv::createHausdorffDistanceExtractor() 产生。

cv::Ptr<cv::HausdorffDistanceExtractor> cv::createHausdorffDistanceExtractor(
  int   distanceFlag = cv::NORM_L2,
  float rankProp     = 0.6
);

同时 cv::HausdorffDistanceExtractor 和 Shape  Context  distance  extractor 具有相同的接口,因此同样可以使用 cv::computeDistance() 计算对比目标的距离。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值