Jetaon Nanoで異物を検出したいのですが、まずは使い慣れたPCで作ってみます。
カメラ
エレコム WEBカメラ 200万画素 マイク内蔵 MAC対応 ブラック UCAM-C0220FBNBK
2015年に買った、2000円くらいの安物です。
レア過ぎて価格にプレミアがついていました。
インストール
参考にさせていただきました:https://www.kkaneko.jp/tools/win/cv2.html
Python3.7.7を入れました。最新の3.8.2はOpenCVが対応してない可能性があるのでやめておきました。
https://www.python.org/downloads/release/python-377/
cmdでpythonとして、 3.7.7が起動させます。
pip listをすると
pip 19.2.3
setuptools 41.2.0
WARNING: You are using pip version 19.2.3, however version 20.0.2 is available.
You should consider upgrading via the ‘python -m pip install –upgrade pip’ command.
とバージョンアップを促されるので、
python -m pip install –upgrade pip
します。
numpyも入れてみましょう。
pip install numpy で1.18.2が入りました。
ではVScodeを起動させて、メニュー>view > select interpriter で python 3.7.7を選択し・・・たいところですが、ありません。
一旦VScodeを起動させ(まだpython 3.7.7は表示されない。)、そしてVScode下部のターミナルで一旦pythonを起動させます。3.7.7が起動しました。
スクリプト実行を何回かさせて、numpyがないよー、と言われながら、interpriterの指定を開くと,python 3.7.7が選択できるようになっていました。
で、スクリプトを実行すると、今度はcv2がないよー、といわれましたので、cmdから
1 |
$ py -m pip install -U opencv-python |
で入れました。opencv-python-4.2.0.34が入りました。
cmdでエラーが出るときは、Power Shellを管理者権限で開いてやってみましょう。
で、確認です。
1 2 3 4 |
> python import cv2 cv2.__version__ '4.2.0' |
その他のライブラリをインストール
VScodeをいじっているとpylintをいれてと促されるので
$ -m pip install -U pylint –user
いろんなライブラリも同時に勝手にはいりました。
Package Version
—————– ——–
astroid 2.3.3
colorama 0.4.3
isort 4.3.21
lazy-object-proxy 1.4.3
mccabe 0.6.1
numpy 1.18.2
opencv-python 4.2.0.34
pip 20.0.2
pylint 2.4.4
setuptools 41.2.0
six 1.14.0
typed-ast 1.4.1
wrapt 1.11.2
エラー
height, width, channels = frame.shape
AttributeError: ‘NoneType’ object has no attribute ‘shape’
frame.shepe[:3]にして、カメラ番号を1から0に変更したら内蔵カメラが映りました。
webカメラをUSBに指して、カメラ番号1に戻したらこれでも映りました。
ということで[:3]は関係なかったと思われます。
画像処理
OpenCVで
案1:BGR画像→グレースケール→検査窓トリミング→現在画像→二値化
案2:BGR画像→グレースケール→検査窓トリミング→初期画像と現在画像を差分を取る→二値化
の2案をやってみましたが、結果的に案1を採用します。
案1は、背景が動いたりカメラが動いたりしたときでも、検出精度は変わらない利点があります。
案2は、初期画像と現在画像を比較するので、背景になんらかの均一でない構造があったとしても、初期画像と現在画像で比較するので異物検出のロバスト性が高くなる利点があります。しかし、背景が動いたりカメラが動いたりして、初期画像とのズレが発生するとと検出できないか検出精度が大幅に落ちます。
今回は、よりシンプルな案1でやっていきます。
結果
スクリプトを回すと自動的に時系列データがcsvファイルとして入ってきます、
グラフ化
作成されたcsvファイル読み込んでグラフ化しました。
横軸が時間、縦軸は検出された異物(黒)の画素数です。
検出画像
開始5秒の画像は異物がない、まっさらな状態です。影一つありません。
開始15秒で5mm程度の異物を置いてみました。黒い点が現れます。
開始60秒で1mmと5mm程度の異物を置いてみました。どちらも黒い点になって現れます。200万画素の安物カメラでもこれだけの精度が用意に出ます。
周囲の照明コンディションが良ければ、0.5mm程度までは検出できそうです。
メインスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
import numpy as np import cv2 import datetime import time def interval_file_save(date, current_time, time_cnt, pixels_objects, pixels_percentile): # データをcsvファイルとして書き込む file_path = r'C:\Users\omoiy\scripts\shihenDetector\data\data.csv' with open(file_path, mode='a') as f: f.write(str(date) + ',' + str(current_time) + ',' + str(time_cnt)+ ',' + str(pixels_objects) + ',' + str(pixels_percentile) + '\n') def onTrackber(position): # 2値化のスレショルド用トラックバー global threshold threshold = position if __name__ == '__main__': # グローバル変数を定義 ESC_KEY = 27 # Escキー INTERVAL= 33 # インターバル FRAME_RATE = 30 # フレームレートfps # 検出範囲を指定 x_min = 150 x_max = 640 - x_min y_min = 120 y_max = 480 - y_min # 使用カメラの選択 1: 外部UVCカメラ DEVICE_ID = 1 # カメラ映像を取得 cap = cv2.VideoCapture(DEVICE_ID) # 初期フレームを読み込む end_flag, frame = cap.read() height, width, channels = frame.shape # 基準画像の定義 img_bgr = frame img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # RGB to Gray:Y←0.299⋅R+0.587⋅G+0.114⋅B img_partial_gray = img_gray[y_min : y_max, x_min : x_max] img_reference = img_partial_gray # 初期フレームは以後のフレームより若干暗いので補正係数を掛けて調整する c = 1.0 # 補正係数 img_reference = (img_reference * c ).astype('uint8') # 初期画像 以後、この画像を基準して参照する #2値化用トラックバー #cv2.namedWindow("img_diff_abs_adbinary") #threshold = 100 #初期値 #cv2.createTrackbar("track", "img_diff_abs_adbinary", threshold, 255, onTrackber) # 画像処理 date_init = datetime.datetime.now() pixels_objects = 0 # 初期値 pixels_percentile = 0 # 初期値 pixels_objects1 = 0 # 初期値 pixels_percentile1 = 0 # 初期値 # データ保存の時間間隔(インターバル) initial_time = datetime.datetime.now() # 初期値 time_cnt = 0 # sec 初期値 time_interval = 5 # sec 定期実行の時間間隔 # OpenCV リアルタイム処理開始 while end_flag == True: # 現在時間 sec current_time = (datetime.datetime.now() - initial_time).total_seconds() # 画像行列の取得と変換 img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) img_partial_gray = img_gray[y_min : y_max, x_min : x_max] # BGRカラー画像を表示する img_bgr = frame line_thickness = 10 cv2.rectangle(img_bgr, (x_min-line_thickness, y_min-line_thickness), (x_max+line_thickness, y_max+line_thickness), color=(100,100,255), thickness = line_thickness) cv2.imshow('img_bgr', img_bgr) # グレースケール画像を表示する #cv2.imshow('img_gray', img_gray) # 基準画像を表示する cv2.imshow('img_referece', img_reference) # リアルタイム画像を表示する cv2.imshow('img_partial_gray', img_partial_gray) # リアルタイム画像と基準画像との差を算出して表示する (負の場合は絶対値が自動的に取られる) img_diff_abs = np.abs(img_partial_gray - img_reference) cv2.imshow('img_diff_abs', img_diff_abs) # リアルタイム画像と基準画像との差の2乗算出して表示する #img_diff_sqared = np.square(img_partial_gray - img_reference) #cv2.imshow('img_diff_sqared', img_diff_sqared) # リアルタイム画像と基準画像との差の画像をアダプティブスレッショルドで2値化する # img_diff_abs_adbinary = cv2.adaptiveThreshold(img_diff_abs, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, threshold) #アダプティブスレショルド # cv2.imshow("img_diff_abs_adbinary", img_diff_abs_adbinary) # リアルタイム画像と基準画像との差の画像を固定スレッショルドで2値化する ret, img_diff_abs_binary = cv2.threshold(img_diff_abs,127, 255, cv2.THRESH_BINARY) # 上記img_diff_abs_binary画像の異物の画素数と割合を出力する(ただし1フレーム前) txt = str(time_cnt) + 's,' + str(pixels_objects) + 'pix,{:.2f}%'.format(pixels_percentile) cv2.putText(img=img_diff_abs_binary, text=txt, org=(10, 30), fontFace=cv2.FONT_HERSHEY_PLAIN, fontScale=1.5, color=(255, 255, 255), thickness=1, lineType=cv2.LINE_AA) cv2.imshow("img_diff_abs_binary", img_diff_abs_binary) # リアルタイム画像の画像を固定スレッショルドで2値化する ret, img_partial_gray_binary = cv2.threshold(img_partial_gray,127, 255, cv2.THRESH_BINARY) # 上記img_partial_gray_binary画像の異物の画素数と割合を画面内に出力する(ただし1フレーム前) txt1 = str(time_cnt) + 's,' + str(pixels_objects1) + 'pix,{:.2f}%'.format(pixels_percentile1) cv2.putText(img=img_partial_gray_binary, text=txt1, org=(10, 30), fontFace=cv2.FONT_HERSHEY_PLAIN, fontScale=1.5, color=(0, 0, 0), thickness=1, lineType=cv2.LINE_AA) cv2.imshow("img_partial_gray_binary", img_partial_gray_binary ) #cv2.imwrite(image_path + '.new.jpg', img) # ==== 異物の画素数をカウントする ==== # 検出窓の画素数 pixels_window = (img_partial_gray_binary.shape[0] * img_partial_gray_binary.shape[1]) # ---- 異物の画素数 と 割合 をターミナルに出力する ---- # img_diff_abs_binary リアルタイム画像と基準画像との差の画像を固定スレッショルドで2値化した画像 date = datetime.datetime.now() pixels_objects = np.count_nonzero(img_diff_abs_binary == 255) pixels_percentile = pixels_objects / pixels_window * 100 print(date, ' : ', date - date_init, ' : ', '異物の画素数 / pixels: ', pixels_objects, '割合 / % : {:.2f}'.format(pixels_percentile)) # img_partial_gray_binary リアルタイム画像の画像を固定スレッショルドで2値化した画像 pixels_objects1 = np.count_nonzero(img_partial_gray_binary == 0) pixels_percentile1 = pixels_objects1 / pixels_window * 100 print(date, ' : ', date - date_init, ' : ', '異物の画素数 / pixels: ', pixels_objects1, '割合 / % : {:.2f}'.format(pixels_percentile1)) # ---- time_interval毎に定期実行する内容 ---- if current_time >= time_cnt : # データをテキストデータとしてファイルに書き出す if time_cnt == 0: pixels_objects = 0 # フレーム1枚目はカメラの光量不足で大きな数値になってしまうので強制的にゼロにする else: interval_file_save(date, current_time, time_cnt, pixels_objects1, pixels_percentile1) # 画像データを保存する # file_path = r'C:\Users\omoiy\scripts\shihenDetector\img.jpg' numbering = time_cnt file_path = r'C:\Users\omoiy\scripts\shihenDetector\img\img_' + str(numbering) + r'.jpg' cv2.imwrite(file_path, img_partial_gray_binary) # 次回、定期実行する時刻 time_cntを更新 time_cnt += time_interval # sec # Escキーで終了 key = cv2.waitKey(INTERVAL) if key == ESC_KEY: break # 次のフレーム読み込み end_flag, frame = cap.read() # 終了処理 cv2.destroyAllWindows() cap.release() |
グラフ作成スクリプト
pandasとmatplotlibを使うのでインストールします。
pip install pandas
installed pandas-1.0.3 python-dateutil-2.8.1 pytz-2019.3
pip install matplotlib
installed cycler-0.10.0 kiwisolver-1.2.0 matplotlib-3.2.1 pyparsing-2.4.7
はい、ではスクリプト実行しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import numpy as np import pandas as pd import matplotlib.pyplot as plt file_path = r'C:\Users\omoiy\scripts\shihenDetector\data\data.csv' df = pd.read_csv(file_path, sep=',', names=('date', 'current_time','time_cnt', 'pixels', 'ratio')) print(df.head()) #print(df.current_time, df.time_cnt) plt.plot(df['time_cnt'], df['pixels'], label='pixels') plt.title('Object Pixels', fontsize=18) plt.xlabel('Time / sec', fontsize=18) plt.ylabel('Pixels', fontsize=18) plt.legend(loc='best', fontsize=18) plt.grid() plt.show() # time_cntとcurrent_timeとのズレを確認 : 0~70sec # plt.plot(df.time_cnt, df.current_time-df.time_cnt) # plt.show() |
以上です。