差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
| tutorial_2 [2022/01/04 01:31] – satoshi | tutorial_2 [2022/05/02 14:09] (現在) – satoshi | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| - | ====== 第2章 スターターコード【初級編 エリア判定(立ち止まり判定)】 ====== | + | ====== 第2章 エリア判定(立ち止まり判定)【初級編】 ====== |
| - | ===== 第一項 SCORER Edge SDKの利用準備 ===== | ||
| - | SCORER People Trackerがインストールされた状態を想定しているため、カメラの設定などはすでに済んでいるものとします。\\ | ||
| - | SCORER Edgeに接続した状態で[[http:// | ||
| - | 新規でプロジェクトを作成し、 | ||
| - | * Instance ID:lesson | ||
| - | * Nickname: | ||
| - | * Environment: | ||
| - | * Network Access: Allow all | ||
| - | とします。\\ | ||
| - | {{:: | ||
| - | SDKインスタンスが立ち上がったらOpen Jupyter Labで起動し、dev@scorerでログインしましょう。\\ | + | ===== 第一項 スターターコードの中身を確認する ===== |
| - | 立ち上げ時はあまりコマンドが登録されていないため\\ | + | |
| - | < | + | |
| - | apt update | + | |
| - | apt install zip | + | |
| - | </ | + | |
| - | を行い、ここにある{{ :: | + | |
| - | < | + | |
| - | unzip tutorial.zip | + | |
| - | </ | + | |
| - | で展開し、\\ | + | |
| - | < | + | |
| - | cd tutorial | + | |
| - | </ | + | |
| - | に行けばチュートリアルに必要なファイルが一式揃う形になります。\\ | + | |
| - | + | ||
| - | ===== 第二項 スターターコードの中身を確認する ===== | + | |
| 第2章のスターターコードは2つのプログラムコードに分かれます\\ | 第2章のスターターコードは2つのプログラムコードに分かれます\\ | ||
| - | 一つがSCORER People | + | 一つがSCORER People |
| もう一つが保存したログからあらかじめ指定したエリア内で立ち止まりがあるかどうかを調べるプログラムとなります。 | もう一つが保存したログからあらかじめ指定したエリア内で立ち止まりがあるかどうかを調べるプログラムとなります。 | ||
| 行 37: | 行 11: | ||
| <sxh Python; | <sxh Python; | ||
| # | # | ||
| + | |||
| # This script dumps various streams | # This script dumps various streams | ||
| 行 49: | 行 24: | ||
| import math | import math | ||
| from datetime import datetime, timedelta, timezone | from datetime import datetime, timedelta, timezone | ||
| + | |||
| # Handle arguments | # Handle arguments | ||
| 行 67: | 行 43: | ||
| DEFAULT_INGRESS_ADDR=" | DEFAULT_INGRESS_ADDR=" | ||
| DEFAULT_INGRESS_TOPICS=[ ' | DEFAULT_INGRESS_TOPICS=[ ' | ||
| + | |||
| ap = argparse.ArgumentParser( | ap = argparse.ArgumentParser( | ||
| 行 89: | 行 66: | ||
| args = ap.parse_args() | args = ap.parse_args() | ||
| + | |||
| # Flatten the lists | # Flatten the lists | ||
| 行 111: | 行 89: | ||
| else: | else: | ||
| json_opts = {' | json_opts = {' | ||
| + | |||
| + | |||
| # -------------------------------- | # -------------------------------- | ||
| 行 129: | 行 109: | ||
| if args.verbose >= level_: | if args.verbose >= level_: | ||
| print(*args_, | print(*args_, | ||
| + | |||
| + | |||
| # -------------------------------- | # -------------------------------- | ||
| 行 137: | 行 119: | ||
| TOPIC_JPEG_FRAME = b' | TOPIC_JPEG_FRAME = b' | ||
| POLL_WAIT_TIME | POLL_WAIT_TIME | ||
| + | |||
| def subscribe_streams(ctx_, | def subscribe_streams(ctx_, | ||
| 行 168: | 行 151: | ||
| | | ||
| raw_json_file = open(dirpath+'/' | raw_json_file = open(dirpath+'/' | ||
| + | |||
| | | ||
| while True: | while True: | ||
| 行 175: | 行 159: | ||
| if events.get(socki) != zmq.POLLIN: | if events.get(socki) != zmq.POLLIN: | ||
| continue | continue | ||
| + | |||
| # Receive a message | # Receive a message | ||
| 行 249: | 行 234: | ||
| socki.close() | socki.close() | ||
| dprint(4,' | dprint(4,' | ||
| + | |||
| + | |||
| # Main | # Main | ||
| 行 290: | 行 277: | ||
| | | ||
| json_open = open(area_def_file, | json_open = open(area_def_file, | ||
| - | result_json_file = open(' | + | result_json_file = open(' |
| areadef = json.load(json_open) | areadef = json.load(json_open) | ||
| resultarr = {} | resultarr = {} | ||
| - | for area in areadef: | + | for areaidx, |
| - | | + | |
| + | print(areaname) | ||
| + | resultarr[areaname]={} | ||
| print(" | print(" | ||
| - | for area in areadef: | + | for areaidx, |
| - | | + | |
| + | print(areaname) | ||
| polygon = [] | polygon = [] | ||
| for point in area[' | for point in area[' | ||
| 行 306: | 行 296: | ||
| for filename in file_list: | for filename in file_list: | ||
| - | print(area[' | + | print(areaname+" |
| if os.path.getsize(filename) == 0: | if os.path.getsize(filename) == 0: | ||
| continue | continue | ||
| 行 318: | 行 308: | ||
| for person in people: | for person in people: | ||
| | | ||
| - | #print(area[' | + | #print(areaname+' |
| personpos = [(person[" | personpos = [(person[" | ||
| test = cv2.pointPolygonTest(contour, | test = cv2.pointPolygonTest(contour, | ||
| if test>0: | if test>0: | ||
| - | if person[' | + | if person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | if resultarr[area[' | + | if resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| else: | else: | ||
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| json.dump(resultarr, | json.dump(resultarr, | ||
| 行 335: | 行 325: | ||
| for areaname, records in resultarr.items(): | for areaname, records in resultarr.items(): | ||
| print(areaname) | print(areaname) | ||
| - | f = open(sys.argv[1]+" | + | f = open(area_def_file[: |
| write = csv.writer(f) | write = csv.writer(f) | ||
| write.writerow([" | write.writerow([" | ||
| 行 341: | 行 331: | ||
| write.writerow([record[' | write.writerow([record[' | ||
| | | ||
| - | f.close() | + | f.close() |
| </ | </ | ||
| 行 347: | 行 337: | ||
| <sxh JavaScript; | <sxh JavaScript; | ||
| - | [ | + | { |
| - | | + | "areas": |
| - | "name": | + | { |
| - | " | + | |
| - | { | + | {" |
| - | | + | {" |
| - | | + | {" |
| - | | + | {" |
| - | { | + | ] |
| - | | + | |
| - | | + | |
| - | | + | |
| - | { | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | { | + | |
| - | | + | |
| - | | + | |
| - | } | + | |
| ] | ] | ||
| - | | + | } |
| - | ] | + | |
| </ | </ | ||
| - | ===== 第三項 座標ログをSCCORERシステムから受け取る ===== | + | ===== 第二項 座標ログをSCORERシステムから受け取る ===== |
| - | 以下、第二項でのプログラムの解説を中心に、最終形を作る過程のプログラムも併せて掲載します。 | + | 以下、前項でのプログラムの解説を中心に、最終形を作る過程のプログラムも併せて掲載します。 |
| SCORERでのデータのやり取りは様々な機能を実現するため、メッセージングキューという比較的複雑な手法で行っています。 | SCORERでのデータのやり取りは様々な機能を実現するため、メッセージングキューという比較的複雑な手法で行っています。 | ||
| 行 619: | 行 598: | ||
| 次はログの情報を記録するプログラムに変更しましょう。\\ | 次はログの情報を記録するプログラムに変更しましょう。\\ | ||
| - | ===== 第四項 座標ログを保存する ===== | + | ===== 第三項 座標ログを保存する ===== |
| - | 第三項でログが取得できることが確認できました。\\ | + | 前項でログが取得できることが確認できました。\\ |
| 受信したデータを一つのファイルに保存してもよいのですが、これらのログは人が多く映っている時は非常に大量にあり、1分間に300-600レコードものデータとなります。\\ | 受信したデータを一つのファイルに保存してもよいのですが、これらのログは人が多く映っている時は非常に大量にあり、1分間に300-600レコードものデータとなります。\\ | ||
| 今回は1分に1ファイル、書きこんでいるファイルに60秒分書きこんだらファイルを次の時刻に切り替えて保存する方式とします。\\ | 今回は1分に1ファイル、書きこんでいるファイルに60秒分書きこんだらファイルを次の時刻に切り替えて保存する方式とします。\\ | ||
| 行 626: | 行 605: | ||
| ちなみに取得したログを見ると、どのカメラからのデータなのか、いつのフレームの時刻なのかの情報がありません。\\ | ちなみに取得したログを見ると、どのカメラからのデータなのか、いつのフレームの時刻なのかの情報がありません。\\ | ||
| - | こちらはデータ取得時の時間を追記して保存しましょう(将来のSCORER People | + | こちらはデータ取得時の時間を追記して保存しましょう(将来のSCORER People |
| さて変更した部分をハイライトしておきます。 | さて変更した部分をハイライトしておきます。 | ||
| 行 864: | 行 843: | ||
| これでログを保存することができるようになりました。\\ | これでログを保存することができるようになりました。\\ | ||
| - | ===== 第五項 SCORER People Trackerから取得できる情報 ===== | + | ===== 第四項 SCORER People Trackerから取得できる情報 ===== |
| SCORER People Trackerの内容やログ形式については1章でも書いたものとなっており、再掲すると\\ | SCORER People Trackerの内容やログ形式については1章でも書いたものとなっており、再掲すると\\ | ||
| {{: | {{: | ||
| となっています。\\ | となっています。\\ | ||
| - | なお、四項で保存したログのサンプルデータ(最初3フレーム分)は | + | なお、前項で保存したログのサンプルデータ(最初3フレーム分)は |
| <sxh JavaScript; | <sxh JavaScript; | ||
| [ | [ | ||
| 行 1043: | 行 1022: | ||
| SCORER People Trackerでは1秒間に5-10回もの高頻度で検知を行っているため、前後の検知結果同士を見比べれば同じ人物であることが容易に推定されます。\\ | SCORER People Trackerでは1秒間に5-10回もの高頻度で検知を行っているため、前後の検知結果同士を見比べれば同じ人物であることが容易に推定されます。\\ | ||
| {{:: | {{:: | ||
| - | 本来は複数の人物がすれ違った時や、画面の外に出てまた入ってきた場合はどうなるかなどいろいろ考えるべきことはありますが、そのあたりはSCORER People | + | 本来は複数の人物がすれ違った時や、画面の外に出てまた入ってきた場合はどうなるかなどいろいろ考えるべきことはありますが、そのあたりはSCORER People |
| 今回は画面内にある人物の判定と前提があるため、あまり気にせず固有IDをそのまま活用していきましょう。\\ | 今回は画面内にある人物の判定と前提があるため、あまり気にせず固有IDをそのまま活用していきましょう。\\ | ||
| - | ===== 第六項 エリア座標判定処理の考え方 ===== | + | ===== 第五項 エリア座標判定処理の考え方 ===== |
| さて、ここでどのようなデータを取得したいか具体的に定義していきましょう。\\ | さて、ここでどのようなデータを取得したいか具体的に定義していきましょう。\\ | ||
| 行 1078: | 行 1057: | ||
| - 固有IDが初めて出てきたら配列に固有IDと座標等を登録。2回目以降は配列に座標を追加。 | - 固有IDが初めて出てきたら配列に固有IDと座標等を登録。2回目以降は配列に座標を追加。 | ||
| - 同時にエリア内にいたかどうかを判定。いた場合は固有ID配列に時刻ごとに「通過したフラグ」を立てる。 | - 同時にエリア内にいたかどうかを判定。いた場合は固有ID配列に時刻ごとに「通過したフラグ」を立てる。 | ||
| - | - 5秒前の情報からエリアにいた(検知漏れも考慮し9割以上の頻度で)場合は立ち止まったと判断して固有IDに「立ち止まったフラグ」を立てる。 | + | - 5秒前の情報からエリアにいた場合は立ち止まったと判断して固有IDに「立ち止まったフラグ」を立てる。 |
| - 全部の集計が完了したら配列をCSVにして出力する。 | - 全部の集計が完了したら配列をCSVにして出力する。 | ||
| という流れになります。\\ | という流れになります。\\ | ||
| 行 1116: | 行 1095: | ||
| なお、これらはカメラの数や試行回数が多くなると途端に面倒になるものですので、次の章ではWEB画面で簡単に設定できるようにしていきます。\\ | なお、これらはカメラの数や試行回数が多くなると途端に面倒になるものですので、次の章ではWEB画面で簡単に設定できるようにしていきます。\\ | ||
| - | <sxh Python; | + | <sxh Python; |
| - | [ | + | { |
| - | | + | "areas": |
| - | "name": | + | { |
| - | " | + | |
| - | { | + | {" |
| - | | + | {" |
| - | | + | {" |
| - | | + | {" |
| - | { | + | ] |
| - | | + | |
| - | | + | |
| - | | + | |
| - | { | + | |
| - | | + | |
| - | | + | |
| - | | + | |
| - | { | + | |
| - | | + | |
| - | | + | |
| - | } | + | |
| ] | ] | ||
| - | | + | } |
| - | ] | + | |
| </ | </ | ||
| 取得した座標と立ち止まり判定継続時間(秒)を設定します。\\ | 取得した座標と立ち止まり判定継続時間(秒)を設定します。\\ | ||
| また、今回のスターターコードでは複数のエリアを同時に設定しても問題ないように書いてあります。\\ | また、今回のスターターコードでは複数のエリアを同時に設定しても問題ないように書いてあります。\\ | ||
| - | <sxh Python; | + | <sxh Python; |
| - | [ | + | { |
| - | | + | "areas": |
| - | "name": | + | { |
| - | " | + | |
| - | { | + | {" |
| - | | + | {" |
| - | | + | {" |
| - | | + | |
| - | { | + | ] |
| - | " | + | |
| - | " | + | { |
| - | }, | + | " |
| - | { | + | {" |
| - | " | + | {" |
| - | | + | {" |
| - | | + | |
| - | { | + | ] |
| - | " | + | |
| - | " | + | |
| - | } | + | |
| ] | ] | ||
| - | | + | } |
| - | { | + | |
| - | " | + | |
| - | " | + | |
| - | { | + | |
| - | " | + | |
| - | " | + | |
| - | }, | + | |
| - | { | + | |
| - | " | + | |
| - | " | + | |
| - | }, | + | |
| - | { | + | |
| - | " | + | |
| - | " | + | |
| - | }, | + | |
| - | { | + | |
| - | " | + | |
| - | " | + | |
| - | } | + | |
| - | ] | + | |
| - | } | + | |
| - | ] | + | |
| </ | </ | ||
| - | ===== 第七項 スターターコードの完成 ===== | + | ===== 第六項 スターターコードの完成 ===== |
| さて、前項でエリア情報をファイルで定義し、判定する手順も決めたため集計プログラムを作成するうえで必要な情報はすべてそろいました。\\ | さて、前項でエリア情報をファイルで定義し、判定する手順も決めたため集計プログラムを作成するうえで必要な情報はすべてそろいました。\\ | ||
| 再掲となりますが判定プログラムがこちらです。 | 再掲となりますが判定プログラムがこちらです。 | ||
| 行 1219: | 行 1163: | ||
| | | ||
| json_open = open(area_def_file, | json_open = open(area_def_file, | ||
| - | result_json_file = open(' | + | result_json_file = open(' |
| areadef = json.load(json_open) | areadef = json.load(json_open) | ||
| resultarr = {} | resultarr = {} | ||
| - | for area in areadef: | + | for areaidx, |
| - | | + | |
| + | print(areaname) | ||
| + | resultarr[areaname]={} | ||
| print(" | print(" | ||
| - | for area in areadef: | + | for areaidx, |
| - | | + | |
| + | print(areaname) | ||
| polygon = [] | polygon = [] | ||
| for point in area[' | for point in area[' | ||
| 行 1235: | 行 1182: | ||
| for filename in file_list: | for filename in file_list: | ||
| - | print(area[' | + | print(areaname+" |
| if os.path.getsize(filename) == 0: | if os.path.getsize(filename) == 0: | ||
| continue | continue | ||
| 行 1247: | 行 1194: | ||
| for person in people: | for person in people: | ||
| | | ||
| - | #print(area[' | + | #print(areaname+' |
| personpos = [(person[" | personpos = [(person[" | ||
| test = cv2.pointPolygonTest(contour, | test = cv2.pointPolygonTest(contour, | ||
| if test>0: | if test>0: | ||
| - | if person[' | + | if person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| - | if resultarr[area[' | + | if resultarr[areaname][person[' |
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| else: | else: | ||
| - | resultarr[area[' | + | resultarr[areaname][person[' |
| json.dump(resultarr, | json.dump(resultarr, | ||
| 行 1264: | 行 1211: | ||
| for areaname, records in resultarr.items(): | for areaname, records in resultarr.items(): | ||
| print(areaname) | print(areaname) | ||
| - | f = open(sys.argv[1]+" | + | f = open(area_def_file[: |
| write = csv.writer(f) | write = csv.writer(f) | ||
| write.writerow([" | write.writerow([" | ||
| 行 1270: | 行 1217: | ||
| write.writerow([record[' | write.writerow([record[' | ||
| | | ||
| - | f.close() | + | f.close() |
| </ | </ | ||
| 実行は\\ | 実行は\\ | ||
| 行 1284: | 行 1231: | ||
| 24行目でエリア情報のファイルを開きます。(JSON形式のため26行目で配列に変換しています)\\ | 24行目でエリア情報のファイルを開きます。(JSON形式のため26行目で配列に変換しています)\\ | ||
| 25行目で結果CSVファイルを新規で開きます。\\ | 25行目で結果CSVファイルを新規で開きます。\\ | ||
| - | 28-29行目では結果データの配列をエリアごとに事前に初期化します。\\ | + | 28-31行目では結果データの配列をエリアごとに事前に初期化します。\\ |
| さて、今回多角形のエリアに対応した判定ができるようなコードとなっています。\\ | さて、今回多角形のエリアに対応した判定ができるようなコードとなっています。\\ | ||
| ここで初めて出てきますが、映像解析でよく使われる計算ライブラリにOpenCVというものがあります。\\ | ここで初めて出てきますが、映像解析でよく使われる計算ライブラリにOpenCVというものがあります。\\ | ||
| このライブラリを利用して多角形内にある点が入っているか入っていないかが簡単に判明します。\\ | このライブラリを利用して多角形内にある点が入っているか入っていないかが簡単に判明します。\\ | ||
| - | 54-55行目\\ | + | 57-58行目\\ |
| < | < | ||
| personpos = [(person[" | personpos = [(person[" | ||
| 行 1307: | 行 1254: | ||
| これは結果データを個体IDごとに集計しなおすことで、一つのIDに判定情報を蓄積していく形としています。\\ | これは結果データを個体IDごとに集計しなおすことで、一つのIDに判定情報を蓄積していく形としています。\\ | ||
| - | 56-64行目ではエリア内だった場合に、初めてその個体IDが現れた場合は初期化をし時刻を書きこみます。二回目以降出てきた場合は時刻を更新して、最初に検知された時刻との差分を増やしていき、5秒以上になった場合は立ち止まり判定フラグにTrueを入れます。\\ | + | 59-67行目ではエリア内だった場合に、初めてその個体IDが現れた場合は初期化をし時刻を書きこみます。二回目以降出てきた場合は時刻を更新して、最初に検知された時刻との差分を増やしていき、5秒以上になった場合は立ち止まり判定フラグにTrueを入れます。\\ |
| なお、エリアに一度も入っていない場合は今回集計対象からは外しています。\\ | なお、エリアに一度も入っていない場合は今回集計対象からは外しています。\\ | ||
| - | 68-76行目では一度すべての情報を配列で集計し終わった後、CSVファイルに書き込む処理をしています。CSVファイルの1行目には列のヘッダー情報を入れておきます。\\ | + | 71-79行目では一度すべての情報を配列で集計し終わった後、CSVファイルに書き込む処理をしています。CSVファイルの1行目には列のヘッダー情報を入れておきます。\\ |
| 細かいところなどは省きましたが、上記の動作するコードに対して様々な変更を加えて自分なりに動作を確認してみましょう。\\ | 細かいところなどは省きましたが、上記の動作するコードに対して様々な変更を加えて自分なりに動作を確認してみましょう。\\ | ||
| 次の章ではエリア座標を設定する手間を大幅に軽減するためにWEB画面で変更できるところなども学んでみましょう。 | 次の章ではエリア座標を設定する手間を大幅に軽減するためにWEB画面で変更できるところなども学んでみましょう。 | ||