顔認識して天気を答える

顔認識して天気を答える

Weather map api で現在地の天気を取得し
Voicevox へ curl で送って音声を作成し
Pygame でしゃべるまではできたので

次にこれを関数にしてkao.pyで実行するようにする

まずはubuntuへ転送して実験する

pip install pygame
pip install --upgrade deepl

で足りないものを入れる

そして

scp weather_* snowpool@192.168.1.69:/home/snowpool/aw10s/

でコピーして

python weather_voice.py

を実行するとubuntu でもできた

次に、このweather_voide.pyのコードを関数にする

現在の天気を取得するコードは

import requests
import json
from datetime import datetime
import deepl

def get_weather_forecast(latitude, longitude, API_key, deepl_auth_key):
    # DeepL Translatorのインスタンスを生成
    translator = deepl.Translator(deepl_auth_key)

    # OpenWeather APIのURL
    url = "https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=hourly,minutely&units=metric&lang=ja&appid={API_key}"
    url = url.format(lat=latitude, lon=longitude, API_key=API_key)

    # APIリクエスト
    response = requests.get(url)
    jsondata = response.json()

    # 今日の日付を取得
    today = datetime.now().date()

    # 今日の天気予報を探す
    for daily_forecast in jsondata["daily"]:
        date = datetime.fromtimestamp(daily_forecast["dt"]).date()
        if date == today:
            min_temp = daily_forecast["temp"]["min"]
            max_temp = daily_forecast["temp"]["max"]
            weather = daily_forecast["weather"][0]["main"]
            description = daily_forecast["weather"][0]["description"]
            break

    # 天気をdeeplで日本語に翻訳
    weather_japanese = translator.translate_text(weather, target_lang="JA").text

    # 今日の天気予報をまとめる
    today_weather_repo = f"今日の天気は{weather_japanese}、予想最高気温は{max_temp}度、予想最低気温は{min_temp}度です"
    return today_weather_repo

# 関数を使用して天気予報を取得
latitude = "緯度"
longitude = "経度"
API_key = "open weather map APIキー"
deepl_auth_key = "deepLのAPIキー"

weather_report = get_weather_forecast(latitude, longitude, API_key, deepl_auth_key)

# 天気予報をテキストファイルに保存
with open('weather.txt', 'w') as file:
    file.write(weather_report)

これは
weather_forecast.py
にまとめてある

次に
weather_voice.py
をほかで呼べるように関数にする

現状は

import subprocess
import pygame
import time
from weather_forecast import get_weather_forecast

# 天気予報を取得してファイルに保存
latitude = "緯度"
longitude = "経度"
API_key = "open weather map API キー"
deepl_auth_key = "deepLのAPIキー"

weather_report = get_weather_forecast(latitude, longitude, API_key, deepl_auth_key)

with open('weather.txt', 'w') as file:
    file.write(weather_report)

# JSONファイルを作成するためのcurlコマンド
command_json = [
    "curl", "-s", "-X", "POST",
    "192.168.1.69:50021/audio_query?speaker=1",
    "--get", "--data-urlencode", "text@weather.txt"
]

# 音声ファイルを作成するためのcurlコマンド
command_audio = [
    "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
    "-d", "@query.json", "192.168.1.69:50021/synthesis?speaker=1"
]

# 最初のコマンドを実行してJSONファイルを作成
with open('query.json', 'w') as file:
    subprocess.run(command_json, stdout=file)

# 第二のコマンドを実行して音声ファイルを作成
with open('test_audio.wav', 'wb') as file:
    subprocess.run(command_audio, stdout=file)

# Pygameの初期化
pygame.init()
pygame.mixer.init()

# WAVファイルを読み込む
sound = pygame.mixer.Sound("test_audio.wav")

# 再生
sound.play()

# 再生が終了するまで待機
while pygame.mixer.get_busy():
    time.sleep(0.1)

これを関数にする

Chatgpt の答えは

import subprocess
import pygame
import time
from weather_forecast import get_weather_forecast

def generate_and_play_weather_report(latitude, longitude, API_key, deepl_auth_key):
    # 天気予報を取得してファイルに保存
    weather_report = get_weather_forecast(latitude, longitude, API_key, deepl_auth_key)
    with open('weather.txt', 'w') as file:
        file.write(weather_report)

    # JSONファイルを作成するためのcurlコマンド
    command_json = [
        "curl", "-s", "-X", "POST",
        "192.168.1.69:50021/audio_query?speaker=1",
        "--get", "--data-urlencode", "text@weather.txt"
    ]

    # 音声ファイルを作成するためのcurlコマンド
    command_audio = [
        "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
        "-d", "@query.json", "192.168.1.69:50021/synthesis?speaker=1"
    ]

    # JSONファイルと音声ファイルを作成
    with open('query.json', 'w') as file:
        subprocess.run(command_json, stdout=file)
    with open('test_audio.wav', 'wb') as file:
        subprocess.run(command_audio, stdout=file)

    # Pygameで音声ファイルを再生
    pygame.init()
    pygame.mixer.init()
    sound = pygame.mixer.Sound("test_audio.wav")
    sound.play()
    while pygame.mixer.get_busy():
        time.sleep(0.1)

変更点を確認しておく

インポート部分は変化なし

import subprocess
import pygame
import time
from weather_forecast import get_weather_forecast


# 天気予報を取得してファイルに保存
latitude = "34.745755"
longitude = "137.91146"
API_key = "open weather map のAPIキー"
deepl_auth_key = "deepLのAPIキー"

の部分を削除しkao.pyへ移動させる

また

import weather_voice

をkao,oyへ追加

そしてprintぶんの下へ

weather_voice.generate_and_play_weather_report(latitude, longitude, API_key, deepl_auth_key)

を追記して保存

実験のため
VNC接続して実行

ubuntuで

tigervncserver -xstartup /usr/bin/gnome-session -geometry 800x600 -localhost no :1 

を実行後

Mac の場合
Finder から
移動 > サーバーに接続で

vnc://192.168.1.69:5901

で接続

これで実験すると顔認識した時に今日の天気を教えてくれるようになった

次は画面表示せずに処理するようにコードを書き換える

これは単純にishowの画面表示部分と
認識した部分を枠で囲む部分を削除するだけでOK

import cv2
import time

import weather_voice

# 天気予報を取得してファイルに保存
latitude = "緯度"
longitude = "経度"
API_key = "open weather map APIキー"
deepl_auth_key = "deepLのAPIキー"

# Haar Cascade分類器の読み込み
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

# Webカメラの設定
cap = cv2.VideoCapture(0)  # 0番目のカメラを使用する場合

# 最後の顔検出時刻
lastTime = None

# メインループ
while True:


    # カメラからのフレームの取得
    ret, frame = cap.read()
    
    # フレームのグレースケール化
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 顔の検出
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    
    # 検出された顔に対する処理
    for (x, y, w, h) in faces:
        # 検出自の処理(検出から1分たったら再度イベント動かす
        if lastTime is None or time.perf_counter() - lastTime > 60:
            # 検出時刻更新
            lastTime = time.perf_counter()
            print("人間発見、警戒せよw")
            weather_voice.generate_and_play_weather_report(latitude, longitude, API_key, deepl_auth_key)
        
        #画像を表示する場合
        #cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
        #roi_gray = gray[y:y+h, x:x+w]
        #roi_color = frame[y:y+h, x:x+w]
        # 以下は目もマークする場合
        # eyes = eye_cascade.detectMultiScale(roi_gray)
        # for (ex, ey, ew, eh) in eyes:
        #     cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (255, 0, 0), 2)

    
    # 結果の表示
    #cv2.imshow('Facial Feature Detection', frame)
    
    # 終了のキー入力
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 後処理
cap.release()
cv2.destroyAllWindows()

とすればOK

あとは終了する方法が
Ctrl +c 以外にないので
他に終了させる方法があるか考える

それとスペック不足のためか
顔認識してから音声が出るまでに時間がかかる

とりあえず不要部分を削除したのが

import cv2
import time

import weather_voice

# 天気予報を取得してファイルに保存
latitude = "34.745755"
longitude = "137.91146"
API_key = "1082c12d65462d76f7dd1b7ef93c7849"
deepl_auth_key = "5f169e4d-3701-9eff-08fc-bf6065b64c8f:fx"

# Haar Cascade分類器の読み込み
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

# Webカメラの設定
cap = cv2.VideoCapture(0)  # 0番目のカメラを使用する場合

# 最後の顔検出時刻
lastTime = None

# メインループ
while True:


    # カメラからのフレームの取得
    ret, frame = cap.read()
    
    # フレームのグレースケール化
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 顔の検出
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    
    # 検出された顔に対する処理
    for (x, y, w, h) in faces:
        # 検出自の処理(検出から1分たったら再度イベント動かす
        if lastTime is None or time.perf_counter() - lastTime > 60:
            # 検出時刻更新
            lastTime = time.perf_counter()
            print("今日の天気を顔認識したのでお知らせ")
            weather_voice.generate_and_play_weather_report(latitude, longitude, API_key, deepl_auth_key)
        

# 後処理
cap.release()
cv2.destroyAllWindows()

次にセキュリティのため

vim config.ini

でファイルを作り

[API_KEYS]
OPENWEATHER_API_KEY = open weather map API キー
DEEPL_AUTH_KEY = deepLのAPIキー

というようにしてキーを設定ファイルから読み込むようにする

なおキーを”” で囲むとバグるので注意

あとは
Pythonスクリプトでconfigparserモジュールを使用して設定ファイルから情報を読み込む

import configparser
# 設定ファイルを読み込む
config = configparser.ConfigParser()
config.read('config.ini')

# APIキーを取得
API_key = config['API_KEYS']['OPENWEATHER_API_KEY']
deepl_auth_key = config['API_KEYS']['DEEPL_AUTH_KEY']

これを使うようにコードを変更する

import cv2
import time

import configparser
import weather_voice

# 天気予報を取得してファイルに保存
latitude = "緯度"
longitude = "経度"

# 設定ファイルを読み込む
config = configparser.ConfigParser()
config.read('config.ini')


# APIキーを取得
API_key = config['API_KEYS']['OPENWEATHER_API_KEY']
deepl_auth_key = config['API_KEYS']['DEEPL_AUTH_KEY']

# Haar Cascade分類器の読み込み
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')

# Webカメラの設定
cap = cv2.VideoCapture(0)  # 0番目のカメラを使用する場合

# 最後の顔検出時刻
lastTime = None

# メインループ
while True:


    # カメラからのフレームの取得
    ret, frame = cap.read()
    
    # フレームのグレースケール化
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 顔の検出
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    
    # 検出された顔に対する処理
    for (x, y, w, h) in faces:
        # 検出自の処理(検出から1分たったら再度イベント動かす
        if lastTime is None or time.perf_counter() - lastTime > 60:
            # 検出時刻更新
            lastTime = time.perf_counter()
            print("今日の天気を顔認識したのでお知らせ")
            weather_voice.generate_and_play_weather_report(latitude, longitude, API_key, deepl_auth_key)
        

# 後処理
cap.release()
cv2.destroyAllWindows()

とりあえずこれで完成
どのみちバックグランドで動作させるので
停止方法は保留

あとは GitHub へコードをアップする

pygame で読み上げ

一度に全て処理しpygame で読み上げ

wavファイルの読み上げを gygame にすることでエラー対処

import requests
import json
from datetime import datetime
import deepl

def get_weather_forecast(latitude, longitude, API_key, deepl_auth_key):
    # DeepL Translatorのインスタンスを生成
    translator = deepl.Translator(deepl_auth_key)

    # OpenWeather APIのURL
    url = "https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=hourly,minutely&units=metric&lang=ja&appid={API_key}"
    url = url.format(lat=latitude, lon=longitude, API_key=API_key)

    # APIリクエスト
    response = requests.get(url)
    jsondata = response.json()

    # 今日の日付を取得
    today = datetime.now().date()

    # 今日の天気予報を探す
    for daily_forecast in jsondata["daily"]:
        date = datetime.fromtimestamp(daily_forecast["dt"]).date()
        if date == today:
            min_temp = daily_forecast["temp"]["min"]
            max_temp = daily_forecast["temp"]["max"]
            weather = daily_forecast["weather"][0]["main"]
            description = daily_forecast["weather"][0]["description"]
            break

    # 天気をdeeplで日本語に翻訳
    weather_japanese = translator.translate_text(weather, target_lang="JA").text

    # 今日の天気予報をまとめる
    today_weather_repo = f"今日の天気は{weather_japanese}、予想最高気温は{max_temp}度、予想最低気温は{min_temp}度です"
    return today_weather_repo

# 関数を使用して天気予報を取得
latitude = "緯度"
longitude = "経度"
API_key = "weather map apiキー"
deepl_auth_key = "deeplのAPIキー"

weather_report = get_weather_forecast(latitude, longitude, API_key, deepl_auth_key)

# 天気予報をテキストファイルに保存
with open('weather.txt', 'w') as file:
    file.write(weather_report)

として関数にして
weather_forecast.py
として保存

次に

import subprocess
import pygame
import time
from weather_forecast import get_weather_forecast

# 天気予報を取得してファイルに保存
latitude = "緯度"
longitude = "経度"
API_key = "weather map apiキー"
deepl_auth_key = "DeepL APIキー"

weather_report = get_weather_forecast(latitude, longitude, API_key, deepl_auth_key)

with open('weather.txt', 'w') as file:
    file.write(weather_report)

# JSONファイルを作成するためのcurlコマンド
command_json = [
    "curl", "-s", "-X", "POST",
    "192.168.1.69:50021/audio_query?speaker=1",
    "--get", "--data-urlencode", "text@weather.txt"
]

# 音声ファイルを作成するためのcurlコマンド
command_audio = [
    "curl", "-s", "-H", "Content-Type: application/json", "-X", "POST",
    "-d", "@query.json", "192.168.1.69:50021/synthesis?speaker=1"
]

# 最初のコマンドを実行してJSONファイルを作成
with open('query.json', 'w') as file:
    subprocess.run(command_json, stdout=file)

# 第二のコマンドを実行して音声ファイルを作成
with open('test_audio.wav', 'wb') as file:
    subprocess.run(command_audio, stdout=file)

# Pygameの初期化
pygame.init()
pygame.mixer.init()

# WAVファイルを読み込む
sound = pygame.mixer.Sound("test_audio.wav")

# 再生
sound.play()

# 再生が終了するまで待機
while pygame.mixer.get_busy():
    time.sleep(0.1)

として作成したファイルを元に
Subprocess で
Curl で音声ファイルを作成

それをpygame で読み上げるようにした

とりあえず動作確認はできたので
次に顔認識と合わせてみる

現在地の明日の天気を取得

現在地の明日の天気を取得

毎回アレクサで聴くのは面倒なので
部屋に入った時、顔認識したら明日の天気を知らせるようにする

夜なら明日の天気
朝なら今日の天気とする

なので音声合成と画像認識、顔認識ができればOK

とりあえず明日の天気の取得をする

とりあえずChatGPT で調べる

Weather APIを使うようなので

無料で使える天気API「Free Weather API」の利用登録とキー発行手順
を参考に

https://www.weatherapi.com/signup.aspx
から登録しようとしたら
502 Bad Gateway
となるので

OpenWeatherMapAPI
を使うことにする

openweathermap.orgのAPIを使って天気表示

天候・気温予測サービス “OpenWeather” のAPIを活用する
を参考に

まずはAPI key の取得

https://home.openweathermap.org/users/sign_up
からできる

規約には
We will use information you provided for management and administration purposes, and for keeping you informed by mail, telephone, email and SMS of other products and services from us and our partners. You can proactively manage your preferences or opt-out of communications with us at any time using Privacy Centre. You have the right to access your data held by us or to request your data to be deleted. For full details please see the OpenWeather Privacy Policy.

I am 16 years old and over
I agree with Privacy Policy, Terms and conditions of sale and Websites terms and conditions of use

I consent to receive communications from OpenWeather Group of Companies and their partners:

System news (API usage alert, system update, temporary system shutdown, etc)
Product news (change to price, new product features, etc)
Corporate news (our life, the launch of a new service, etc)

日本語にすると
当社は、お客様から提供された情報を管理および管理の目的で使用し、また当社および当社のパートナーからの他の製品やサービスについて郵便、電話、電子メール、SMS でお客様に通知し続けるために使用します。お客様は、プライバシー センターを使用して、いつでも自分の設定を積極的に管理したり、当社とのコミュニケーションをオプトアウトしたりできます。あなたには、当社が保有する自分のデータにアクセスする権利、または自分のデータの削除を要求する権利があります。詳細については、OpenWeather プライバシー ポリシーをご覧ください。

私は16歳以上です
プライバシー ポリシー、販売条件、およびウェブサイトの利用条件に同意します
私は、OpenWeather Group of Companies およびそのパートナーからの連絡を受け取ることに同意します。

システムニュース(API使用状況のアラート、システムアップデート、システムの一時シャットダウンなど)
製品ニュース(価格変更、新製品特長など)
企業ニュース(私たちの生活、新サービスの開始など)

登録しようとしたら
既に去年登録していた….

キーは
Gmailに送られていてた

これを元に
https://qiita.com/K_Nemoto/items/51e124b3628106c6ef0a#apiを活用する
を参考に

import requests
import json
from pprint import pprint
url = “https://api.openweathermap.org/data/2.5/weather?zip={zip_place}&units=metric&appid={API_key}”
# xxxxx
url = url.format(zip_place = “任意の郵便番号,JP”, API_key = “取得したAPIキー”)

jsondata = requests.get(url).json()
pprint(jsondata)

print(“天気:”,jsondata[“weather”][0][“main”])
print(“天気詳細:”,jsondata[“weather”][0][“description”])

print(“都市名:”,jsondata[“name”])
print(“気温:”,jsondata[“main”][“temp”])
print(“体感気温:”,jsondata[“main”][“feels_like”])
print(“最低気温:”,jsondata[“main”][“temp_min”])
print(“最高気温:”,jsondata[“main”][“temp_max”])
print(“気圧:”,jsondata[“main”][“pressure”])
print(“湿度:”,jsondata[“main”][“humidity”])

print(“風速:”,jsondata[“wind”][“speed”])
print(“風の方角:”,jsondata[“wind”][“deg”])

で実行すると

{‘base’: ‘stations’,
‘clouds’: {‘all’: 0},
‘cod’: 200,
‘coord’: {‘lat’: 緯度, ‘lon’: 経度},
‘dt’: 1701631236,
‘id’: 0,
‘main’: {‘feels_like’: 4.12,
‘humidity’: 60,
‘pressure’: 1021,
‘temp’: 6.65,
‘temp_max’: 6.65,
‘temp_min’: 6.65},
‘name’: ‘Kawai’,
‘sys’: {‘country’: ‘JP’,
‘id’: 2008260,
‘sunrise’: 1701639567,
‘sunset’: 1701675443,
‘type’: 2},
‘timezone’: 32400,
‘visibility’: 10000,
‘weather’: [{‘description’: ‘clear sky’,
‘icon’: ’01n’,
‘id’: 800,
‘main’: ‘Clear’}],
‘wind’: {‘deg’: 288, ‘gust’: 5.36, ‘speed’: 3.58}}
天気: Clear
天気詳細: clear sky
都市名: Kawai
気温: 6.65
体感気温: 4.12
最低気温: 6.65
最高気温: 6.65
気圧: 1021
湿度: 60
風速: 3.58
風の方角: 288

となり入力した郵便番号の今日の天気が出る

とりあえず目的は今日、明日の天気の取得

他にも三時間ごとの天気とかもあるけど
今は不要
後々サーバー作って手持ちのスマホとかのGPSとリンクして
現在地の三時間ごとの天気で雨とか霧が出るなら注意とか
移動予定のところで滞在時間内に雨になりそうなら表示する感じか

パラメータとしては
https://hibi-update.org/other/openweathermap-api/
がわかりやすい

次に現在地のものを調べてみた

https://qiita.com/iwasan06/items/94db02186b17bf2d09fc
にあるけど
.erb なのでこれはrailsコード

位置情報取得にはGCPを使ってるが高いので別のものにする

https://zenn.dev/amuro/articles/96f61aff90e9da
にはスマホアプリへの現在地の実装方法が載ってるけど
今じゃない

https://note.com/ai_frontline/n/na22bd0ed7870
だとCHAT GPTと組み合わせだけど
それじゃない

むしろ
https://3pysci.com/openweathermap-5/
によれば

https://api.openweathermap.org/data/2.5/onecall?lat=
{lat}&lon={lon}&exclude={part}&appid={API key}

 となっているので

https://api.openweathermap.org/data/2.5/onecall?lat=33.44&lon=-94.04&appid={API key}

というように
緯度経度とAPI key があれば可能らしい
これなら現在地で取得可能

ということで
Pythonで現在地の緯度経度を取得するコードを作る

しかし考えた結果、現在地取得は不要
自宅の今日あすの天気と移動さきは移動さきの地名を入れるので
緯度経度で出すことはほぼない

次はopencv関連をやる