知能情報学実験及び演習(田中)第3回目

エッジ抽出

 

エッジ抽出

一次微分とオペレータの関係

Sobelオペレータ cv::Sobel

 

縦と横にエッジがある画像に対して,Sobelオペレータを試してみる.

 

#include <iostream>

#include <opencv2/opencv.hpp>

#include "pragma230.h"

#define THRESHOLD 128

int main(int argc, char **argv)

{

string window_capture = "original"; // 入力した画像用

cv::namedWindow(window_capture, CV_WINDOW_AUTOSIZE);

string window_edge = "edge"; // 出力エッジ画像用

cv::namedWindow(window_edge, CV_WINDOW_AUTOSIZE);

cv::Mat frame=cv::imread("lena.jpg");

cv::Mat gray, sobel, edge, edge_x, edge_y, result;

cv::cvtColor(frame, gray, CV_BGR2GRAY);

cv::Sobel(gray, sobel, CV_16S, 1, 0, 3);

cv::convertScaleAbs(sobel, edge_x);

cv::Sobel(gray, sobel, CV_16S, 0, 1, 3);

cv::convertScaleAbs(sobel, edge_y);

edge = edge_x + edge_y;

// 画像を表示する

cv::imshow(window_capture, frame);

cv::imshow(window_edge, edge);

int key = cv::waitKey(0);

return 0;

}

以下のプログラムは、細線化の機能を追加したものである。

#include <iostream>

#include <opencv2/opencv.hpp>

#include "pragma230.h"

#define THRESHOLD       128             // 輪郭線抽出の閾値

 

//

// 画像に細線化を施す

//

// 引数:

//     sourceImage : 入力画像

//     Image       : 出力画像

//

 

void Thinning(cv::Mat &img)

{

  const int width = img.cols;

  const int height = img.rows;

 

  cv::rectangle(img, cv::Point(0, 0), cv::Point(width - 1, height - 1), CV_RGB(0, 0, 0));

 

       

  // 4近傍用縮退処理用のカーネルを生成

  cv::Mat four_neighbor = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));

 

  int step = 0; // 繰り返し数

  while (1) {

    step++;             // 繰り返し数を1増やす

 

    // 候補画素 = 4近傍に0画素が一つ以上存在する1画素 を選び出す

 

    cv::Mat candidate = img.clone();

    cv::erode(candidate, candidate, four_neighbor);

    candidate = img - candidate;

 

    // 左上から順次走査して、削れるか、削れないかを判断する

    int num_delete = 0;         // 削った画素の数を保持

    for (int y = 1; y < height - 1; y++) {

      for (int x = 1; x < width - 1; x++) {

          if (candidate.at<uchar>(y, x) == 0) { // 自分が候補?

          continue;     // そうでなければ次の画素の処理へ

        }

        // 周辺画素の状態を調べる

        int dx[8] = {1,  1,  0, -1, -1, -1,  0,  1};

        int dy[8] = {0, -1, -1, -1,  0,  1,  1,  1};

        unsigned char n[8];     // 隣接画素の状態

        int num = 0;                    // 隣接画素数

        for (int i = 0; i < 8; i++) {

          n[i] = img.at<uchar>(y + dy[i], x + dx[i]) ? 1 : 0;

          num += n[i];

        }

        // 8近傍に"1"が三つ以上なかったら、消去できないので次の画素へ

        if (num < 3) {

          continue;

        }

        // 連結数を調べる

        int m = 0;      // 連結数

        for (int i = 0; i < 4; i++) {

          int k0 = i * 2;

          int k1 = (k0 + 1) % 8;

          int k2 = (k1 + 1) % 8;

          m += n[k0] * (1 - n[k1] * n[k2]);

        }

        // 連結数が1ならその画素は消去可能なので、消去する

        if (m == 1) {

          img.at<uchar>(y, x) = 0;

          num_delete++;

        }

      }

    }

 

    // 終了判定

    if (num_delete == 0) {              // 一画素も削らなかったら

      break;                                    // 終了

    }

  }

}

 

int main(int argc, char **argv)

{

  string window_capture = "capture"; // キャプチャした画像用

  cv::namedWindow(window_capture, CV_WINDOW_AUTOSIZE);

  string window_edge = "edge"; // キャプチャした画像用

  cv::namedWindow(window_edge, CV_WINDOW_AUTOSIZE);

  string window_output = "output"; // 結果表示用

  cv::namedWindow(window_output, CV_WINDOW_AUTOSIZE);

       

  cv::VideoCapture cap;

  cap.open(0);

  if (!cap.isOpened()) {

    std::cerr << "cannot find camera or file" << std::endl;

    return -1;

  }

 

  while (1) {

    cv::Mat frame;

    cap >> frame;

 

    cv::Mat gray, sobel, edge, edge_x, edge_y, result;

    cv::cvtColor(frame, gray, CV_BGR2GRAY);

    cv::Sobel(gray, sobel, CV_16S, 1, 0, 3);

    cv::convertScaleAbs(sobel, edge_x);

    cv::Sobel(gray, sobel, CV_16S, 0, 1, 3);

    cv::convertScaleAbs(sobel, edge_y);

    edge = edge_x + edge_y;

    cv::threshold(edge, result, THRESHOLD, 255, CV_THRESH_BINARY);

 

    // 細線化

    Thinning(result);

 

    // 画像を表示する

    cv::imshow(window_capture, frame);

    cv::imshow(window_edge, edge);

    cv::imshow(window_output, result);

 

    int key = cv::waitKey(5);

    if (key == 3 || key == 27 || key == 'q') {

      break;

    }

  }

  return 0;

}

 

 

 

説明 cv::Matクラスでの演算(左の列)は、右の列のようにして行うことができる。

X = A + B

X = A + B

X = A - B

X = A - B

X = AB

X = A * B

X = A-1 (逆行列)

X = A.inv()

X = AT (転置行列)

X = A.t()

X = AB (内積)

X = A.dot(B)

X = A × B (外積)

X = A.cross(B)

|A| (行列式)

determinant(A)

 

---------------------------------------------------------------------------------------------