天天看點

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);
}