画像認識技術で空港の飛行機数を数えてツイート投稿する

休日にやることないから、画像認識を使って羽田空港の飛行機を数えてツイートに投稿するシステムを開発しました。
Darkflowというオープンソースの物体検出のライブラリを使います。

Darkflow のgitリンク
https://github.com/thtrieu/darkflow

環境：
Windows 10
Anaconda3
tensorflow 1.14.0
Python 3.7.0
OpenCV 4.5.1

まず、githubからクーロンしましょう
```
git clone https://github.com/thtrieu/darkflow.git
cd darkflow
```

darkflow-masterフォルダ内にbinフォルダを作成し、binフォルダ内にyolo.weightsをダウロードしましょう。
★少し時間かかります。直接ウェブサイトからダウンロードしてもOKです。
```
cd bin
pip download  https://pjreddie.com/media/files/yolov2.weights
```


darkflowをインストールする．
★darkflowディレクトリで実行（どれでもいい）
```
python3 setup.py build_ext --inplace
pip3 install -e .
pip3 install .
```

今回こちらの羽田空港のYoutubeライブカメラを使います。
https://www.youtube.com/watch?v=Ls9p1htZsXM



ツイートを利用するためのライブラリをインストール
```
pip install tweepy
```

出力結果の整形するライブラリをインストール
```
pip install pprint
```


実際のソースコード

```
#!/usr/bin/env python
# coding: utf-8

from darkflow.net.build import TFNet
import cv2
import pafy
import numpy as np
import tweepy
from pprint import pprint
import datetime

#タイムゾーン設定
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')


# API情報を記入
BEARER_TOKEN  = "自分の情報"
ACCESS_TOKEN = "自分の情報"
ACCESS_TOKEN_SECRET = "自分の情報"
API_KEY = "自分の情報"
API_SECRET = "自分の情報"

# ツイートクライアント関数を作成
def ClientInfo():
    client = tweepy.Client(bearer_token    = BEARER_TOKEN,
                           consumer_key    = API_KEY,
                           consumer_secret = API_SECRET,
                           access_token    = ACCESS_TOKEN,
                           access_token_secret = ACCESS_TOKEN_SECRET,
                          )
    
    return client


# ツイート送信関数
def CreateTweet(message):
    tweet = ClientInfo().create_tweet(text=message)
    return tweet

#yolov2.weightsを使用、Darkflowはyolov3以上非対応のようです。
options = {"model": "cfg/yolo.cfg", "load": "bin/yolov2.weights", "threshold": 0.1}
tfnet = TFNet(options)

# カメラの起動
#cap = cv2.VideoCapture(0)
                   
#ライブカメラ映像
url="https://www.youtube.com/watch?v=Ls9p1htZsXM"
video = pafy.new(url)
best = video.getbest(preftype="mp4")
cap = cv2.VideoCapture(best.url)

# 動画のプロパティを取得
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_num = cap.get(cv2.CAP_PROP_FRAME_COUNT)

# 動画のプロパティを表示
print("WIDTH:", width)
print("HEIGHT:", height)
print("FPS:", fps)
print("FRAME NUM:", frame_num)

#クラス種別
class_names = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle',
              'bus', 'car', 'cat', 'chair', 'cow', 'diningtable',
              'dog', 'horse', 'motorbike', 'person', 'pottedplant',
              'sheep', 'sofa', 'train', 'tvmonitor']

num_classes = len(class_names)
class_colors = []
for i in range(0, num_classes):
    hue = 255*i/num_classes
    col = np.zeros((1,1,3)).astype("uint8")
    col[0][0][0] = hue
    col[0][0][1] = 128
    col[0][0][2] = 255
    cvcol = cv2.cvtColor(col, cv2.COLOR_HSV2BGR)
    col = (int(cvcol[0][0][0]), int(cvcol[0][0][1]), int(cvcol[0][0][2]))
    class_colors.append(col)

def main():
    
    while(True):
        # 動画ストリームからフレームを取得
        ret, frame = cap.read()
        result = tfnet.return_predict(frame)
        n=0
        for item in result:
            tlx = item['topleft']['x']
            tly = item['topleft']['y']
            brx = item['bottomright']['x']
            bry = item['bottomright']['y']
            label = item['label']
            conf = item['confidence']
            
            #confで精度はある程度調整できます。
            if conf > 0.3:
                
                for i in class_names:
                    if label == i:
                        class_num = class_names.index(i) 
                        #飛行機をカウントする
                        if label=='aeroplane':
                            n=n+1   
                        break
                    
                    
                #枠の作成
                cv2.rectangle(frame, (tlx, tly), (brx, bry), class_colors[class_num], 2)

                #ラベルの作成
                text = label + " " + ('%.2f' % conf)
                cv2.rectangle(frame, (tlx, tly - 15), (tlx + 100, tly + 5), class_colors[class_num], -1)
                cv2.putText(frame, text, (tlx, tly), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,0), 1)
                
        # メッセージを指定
        now = datetime.datetime.now(JST)
        t = now.time().strftime('%X') 
        print(t)
        message = "Python自動送信：現在時刻"+t+"、羽田空港の飛行機数は"+str(n)+"機"+"ライブ映像：https://www.youtube.com/watch?v=T8yogtAUSfg"
        #12時になったら自動送信
        if t=='12:00:00':
            print(message)
            pprint(CreateTweet(message))
        
       
        
        # 表示
        img_resize = cv2.resize(frame,dsize=(800, 600))
        cv2.imshow("Videos Detection", img_resize)
       

        # qを押したら終了。
        k = cv2.waitKey(10);
        if k == ord('q'):  break;

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':    
    main() 
```

実行コード
```
Python aeroplane_count.py
```


飛行機が認識されていた様子
![aeroplane_count.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879756/be7df1ad-d6b1-5489-0bde-fbd114aa57f9.png)


設定された時刻になったらツイートも送信されました。