線・ラインを描く【道具としての OpenCV 画像認識】

# 本記事でできること
始点と終点を指定して線を描き、支点から長さと角度を指定して線を描いて画像を画面に表示します。
![draw_line.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37698/6d8f27cc-dbab-bdd0-7e67-da18c1623aee.png)

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

## 線・ラインを描く
カメラ画像を使用したロボット制御をする際、画像中の基点を表示したい、画像中に分割線を表示したい、認識物体の移動ベクトルを図示したいなど、線やラインを描きたいと思うことがあります。本記事では、線描画の指示の仕方を２種類ご紹介します。

# 実行環境
- OS：Ubuntu 22.04 LTS
- 言語：C++
- クルーボ：v5.1.0

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

- クルーボの製品サイト：https://chitose-robotics.com/product

## プログラム抜粋
```c++
cv::Mat draw_line(
        const cv::Mat image,
        const cv::Point start,
        const cv::Point finish,
        const cv::Scalar color,
        const int thickness) {
    cv::Mat drawed_image = image.clone();
    cv::line(drawed_image, start, finish, color, thickness, cv::LINE_4);
    return drawed_image;
}

cv::Mat draw_line(
        const cv::Mat image,
        const cv::Point start,
        const float length,
        const float angle,
        const cv::Scalar color,
        const int thickness) {
    cv::Point finish;
    finish.x = start.x + static_cast<int>(length * cos(DEG2RAD(-angle)));
    finish.y = start.y - static_cast<int>(length * sin(DEG2RAD(-angle)));
    return draw_line(image, start, finish, color, thickness);
}
```

## 補足：角度のラジアンへの変換
描画に三角関数を使用するときにラジアン変換が必要になります。よく使うので、お助け関数として覚えておくと便利です。
```c++
float DEG2RAD(const float degree) { return degree * (static_cast<float>(M_PI) / 180.f); }
```

## 全体プログラム
```c++
#include <iostream>
#include <opencv2/opencv.hpp>

float DEG2RAD(const float degree) { return degree * (static_cast<float>(M_PI) / 180.f); }

cv::Mat draw_line(
        const cv::Mat image,
        const cv::Point start,
        const cv::Point finish,
        const cv::Scalar color,
        const int thickness) {
    cv::Mat drawed_image = image.clone();
    cv::line(drawed_image, start, finish, color, thickness, cv::LINE_4);
    return drawed_image;
}

cv::Mat draw_line(
        const cv::Mat image,
        const cv::Point start,
        const float length,
        const float angle,
        const cv::Scalar color,
        const int thickness) {
    cv::Point finish;
    finish.x = start.x + static_cast<int>(length * cos(DEG2RAD(-angle)));
    finish.y = start.y - static_cast<int>(length * sin(DEG2RAD(-angle)));
    return draw_line(image, start, finish, color, thickness);
}

int main(void) {
    const cv::Size image_size(1280, 960);
    const cv::Mat black_image = cv::Mat::zeros(image_size.height, image_size.width, CV_8UC3);

    cv::Mat drawed_image = black_image.clone();

    const cv::Point start_point(100, 100);
    const cv::Point finish_point(900, 600);
    const cv::Scalar color_red(0, 0, 255);
    drawed_image = draw_line(drawed_image, start_point, finish_point, color_red, 10);

    const float length = 350.f;
    const float angle = 70.f;
    const cv::Scalar color_white(255, 255, 255);
    drawed_image = draw_line(drawed_image, start_point, length, angle, color_white, 20);

    cv::imshow("drawed_image", drawed_image);
    cv::waitKey(0);
}

```

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