天天看点

OpenCV + CPP 系列(卅四)图像特征提取(亚像素级别角点检测)

文章目录

  • ​​亚像素级别角点检测​​
  • ​​演示像素坐标检测​​

亚像素级别角点检测

亚像素:在生成数字图像处理时(拍照等)我们是将物理世界中连续的图像进行了离散化处理。现实世界中颜色为连续的且有无数种类,成像到像素面上每一个像素点只代表其附近的颜色,我们常使用的3通道图像颜色种类(255*255*255)代表,至于“附近”到什么程度?就很困难解释。两个像素之间有5.2微米的距离,在宏观上可以看作是连在一起的。但是在微观上,它们之间还有无限的更小的东西存在。这个更小的东西我们称它为“亚像素”。实际上“亚像素”应该是存在的,只是硬件上没有个细微的传感器把它检测出来。于是软件上把它近似地计算出来。

亚像素级别角点检测,提高检测精准度;由于理论与现实总是不一致的,实际情况下几乎所有的角点不会是一个真正的准确像素点。例如(100, 5) 实际上(100.234, 5.789)。

特别是在:跟踪,三维重建,相机校正方面,为了获取更加精准的角点,这样一来就需要亚像素级别角点检测。

亚像素定位方法

  • 插值方法
  • 基于图像矩计算
  • 曲线拟合方法 -(高斯曲面、多项式、椭圆曲面)
OpenCV + CPP 系列(卅四)图像特征提取(亚像素级别角点检测)

除了利用 Harris和 Shi-Tomasi方法进行角点检测 外, 还可以使用cornerEigenValsAndVecs() 函数和 cornerMinEigenVal() 函数自定义角点检测函数。

如果对角点的精度有更高的要求,可以用 cornerSubPix() 函数将角点定位到子像素,从而取得亚像素级别的角点检测效果。

函数简介

函数goodFeaturesToTrack()函数只能提供简单的像素的整数坐标值,若需要实数坐标值则需要使用cornerSubPix()函数,用于寻找亚像素角点的位置:

InputArray image,          输入图像;

InputOutputArray corners,    初始化输入角点与精确的输出坐标

Size winSize,         搜索窗口半径。若Size(5,5),表示(5*2+1)*(5*2+1)=11*11大小的搜索窗口。

Size zeroZone,        表示死区的一半尺寸。为不对搜索区的中央位置做求和运算,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。

TermCriteria criteria      求角点的迭代过程的终止条件。

);

  • 其中:

    cv::TerCriteria::MAX_ITER :迭代终止条件为达到最大迭代次数终止

    cv::TerCriteria::EPS :   迭代到阈值终止

    cv::TerCriteria::MAX_ITER+cv::TerCriteria::EPS :两者都作为迭代终止条件

头文件 ​

​image_feature_all.h​

​:声明类与公共函数

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

class ImageFeature {
public:
  void subpixel_corner_demo(Mat& image);
  
};      

主函数​

​main.cpp​

​调用该类的公共成员函数

#include "image_feature_all.h"



int main(int argc, char** argv) {
  const char* img_path = "D:\\Desktop\\jianzhu.jpg";
  Mat image = imread(img_path);
  if (image.empty()) {
    cout << "图像数据为空,读取文件失败!" << endl;
  }
  ImageFeature imgfeature;
  imgfeature.subpixel_corner_demo(image);

  imshow("image", image);
  waitKey(0);
  destroyAllWindows();
  return 0;
}      

演示像素坐标检测

检测出角点,再拟合亚像素级别角点位置

static void on_subpixel(int num_corner, void* userdata) {
  Mat image = *((Mat*)userdata);
  Mat gray_src;
  cvtColor(image, gray_src, COLOR_BGR2GRAY);

  if (num_corner < 5) { num_corner = 5; }

  //角点检测Shi-Tomasi
  vector<Point2f> corners;
  double qualityLevel = 0.03;
  double minDistance = 10;
  int blockSize = 3;
  bool userHarris = false;
  double k = 0.04;
  goodFeaturesToTrack(gray_src, corners, num_corner, 
    qualityLevel, minDistance, Mat(), blockSize, userHarris, k);
  cout << "corners.size() = " << corners.size() << endl;

  //可视化
  RNG rng(12345);
  Mat resultImg = gray_src.clone();
  cvtColor(resultImg, resultImg, COLOR_GRAY2BGR);
  for (size_t i = 0; i < corners.size(); i++){
    Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
    circle(resultImg, corners[i], 2, color, 1, 8, 0);
  }
  imshow("subpixel", resultImg);

  //拟合亚像素角点位置并计算亚像素角点位置
  Size winSize = Size(5, 5);
  Size zerozone = Size(-1, -1);
  TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
  cornerSubPix(gray_src, corners, winSize, zerozone, tc);

  //打印精细坐标
  for (size_t i = 0; i < corners.size(); i++){
    cout << (i + 1) << ".point[x,y]\t" << corners[i].x << "," << corners[i].y << endl;
  }
  return;
}


void ImageFeature::subpixel_corner_demo(Mat& image) {
  cv::namedWindow("subpixel", WINDOW_NORMAL);
  int num_corner = 100;
  int max_corner = 500;
  createTrackbar("CorNum", "subpixel", &num_corner, max_corner, on_subpixel, (void*)&image);
  on_subpixel(0, &image);
}