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点加えるプログラムを作り、ノイズの載った画像にオープニングとクロージングの処理を適用してノイズを除去してみよう。
コメント