にゃみかんてっくろぐ

猫になりたい

Fargate Spotのお値段比べてみた

Fargate Spotが発表された.

aws.amazon.com

70%ディスカウントは魅力的であるものの,EC2と比べて高いのか安いのかがわからない.ということで表にした.

タイプ vCPU Mem EC2
オンデマンド
EC2
スポット
EC2
リザーブ
(1年前払い)
Fargate Fargate
Spot
m5.large 2 8 0.124 0.035 0.081 0.145 0.044
c5.large 2 4 0.107 0.033 0.072 0.123 0.037
r5.large 2 16 0.152 0.037 0.089 0.190 0.057

※ap-northeast-1(アジアパシフィック (東京)),2019年12月5日(木)23時時点,単位は USD/h

厳密には, C5 は M5, R5 とプロセッサのクロック周波数が異なるし,そもそもFargateのプロセッサシリーズは公表されていない点に注意.

見づらい表になってしまったが,Fargateはメモリ単価が少々高いという印象.

気象庁の天気予報文をパースする ☀☁☂

天気予報文

気象庁の天気予報には 予報文 があります。

北東の風 日中 東の風 くもり 昼過ぎ まで 時々 晴れ 所により 夜遅く 雨

気象庁 | 天気予報 : 東京都 (10/20 11:00発表 21日の天気予報文)

やや難解ですが、 気象庁|予報用語 と照らし合わせることで下記であることが分かります。

時間 天気
0〜15時 くもり 時々 晴れ
15〜21時 くもり
21〜24時 くもり 所により 雨

天気予報文と正規表現

ここからは根拠のない独自研究です。

文法

気象庁が発表する天気予報文には 文法 が存在し、どの予報文もいずれかの文法にマッチします。以下は例です。

文法
天候 晴れ
天候 頻度 天候 晴れ 時々 くもり
天候 時間帯 天候 くもり 昼過ぎ から 夕方
天候 時間帯 頻度 天候 くもり 昼前 まで 時々 晴れ
天候 頻度 天候 時間帯 天候 晴れ 時々 くもり 夜のはじめ頃 から
天候 所により 天候 くもり 所により
天候 所により 時間帯 天候 くもり 所により 明け方 まで
天候 所により 修飾 所により 雷を伴い 激しく 降る
天候 所により 時間帯 修飾 所により 夕方 から 雷 を伴う
天候 頻度 天候 時間帯 天候 所により 時間帯 修飾 晴れ 時々 くもり 夕方 から 所により 夜遅く 雷を伴い 激しく 降る

正規表現

文法を正規表現で表すと、次のようになります(雪 後 雪などのありえない表現もマッチしますが、今回は無視します)。

(晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る)( (時々|一時|後) (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る))?( ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?)( (時々|一時))? (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る))?( (所により|[^ ]+ では)( ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?))? ((晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る)|(雷 を伴う|激しく 降る|雷を伴い 激しく 降る|霧)))?

何を言っているんだという感じなので、擬似的に表現をすると次のようになります。

天候( 頻度 天候)?( 時間帯( 頻度)? 天候)?( (所により|地名)( 時間帯)? (天候|修飾))?
項目 正規表現
天候 (晴れ|くもり|雨|雪|雨か雪|雪か雨|雪 で ふぶく|雨 で 雷 を伴う|雨 で 雷を伴い 激しく 降る)
頻度 (時々|一時|後) ※時間帯の後ろは (時々|一時)
時間帯 ((朝晩|朝夕|夜)|(未明|明け方|朝|昼前|昼過ぎ|夕方|夜のはじめ頃|夜遅く)( (まで|から( 未明| 明け方| 朝| 昼前| 昼過ぎ| 夕方| 夜のはじめ頃| 夜遅く)?))?)
地名 気象庁 | 気象警報・注意報や天気予報の発表区域 の「市町村等をまとめた地域」に準じる
修飾 (雷 を伴う|激しく 降る|雷を伴い 激しく 降る|霧)

※「雪 で ふぶく」等は修飾が入っていますが、簡単のために天候にまとめています。
※カバー出来ていない天気予報文が存在する可能性があります。

テスト

下記サイトで試してみるとマッチします。

f:id:no_clock:20191021004546p:plain

天気予報文とパーサ

正規表現のままパーサを書くのはつらいため、より簡単にして概ね次のルールでパーサを書きますました。

  • 入力は予報文、出力は0〜21時の3時間ごとの予報
  • 「時間帯」「所により(地名)」「何もない(終端)」が出現した場合に分割
  • 「所により」「頻度」が含まれる場合は予報を追記。それ以外は指定時間帯の予報として上書き

動作

$ ruby runner.rb

天気予報を入力: 晴れ 時々 くもり 夕方 から 雨 所により 夜遅く 雷を伴い 激しく 降る
解析結果:
{:wind=>nil,
 :weather=>
  {0=>"晴れ時々くもり",
   3=>"晴れ時々くもり",
   6=>"晴れ時々くもり",
   9=>"晴れ時々くもり",
   12=>"晴れ時々くもり",
   15=>"雨",
   18=>"雨",
   21=>"雨所により雷を伴い激しく降る"}}

天気予報を入力: 北東の風 日中 東の風 くもり 昼過ぎ まで 時々 晴れ 所により 夜遅く 雨
解析結果:
{:wind=>"北東の風 日中 東の風",
 :weather=>
  {0=>"くもり時々晴れ",
   3=>"くもり時々晴れ",
   6=>"くもり時々晴れ",
   9=>"くもり時々晴れ",
   12=>"くもり時々晴れ",
   15=>"くもり",
   18=>"くもり",
   21=>"くもり所により雨"}}

3時間ごとの天気予報がわかるようになりました。めでたしめでたし。

同じ構図で何枚も撮って,最も手ブレしていない 1 枚だけを残す

結論

  • ImageMagickcompare コマンドで, Perceptual Hash (pHash) により同一構図かチェックする.
  • ImageMagickconvertidentify コマンドで, Canny 法によりエッジ検出し同一構図の n 枚から最も鮮明な 1 枚を残す.
  • ソースはここに置いた: GitHub - typewriter/unblurred-photo-picker

背景

手ブレしたくないが, ISO 感度もあまり上げたくない(綺麗に撮りたい).その場合,ギリギリのシャッタースピードで何枚も撮る,ということをよくやります.しかし,撮影後の写真の選別が極めて面倒なうえ,かなり機械的な作業です.

  1. 同一構図で撮られた写真を探し出す
  2. そのうち最も鮮明と思われる写真 1 枚を残し,それ以外を重複とする.
  3. (重複写真は Flickr 上で非公開に設定する*1

機械的な作業… ということは機械で出来るのではないでしょうか.やってみました.

同一構図の判定

同一構図の判定は,画像の類似度が高いかどうかで行うこととします.ただし,画像処理はさっぱり分からないため, ImageMagick に全力で甘えます.

ImageMagick には compare コマンドがあり,お手軽に画像比較を行うことが可能です. 画像比較に使用できるメトリクスの一覧は ImageMagick - Command-line Options#-metric type にありますが,今回は PHASH (Perceptual Hash) を用います.

# ImageMagick 7 系.小さいほど類似度が高い.
$ magick compare -metric PHASH P9240031.JPG P9240035.JPG NULL:
13.7601

Perceptual Hash ではハミング距離を取って類似度とする例を見かけましたが, ImageMagick の数値はそうではないようです.まあ気にせずいくつかの画像で試してみます.

画像1 画像2 備考 出力値
f:id:no_clock:20191014202721j:plain f:id:no_clock:20191014202732j:plain 同じ構図 0.501766
f:id:no_clock:20191014202721j:plain f:id:no_clock:20191014202751j:plain 少し下に向けた 8.26181
f:id:no_clock:20191014202721j:plain f:id:no_clock:20191014202816j:plain 別構図 39.5355

同一構図では数値が小さく,異なる構図では数値が大きくなりました.使えそうです.

「最も鮮明な」写真の判定

やはり画像処理はさっぱり分からないため,こちらも ImageMagick に頼ります.

当初は「標準偏差 (Standard deviation) が大きければ値のバラつきが大きい→鮮明」と考えていましたが,手ブレした写真のほうが標準偏差が高いケースが複数枚でみられたため不採用です.

$ magick identify -format "{ \"Kurtosis\": %[kurtosis], \"StandardDeviation\": %[standard-deviation], \"Skewness\": %[skewness], \"Entropy\": %[entropy] }"  20171104_PB040552.JPG
{ "Kurtosis": 3.04856, "StandardDeviation": 11783.4, "Skewness": 1.91352, "Entropy": 0.760502 }
画像(切り抜き) 標準偏差
f:id:no_clock:20191014203238j:plain 11783.4
f:id:no_clock:20191014203249j:plain 11899.3

代わりに, Canny Edge Detection でエッジ検出した結果の標準偏差を用いることにします.

$ magick convert 20171104_PB040552.JPG -canny 0x1+10%+30% edge.png
$ magick identify -format "{ \"Kurtosis\": %[kurtosis], \"StandardDeviation\": %[standard-deviation], \"Skewness\": %[skewness], \"Entropy\": %[entropy] }"  edge.png
{ "Kurtosis": 13.5456, "StandardDeviation": 14823.4, "Skewness": 3.94279, "Entropy": 0.303514 }
画像(切り抜き) エッジ検出後(同左) 標準偏差
f:id:no_clock:20191014203238j:plain f:id:no_clock:20191014203811p:plain 14823.4
f:id:no_clock:20191014203249j:plain f:id:no_clock:20191014203853p:plain 11678.3

良さげな感じになりました.

スクリプト

Rubyで書きました。裏でImageMagickのコマンドガンガン実行します。

参考

*1:バックアップも兼ねているのでアップロードはしている

日用品のストック管理ツール Stokk を作った

日用品のストックを管理出来るツール「Stokk」を公開しました。

以下はポエムです。

技術選定

  • サーバをわざわざ建立するほどの機能はない
  • mBaaSと親和性が高そう
  • フロントエンドを書きたい

ということで、SPA + mBaaSで作ることにしました。

使ったもの

ツール: Adobe XD

初使用。無料の「スタータープラン」があり、アカウントさえあればシュッと使えます。

プロトタイプは作らず、ちょっと便利な図形描画ツール程度に使いました。事前にデザインや要件を整理でき、その後データやイベントの流れを練り練りするのにも大いに役立ちました。

f:id:no_clock:20190916220935p:plain
Adobe XDでのデザイン

言語: TypeScript

JavaScriptを操れる自信はなかったのでTypeScriptで書きました。エディタはVisual Studio Code

ただ、厳格に型付けをするのは諦めました。動く状態で、「thisはVueだからお前が定義したプロパティなぞ存在せんわ!」などと言われていました。「ぐ、Gradual Typingだから…」と意味不明な言い訳をしつつキャストしたりanyにしたりしました。

f:id:no_clock:20190916221003p:plain
怒られが発生している様子

フレームワーク: Vue.js + Vue Router, Bootstrap + BootstrapVue

ふわっと書きたかったのとReactは仕事で書く機会があるのとでVue.jsにしました。Vue CLIでTypeScriptも含めて一括で初期設定でき、とても楽でよいです。

デザインは安定のBootstrap。BootstrapVueも入れましたが、「BootstrapのListってBootstrapVueでどう書くんだっけ」と毎回マッピングしつつ書いていました。効率的に書けたかは分かりません。

その他ライブラリ: Lodash, Font Awesome

Lodashはdebounceに、Font Awesomeはアイコンに。

mBaaS: Cloud Firestore, Firebase Hosting, Firebase Authentication + FirebaseUI

作るサービスと親和性高そうなので全面的に依存しました。

FirebaseUIについては、 Firebase 6.6.1 と FirebaseUI 4.2.0 でドキュメント通りに書いたつもりで動作せず(firebase.auth()と書くとfirebaseがないと怒られる)。 メジャーバージョンを1つ落として FirebaseUI 3.6.1 を使用することで落ち着きました。何故なんだ。

そのほか

命名: Stokk

当初「にちまね!(日用品在庫マネージャ)」で けいおん!風にしようと思っていました。が、サブドメインを割り当てるタイミングで nichimane は無いな… と思って在庫(stock)を意図的に誤字らせました。

アニメーション

「動いているんだけど動いている感がない」という印象を受けたため、「小気味よく動いている」感を演出するべくアニメーションを設定しました。

  • アイテム追加、削除、並び替え時
  • 読み込み・保存時(スピナー表示)

f:id:no_clock:20190916221048g:plain

ソースコード

やっつけ感満載のソースコードGitHubにあります。

ページの好き嫌いを学習・分類するChrome拡張を作った

ネタです.

ベイジアンフィルタで,ページの好き嫌いを学習・分類するChrome 拡張機能を作りました.

動作サンプル

構成

学習・分類サーバがあり,Chrome拡張はそのクライアントとして機能します.

f:id:no_clock:20190706163910p:plain

全文ではなくURLを送っているのは,認証が必要なエリアのコンテンツをうっかり学習させないためです.学習データに個人情報が混ざっているとか怖くて扱えない.

学習・分類サーバ

ソースファイルと利用方法

chrome.google.com

Chromeウェブストアから拡張機能を追加するか,上記リポジトリをcloneしてデベロッパーモードで拡張機能を読み込んでください.

URLがサーバに送信されますので,気になる方は自前でサーバを立ててください.

Oracle Code Cardを触ってみる (触っただけ編)

Oracle Code Cardという謎のカードを譲ってもらった.「イベントでもらったけど使わない」「ボタン電池が高い(500円)」という情報も付属していた.

観察

赤い基板に電子ペーパーが載っている.イベントで配布するにしては贅沢だ.いくら掛かっているんだろう.

f:id:no_clock:20190613224614j:plain
表面

裏面には「Live for the Code」との記述がある.

f:id:no_clock:20190614002940j:plain
裏面

ボタン電池のホルダがあるが,LIR2450 3.6V,という珍しい電池のようだ.なんと RECHARGEABLE の記述がある.充電池.

f:id:no_clock:20190614002957j:plain
LIR2450

よく見ると,ESP-12Fチップが搭載されている.これはArduino互換のような気がする.調べてみるとどうやら確からしい.

f:id:no_clock:20190613224857j:plain
ESP-12FとWi-Fiの刻印があるチップ

ja.wikipedia.org

あっちこっちのページを探索していると,USB-シリアル(UART)変換チップも載っていて,シリアル接続で通信出来るとのこと.確かに載っている.

f:id:no_clock:20190614001520j:plain
SIL2104の刻印 (Silicon Labs CP2104)

というか,リポジトリがある.

github.com

通信

リポジトリの記述に従って接続し,スイッチON/OFFを切り替えたが何も起きない.

Aボタンを押すと,どうやらWi-Fiに接続しようとしている旨のメッセージが流れる.Type 'help' for more info. と表示されるが,直後にシャットダウンしていて反応はない.

f:id:no_clock:20190614001933p:plain
Arduino IDE シリアルモニタ

詰んだ,と思った.とりあえず適当にA/Bボタンを同時押ししてみた.

…すると,なんと当たりだったようで,電子ペーパーORACLEのロゴが現れた後,シャットダウンしなくなった.

そのままhelpと入力すると,期待通りヘルプが出てきた.うまくいったようだ.

f:id:no_clock:20190614000429p:plain
Arduino IDE シリアルモニタ

さて,どう調理しようかな.つづく.

参考

developer.oracle.com

hackaday.com

theappslab.com

死活監視をFirebase Realtime Databaseでお手軽にやってみる

死活監視といえば,たとえば MackerelUptime Robot などが使えます.

ただし,こうしたサービスは「エージェントをインストールする」か「外部からサービスにアクセスできる」必要があるほか,無料版に制限があるなど少々ネックがあります.

そこで,Firebase Realtime Databaseで死活監視チャレンジしてみました.

監視対象の条件

  • インターネットに到達できる(外部公開は不要)
  • cron または 任意の定期実行可能なジョブ管理システム がある
  • cURL または 任意のHTTPクライアント がある

監視対象: REST APIを定期的に叩く

監視対象のホストから,定期的にハートビートを送信します. データの保存  |  Firebase に記載されている Realtime Database の REST API を用います.

crontab -eでcrontabに定義を追加します.

*/10 * * * * curl -X PUT -d "\"`date '+\%Y-\%m-\%d \%H:\%M'`\"" https://examples.firebaseio.com/heartbeats/`hostname`.json

Realtime Databaseにデータが追加されます.

f:id:no_clock:20190603232659p:plain
Firebase Realtime Databaseのようす

アラート: Cloud FunctionsからSlackに通知する

アラートも作ります.Cloud Functionsで定期実行するようにします.

Node.jsですが普段あまり書かないのでクオリティはお察しです.

export const scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
  request.get({
    url: "https://examples.firebaseio.com/heartbeats.json",
  }, function(error: any, response: any, body: any) {
    const info = JSON.parse(body)
    for (let hostname in info) {
      const diff = Date.now() - Date.parse(`${info[hostname]}+09:00`)
      if (diff < 1000 * 60 * 30) { continue; }

      const options = {
        url: 'https://hooks.slack.com/services/<fugafuga>',
        form: `payload={ "text": "${hostname}: ${info[hostname]} 以降 heartbeatがありません." }`,
        json: true
      }
      request.post(options)
    }
  });
  return 1
});

ためしにしきい値を1分とかにして毎分実行させると,ちゃんとSlackに通知されます.

f:id:no_clock:20190603235821p:plain
ゆかりん

注意事項

おわりに

Realtime Databaseとは… という感じですが,Realtime Databaseは課金対象がストレージとトラフィックのみで,リクエスト数に制限がないため,少量のデータを高頻度にやり取りするケースによく合います.もっとも,今回の規模であればFirestoreでも無料枠で済みますが…

めでたしめでたし.