プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

SeleniumとOpen CVでリアルタイム雨雲情報を取得

ラズパイで天気予報を取得して毎朝Tweetするようにしていますが、より細かい雨の様子を取得してみたいと思います。
これには、日本気象協会のHPから自宅付近の雨雲の様子を取得して解析すればできそうです。
tenki.jp

python3とSeleniumで雨雲の様子を取得し、Open CVで雨雲の分析をします。
雨が降っていたら、さらにSlackerに画像をアップするようにしました。
ブラウザはヘッドレスのPhantomJSを使用しました。

今回使うMojuleはAnacondaの他に↓のあたりが追加で必要となります。(Windowsです。)
入ってないときはAnacondano Promptからpip installします。

pip install opencv-python
pip install selenium
pip instal slacker

今回のプログラム構成

①ヘッドレスブラウザで天気情報(雨雲)にアクセス
②画面キャプチャ
③必要な部分を切り取る
一度試しに各自の環境にてキャプチャをとってみて、必要な部分をトリミングします。
④雨雲情報を取得したい場所を指定
⑤雨雲情報を取得した部分をわかりやすく四角で囲む
ここでは雨雲情報を取得したい場所を中心に上下15pxの四角に囲みました
⑥画像を保存
⑦雨雲状況を判断
⑧雨だったらslackに投稿

雨の判定

これが今回結構難しかったです。
こういうのをやろうとすると、すぐにディープラーニングとかNNとかAIとか考えがちですが、そこまでやらなくてもできます。
f:id:s51517765:20180304174500p:plain
雨雲情報の右下にある雨の強さを表すカラーバーにしたがって、色情報を取得して対応させます。

まずは、このカラーバーの色情報を分析してみました。
このスクリーンショットのカラーバーをOpen CVで解析します。
画像の下から上までのRGB情報を取得します。

def bar():
    clp = cv2.imread("bar.png")
    y=12
    print("B,G,R")
    for x in range(120):
        pixelValue = [clp[120-x, y, 0], clp[120-x, y, 1], clp[120-x, y, 2]]
        print(pixelValue[0],",",pixelValue[1],",",pixelValue[2],)

これをグラフにすると、↓のようになります。
f:id:s51517765:20180304175535p:plain
ここから、弱い雨はCyan、その次がBlueということがわかり、RGBで判断できます。
閾値は、いくつかの雨の強度の違うところの情報を取得して決めました。
今日は、日本全国あまり雨が降っていないので確認できるのは2段階ぐらいですが。
東京は雨が降っていないので、青森県へ出張しています(嘘です)。

弱い雨

f:id:s51517765:20180304180144p:plain

やや強い雨

f:id:s51517765:20180304180229p:plain
海の上だけど(笑)

Slackへの通知

↓のあたりを参考にBotのアカウントで投稿するようにしました。
nuxx.noob.jp
qiita.com


主要部分のコード

# -*- coding: utf-8 -*-
from slacker import Slacker 
import slackbot_settings as setting #アカウント情報
from datetime import datetime as dt
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver 
import cv2 
import  requests

# botアカウントのトークンを指定
BOT_TOKEN = setting.API_TOKEN  #別のファイルから読み込むようにしておくと間違ってアップロードする危険性が低くなる

def main():
    tdatetime = dt.now()
    time=tdatetime.strftime('%Y%m%d%H%M%S')

    browser = webdriver.PhantomJS()
    #url = "https://tenki.jp/radar/3/16/" #東京
    url="https://tenki.jp/radar/2/5/" #青森
    browser.get(url)
    print("get url")
    browser.save_screenshot("tmp.png") #スクリーンショット  …①
    img = cv2.imread("tmp.png")  #…②
    clp = img[320:980, 20:708]  # [高さ, 幅] スライス  #…③
    file = "Pictures/weather" + time + ".png"
    cv2.imwrite(file, clp)

    x,y=274,355 #pxを取得する座標 #…④
    dx,dy=15,15  #…⑤
    B,G,R = clp[y,x,0],clp[y,x,1],clp[y,x,2]
    cv2.line(clp, (x-dx, y-dy), (x+dx, y-dy), (0, 0, 255), 2) #ファイル名,(始点x,y),(終点x,y),(color),太さ)
    cv2.line(clp, (x+dx, y-dy), (x+dx, y+dy), (0, 0, 255), 2)
    cv2.line(clp, (x+dx, y+dy), (x-dx, y+dy), (0, 0, 255), 2)
    cv2.line(clp, (x-dx, y+dy), (x-dx, y-dy), (0, 0, 255), 2)

    print("BGR= ")
    print(B, ",", G, ",", R)
    file = "Pictures/1weather"+time+".png" # …⑥
    cv2.imwrite(file, clp)

    print("Time = "+time)
    if R<30 and B>190 :  #…⑦
        print("弱い雨")
        text ="少し雨が降ってるよ!"
        slacker.chat.post_message('general', 'テスト', as_user=True) #…⑧
        slacker.files.upload(file, channels=['general'], title=text)
    elif R < 30 and B < 190 and G<150:
        print("やや強い雨")
        text ="かなり雨降ってるよ!!"
        slacker.chat.post_message('general', 'テスト', as_user=True)
        slacker.files.upload(file, channels=['general'], title=text)
    else:
        print("多分晴れ")
        text="多分晴れ"

Pythonでは

    x,y=274,355 
    dx,dy=15,15 

のような変数の代入の記法があります。視認性が高くていいですね。

出来上がった動作

f:id:s51517765:20180304172105p:plain