Jetson Nanoでやってみたかったことの一つに、人の「動線分析」があります。
お店や工場の中で、ここはよく通る、ここはあまり人が通らないという人の動きを視覚化する、その動きを分析することでお店の商品配列や工場内の配置を変えるというのを「動線分析」と言います。
例えば、こんな感じのやつ。
こういう人が歩く場所の動画から、
こんな感じに、人が通ったところほどに色が付き、特に人が滞留しやすい場所ほど赤く表示させる(ヒートマップ化)というやつを、このJetson Nanoにやらせてみました。
(ちなみに上の画像は、Jetson Nanoの”/usr/share/visionworks/sourses/data”の中にあったデモ用の動画”pedestrians.mp4”を使って作ってます)
なお、この人検出 → ヒートマップ化するというコードが、全然ネット上に公開されていないんです。
やっぱり、動線分析って商売になるからでしょうかね。いくらググっても、動線分析のソフトやベンダーばかりが引っ掛かって見つかりません。
悔しいので、なんとか試行錯誤して無理矢理作りましたので、公開します。
あ、これ以降は、下記サイトで紹介した「Jetson Nano 2GB版+USBカメラでの物体検出」を実行済みであることが前提です。
Jetson Nano 2GBを購入しました: EeePCの軌跡
それじゃ、順番に解説。
流れとしては、1.Jetson Nanoで人検出データを作成(csvファイル) → 2.PCにてヒートマップ化 の2つです。
リアルタイムに、ヒートマップ表示できるわけではありません。また、Jetson Nanoだけで完結できませんでした。
その辺りは予め、ご了承ください。
【1.人検出データ】
Jetson Nanoで人の位置を検出させます。
そのために前回使ったDockerを使って、物体検出させて、”人”として認識されたボックスの中心座標を、CSVファイルとして出力させます。
その作業ディレクトリを作成。
ホームディレクトリの直下に「heatmap」というディレクトリを作りました。
$ mkdir heatmap
でその中に、以下のコードをエディターで書きこみ、「detect.py」という名前で保存しておきます。
import jetson.inference
import jetson.utils
net = jetson.inference.detectNet("ssd-mobilenet-v2", threshold=0.5)
camera = jetson.utils.videoSource("/dev/video0") # '/dev/video0' for V4L2
display = jetson.utils.videoOutput("display://0") # 'my_video.mp4' for file
frg=0
with open("./xydetect.csv","w") as f:
while display.IsStreaming():
img = camera.Capture()
if frg==0:
jetson.utils.saveImageRGBA('test.png',img,img.width,img.height)
frg=1
detections = net.Detect(img)
display.Render(img)
display.SetStatus("Object Detection | Network {:.0f} FPS".format(net.GetNetworkFPS()))
for detect in detections:
#print(detect.ClassID,",",detect.Confidence,",",detect.Center)
datad = str(detect.ClassID) + ","+str(detect.Confidence) + "," + str(detect.Center)
data2 = str(detect.Center).replace("(","").replace(")","") + "\n"
print(datad)
if detect.ClassID==1:
f.write(data2)
かなり無理やり感たっぷりなコードです。ご了承ください。
その後に、以下のコマンドでDockerを起動。
$ docker/run.sh --volume ~/heatmap:/heatmap
このコマンドで、先ほど作った「heatmap」というディレクトリがDocker内でもマウントされて、先ほどのプログラムコードにアクセスできます。
いや、最初、これが全然わからなくて、苦労しました。なにせ、Dockerというもの自体が初めてなもので。
で、sudoのパスワードを打ち込むと、Dockerに入ります。
そこで、すかさずディレクトリを移動。
$ cd /heatmap
と入力して、先ほど作ったディレクトリに移動します。
ここで、
$ python3 detect.py
と実行すると、USBカメラからの画像から物体を検出するコードが走り出します。
こんな感じです。
しばらく、うろうろします。
止めるときは、実行したターミナルで「Ctrl+C」を押します。
で、Jetson Nano内でファイルマネージャーを開き、「ホームフォルダ」の「heatmap」の中を覗くと、「test.png」「xydetect.csv」という2つのファイルができているかと思います。
「test.png」・・・USBカメラの1枚目の画像
「xydetect.csv」・・・人の動きの中心座標
この2つのファイルを、どうにかしてPCへ持っていきます。
私はTeratermのSSH_SCPの転送を使いましたが、USBメモリーを挿して持って行ってもOKです。
【2.PCにてヒートマップ化】
すべてJetson Nano上で完結したかったのですが、Jetson Nanoって、どういうわけかOpenCVがうまく使えないんですよね(入って入るようですが、メモリー不足のようなメッセージが出て動作せず)。
おまけにpipコマンドまで省かれているので、もう手も足も出ません。
というわけで、データ取得後から先は、PCで作業します。
うちのPCには、Python 3.7.9、ライブラリとしては、OpenCV、numpy、matplotlibが入ってます。まずは、この辺りを予め入れておいてください。
そこで、以下のコード「heatmap_detect.py」を適当なフォルダに入れておきます。
import numpy as np
import matplotlib.pyplot as plt
import csv
import math
import cv2
#画像サイズを入力
xmax = 1280
ymax = 720
x = []
y = []
with open("./xydetect.csv","r") as f:
reader = csv.reader(f)
for line in reader:
x.append(math.floor(float(line[0])))
y.append(math.floor(float(line[1])))
# 無理やり画像サイズを入れる
x.append(0)
x.append(xmax)
y.append(0)
y.append(ymax)
# numpy形式に変換
x = np.array(x)
y = np.array(y)
heatmap, xedges, yedges = np.histogram2d(y, x, bins=10)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
heatmap = cv2.resize(heatmap, (xmax,ymax))
heatmapshow = None
heatmapshow = cv2.normalize(heatmap, heatmapshow, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
heatmapshow = cv2.applyColorMap(heatmapshow, cv2.COLORMAP_JET)
cv2.imshow("Heatmap", heatmapshow)
cv2.waitKey(0)
img1 = cv2.imread('test.png')
img1 = cv2.resize(img1,(xmax,ymax))
alpha = 0.5
blended = cv2.addWeighted(img1, alpha, heatmapshow, 1 - alpha, 0)
cv2.imwrite('image.png',blended)
# 結果を表示する。
plt.imshow(cv2.cvtColor(blended, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()
このコードと同じ場所に、先ほどの「test.png」「xydetect.csv」を入れておきます。
そういえば、うちのカメラは「1280×720」の解像度なので、コード中のxmax、ymaxを上のようにしてますが、そこは「test.png」の解像度に合わせておいてください。
で、あとはこれを走らせます。Windows PowerShellあたりで上のコードの入ったフォルダに入った後、
$ python heatmap_detect.py
と実行するだけ。
以下のような画像(image.png)が出力されるはずです。
ちょっと見づらいですが、写真とヒートマップが重なってます。
入り口付近と、その少し横の引き戸の辺りにピークがありますね。この両方を往復していたため、こういうデータが得られたわけですが。
なお、先ほどの人検出したCSVファイルをプロットしてみると、こんな感じでした。
部屋が狭いので、ほとんど横しか動けてませんね。多少、しゃがんだりはしたのですが、もうちょっと派手に動けば、よかったですかね?
このため、ヒートマップもほぼ真横にしか色がついてません。
これが公園や、あるいは食品売り場のような、人が縦横にうろうろする場所に設置すれば、もっと面白い絵が取れるんでしょうけど。
ともかく、これで一応、目的のものは作れました。
なお、「detect.py」の5行目、
camera = jetson.utils.videoSource("/dev/video0")
を
camera = jetson.utils.videoSource("./pedestrians.mp4")
とすると、動画ファイルを読み込んでくれます。
また、24行目にあるif文
のClassIDを
1から3に変えると、「人」ではなく、「車」を検出するコードに早変わりです。
試しに、うちの隣の駐車場を写してみましたが……あまり物体検出精度が高くなくて、微妙なヒートマップができました。
正面から撮れば、もうちょっと色が付いたであろうと思われます。
こんな感じに、いろいろと使えそうです。
Raspberry Pi 4あたりと変わらないくらいの廉価なJetson Nano 2GBですが、こんなものが作れてしまうとは……正直、驚いてます。
とりあえず私はこれを使って、会社内の工場や会議場内の人の動線分析をやろうかと企んでます。
これと、以前に紹介した「Raspberry Pi 4でmediapipeを動かしてみた: EeePCの軌跡」も使えば、様々な人の動きがデータ化できます。
こうなったら、会社中をRaspberry PiとJetson Nanoだらけにしてやろうかと。
少々、使い勝手の悪いコードですが、使ってみたいという方はご自由に。
最近のコメント