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

技術屋の末端。プログラミングも電気回路も専門外です。 コードに間違いなど見つけられたら、気軽にコメントください。 VC#、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