2値化は,文字をファックスで送る場合やOCR(Optical Character Reader)によって文字認識を行う場合などに必要となる処理.
白い紙に文字が印刷されている文書を,保存/再現のためスキャナで画像データ化する場合には,
このような場合,中間値をなくし,完全な白または黒の2値の画像に変換することを2値化という.
2値画像(binary image)は,ある値以上の画素値を1(白画素)に,それ未満の画素値を0(黒画素)に変換することにより得られる.この値をしきい値(threshold)とよび,この変換をしきい値処理という.
2値化の方法:p-タイル法,モード法,判別分析法 など
//07Binarization.cpp
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat gray_img, bin_img1, bin_img2;
cv::Mat input_img = cv::imread("VRML.jpg"); // RGB画像を読み込み
cvtColor(input_img, gray_img, cv::COLOR_BGR2GRAY); //カラー画像をグレースケール変換 (R1~)
// cvtColor(input_img, gray_img, CV_BGR2GRAY); //カラー画像をグレースケール変換 (~H30)
cv::threshold(gray_img, bin_img1, 200, 255, cv::THRESH_BINARY);
//閾値200で2値画像に変換
cv::threshold(gray_img, bin_img2, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
//閾値を自動で設定
imshow("Input_img", input_img); //ウィンドウに画像表示
imshow("THRESH 200", bin_img1); //ウィンドウに画像表示
imshow("THRESH_OTSU", bin_img2); //ウィンドウに画像表示
cv::waitKey(0); //任意のキーが押されるまで待機
return 0;
}
輪郭追跡(contour tracking):連結成分の境界を求めること
ラスタスキャン:画像の左上を起点に,左端から右に画素を調べ,もし,右端に着いたら,行を一つ下がって,左端から右に画素を調べる操作
//08ContourTracking.cpp
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int main()
{
cv::Mat gray_img, bin_img;
cv::Mat input_img = cv::imread("VRML.jpg"); //画像を読み込み
cv::cvtColor(input_img, gray_img, cv::COLOR_BGR2GRAY); //カラー画像をグレースケール変換 (R1~)
// cv::cvtColor(input_img, gray_img, CV_BGR2GRAY); //カラー画像をグレースケール変換 (~H30)
cv::threshold(gray_img, bin_img, 180, 255, cv::THRESH_BINARY); //2値画像に変換
imshow("input_img", input_img); //ウィンドウに画像表示
imshow("gray_img", gray_img); //ウィンドウに画像表示
imshow("bin_img1", bin_img); //ウィンドウに画像表示
std::vector<std::vector<cv::Point>> contours;
//CV_RETR_LIST: すべての輪郭を抽出し,それらをリストに保存する
//CV_CHAIN_APPROX_NONE: 輪郭の全部の点を出力
std::vector<cv::Vec4i> hierarchy;
findContours(bin_img, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
// findContours(bin_img, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); //*200719時点
// findContours(bin_img, contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); //"*"で動かない場合はこちらを試してみてください。
imshow("bin_img2", bin_img); //ウィンドウに画像表示
// 抽出した輪郭を描画
cv::Mat contourImage(bin_img.size(), CV_8U, cv::Scalar(255));
const int drawAllContours = -1;
cv::drawContours(contourImage, contours, drawAllContours, cv::Scalar(0));
cv::imshow("contourImage", contourImage); //ウィンドウに画像表示
cv::waitKey(0); // 任意のキーが押されるまで待機
return 0;
}
膨張(dilation):背景や穴に接する対象の画素をひとまわり加える処理.処理を繰り返すと穴は小さくなり,やがて消え去る.出力ピクセル値は,入力ピクセル近傍の中で,すべてのピクセルの最大値となる.画素値は黒が0,白が1なので,最大値は白となります.つまり,白の領域が増えることになる.
収縮(erosion):背景や穴に接する対象の画素をひとまわりはぎとる処理.処理を繰り返すと対象の領域は小さくなり,やがて消え去る.出力ピクセル値は,入力ピクセル近傍の中で,すべてのピクセルの最小値となる.画素値は黒が0,白が1ですので,最小値は黒となる.つまり,黒の領域が増えることになる.
クロージング(closing):同じ回数だけ,膨張したのち収縮する処理.画像の小さな穴を除くことができる.
オープニング(opening):同じ回数だけ,収縮したのち膨張する処理.画像の小さな連結成分を除くことができる.
//09ErosionDilation.cpp
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
int main()
{
cv::Mat input_img = cv::imread("ErosionDilation.jpg");
if (input_img.empty()) return -1;
cv::Mat d1_img, d5_img, e1_img, e5_img, ed5_img, de5_img;
cv::dilate(input_img, d1_img, cv::Mat(), cv::Point(-1, -1), 1); //膨張 1回
cv::dilate(input_img, d5_img, cv::Mat(), cv::Point(-1, -1), 5); //膨張 5回
cv::erode(input_img, e1_img, cv::Mat(), cv::Point(-1, -1), 1); //収縮 1回
cv::erode(input_img, e5_img, cv::Mat(), cv::Point(-1, -1), 5); //収縮 5回
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::morphologyEx(input_img, ed5_img, cv::MORPH_OPEN, kernel, cv::Point(-1, -1), 5); //オープニング
cv::morphologyEx(input_img, de5_img, cv::MORPH_CLOSE,kernel, cv::Point(-1, -1), 5); //クロージング
// cv::dilate(e5_img, ed5_img, cv::Mat(), cv::Point(-1, -1), 5); //収縮 5回->膨張 5回:オープニング
// cv::erode(d5_img, de5_img, cv::Mat(), cv::Point(-1, -1), 5); //膨張 5回->収縮 5回:クロージング
cv::imshow("input_img", input_img); //ウィンドウに画像表示
cv::imshow("Bocho_d1_img", d1_img); //ウィンドウに画像表示
cv::imshow("Bocho_d5_img", d5_img); //ウィンドウに画像表示
cv::imshow("Shushuku_e1_img", e1_img); //ウィンドウに画像表示
cv::imshow("Shushuku_e5_img", e5_img); //ウィンドウに画像表示
cv::imshow("Opening_ed5_img", ed5_img); //ウィンドウに画像表示
cv::imshow("Closing_de5_img", de5_img); //ウィンドウに画像表示
cv::waitKey(0); //任意のキーが押されるまで待機
return 0;
}
07Binarization.cppでは,閾値に固定値としてプログラム中に直接書き込んだ.一方,入力画像の状況は多様であり,ユーザがインタラクティブ(対話的)に閾値を変更できるようにしたい.そこで,下記の目的を達成するシステムを実装してください.
目的:トラックバー(スライダー)を取り付けて,ユーザが,マウスでインタラクティブに移動させて,閾値を変更させ,その2値化処理結果をリアルタイムに表示する.