C++とOpenCVで学ぶハフ変換による直線と円の検出実践ガイド
C++でのOpenCVハフ変換は、画像から直線や円などの形状を抽出する技術です。
処理前にエッジ検出や平滑化を行い、OpenCVのHoughLines
やHoughCircles
を使用して特徴を捉えます。
簡単に形状検出が可能なため、実用的な画像解析に柔軟に対応できます。
ハフ変換の基本
直線検出の仕組み
画像中の直線を検出する処理は、エッジ検出で得られた情報をもとに、画像内の点をパラメータ空間に変換して投票を行う方法です。
具体的には、各点から直線のパラメータである距離
この仕組みでは、画像のエッジ情報が直線の特徴として反映されるため、エッジ検出の正確さが重要となります。
円検出の仕組み
円の検出は、直線検出と同様に、エッジ情報を利用します。
画像中の各点に対して、円の中心位置と半径の組み合わせを求め、投票によって候補円を特定します。
この方法では、円のパラメータ(中心座標と半径)を正確に設定することで、目的の円を効率よく検出することができます。
C++ OpenCVでの実装例
直線検出の実装
コード例と説明
以下のサンプルコードは、直線検出の実装例です。
コード中には簡単なコメントを入れて、各処理の目的を示しています。
#include <opencv2/opencv.hpp>
#include <vector>
#include <cmath>
int main() {
// 画像をグレースケールで読み込みます
cv::Mat grayImage = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
if (grayImage.empty()) {
return -1;
}
// Cannyエッジ検出でエッジを抽出します
cv::Canny(grayImage, grayImage, 50, 150);
// ハフ変換で直線を検出します
std::vector<cv::Vec2f> lines;
cv::HoughLines(grayImage, lines, 1, CV_PI / 180, 100);
// 結果表示のため、グレースケール画像をカラーに変換します
cv::Mat colorImage;
cv::cvtColor(grayImage, colorImage, cv::COLOR_GRAY2BGR);
// 検出された直線を赤色の線で描画します
for (size_t i = 0; i < lines.size(); i++) {
float rho = lines[i][0];
float theta = lines[i][1];
double a = std::cos(theta);
double b = std::sin(theta);
double x0 = a * rho;
double y0 = b * rho;
cv::Point pt1(cvRound(x0 + 1000 * (-b)), cvRound(y0 + 1000 * (a)));
cv::Point pt2(cvRound(x0 - 1000 * (-b)), cvRound(y0 - 1000 * (a)));
cv::line(colorImage, pt1, pt2, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
}
// 結果のウィンドウを表示します
cv::imshow("Detected Lines", colorImage);
cv::waitKey(0);
return 0;
}
円検出の実装
コード例と説明
次に、円検出の実装例を示します。
画像に対してガウシアンブラーで平滑化を行い、ハフ変換で円を検出する処理です。
#include <opencv2/opencv.hpp>
#include <vector>
int main() {
// 画像をグレースケールで読み込みます
cv::Mat grayImage = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
if (grayImage.empty()) {
return -1;
}
// ガウシアンブラーでノイズを低減します
cv::GaussianBlur(grayImage, grayImage, cv::Size(9, 9), 2, 2);
// ハフ変換で円を検出します
std::vector<cv::Vec3f> circles;
cv::HoughCircles(grayImage, circles, cv::HOUGH_GRADIENT, 1, grayImage.rows / 8, 100, 30, 1, 30);
// 結果表示のため、グレースケール画像をカラーに変換します
cv::Mat colorImage;
cv::cvtColor(grayImage, colorImage, cv::COLOR_GRAY2BGR);
// 円の中心と輪郭を描画します
for (size_t i = 0; i < circles.size(); i++) {
cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// 中心を緑色の小さな円で描画します
cv::circle(colorImage, center, 3, cv::Scalar(0, 255, 0), -1);
// 外枠を赤色で描画します
cv::circle(colorImage, center, radius, cv::Scalar(0, 0, 255), 3);
}
// 結果のウィンドウを表示します
cv::imshow("Detected Circles", colorImage);
cv::waitKey(0);
return 0;
}
パラメータ設定と調整
直線検出向けパラメータ
距離 と角度 の設定
直線検出では、パラメータの分解能を設定する必要があります。
は画像内の各点から直線までの最短距離で、1ピクセル単位の分解能を設定することが一般的です。 は角度の分解能で、通常は (1度刻み)の設定が用いられます
これらのパラメータを適切に設定することで、直線の検出精度を向上させることができます。
円検出向けパラメータ
円の検出条件と閾値
円検出では、以下のパラメータを調整することで、目的の円を正確に抽出することができます。
- 検出器の解像度(最小値や最大値など)
- 投票の閾値として用いるパラメータ
- 検出した円同士の最低距離(最小中心間距離)
これらの設定は、画像のサイズや円の大きさによって調整する必要があり、実際の画像に合わせて試行錯誤することがおすすめです。
エッジ検出と前処理
Cannyエッジ検出の利用
ハフ変換を適用する前に、画像のエッジ情報を明確に抽出する必要があります。
cv::Canny
関数を用いると、画像内のエッジがしっかりと抽出され、直線や円の検出精度が向上します。
また、エッジが明瞭になることで、不要なノイズの影響を軽減できます。
ガウシアンブラーの適用
円検出の前処理として、ノイズ除去のためにガウシアンブラーを適用することがおすすめです。
cv::GaussianBlur
関数を利用することで、画像の平滑化が行われ、エッジ検出時の誤検出が減少します。
この前処理により、画像全体が柔らかくなり、円検出の結果も安定しやすくなります。
まとめ
直線と円の検出には、各種前処理やパラメータ設定が重要なポイントです。
実装例を参考に、画像処理の特性に合わせた調整を実施すれば、成果が期待できます。
今回紹介した内容を元に、柔軟な検出手法をぜひ試してみてください。