はじめに
みなさん、こんにちは。インプルの新入社員 てる です!
4月も今週で終わり、いよいよ5月に入りました。本社のある札幌も春らしい気候になってきており、はちょうど桜が見頃の時期です。
さて、4月から続いたゴミ判別プログラムも、いよいよ4回目を迎えました。前回は、機械学習させたモデルを使って、Streamlitというフレームワークでwebアプリを作りましたね。まだご覧になられてない方、ぜひご覧ください!
機能を充実させてみる
追加する機能
さて、前回作ったwebアプリのおさらいです。
前回は、画像をアップロードしたら、機械学習をさせたモデルがゴミを判断し、それが何ゴミかをテキストで表示してくれるwebアプリを制作しました。しかし、これを実際にスマホで使用することを考えると、使いやすさの面でいくつか問題が出てきそうです。
まず、今の機能ではゴミの画像をアップロードしなければいけませんんが、一度カメラアプリを開いて写真を撮って、ゴミ出しアプリを開いて画像を入れる作業は、少し煩わしいですよね。写真を撮ってゴミの画像がアップロードされるところまで、アプリで完結するのが理想です。なので、アプリ内で写真が撮れるようにする機能が必要があります。
また、第1回目の記事でも問題提起しましたが、ゴミ出しの分別方法は全国共通ではなく、自治体によって出し方が変わります。なので、どこに住んでいるのかが何かしらの手段でわかれば、その場所のゴミの種類を教えてあげることができます。なので、位置情報が取得できるようにする機能が必要があります。
使用する技術
さて、先ほど追加する機能であげた二つの機能ですが、今回は以下の方法を使って実装してみます。
まず、アプリ内で写真が撮れるようにする機能ですが、これはwebアプリを制作する際に使用したStreamlitというフレームワークの機能に実装されています。st.camera_input()というものを使えば、いままで実装してきた内容に付け足すだけで機能を追加できそうです!

続いて、位置情報が取得できるようにする機能ですが、これは2通りの方法が考えられます。
まず一つ目は、IPアドレスを使う方法。この方法では、ipinfoと呼ばれるサービスを利用して位置情報を求めることにしました。ですが、IPアドレスでおおよその国や都道府県までは特定できるものの、詳細な位置までは特定することが難しいです。

そこで、精度を高めるもう一つの方法として、GPSを使う方法があります。これは、端末の位置情報サービスを利用して、座標から位置を求める方法です。今回は、geopyと呼ばれるライブラリを使用して位置情報を得ることにしました。geopyは、Pythonでジオコーディング(座標から現在地を特定すること)を簡単に行えるライブラリです。
今回は、この両方を使って、精度の高い方を採用していくことにしましょう!
また、判別されたゴミが、現在位置では何ゴミになるのか、データベースを作る必要もあります。
ですが、今回はあくまで上記の2つの機能実装をメインとしているため、3つの都市のゴミ分別データから正しく判定できているか確認することにします。そのため、jsonファイルを作って、そこから地域に応じたゴミの名前を読み込ませる形にします。
今回はjsonファイルでのテキストデータにはなりますが、よりしっかりとしたデータベースがあれば、多くのデータを取り扱うこともできますし、もしゴミの出し方に変更があった際も、気楽に対応することが可能です。データベースとの連携は、次回以降のテーマとして取り扱いたいと思います!
作っていこう!
カメラの実装
まずはカメラの実装をしていきましょう!
カメラの実装では、先ほど紹介したStreamlitで、st.camera_input()というものを使えば、実装できると紹介しました。一度、カメラだけが機能するように作成してみましょう。
カメラで写真を撮影し、表示させるようにプログラムを組んでみました。果たしてうまく実行できるでしょうか。
camera_image = st.camera_input("写真を撮影してください")
if camera_image is not None:
image = Image.open(camera_image).convert("RGB")
st.image(image, caption="撮影された画像", use_column_width=True)
これを実行してみると、無事にカメラ機能が動作し、写真を撮影することも出来ました。カメラを起動させるときには、ブラウザの許可が必要になるので、許可してあげましょう。(許可しないと動作しません!)カメラ機能は問題なさそうですね。



位置情報の実装
IPアドレスのパターンを作ってみる
IPアドレスの場合、先ほど紹介したipinfoというサービスを利用していきます。
まずは現在地が簡単に呼び出せるプログラムを実行して、精度がどうか確認してみましょう。
def get_city():
res = requests.get("https://ipinfo.io")
data = res.json()
return data.get("city", "Unknown")
city = get_city()
print(f"現在地: {city}")
このプログラムを実行すると、現在地は東京と表示されてしまいました・・・。これは実用的ではないですね。

GPSのパターンを作ってみる
続いて、GPSのパターンも作ってみます。
座標(緯度,経度)を入力して現在地を推定するプログラムを作ってみます。先ほども紹介したように、geopyというライブラリを使用して、座標から位置情報を求めます。位置情報を求める際には、Nominatimという地図サービスと連携させて、緯度経度から逆引きさせます。Nominatimは、アカウントやAPIが不要で、無料で利用することができます。一方で、大量アクセスには向いていないので、商用化する際には別のサービスの方が良さそうです。
coords = st.text_input("現在位置(緯度,経度)")
if coords:
try:
lat, lon = map(float, coords.split(","))
geolocator = Nominatim(user_agent="my_gomi_app")
time.sleep(1) # Nominatimへの連続アクセス回避
location = geolocator.reverse((lat, lon), language='ja')
address = location.raw.get('address', {})
city = address.get("city") or address.get("town") or address.get("state") or "不明"
st.success(f"📍 現在地推定: **{city}**")
except Exception as e:
st.error("位置情報の取得に失敗しました。")
st.exception(e)
例えば、札幌駅付近の座標を入れて実行すると、正しく札幌市と表示されました!

さらに、北九州市の小倉駅の座標を入力すると、こちらも正しく北九州市と表示されました!

この精度であれば、実用的に使用できそうです。今回はGPSを採用していきましょう!
データベース
ゴミをカメラで判別し、位置情報を取得した後は、何ゴミに当たるのか判定をしなければいけません。そのためのデータベースを、今回はjsonファイルで作っていきます。
アプリ側には、jsonファイルを読み込ませるためのプログラムを入力してあげます。
今回は以下のプログラムで読み込ませ、area_class_map.jsonというファイル名にしました。
with open("area_class_map.json", encoding="utf-8") as f:
area_class_map = json.load(f)
area_class_map.jsonのファイルの中は以下のような構成にしました。
今回はカメラ機能と位置情報を確認したいため、3つの地域の情報を入力しました。ここで入力している都市名は、先ほど紹介した位置情報で表示された都市名と完全一致していない場合、うまく動作しません。そのため、もし皆さんが実践してみる時は、位置情報の都市名が一致しているか、確認してみてください!
{
"札幌市": {
"燃えるごみ": "燃やせるごみ",
"プラスチック": "容器包装プラスチック",
"ペットボトル": "資源ごみ",
"ダンボール": "資源ごみ(紙類)"
},
"千代田区": {
"燃えるごみ": "可燃ゴミ",
"プラスチック": "プラスチック製包装容器",
"ペットボトル": "資源ごみ",
"ダンボール": "資源ごみ(古紙・ダンボール)"
},
"北九州市": {
"燃えるごみ": "燃やすごみ",
"プラスチック": "プラスチック製包装容器",
"ペットボトル": "資源ごみ",
"ダンボール": "資源ごみ(古紙)"
}
}
実行してみよう
動作確認の方法
今回は次のように動作確認していきます。
まず、判定させるゴミは、すべて燃えるゴミを判定させます。先ほどのダミーデータの項目を見ていただくとわかるのですが、燃えるゴミは地域によって呼び方が若干変わってきます。今回は、これが地域が変わると呼び方(判別)も正しく変わるのか、これを確認していくことにしましょう。正しく変われば成功です!
実行してみよう!
それでは早速実行させてみます。以下のコマンドを入力してブラウザを立ち上げましょう。gomi_app.pyの部分は、みなさんのアプリのファイル名を入れてください!
streamlit run gomi_app.py
すると、ブラウザが立ち上がりますので、まずは位置情報とカメラ機能を許可してましょう。今回は他の地域の座標を入力できるよう、位置情報はオフにしています。無事、立ち上がると下記の写真のようになります。
.png?resize=510%2C287&ssl=1)
位置情報が自動で入力され、現在位置が判定されると、緑の文字で表示されると思います。その後は、Take Photoをクリックすると、写真が撮れるようになっています。操作については問題ないですね!
.png?resize=510%2C287&ssl=1)
最初に、札幌市のパターンでやってみましょう。
まずは位置情報ですが、これは先ほどの通り問題なく動作しています。そして、ティッシュの写真を撮ります。さて、判定はどうなるでしょうか・・・。

写真を撮ると、自動的に判定が始まります。結果は、燃やせるごみと表示されました。先ほどのデータには、札幌市の燃えるゴミの判定は、燃やせるごみとしていたので、正しく動作できているようです!

その後、他の二つの地域も試してみました。すると、東京の千代田区は可燃ゴミ、北九州市は燃やすごみと、表記が正しく変更されたことが確認できました。問題なく動作できているようです。今回作りたかった内容は、無事に成功です!

まとめ
さて、今回は前回webアプリで制作した内容に、カメラと位置情報の機能を追加させて、住んでいる地域のゴミ判別に正しく対応できる仕組みを作ってみました。今回はいつもよりもボリュームたっぷりの内容でしたが、いかがだったでしょうか!?
今回作った内容は、GitHubにも公開していますので、ぜひご覧ください。
ですが、みなさんの地域はデータベースに入っていないと思いますので、この機会に、住んでいる地域の正しいゴミ分別を調べて、データベースに都市名とともに入力してみてください!
もし入力しなかった場合は、default_classesに入力した名前が表示されます。
次回以降は、途中でも話題に上げたデータベースのづくりにも取り組んでいきたいと思います!
ご覧いただき、ありがとうございました!