はじめに
前回の記事ではWSL2上にOpenCV環境を構築し、画像を表示するところまで解説しました。
今回はグレースケール変換・エッジ検出・2値化といった基本的な画像処理技術を紹介します。
準備
前回と同じプロジェクト構成を使います。src/main.cpp を編集していきます。
my_opencv_project/
├── images/
│ └── meter.jpg
└── src/
├── CMakeLists.txt
└── main.cpp今回はビルドプロセスなどを簡単にするため、ルートディレクトリにMakefileを作成しておきます。
.PHONY: build
build:
mkdir -p build
cd build && cmake ../src && make今後はmake build コマンドでビルドできるようになります。
作成後のプロジェクト構成は以下のようになります。
├── Makefile
├── images/
│ └── meter.jpg
└── src/
├── CMakeLists.txt
└── main.cpp表示ウィンドウのサイズを固定する
前回は cv::imshow() で画像を表示しましたが、ウィンドウサイズが画像サイズに合わせて自動調整されていました。
画像のサイズによってはウィンドウが大きすぎたり小さすぎたりすることがあります。
筆者が使用している画像は大きいサイズのため、ウィンドウサイズを固定して表示したいと思います。
ウィンドウサイズを固定したい場合は、cv::namedWindow() を使ってウィンドウを作成し、cv::WINDOW_NORMAL フラグを指定します。
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat img = cv::imread("images/meter.jpg");
// ウィンドウを作成してサイズを固定
cv::namedWindow("image", cv::WINDOW_NORMAL);
cv::resizeWindow("image", 800, 600); // ウィンドウサイズを800x600に設定
cv::imshow("image", img);
cv::waitKey(0);
return 0;
}グレースケール変換
カラー画像をグレースケール(白黒)に変換するには cv::cvtColor() を使います。
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat img = cv::imread("images/meter.jpg");
// グレースケール変換
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// ウィンドウを作成してサイズを固定
cv::namedWindow("gray", cv::WINDOW_NORMAL);
cv::resizeWindow("gray", 800, 600); // ウィンドウサイズを800x600に設定
cv::imshow("gray", gray);
cv::waitKey(0);
return 0;
}cv::COLOR_BGR2GRAY を指定することで、BGR(Blue/Green/Red)の3チャンネル画像を1チャンネルのグレースケール画像に変換できます。

エッジ検出
エッジ検出には Canny法 がよく使われます。cv::Canny() で実行できます。
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat img = cv::imread("images/meter.jpg");
// グレースケール変換
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// Cannyエッジ検出
cv::Mat edges;
cv::Canny(gray, edges, 50, 150);
// ウィンドウを作成してサイズを固定
cv::namedWindow("edges", cv::WINDOW_NORMAL);
cv::resizeWindow("edges", 800, 600); // ウィンドウサイズを800x600に設定
cv::imshow("edges", edges);
cv::waitKey(0);
return 0;
}cv::Canny() の第3・第4引数はそれぞれ低閾値と高閾値です。値を調整することで検出されるエッジの量を制御できます。

2値化(しきい値処理)
2値化とは、画像の各ピクセルを白(255)または黒(0)に変換する処理です。cv::threshold() を使います。
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat img = cv::imread("images/meter.jpg");
// グレースケール変換
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
// 2値化
cv::Mat binary;
cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);
// ウィンドウを作成してサイズを固定
cv::namedWindow("binary", cv::WINDOW_NORMAL);
cv::resizeWindow("binary", 800, 600); // ウィンドウサイズを800x600に設定
cv::imshow("binary", binary);
cv::waitKey(0);
return 0;
}cv::threshold() の引数は順に、入力画像・出力画像・閾値・最大値・2値化の種類です。cv::THRESH_BINARY では閾値以上のピクセルを最大値(255)に、未満を0にします。

大津の2値化
閾値を自動で決定したい場合は 大津の2値化 が便利です。
cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);cv::THRESH_OTSU フラグを追加するだけで、閾値が自動計算されます。
まとめ
今回はOpenCVを使った基本的な画像処理として以下を紹介しました。
- グレースケール変換:
cv::cvtColor()でカラー→グレースケール - エッジ検出:
cv::Canny()でCannyエッジ検出 - 2値化:
cv::threshold()で固定閾値または大津の2値化
今回利用した画像処理が使えると簡単なメーター読み取りなども実装できるようになります。
次回はメーターの読み取りに挑戦してみたいと思います。


コメント