LoginSignup
4
3

照明の影に影響されない2値化【道具としての OpenCV 画像認識】

Last updated at Posted at 2024-03-20

本記事でできること

元画像(影がかかっている)
green_sample_with_shadow.jpg

普通の2値化(影を除去できていない)
Screenshot from 2024-03-10 17-46-00.png

適応的2値化(影の影響を受けずに2値化する)
Screenshot from 2024-03-10 17-46-45.png

想定シーン

ロボットをビジュアルフィードバック制御する

カメラを使ったロボットアームの制御方法を、ビジュアルフィードバック制御といいます。ここでは、ビジュアルフィードバック制御を使った自動化システムを開発する際に必要となる画像認識プログラムをまとめています。

照明の影に影響されない2値化

画像認識プログラムを制作しているとき、照明環境やロボットの姿勢、認識対象物の位置などによって、影ができてしまい、明るさのムラが撮像したカメラ画像に現れることがあります。この影がある画像では、普通の2値化をかけても除去することができず、撮像後のカメラ画像内に一律で明るさ調整をかけても除去することが難しいです。そこで、適応的2値化という単純な明るさだけではなく、輪郭の情報を併用する2値化手法を用いることがあります。本記事では適応的2値化のプログラムを掲載します。

実行環境

  • OS:Ubuntu 22.04 LTS
  • 言語:C++
  • クルーボ:v5.1.0

クルーボについて

ロボットアプリケーション開発には、株式会社チトセロボティクスのロボット制御ソフトウェア「クルーボ」を使用します。本記事のプログラムは、クルーボがインストールされた制御コンピュータ上で動作します。

プログラム抜粋

cv::Mat adaptive_binary(const cv::Mat& image, const int block_size, const int param_c) {
    cv::Mat image_gray = convert_grayscale(image);
    cv::Mat binary_image;
    int input_block_size = block_size;
    if (input_block_size < 3) {
        input_block_size = 3;
    }
    if (input_block_size % 2 == 0) {
        input_block_size = block_size + 1;
    }
    cv::adaptiveThreshold(
            image_gray,
            binary_image,
            255,
            cv::ADAPTIVE_THRESH_GAUSSIAN_C,
            cv::THRESH_BINARY,
            input_block_size,
            param_c);
    return binary_image;
}

全体プログラム

#include <iostream>
#include <opencv2/opencv.hpp>

cv::Mat load_image(const std::string file_path) {
    cv::Mat loaded_image = cv::imread(file_path, 1);
    if (loaded_image.empty()) {
        throw std::runtime_error("画像を正常に読み込めませんでした。");
    }
    return loaded_image;
}

cv::Mat convert_grayscale(const cv::Mat& image) {
    const int grayscale_channel_num = 1;
    if (image.channels() == grayscale_channel_num) {
        return image.clone();
    }
    cv::Mat gray_image;
    cvtColor(image, gray_image, cv::COLOR_BGR2GRAY);
    return gray_image;
}

cv::Mat binary(const cv::Mat& image, const int threthold) {
    cv::Mat image_gray = convert_grayscale(image);
    cv::Mat binary_image;
    if (threthold == 0) {
        threshold(image_gray, binary_image, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
    } else {
        threshold(image_gray, binary_image, threthold, 255, cv::THRESH_BINARY);
    }
    return binary_image;
}

cv::Mat adaptive_binary(const cv::Mat& image, const int block_size, const int param_c) {
    cv::Mat image_gray = convert_grayscale(image);
    cv::Mat binary_image;
    int input_block_size = block_size;
    if (input_block_size < 3) {
        input_block_size = 3;
    }
    if (input_block_size % 2 == 0) {
        input_block_size = block_size + 1;
    }
    cv::adaptiveThreshold(
            image_gray,
            binary_image,
            255,
            cv::ADAPTIVE_THRESH_GAUSSIAN_C,
            cv::THRESH_BINARY,
            input_block_size,
            param_c);
    return binary_image;
}

int main(void) {
    std::string file_path = "../data/green_sample_with_shadow.jpg";
    const cv::Mat loaded_image = load_image(file_path);

    cv::Mat binary_image = binary(loaded_image, 0);
    cv::imshow("window", binary_image);

    cv::Mat adaptive_binary_image = adaptive_binary(loaded_image, 50, 10);
    cv::imshow("window", adaptive_binary_image);
}

おわりに

人手作業をロボットアームで自動化するために、カメラを使ったロボット制御=ビジュアルフィードバック制御が大切です。
ロボット制御用の画像認識でも中身のひとつひとつはシンプルなので、要素に分解して解説していきたいと思います。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3