上一個章節我們透過霍夫線轉換來找到影像上的線,接下來我們要使用霍夫圓轉換找到影像上圓的資訊,包含圓心座標和半徑。
從高中/高職的數學,我們應該學過圓的方程式,其中,r為圓半徑、C(a,b)為圓心座標:
霍夫圓檢測的的核心觀念是在每個輪廓點上畫一個半徑為r的圓,重疊次數最多的交點即為圓心,而檢測出圓的半徑為r,很簡單對吧。
然而理想很美好,現實總是很骨感,可以看到下圖右邊,當我們並不知道圓的半徑為多少,必須透過窮舉的方式求出r值,而大多數的在應用上我們無法預先知道在影像上的圓形半徑多少。好在我們可以透過霍夫梯度方法解決上述的問題,但因為牽扯到過多的數學,這邊就不多做講解。
使用OpenCV中的霍夫轉換方法 cv::HoughCircles
來檢測影像中的圓形。
cv::HoughCircles(grayImage, circles, cv::HOUGH_GRADIENT, 1,200, 100, 30,min_r); // 使用霍夫轉換來檢測圓
grayImage
:這是輸入的灰階影像,也就是待檢測的圓形可能存在的影像。circles
:這是一個向量(vector
),用於存儲檢測到的圓形的參數。每個檢測到的圓形都由三個值表示,分別是圓心的 (x, y) 座標和半徑。cv::HOUGH_GRADIENT
:這是霍夫轉換的方法,指定了使用基於梯度的方法來檢測圓形。1
:這是霍夫轉換的解析度,表示圓心之間的像素距離。在這裡,設定為1,表示每個像素都參與檢測。200
:檢測到的圓的中心之間的最小距離。如果參數太小,除了一個真實的圓之外,還可能會誤檢測到多個附近的圓。如果太大,可能會遺漏一些圓。100
:這是Canny邊緣檢測的閾值,用於檢測影像中的邊緣。30
:這是霍夫轉換的投票閾值,表示檢測一個圓所需的最小投票數。當某個圓形的投票數超過這個閾值時,才被視為有效的圓形。min_r
:這是用於篩選圓形的參數,表示最小圓半徑。只有半徑大於 min_r
的才會被保留。grayImage
進行邊緣檢測。#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/utils/logger.hpp"
using namespace std;
void on_circle_change(int position, void*);
int min_r;
// 存儲灰階影像的變數
cv::Mat grayImage;
vector<cv::Vec2f> lines;
int main()
{
cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_SILENT);
grayImage = cv::imread("C:\\Users\\vince\\Downloads\\test_image6.jpg", cv::IMREAD_GRAYSCALE); // 讀取灰階影像
cv::namedWindow("Circle", cv::WindowFlags::WINDOW_NORMAL); // 建立一個視窗用於顯示圓檢測結果
cv::resizeWindow("Circle", 512.0f * ((float)grayImage.cols / grayImage.rows), 512);
cv::createTrackbar("Min R", "Circle", &min_r, 1000, on_circle_change); // 建立一個滑動條用於調整最小半徑
cv::waitKey(0);
return 0;
}
// 當滑動條"Min R"的值發生變化時調用的函數
void on_circle_change(int position, void*) {
if (min_r == 0)
return;
cv::Mat output;
cv::cvtColor(grayImage, output, cv::COLOR_GRAY2BGR);
vector<cv::Vec3f> circles;
cv::HoughCircles(grayImage, circles, cv::HOUGH_GRADIENT, 1,200, 100, 30,min_r); // 使用霍夫轉換來檢測圓
for( size_t i = 0; i < circles.size(); i++ )
{
cv::Vec3i c = circles[i];
cv::Point center = cv::Point(c[0], c[1]);
cv::circle( output, center,5, cv::Scalar(0,0,255),-1, cv::LINE_AA);
int radius = c[2];
cv::circle(output, center, radius, cv::Scalar(255, 0, 255), 3, cv::LINE_AA);
}
cv::imshow("Circle", output);
}
上方程式碼有誤,霍夫圓變換不需要進行邊緣檢測,把cv::Canny刪掉即可。
我把霍夫轉換的投票閾值,設成35後,就只認到2個圓圈了。
感謝