RobotVision勉強会第6回の内容メモです。開発環境はUbuntu16.04、OpenCV3.2.0です。第6回は膨張、収縮を使ったノイズ除去です。これはとても良く使います。C++言語とOpenCV APIで実装しましょう。
以下のサンプルプログラムは「OpenCVによる画像処理入門、小枝、上田、中村著、講談社」のサンプルコードを参考に作成しました。
1.サンプルコード
/ Robot Vision勉強会 sample6.cpp // 2017-11-30 // フィルタ処理のサンプルプログラム #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <opencv2/core/core.hpp> #include <iostream> // 膨張(4近傍) void myDilate(cv::Mat img_src, cv::Mat& img_dst) { int height = img_src.rows; // 画像の高さ[pixel] int width = img_src.cols; // 画像の幅[pixel] for (int y =1; y < height-1; y++) { for (int x = 1; x < width-1; x++) { if ((img_src.data[y * width + x] == 255) || (img_src.data[y * width + (x-1)] == 255) || (img_src.data[y * width + (x+1)] == 255) || (img_src.data[(y-1) * width + x] == 255) || (img_src.data[(y+1) * width + x] == 255)) { img_dst.data[y * width + x] = 255; } else { img_dst.data[y * width + x] = 0; } } } } // 収縮(4近傍) void myErode(cv::Mat img_src, cv::Mat& img_dst) { int height = img_src.rows; // 画像の高さ[pixel] int width = img_src.cols; // 画像の幅[pixel] for (int y =1; y < height-1; y++) { for (int x = 1; x < width-1; x++) { if ((img_src.data[y * width + x] == 0) || (img_src.data[y * width + (x-1)] == 0) || (img_src.data[y * width + (x+1)] == 0) || (img_src.data[(y-1) * width + x] == 0) || (img_src.data[(y+1) * width + x] == 0)) { img_dst.data[y * width + x] = 0; } else { img_dst.data[y * width + x] = 255; } } } } int main() { cv::Mat src; std::string WINDOW_NAME_ORG = "Original image"; std::string WINDOW_NAME_BINARY = "Binary image"; std::string WINDOW_NAME_DILATE = "Dilate"; std::string WINDOW_NAME_ERODE = "Erode"; std::string WINDOW_NAME_MY_DILATE = "My Dilate"; std::string WINDOW_NAME_MY_ERODE = "My Erode"; // 画像のロード src = cv::imread("winkit2.jpg", cv::IMREAD_COLOR); if(src.empty()) { std::cerr << "Failed to open image file." << std::endl; return -1; } int height = src.rows; // 画像の高さ[pixel] int width = src.cols; // 画像の幅[pixel] int step = src.step; // 1行のチャンネル総数 int c = src.channels(); // チャンネル数 cv::Mat tmp = src.clone(); cv::Mat gray_img, binary_img; // ウィンドウの生成 cv::namedWindow(WINDOW_NAME_ORG, CV_WINDOW_AUTOSIZE); cv::namedWindow(WINDOW_NAME_BINARY, CV_WINDOW_AUTOSIZE); cv::namedWindow(WINDOW_NAME_DILATE, CV_WINDOW_AUTOSIZE); cv::namedWindow(WINDOW_NAME_MY_DILATE, CV_WINDOW_AUTOSIZE); cv::namedWindow(WINDOW_NAME_ERODE, CV_WINDOW_AUTOSIZE); cv::namedWindow(WINDOW_NAME_MY_ERODE, CV_WINDOW_AUTOSIZE); while (true){ src.copyTo(tmp); cvtColor(tmp, gray_img, CV_BGR2GRAY); // 2値化 int thresh = 200; //cv::threshold(gray_img, binary_img, thresh, 255, cv::THRESH_BINARY); cv::threshold(gray_img, binary_img, 0, 255, cv::THRESH_BINARY| cv::THRESH_OTSU); cv::imshow(WINDOW_NAME_BINARY, binary_img); cv::Mat dilate_img; cv::Mat erode_img; cv::Mat my_dilate_img = cv::Mat::zeros(height, width, CV_8U); cv::Mat my_erode_img = cv::Mat::zeros(height, width, CV_8U); cv::Mat element4 = (cv::Mat_<uchar>(3,3) << 0,1,0,1,1,1,0,1,0); cv::dilate(binary_img, dilate_img, element4, cv::Point(-1,-1), 1); cv::erode(binary_img, erode_img, element4, cv::Point(-1,-1), 1); myDilate(binary_img, my_dilate_img); myErode(binary_img, my_erode_img); cv::imshow(WINDOW_NAME_ORG, tmp); cv::imshow(WINDOW_NAME_DILATE, dilate_img); cv::imshow(WINDOW_NAME_MY_DILATE, my_dilate_img); cv::imshow(WINDOW_NAME_ERODE, erode_img); cv::imshow(WINDOW_NAME_MY_ERODE, my_erode_img); cv::waitKey(1); } return 0; }
2.ハンズオン
(1) このソースコードsample6.tgzをダウンロードして次のコマンドで解凍、コンパイルして実行しよう。
$ tar xvzf sample6.tgz
$ cd sample6
$ cmake .
$ make
./sample6
(2) 膨張を何回か繰り返したあとで、収縮をその回数だけ繰り返す処理をクロージング、収縮を何回か繰り返したあとで、膨張をその回数だけ繰り返す処理をオープニングといいます。それぞれ実装してみよう。
(3) 白黒画像を入手して、それにノイズをランダムに1000点加えるプログラムを作り、ノイズの載った画像にオープニングとクロージングの処理を適用してノイズを除去してみよう。
コメント