@kotyのブログ

PythonとかAWSとか勉強会のこととかを、田舎者SEがつづります。記事のライセンスは"CC BY"でお願いします。

謹賀新年2022

あけましておめでとうございます。大して内容がなく恐縮しつつ、例年通り、振り返りと今年やりたいことなどを書きます。

昨年の振り返り

仕事

転職3年目でした。コロナ禍で出社できないでいたところ、11月になって感染が収まってきたので久しぶりに出社できました。雑談できるのはやはり良いです。

主にやっていたことは、

既存サービスの社内外の問い合わせ窓口

ほかのエンジニアが開発に集中するために誰かが矢面に立つ必要があり、手を挙げました。これはこれで業務知識がつくことや、利用者が実際どう使っているのかが分かりサービスの改善になりますし、自分の勉強にもなります。一方でエンジニアリングスキルはあまりつかず、新しいことに挑戦している他メンバーを見ると焦る部分もあります。

IdPを新規に建てる業務

うまくいかないことの方が多かったんですが、、、技術的には、今までふんわり使っていたOAuth2やOIDCの勉強になりました。サービスをホストするためにAWS ECSも勉強できてよかったです。

非エンジニアのITスキル勉強会

勉強会の講師をする機会が何度かありました。GASやVBAあたりを使えるようになると業務効率の改善につながるので、良い取り組みだと思います。

仕事以外

息子が小学校に上がったので楽になって自分の時間が増えるかと思いきや、新たな悩み事が増えたりであんまり変わりませんでした。保育園の送りがなくなった分、30分ほど自由時間は増えたくらいです。

IT勉強会関係

コロナ禍でオンサイトのイベントがなくほとんど参加しませんでした。

以下にあるように、やはり家庭とオンラインイベントは相性がよろしくないです。平日の業務の中で開催されるかいっそのことオンサイトの方が参加しやすいかなと思いました。 hb.matsumoto-r.jp

趣味プログラミング

こちらも大した活動はせず、強いて挙げるなら以下のネタくらいでした。 koty.hatenablog.com

英語

CNN English Expressを購読してましたが、ちゃんと時間を取って読み聞きできていません。 言い訳としては、毎朝の時間を日経を読むこと(と5時に起きられなくなったこと(小声))にあてるようになったことがあります。

あと、小1の息子が読む本の英語版を読むこともしていました。ストーリーの不明点は息子に聞けるし、息子との話題も作れるしでなかなか良いです。難易度としても私にはちょうど良く、私の英語力はネイティブ小学生1年生程度ということでしょう。しかしこの調子で息子の成長とともにに私の読む洋書のレベルも上がっていきいずれはビジネス書を読みこなせるのではないかと期待しています。

多読(とまでは言えないけど)の結果、TOEICのReadingで初めて最後まで解けました。ただしリスニングの点がガタ落ちして2年前よりも合計点としては50点ほど落ちてしまいました。「トイックは卒業しましたわ」といつの日か言ってみたい。

ワークアウト関係

別途こちらにまとめました。 www.strava.com

関連記事として、後ろ向きな記事がこちら。 koty.hatenablog.com

今年の抱負

なんつーか、24時間を有効に使いたい。仕事も忙しいけどほどほどにして、いろいろやりたい。

目標を作るというよりは、毎日一定の勉強時間を取るのが大事だと思います。一時期は5時起きして英語の勉強をしていたのが最近は起きれなくなってしまったので、それを再開するのが大事かなと。

仕事

引き続き流れに身を任せ、広く浅くいきます。

仕事以外

IT勉強会関係

オフラインのPyConJPに行きたい。

趣味プログラミング

家業のシステムをSaaSに移行したい。

英語

引き続きCNN English Expressで勉強する。毎朝ちゃんと時間を取る!!!

ワークアウト関連

チャリンコ

引き続き山新田と信更の坂道で鍛えて、一度は戸隠、できれば菅平あたりに行きたい。あと20年ほど前に買ったチャリンコを更新したい。

水泳

しっかり準備したうえで、OWSの大会に出たい。

水球

今年も健康第一で。

地方在住 Advent Calendar 2021 9日目の記事

この記事は、地方在住 Advent Calendar 20219日目の記事です。

にしざわと申します。地方の県庁所在地在住でtwitterはkotyというアカウントでやっています。

経歴

学生のときは東京の東急線沿線に住んでいました。家と大学の往復であまり東京らしい生活はしなかった記憶があります。

卒業後、実家近くに本社のあるSIerに新卒で入り、地方自治体の総合行政システムの保守開発からキャリアをスタートしました。 学生の多くが東京の大企業に就職していく中、自分は実家に帰ることをなぜ選んだのかあまり記憶がありません。今思えばもったいな新卒カードの切り方をしたとも思います。 入社後は、税金の計算や納税通知書を印刷するシステムを作ったり、ときは市町村合併華やかりし頃のため移行システムの開発もしました。残業が多いことやパワハラな人が出世しているのを見て会社に未来を感じられず、転職を決意しました。

既に結婚して生活基盤があったため引っ越しせずにいられる会社を探した結果、同じ市内の小さなソフトウェア受託開発会社に転職しました。 会社の規模が小さくなったためお賃金は下がりましたが、当時ようやく利用が広まりだしたPythonAWSなどに積極的に取り組んでおり、最新の技術を身につけることができました。そして、比較的頻繁に変わる案件で関わるお客様も優秀な方が多く、教えていただくことは勉強になりましたし、個人的な人脈もできました。一方で要件が決まって後は手を動かすだけの状態で開発が依頼されてくることに物足りなさを感じたことや、会社として社会にどう貢献するのかのビジョンが欲しくなっていました。ちょうどその頃にお誘いいただき、今の会社に転職しました。

今の会社では、フルリモートにて、主にRailsを使ったWebラーニングシステムの保守開発に携わっています。

このような経歴のため、広く浅く活動しつつWebアプリケーションのバックエンド開発が一番得意です。

私が住む市の紹介と「地方」の定義

私が住む場所は長野駅まで車で15分から20分ほどです。ですので東京都心であれば新幹線を使って2時間ほどで行けてしまいます。「かがやき」が止まるのが大きいですね。そのような便利な場所なのに、市街化調整区域に指定され農地の宅地転用が制限されているため、周りは田畑ばかりでのどかです。

次に気候について。一言でいいますと「夏暑く冬寒い」です。夏は東京に比べて朝夕は涼しいですがエアコンは必要ですし、35℃になる日もあります。冬はマイナス10℃近くまで下がる日があります。でも雪はそれほど降りません。もう少し北に行くととたんに降雪量が増えます。

交通事情については、ご多分に漏れずバスは朝夕それぞれ2本という状況のため、車は必須です。

この記事で言う「地方」とは以上のイメージです。

平日の暮らし

一日の流れ

  • 自宅の前の道は通勤の抜け道になっており交通量が多いため、小学校1年の息子が集団登校の集合場所に行くのを付きそう。
  • リングフィットアドベンチャーで軽く運動する
  • 始業
  • お昼は同居する父母と取る
  • 終業
  • 児童館に息子を迎えに行く
  • 奥さんが残業のときは夕飯の準備
  • 夕飯
  • 風呂
  • 息子と就寝

休日の暮らし

  • 田んぼにドジョウを取りに行ったり、動物園に行ったり。
  • 両親が果樹農家なので、さくらんぼ、桃、プルーン、りんごなどそれぞれの時期には果物が食べ放題です。

遊びに行く場所は以下のようなところです。

f:id:kkotyy:20211207211238p:plain
善光寺(の近くにあるカフェから取った善光寺。当たり障りのない写真がこれしかなかった)

f:id:kkotyy:20211207211427p:plain
戸隠鏡池

f:id:kkotyy:20211207211706p:plain
茶臼山恐竜公園

茶臼山動物園

少年科学センター

城山動物園

地方のメリット

住居費が安い

実家を2世帯住宅に建て替えたため、土地を調達することなく広い土地に住めています。と書いてみたものの、地方関係ないですね。同居することを選んでくれた奥さんに本当に感謝しています。

のどか

家は田畑に囲まれており家どおしが隣接しておらず、ゆったりと暮らせます。気候が良い時期なんかは庭の軒下で仕事したりもできます。

小学校が少人数学級

近隣の小学校はまた状況が違うんですが、私が住む地区の小学校はひとクラス20人前後で非常に手厚い環境になっています。

地方のデメリット

教育環境が貧弱

これが一番ですね。中高一貫校がギリギリ通える範囲に1校だけありますが基本的には近所の公立中学に進学するしかありません。SAPIXや私立小学校なんてあるわけがないです。そんな環境から大学受験で突然同じ土俵に上がらされるわけで気の毒な気はします。

文化資本が貧弱

美術館や博物館、劇場などいわゆる文化資本が少ないです。例えば劇団四季のミュージカルは東京まで出ないと見れませんし、東京佼成ウインドオーケストラ定期演奏会もありません。小学校のときに演劇鑑賞イベントでミュージカルを見た当時は「こんなつまんないもの何で見させるの」と思ったものですが、今思えば貴重な機会でした。

若者・現役世代向けのサービスが貧弱

詳しくはこちらの記事をご覧ください。。。都会よりも高齢化が速く進んでいるため、全体的に老人向けの社会になっています。

簡単にメリデメ挙げてみましたが、日々の生活には満足しています。

We are hiring!

私が所属する会社では、システムエンジニアを募集しています。フルリモート可のため、地方在住でもご応募いただけます!会社紹介をお読みいただき、ご興味あれば @koty までご連絡ください。

テレビに近づき過ぎたら離れるよう注意する仕掛けを作った

ひととおり動くようになったので、記事にまとめます。

背景

子供はテレビが好きである。さらに、夢中になるとどんどんテレビに近づいていく。それをいちいち注意していた。 同じことを3回やったら自動化するのがプログラマーの鉄の掟であるため、 我が家のラズパイと超音波距離計か何かを使えばどうにかならんかなぁとぼんやり考えていた。

そんな中、ギーラボに置いてあった私物のスピーカーを取りに行ったときに「これも持ち帰ってくれ」と渡されたのが私物の初代kinectだった。 これには深度センサーが搭載されていることを思い出し、使ってみることを思いついたのだった。

実現方法の概要

作戦としては、以下を考えた

  1. テレビの上部にkinectを設置
  2. RGBカメラで顔を検知し顔のエリアの平均深度を計測
  3. 閾値よりも近かったら、離れるよう注意する音声をgoogle homeから流す

環境構築

そもそも手持ちのラズパイで使えるのか検証したのが以下の記事。 koty.hatenablog.com

ソースコードの解説

以下にupした。また、それぞれの要素技術に関する参考サイトはソース内のコメントに記載した。 github.com

kinectデータの取得

freenect.runloop というメソッドのコールバックから取得できる。センサーが別なためか、depthとRGBは同時には取得できない。 この仕様は動きの激しいものを相手にする場合は問題になりそうだけど今回は問題にならないだろうと考えて、それぞれの直近のコールバック呼び出しで取得したデータを使うことにした。 また、RGBのコールバックの方が頻繁に呼び出されていたためdepthのコールバックの方でメインロジックを呼ぶことにした。

from is_too_close import is_too_close
from warn_to_son import warn_to_son
#import frame_convert2
import freenect
#import numpy as np

latest_rgb = None
latest_depth = None
has_warned = False
keep_running = True

def process_depth(dev, data, timestamp):
    global has_warned
    global latest_depth
    global keep_running
    # raise freenect.Kill

    if has_warned:
        return
    if latest_rgb is None:
        return
    latest_depth = data

    if not is_too_close(latest_rgb, latest_depth):
        return
    
    warn_to_son()
    #has_warned = True
    #np.save('np_depth.npy', latest_depth)
    #np.save('np_rgb.npy', latest_rgb)
    #keep_running = False

def process_rgb(dev, data, timestamp):
    global latest_rgb
    latest_rgb = data

def body(*args):
    if not keep_running:
        raise freenect.Kill


print('Press ESC in window to stop')
freenect.runloop(depth=process_depth,
                 video=process_rgb,
                 body=body)

顔検出と距離計測

大まかな流れは、以下の通り。

  1. RGBをskimageにてグレースケールに変換
  2. OpenCVにて顔検出
  3. 顔の領域の切り出し
  4. 顔の領域の平均深度を算出
  5. 深度をメートルに変換
  6. 閾値判定

ありものの組み合わせで実現できてしまい驚いた。顔検出ロジックも完全にブラックボックスである。。。

import cv2
import numpy as np
# import matplotlib.pyplot as plt
import math

from skimage.color import rgb2gray
from skimage import io, exposure, img_as_float, img_as_ubyte
import warnings

# 顔検出器
face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")
THRESHOLD_METER = 1.5

def _convert2meter(raw_depth):
    # https://openkinect.org/wiki/Imaging_Information#Depth_Camera
    if raw_depth > 1050:
        # 2.5m以内の場合にしか適用できない数式なので、おおむねそれ以上のraw_depthの場合は一律999mと返す
        return 999
    return 0.1236 * math.tan(raw_depth / 2842.5 + 1.1863)

def _convert2gray(img):
    # https://qiita.com/yoya/items/dba7c40b31f832e9bc2a
    img = img_as_float(img)  # np.array(img/255.0, dtype=np.float64)
    imgL = exposure.adjust_gamma(img, 2.2)  # pow(img, 2.2)
    img_grayL = rgb2gray(imgL)
    img_gray = exposure.adjust_gamma(img_grayL, 1.0/2.2)  # pow(img_grayL, 1.0/2.2)
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        img_gray = img_as_ubyte(img_gray)  # np.array(img_gray*255, dtype=np.uint8)
        return img_gray
    
def is_too_close(rgb, depth):
    # http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html
    # グレースケールに変換
    gray = _convert2gray(rgb)
    # 顔検出
    faces = face_cascade.detectMultiScale(gray)
    for face in faces:
        # array([[310, 332,  33,  33]], dtype=int32)
        # depth画像から顔部分を切り出し
        # depth[332:365, 310:343]
        x_start = face[1]
        x_end = x_start + face[3]
        y_start = face[0]
        y_end = y_start + face[2]
        depth_cropped = depth[x_start:x_end,
                              y_start:y_end]
        # 顔領域の平均距離
        distance = _convert2meter(np.average(depth_cropped))
        # 閾値より近かったら too close
        if distance < THRESHOLD_METER:
            return True
    return False

google homeの音声出力

狙ったgoogle homeをローカルネットワークから探し出し、公開URLに置いた音声を再生する。

import os
import pychromecast
# https://qiita.com/rukihena/items/8af9b8baed49542c033d

CHROMECAST_NAME = os.environ['CHROMECAST_NAME']
DIRECTION_MP3_URL = os.environ['DIRECTION_MP3_URL']

def warn_to_son():
    chromecasts = pychromecast.get_chromecasts()
    google_home = [c for c in chromecasts[0] if CHROMECAST_NAME in c.device.friendly_name][0]
    google_home.wait()
    google_home.media_controller.play_media(DIRECTION_MP3_URL, 'audio/mp3')
    google_home.media_controller.block_until_active()

動作している動画を見せたいところですが、部屋が丸見えになるのでご容赦ください。

今後の展望

実用に向けては細々とした残タスクがある。飽きっぽいので途中で放り出しがちだけどもやっていきたい。。

  • 一度注意したら5分チェックを停止するようにしたい
  • 30分以内に3回注意されたらテレビを消すようにしたい。(テレビは既にnature remoで操作できるようにしてある。)
  • mp3を再生しているが、AWS Pollyにしゃべらせても良いかも

ゴールデンウィークの良い自由研究になった。

おまけ

顔検出ロジックの実装を試行錯誤する際は jupyter lab を使った。とても便利。 f:id:kkotyy:20210507171442p:plain

Kinect for Windows をRasberry Pi Zero Wで使う

いにしえのKinect for Windowsを使いたくなり、やってみた。基本的には以下の記事に沿って行った。前提として、ラズパイのパッケージ類は最新、Python3環境が入っている。

Experimenting with Kinect using opencv, python and open kinect&nbsp;(libfreenect)naman5.wordpress.com

いろいろ試行錯誤したので、この手順で良いかは分かりません。。。何か抜けているかも。

必要なパッケージをインストール

sudo apt-get install git-core cmake freeglut3-dev pkg-config build-essential libxmu-dev libxi-dev libusb-1.0-0-dev

OpenCVをインストール

sudo apt-get install python-opencv

Kinectを操作するためのライブラリをcloneする。ちなみに、歴史あるリポジトリだけどPython3に対応していた。

git clone https://github.com/OpenKinect/libfreenect.git

ビルドする。

cd libfreenect
mkdir build
cd build
cmake -L ..
make
sudo make install

Python で使うためのラッパーをインストールする。

cd ../wrappers/python
sudo python3 setup.py install

ここまでくれば、サンプルプログラムが動くはず。

python3 demo_cv2_async.py

f:id:kkotyy:20210504091540p:plain しかしスーパーもっさりである。ここから顔認識とdepth計測をしたいのだけど出来るんじゃろか。

OAuth徹底入門 読了後メモ

仕事で必要になり手に取った。

入門といえば入門なんだろうか、徹底と言っているだけあって400ページ超と(お値段もそれなり)かなりのボリュームそして情報量であった。

OAuthに関する本はほとんど出回ってなくて同人誌くらいしかなかったんだけど、これはよくまとまっていた。OAuthを手がけるのであれば読んでおくべき良書だと思う。

ドメイン駆動設計入門 読了後メモ

来年度はスクラッチ開発が予定されているので、手に取った。ちなみにいわゆる「DDD本」は挫折して本棚でほこりをかぶっております。DDD本が出てから久しい。みんなこの程度の知識は持っているのだろうか。。。

主にRailsDjangoでも同じ話だな)での開発を念頭に読んで、気になった点を五月雨に書きます。

Chapter 2. 値オブジェクト

所感

静的型な処理系であれば誤った代入も防げるし、ロジックは集約できるし、価値は理解できるんだけど、ORMと相性が悪そうだなーとは思った。どうしてんだろうな。

Chapter 4. ドメインサービス

所感

各機能に必ずドメインサービスを作りましょうみたいなSIerな考え方だと小規模サービスではかなり窮屈で開発効率が悪くなりそう。 基本的にはモデルに業務ロジックを集約しつつ、モデルをまたがるような処理はドメインサービスに書くのが良さそう。

さすがにコントローラーに長大なロジックを書くことはないんだけど、モデルは大きくなりがちだと思う。理由としては以下が挙げられる。

  • DBの正規化が不足していてフィールドが多すぎる
  • モデルにまたがった処理が書かれている

後者の場合はドメインサービスの概念を導入してやるとうまくいくんでしょう。

「業務を表現したものではなく、アプリケーションを作るために必要なのであれば、アプリケーションサービスに書くべき」とのこと。メール送信とかはどうなんだろうな。

Chapter 5. リポジトリ

データの永続化を担当。

所感

Railsだとアクティブレコードがあるから不要なようにも思った。

Chapter 6. アプリケーションサービス

ユースケースを実現するためのもの。

所感

MVCアーキテクチャのコントローラーか、バッチ処理のエントリーポイントかな。

アプリケーションサービスがドメインの振る舞いを呼び出せてしまうと、各所にロジックが散らばってしまうので、データを詰め替えるべきとの主張だった。大規模アーキテクチャにありがちなルールだなぁ。

6.4. 凝集度

すべてのインスタンス変数はすべてのメソッドで使われるべき。一部のメソッドでしか使われない変数があるのであれば、クラスとして切り出すべき。

所感

あまり考えられていなかったので、意識したい。モデルやコントローラーのクラスは凝集度が低くなりがちだとは思う。

Chapter 7. 依存関係のコントロール

本来、DBアクセスなどの低レベルモジュールに高レベルモジュールが影響を受けるべきではない。永続化方法はドメインに関係がないはずである。インターフェースを宣言し、低レベルのモジュールはそのインターフェースに合わせて実装することで、依存関係を切り離すことができる。

ここで問題になるのが実装クラスをどうやって作るかだが、DI Containerを使うと良い。

所感

例示されたケースだと、リポジトリ入れ替え可能になるのでテストが楽になるよ、とのことだったが、Railsの場合は本物のDBに向かってテストを実行して大きな問題は感じていない。。。

Chapter 9. ファクトリ

ただ漫然とnewするのではなく、ファクトリを導入すべきか検討する習慣をつけるべき。

所感

これはまあおっしゃるとおりだと思った。

Chapter 12. 集約

集約の外部から教会の内部のオブジェクトを操作してはいけない。集約を操作するための直接のインターフェースとなるオブジェクトは集約ルート(Aggregate Root)と呼ばれるオブジェクトに限定される。

所感

自分の想像していた「集約」とは違った。集約ルートの考え方は良いと思った。ただの代入であっても、それにどんな業務的な意味があるのか、あるのであればメソッドにくくることを検討する習慣を持ちたい。

おしまい

Railsだったらどうするかなーと考えながら読んだ。大前提としてドメイン駆動設計をちゃんとやらなければいけないケースでは静的型が必要で、Railsドメイン駆動設計には不向きなんだろうと思う。

RubyPythonなどの動的型付け言語に慣れ切ってしまったところにこの本を読むと、静的型付け言語を使いこなす自信がなくなってくる。ある程度静的型付き言語に慣れた後に読み直すとまた感じ方が違うんだろう。

実際の開発では何らかのWebフレームワークやORMに乗ることになるので、そのフレームワーク流儀に従ったうえでDDDを適用していくんだろう。

ミル付きコーヒーメーカー所感

ふたつほど使ったことがあるので、突然ですが所感を書きます。私としては、このふたつなら手入れが楽なパナソニックの方をおすすめします。

シロカ

1年ほど使用。ステンレスフィルター。ガラス製サーバーを落として割ってしまい、スレンレス製サーバーにかえた。

長所

  • フィルターの在庫を気にする必要がない
  • 見た目がややおしゃれ

短所

  • スレンレスフィルターをよく洗うなど、毎回の部品洗浄が面倒
  • 連続して淹れるときは、洗った部品を拭いて乾かす必要がある

追記(2021.11.14):ほぼ毎日1回利用。 ステンレスフィルターは1年半ほどで穴が空いてきたのでフィルターだけ買い替えた。今は在庫切れだな。 バケットの方も1年ほど使って異音がしてきたので買い替えた。こっちはもしかしたら食用グリスとかを塗ればよいのかもしれない。

パナソニック

10年近く使用。ミルが動いた時に異音が出るようになって買い替えた。

長所

  • 掃除が楽。紙フィルターを捨てた後に軽く洗うだけ
  • ミル部分は自動洗浄されるため、毎回の掃除は不要

そういえば連続して淹れたい時はどうしてたかな、、、忘れてしまった。ミル部分を乾かしたりはしていなかったと思う。

短所

  • 紙フィルターの在庫を気にする必要がある

ミル稼働時の音

シロカの方が小さいような気がするけど、パナソニックの方も今は改良されているかもしれない。どっちにしても集合住宅で早朝深夜に動かすのはちゅうちょするレベルだと思う。

味はどうなのよ

どちらもうまいです。なお参考情報↓