前回のおさらい
除雪の完全自動化を実現するために、機械学習を用いて積雪量を識別できないかと考えています。
そこで積雪量を「少ない」「普通」「多い」で識別できれば、自動除雪機を導入したときに、路上の積雪量を識別して勝手に除雪してくれるようなシステムが作れるのではないかなと思いました。
そのためには、大量の積雪画像のデータセットが必要ですが、現時点では十分な枚数を収集することができていません。
また前回の取り組みでは、以下(右)の積雪画像をそのまま学習用のネットワークに入力すると、積雪の特徴量をうまく得られないことがわかりました。そこで、雪以外の領域を手動でマスク処理したうえでネットワークに入力したところ、以下(左)の画像のように積雪の特徴がより明確に捉えられることが確認できました。

今年の冬、雪が積もる時期に大量の画像を収集できれば、より高精度なモデルの構築が可能になり、取り組める課題の幅も大きく広がるはずです。
そこで今回は、画像が大量にあることを前提に、画像中の不要な領域に対して自動的にマスク処理を行う方法を考えてみます。
自動マスク処理
物体検出に基づくマスク処理
まず、”不要な物体”をどうするかについては物体検出手法が使えると思いました。
物体検出は画像中の人や建物、車などを検出することができ、図1(左)の赤線で示すバウンディングボックスとして得ることができます。
なお、物体検出は物体が画像のどこにあるかを求めるため、検出結果であるバウンディングボックスは(x1, y1), (x2, y2)のように座標で取得します。
図1のように車を検出できれば、ボックス内にマスク処理をすることが簡単にできるようになります。

物体検出でよく使われるモデルではYOLOが有名ですが、より検出精度が高い事前学習済みの*FCOSを用いて実装しました。
from torchvision.models.detection import fcos_resnet50_fpn
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
model = fcos_resnet50_fpn(pretrained=True)
model.to(DEVICE)
model.eval()
# 物体検出のための前処理
img_tensor = F.to_tensor("画像ファイル")
img_tensor = img_tensor.unsqueeze(0).to(DEVICE)
# 物体検出の実行
with torch.no_grad():
predictions = model(img_tensor)
# 検出結果の取得
boxes = predictions[0]['boxes'].cpu().numpy()
scores = predictions[0]['scores'].cpu().numpy()
draw = ImageDraw.Draw(img)
# ボックス座標
x1, y1, x2, y2 = boxes[i]
left_posi = (int(x1), int(y1))
right_posi = (int(x2), int(y2))
draw.rectangle((left_posi, right_posi), outline = (255, 0, 0), width = 3)
事前学習とは、MSCOCOなどの大規模なデータを活用して学習を済ませた状態のモデルを指します。
このモデルを使うことで、専用のデータセットを作成せずとも車などを検出することが可能です。
機械学習モデルを実装するには、インスタンスなど設定をする必要があるので何十行と必要となるのでそこそこ大変です。
が、今の時代CursorなどAIエージェントがあるので、Cursorを使用してみました!
やりたいことを明確に入力できれば、ものの数分でソースコードを生成してくれてとても便利です。
(このようなツールを使用したことがなかったので便利すぎて感動しました笑)
画像の輝度情報に基づくマスク処理
物体検出によるマスク処理は有効であるものの、図3のように雪や他のオブジェクトに遮られたりすると検出ができないという例もありました。

そこで、画像をグレースケールで読み込むことで対応できないか検討してみました。グレースケールで読み込む場合、各画素が0~255階調の輝度情報のみとなるため濃淡がはっきりとします。
その上で画像全体の輝度の平均値を求め、平均値よりも低い箇所にマスク処理をすることで、図4のように検出ができない画像に対してもマスク処理ができるようになります。
img = Image.open(str(img_paths[i])).convert('RGB') #読み込み
#グレースケール変換
gray = img.convert('L')
#pillowからnumpyに変換
img_gray = np.array(gray)
# 画像の平均値を計算
mean_value = np.mean(img_gray)
# 平均値より低いピクセルをマスク
mask = img_gray < mean_value
img_gray[mask] = 0

完成
以上、二つの方法を入力画像に対して適用した結果、図5のように良い感じにマスク処理ができるようになりました。
今後、積雪の画像を大量に収集して識別モデルを作成する際に、今回の方法を適用することで自動でマスク処理ができるようになるのではないかと思います!

おわりに
いかがでしたでしょうか。
今回は積雪画像が大量にある場合に、ネットワークが良い感じに積雪の特徴量を得られるようにするため、不要なオブジェクトに自動でマスク処理をする方法について検討してみました。
次回からは、過去の気象データを用いて、気温や湿度から降雪・積雪があるかどうかを予測するための取り組みをしていく予定です。