ドローンカメラの画像に、指定物体がいくつ映っているかをリアルタイムに数え上げて、フレーム画像に個数を埋込んでモニタ表示

###やったこと

次の２つの処理を、Python3系でスクリプト化しました。

1. Telloドローンの単眼カメラ画像を、Macbookで受信する。
1. 受信した各瞬間のフレーム画像に、学習済みの物体検出を適用。物体ラベルを指定して、検出個数を自動カウント。
1. 各瞬間のフレーム画像に、検出された物体の輪郭線を重畳表示。
1. 物体ラベル名と検出カウント数を書いた文字列を、OpenCV2のputTextメソッドを使って、「３」のフレーム画像に埋込む。
1. 出来あがった（各瞬間の）フレーム画像をTelloの動きから遅延なしに、リアルタイムにMacbookのウィンドウに表示

これら１〜５の処理を行うにあたり、次の２つの記事を書いた際に作成済みのコードを、部品として使いました。

- [画像ファイルに指定した物体が何個映っているのかを数える処理を自動化してみた](https://qiita.com/electronics_diy721/items/b46850dc30be56049775)
- [Telloドローンのカメラ画像に、現在の飛行高度（ToFセンサ計測高度 ＆ Height高度）を文字列埋込みして、PCモニタ出力した](https://qiita.com/electronics_diy721/items/69f9e2164c3cb73bab40)


###出力結果

今回は、Telloを手で持ち上げて、単眼カメラをテレビ画面と正面向き合う位置に持っていって、Macbookに出力されるフレーム画像を確認しました。

本記事に掲載したスクリプトファイルは、キーボード操作で、Telloを遠隔操作させることができます。
離陸すると、画像フレームの3行目に出力させている__*Height*__が、__*0*__から、__正の数値__に変わります。

今後、特定のキーが押されたら、その瞬間の画像を１枚、Macbookの指定ディレクトリにjpgファイルとしてファイル保存したり、ボタンを押した後、5秒間、フレーム画像をファイル保存し続ける機能を追加したい。

###__（ フレーム画像出力 ）__

![スクリーンショット 2021-08-04 22.57.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754134/028bf0cf-ff2e-b4fc-9e04-ca29437e3df2.png)

![スクリーンショット 2021-08-04 22.58.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754134/e0aaa837-07df-9ddd-1d45-571982c9b3a8.png)

![スクリーンショット 2021-08-04 22.58.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754134/d6db28c4-f9f8-6ff6-c8b3-1b66bac8b5c5.png)

##（ 後日追記 ）

Telloを離陸させて、テレビ画面の正面でホバリング中にウィンドウに表示されたフレーム画像を、掲載しました。

![スクリーンショット 2021-08-05 0.04.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1754134/c6d5d85c-d12e-888d-e587-16ee4dcfc9d3.png)


####__（ Terminal標準出力 ）__

```bash:Terminal
electron@diynoMacBook-Pro examples % python3 keyboard-control-multi_window_input_text_2.py
[INFO] tello.py - 107 - Tello instance was initialized. Host: '192.168.10.1'. Port: '8889'.
[INFO] tello.py - 422 - Send command: 'command'
[INFO] tello.py - 446 - Response command: 'ok'
[INFO] tello.py - 422 - Send command: 'streamon'
[INFO] tello.py - 446 - Response streamon: 'ok'
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] non-existing PPS 0 referenced
[h264 @ 0x7fc963e16600] decode_slice_header error
[h264 @ 0x7fc963e16600] no frame!
Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 3
Num of detected person(s) is 3
ToF Distane 68 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 3
Num of detected person(s) is 3
ToF Distane 65 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 3
Num of detected person(s) is 3
ToF Distane 64 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 3
Num of detected person(s) is 3
ToF Distane 65 cm
Height 0 cm


0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

[h264 @ 0x7fc963e14c00] error while decoding MB 50 40, bytestream -5
Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 0
Num of detected person(s) is 0
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 0
Num of detected person(s) is 0
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 0
Num of detected person(s) is 0
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

Num of detected person(s) is 1
Num of detected person(s) is 1
ToF Distane 10 cm
Height 0 cm

0.1秒以内に操作コマンドを入力して下さい :
操作コマンド入力時間切れ。次のフレーム画像を読み込みます。

（ 省略 ）


```


###今回作成したスクリプト・ファイル

```Python3:keyboard-control-multi_window_input_text_2.py
from timeout_decorator import timeout, TimeoutError
from djitellopy import Tello
import cv2, math, time
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import cvlib as cvl
import datetime
from cvlib.object_detection import draw_bbox

TIMEOUT_SEC = 0.1

@timeout(TIMEOUT_SEC)
def input_with_timeout(msg=None):
   return input(msg)


tello = Tello()
tello.connect()

tello.streamon()
frame_read = tello.get_frame_read()

#tello.takeoff()

while True:
    # In reality you want to display frames in a seperate thread. Otherwise
    #  they will freeze while the drone moves.
    img = frame_read.frame
    #cv2.imshow("drone", img)
    #cv2.imshow('Canny', cv2.Canny(img, 100, 200))
    #bitwised_img = cv2.bitwise_not(img)
    #cv2.imshow('Bitwised', bitwised_img)
    
    #file_name = str(args.file)
    label_name  = "person"
    image = img
    bbox, label, conf = cvl.detect_common_objects(image)
    output_image = draw_bbox(image, bbox, label, conf)
    #plt.imshow(output_image)
    #plt.show()
    #dt_now = datetime.datetime.now()
    message = "Num of detected {0}(s) is {1}".format(label_name, str(label.count(label_name)))
    print(message)
    input_text_0 = message
    cv2.putText(output_image, str(input_text_0), (0, 50), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA)
    
    #https://djitellopy.readthedocs.io/en/latest/tello/#djitellopy.tello.Tello.query_battery
    time_of_flight_distance_senser_val = tello.get_distance_tof()
    input_text_1 = "ToF Distane {0} cm".format(time_of_flight_distance_senser_val)
    
    height= tello.get_height()
    input_text_2 = "Height {0} cm".format(height)
    
    # Terminal標準出力
    print(input_text_0)
    print(input_text_1)
    print(input_text_2)
    
    # カメラ画像にTelloの現在高度（ToFセンサ計測距離(cm)、高さ（cm)）を埋込む
    cv2.putText(output_image, str(input_text_1), (0, 100), cv2.FONT_HERSHEY_TRIPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
    cv2.putText(output_image, str(input_text_2), (0, 150), cv2.FONT_HERSHEY_TRIPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
    cv2.imshow("Video", output_image)

    #plt.imshow(img)
    #plt.show()
    #output_frame_image_file_name = "dummy"
    #cv2.imwrite(output_frame_image_file_name, img)
    
    #次の行（key = cv2.・・・）を削除すると、画像が受信できなくなる。
    key = cv2.waitKey(1) & 0xff
    
    try:
        msg = input_with_timeout('\n{}秒以内に操作コマンドを入力して下さい :'.format(TIMEOUT_SEC))
        print('\n操作コマンド：　{} を受信しました。\n'.format(msg))
        if msg == "i":
            tello.takeoff()
        elif msg == "w":
            tello.move_forward(30)
        elif msg == "s":
            tello.move_back(30)
        elif msg == "a":
            tello.move_left(30)
        elif msg == "d":
            tello.move_right(30)
        elif msg == "e":
            tello.rotate_clockwise(30)
        elif msg == "q":
            tello.rotate_counter_clockwise(30)
        elif msg == "r":
            tello.move_up(30)
        elif msg == "f":
            tello.move_down(30)
        elif msg == "g":
            tello.land()
    except TimeoutError:
        print('\n操作コマンド入力時間切れ。次のフレーム画像を読み込みます。\n')

tello.land()

```