2020年7月10日金曜日


Raspberry PiとDHT11で温度・湿度を測る



「DHT11」という温湿度センサーを使用します。

温湿度センサー「DHT11」

DHT11は、価格も安価で入手し易い温湿度センサーです。
dht11.png
センサー単体、基盤に設置されたものなど、用途に応じた様々な形態で販売されており、秋月電子通商では300円で購入することができます。(左の画像がセンサー単体です)
【秋月電子通商】
秋月電子通商 温湿度センサ モジュール DHT11

回路を組む

秋月電子で公開されているデータシートを参考に回路図を組んでみます。使用するのはDHT11のセンサー単体のものです。
DHT11のピン構成は、
 1番:5V
 2番:GPIO
 3番:NC(Non-Connected = 使用していない)
 4番:GND
という構成になっています。(3番の使用していないピンがあるというのは謎ですね…)
ざっくり説明すると、1番ピン、4番ピンにRaspberry Piの5V、GNDを接続し、2番ピンに任意のGPIOの端子を割り当てて情報を取得する、ということになります。
回路の組み方はいくつかあると思いますが、私は下図のように組んでみました。この後で温度・湿度を取得するプログラムでピンの番号が重要になりますので、ピンの位置は間違えないように注意してください。
dht11_ブレッドボード.png

温度・湿度を取得するスクリプトを作成する

DHT11から情報を取得するPythonのライブラリがGitHubで公開されています。
こちらのライブラリを使って、センサーから温度・湿度の情報を取得します。
DHT11 Python library
Raspberry PiからGitHubにアクセスして、ライブラリを取得しましょう。
そのためにも、apt-getを使用して、gitクライアントをインストールします。
sudo apt-get install git
インストールが完了したら、上記のサイトからクローンしましょう。
git clone https://github.com/szazo/DHT11_Python.git
クローンが完了すると、DHT11_Pythonというフォルダが作成され、以下のファイルが取得されています。
LICENSE.md
README.md
__init__.py
dht11.py
dht11_example.py
dht11.pyがDHT11から情報取得するライブラリ本体、dht11_example.pyがライブラリを使った温度・湿度の情報を取得するサンプルスクリプトです。

サンプルスクリプトを読み解く

dht11_example.pyを少し読み解いてみます。
dht11_example.py
import RPi.GPIO as GPIO
import dht11       # ・・・ ①
import time
import datetime

# initialize GPIO
GPIO.setwarnings(False)       # ・・・ ②
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

# read data using pin 14
instance = dht11.DHT11(pin=14)      # ・・・ ③

while True:
    result = instance.read()    # ・・・ ④
    if result.is_valid():    # ・・・ ⑤
        print("Last valid input: " + str(datetime.datetime.now()))
        print("Temperature: %d C" % result.temperature)    # ・・・ ⑥
        print("Humidity: %d %%" % result.humidity) 

    time.sleep(1)    # ・・・ ⑦
① import文でライブラリ「dht11.py」を読み込みます。
② GPIOの初期化をしています。各コマンドには次のような意味になります。
  GPIO.setwarnings(False) : ワーニングを非表示にします。※1
  GPIO.setmode(GPIO.BCM) : GPIOの数字で指定します。
  GPIO.cleanup() : GPIOの設定を初期化します。
※1.ピンが既に使用中の場合等にワーニングが表示されます。今回のサンプルではワーニングは表示されないため、設定は必須ではありません。
③ データ読み込みのピン番号を設定します。②の初期化でGPIOの番号で設定するようにしているため、引数のpinには今回接続している14を設定します。
④ センサーから温度・湿度情報を取得します。
⑤ センサーから温度・湿度情報が取得できたか判定します。取得できた場合に結果をコンソールに出力します。
⑥ 温度・湿度情報をコンソールに出力します。
  temperature : 温度(摂氏)の値です。
  humidity : 湿度の値です。
⑦ 1秒処理を停止します。

実行結果

サンプルスクリプトのdht11_example.pyを実行します。コマンドは次の通りです。
$ python dht11_example.py 

成功すると下記のような結果を得られます。
shell
Last valid input: 2018-03-10 05:08:35.590508
Temperature: 24 C <= 温度
Humidity: 47 % <= 湿度
上記は1回の結果ですが、サンプルスクリプトは停止させるまで1秒毎に温度・湿度を取得して出力しています。(詳細は「サンプルスクリプトを読み解く」をご覧ください)
また、実行直後の結果には多少の誤差が生じるようで、数値が揺れているように感じます。数回実行すると収束し、安定した結果が得られるようになります。
※揺れは高性能なセンサーにすると解消するかもしれません。手に入れる機会があれば試してみたいと思います。

2020年7月4日土曜日

趣を変えて。ドイツワイン  ピーロートブルー アウスレーゼ naha


昨日(20200703) ピーロート ブルー アウスレーゼ ナーハ が届いた。思いもかけず、安価にしかもベルギーワインなどなど。奥さんの誕生日用に間に合った。
2018年もの中心なので、若いすっきりとした ドイツ白ワイン 日本酒好きの飲み屋さんから刺身盛り合わせを買って、日本酒は一口のんで、ワインに。
さらに、Snのタンブラーで飲むと、恐ろしいことになった。。。卸値で購入できたからよいけど、話題の「夜の街」でなら、、、というか、夜の街でこのワインは飲めないので、、、、。昼から飲む酒です。 フランスワイン好きからは、かなりけなされましたが、しょうがない。人それぞれ好みは違う。ドイツワイン、ケローナのアイスヴァインもよいが、ドイツワインは安定して手に入る。しかし、ドイツ貴腐ワインは、だれにもあげたくない・・。まだ隠し持っている。(密輸じゃないよ!!)2度と取れないらしい。カビが付かなくなったんだとか。

そのうち写真UP!




2020年6月21日日曜日

PSK31 on Linux



ここにいろいろあり

https://pkgs.org/download/linpsk

winpsk オリジナルはここだろうか
http://www.moetronix.com/ae4jy/


  • 画像処理にはPillow(PIL)使います。

インストール

Python

MacははじめからPythonが入っています。
python --version
としてみてください。入っていなければ、
brew install python
とかで入ります。
brewが入っていることが前提ですが。。。

pipのインストール

ライブラリのインストールを楽にするためにPythonのパッケージ管理ツールであるpipをインストールします。
easy_install pip

モジュール(ライブラリ)のインストール

今回は、numpyとPillow(PIL)を使います。numpyは計算に便利なツール類が、Pillowは画像処理関係のツール類が含まれます。
pip install numpy
pip install pillow

画像を操作してみる

とりあえず、画像を読み込んで表示してみます。
from PIL import Image

im = Image.open("./achan.jpg")
im.show()
ただ表示しているだけも何なので回転させて表示してみます。
py01
from PIL import Image

im = Image.open("./achan.jpg")
im.rotate(30).show()
画像の中心を軸に、反時計周りに30度回転されたかと思います。
py01
実務では、これでいいのですが、内部的にどのような処理をしているのかはわかりません。なので、ここでは、1個1個のピクセルを処理して回転させるプログラムを書いてみたいと思います。

ピクセルを操作してみる

では、個々のピクセルを操作してみます。

ネガポジ反転してみる

では、代表的かつ簡単なピクセル操作処理であるネガポジ反転処理を行ってみます。
#coding:utf-8
from PIL import Image

#画像の読み込み
im = Image.open("./achan.jpg")

#RGBに変換
rgb_im = im.convert('RGB')

#画像サイズを取得
size = rgb_im.size

#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)

#loop
#x
for x in range(size[0]):
    #y
    for y in range(size[1]):
        #ピクセルを取得
        r,g,b = rgb_im.getpixel((x,y))

        #反転処理
        r = 255 - r
        g = 255 - g
        b = 255 - b

        #set pixel
        im2.putpixel((x,y),(r,g,b,0))

#show
im2.show()
ネガに反転されました。
py01

グレースケール

グレースケールは、r,g,bが同じ値を持つことでグレーに見えます。ただ、そのようなルールで同じ値にするかはケースバイケースです。ここではr,g,bの平均値を取得し、その値にしてみます(以下、ピクセル操作のみ抜粋)。
#loop
#x
for x in range(size[0]):
    #y
    for y in range(size[1]):
        #ピクセルを取得
        r,g,b = rgb_im.getpixel((x,y))

        #平均化
        g = (r + g + b)/3

        #set pixel
        im2.putpixel((x,y),(g,g,g,0))

#show
im2.show()
とりあえずグレーになりました。
py01

回転させてみる

画像を回転させる方法はいくつかありますが、最も原始的な方法は、回転行列を利用することです。

回転行列

回転行列は、下記の式で表されます。θに回転させたい角度をラジアンで与えると、その角度だけ回転した座表(x2,y2)を得ることができます。

(x2y2)=(cos⁡θ−sin⁡θsin⁡θcos⁡θ)(x1y2)
Pythonではnumpyモジュールを使うことで、上記の回転行列を、
m_matrix = np.matrix([
            [np.cos(rad),-1*np.sin(rad)],
            [np.sin(rad),np.cos(rad)]
        ])
と、直感的に表現することができ、かつ、通常の四則演算のように記述することで和や積を求めることができ、非常に便利です。
ここではmatrixを使っていますが、特に理由がない場合、arrayを使ったほうがいいらしい。

コードを書く

では、回転行列を利用して回転するコードを書いてみます。なお、下記のコードでは画像中心ではなく、左上が回転軸として処理されます。さらにintしか扱えないputpixelを利用してるので、画像にムラが発生します(が、ここではわかり易さ重視でこの方法にしています)。
#coding:UTF-8
from PIL import Image
import numpy as np

#画像の読み込み
im = Image.open("./achan.jpg")

#RGBに変換
rgb_im = im.convert('RGB')

#画像サイズを取得
size = rgb_im.size

#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)

#loop
#x
for x in range(size[0]):
    #y
    for y in range(size[1]):

        #ピクセルを取得
        r,g,b = rgb_im.getpixel((x,y))

        #処理

        #30度
        rad = np.pi/6

        #回転行列
        m_matrix = np.matrix([
            [np.cos(rad),-1*np.sin(rad)],
            [np.sin(rad),np.cos(rad)]
        ])

        #適用座標(元の座標)
        p_matrix = np.matrix([
                [x],
                [y]
            ])

        #行列演算
        p2_matrix = m_matrix * p_matrix

        #移動後の作業を取得(intしかputできないのでintに変換)
        x2 = int(p2_matrix[0,0])
        y2 = int(p2_matrix[1,0])

        #画像サイズの内であれば
        if 0 < x2 < size[0] and 0 < y2 < size[1]:
            #移動後の座標に元RGBを指定
            im2.putpixel((x2,y2),(r,g,b,0))

#show
im2.show()
py01

反転させてみる

x軸、y軸、任意軸に対する反転等も行列で行えます。高校数学の1次変換です。
例えば、y軸反転は、下記で与えられます。

反転行列(y軸)


(x2y2)=(−1001)(x1y2)
この行列は、Pythonでは、
#y軸対象
y_matrix = np.matrix([
    [-1,0],
    [0,1]
])
として表現することができます。

コードを書いてみる

では、コードを書いてみます。なお、普通にy軸反転すると、全ての点が、マイナス方向にずれるため描画されません。そこで、画像の横幅(x軸)分だけ、平行移動させます。
#coding:UTF-8
from PIL import Image
import numpy as np

#画像の読み込み
im = Image.open("./achan.jpg")

#RGBに変換
rgb_im = im.convert('RGB')

#画像サイズを取得
size = rgb_im.size

#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)

#loop
#x
for x in range(size[0]):
    #y
    for y in range(size[1]):

        #ピクセルを取得
        r,g,b = rgb_im.getpixel((x,y))

        #処理

        #y軸対象
        y_matrix = np.matrix([
            [-1,0],
            [0,1]
        ])

        #適用座標(元の座標)
        p_matrix = np.matrix([
                [x],
                [y]
            ])

        #行列演算
        p2_matrix = y_matrix * p_matrix

        #移動後の作業を取得(intしかputできないのでintに変換)
        x2 = int(p2_matrix[0,0]) + size[0] #加増の横サイズだけx座標を平行移動
        y2 = int(p2_matrix[1,0])

        #画像サイズの内であれば
        if 0 < x2 < size[0] and 0 < y2 < size[1]:
            #移動後の座標に元RGBを指定
            im2.putpixel((x2,y2),(r,g,b,0))

#show
im2.show()
py01
反転しました。いわゆる左右反転。
なんか1ピクセル余計にずれてる感じもしますが。。。ここでは気にしません。

近傍処理

行列演算と同様、画像処理では必須処理となる近傍処理をやってみます。近傍処理は「ぼかし処理」や「輪郭抽出」等で活躍します。

ぼかし

ここでは、比較的簡単な「ぼかし処理」をやってみます。いろいろなアルゴリズムがありますが、ここでは最も単純な8近傍の平均値を取得し、セットするという方法をためしてみます。
8近傍とは、基準座標を取り囲む8つのエリアのことです。
py01
それそれの座標のr,g,b値を取得して、平均化します。
1つ注意点があるのは、画像の辺縁部の座標に置いては、x-1が存在しなかったり、x+1が座標からはみ出たりしますので、その処理が必要になります。では、コードを書いてみます。
#coding:utf-8
from PIL import Image

#画像の読み込み
im = Image.open("./achan.jpg")

#RGBに変換
rgb_im = im.convert('RGB')

#画像サイズを取得
size = rgb_im.size

#取得したサイズと同じ空のイメージを新規に作成
im2 = Image.new('RGBA',size)

#loop
#x
for x in range(size[0]):
    #y
    for y in range(size[1]):

        #対象座標のピクセルを取得
        r0,g0,b0 = rgb_im.getpixel((x,y))

        #初期化(とりあえず、全ての近傍値に現座標値をセット)
        r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r0;
        g1 = g2 = g3 = g4 = g5 = g6 = g7 = g8 = g0;
        b1 = b2 = b3 = b4 = b5 = b6 = b7 = b8 = b0;

        #近傍座標の値を取得

        #1
        if x-1 > 0 and y+1 < size[1]:
            r1,g1,b1 = rgb_im.getpixel((x-1,y+1))

        #2
        if y+1 < size[1]:
            r2,g2,b2 = rgb_im.getpixel((x,y+1))

        #3
        if x+1 < size[0] and y+1 < size[1]:
            r3,g3,b3 = rgb_im.getpixel((x+1,y+1))

        #4
        if x-1 > 0:
            r4,g4,b4 = rgb_im.getpixel((x-1,y))

        #5
        if x+1 < size[0]:
            r5,g5,b5 = rgb_im.getpixel((x+1,y))

        #6
        if x-1 > 0 and y-1 > 0:
            r6,g6,b6 = rgb_im.getpixel((x-1,y-1))

        #7
        if y-1 > 0:
            r7,g7,b7 = rgb_im.getpixel((x,y-1))

        #8
        if x+1 < size[0] and y-1 > 0:
            r8,g8,b8 = rgb_im.getpixel((x+1,y-1))


        #近傍のRGBを平均化
        r = (r0 + r1 + r2 + r3 + r4 + r5 + r6 + r7 + r8)/9
        g = (g0 + g1 + g2 + g3 + g4 + g5 + g6 + g7 + g8)/9
        b = (b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8)/9

        #描画
        im2.putpixel((x,y),(r,g,b,0))

#show
im2.show()
ちょっとわかりにくいですが、ぼけました。さらに近傍を拡大したり、アルゴリズムを工夫することでいろいろなぼかしが可能となります。
py01
ピクセルの補正など、実務に耐えうる状態にするにはまだまだですが、とりあえず、基本的な内容はここまで。


しや保存、各種画像処理(クリッピング、リサイズそして回転など)の方法についてまとめます。

環境

    • OS: Ubuntu16.04LTS
    • Python 3.7.0
    • Pillow 5.3.0
公式リファレンス: https://pillow.readthedocs.io/en/5.3.x/index.html

インストール

Pillowは3rdパーティ製のモジュールです。自分の環境に無いときはインストールしましょう。公式リファレンスに記載の通りpipコマンドでできます。
$ pip install Pillow
Anaconda環境の場合は以下でインストール可能です。
$ conda install pillow

使い方

Pillowモジュールのインポート

PillowはPIL(Python Imaging Library)から派生して作られた経緯があり、PILとの互換性を保つため、慣例的に以下のように呼び出すそうです。
from PIL import Image

画像データの読み出し

open()メソッドを使います。画像ファイルまたはファイルオブジェクトを引数に設定します。戻りはImageオブジェクトです。
>>> img = Image.open('sample.jpg')
また、Imageオブジェクトは様々なプロパティを持っており、サイズやフォーマットを確認することができます。
# 画像サイズを横幅、縦幅のタプルで返す
>>> img.size
(640, 426)
# ファイル名を返す
>>> img.filename
'sample.jpg'
# 画像フォーマットを返す
>>> img.format
'JPEG'

読みだした画像データを表示

show()メソッドを使います。
>>> img.show()
ただ、公式リファレンスによると、このメソッドは非効率なのでデバック目的にしか使わないほうが良いよ、とのこと。

画像を保存する

保存ファイル名をsave()メソッドの引数に設定します。拡張子を変更することで、自動的にフォーマットを変換することもできます。これは便利そうですね。
# jpg画像を読み込み
>>> img = Image.open('sample.jpg')
# 読み込んだ画像がjpg形式であることを確認
>>> img.format
'JPEG'

# 拡張子をpngにして保存
>>> img.save('sample.png')
>>> img2 = Image.open('sample.png')
# 読み込んだ画像がpng形式に変換されています
>>> img2.format
'PNG'
また、ファイル名の他にオプション引数を記述すると、画質の調整など細かな設定ができますが、細かくなるのでここでは割愛します。詳細は公式リファレンス:Image file formatsをご参照ください。

新しいImageオブジェクトを生成

new()メソッドを使います。
まずは、Lime色の幅200px, 高さ150pxの矩形を表示してみます。
>>> img = Image.new('RGBA', (200, 150), 'lime')

第一引数はカラーモードを表しており、上記例で設定した”RGBA”の他に”RGB”や”CMYK”などいろいろ設定できます。
<カラーモードの例>
    • RGBA:赤(Red),緑(Green), 青(Blue)、α値(不透明度)
RGB: 0 – 255の値(8bitで表せる数値: 2^8)
α : 0 – 255の値(0:透明 ⇔ 255:不透明)
    • RGB: 赤(Red),緑(Green), 青(Blue)
上記のRGBのみで表したもの。
    • CMYK:シアン(Cyan)、マゼンタ(Magenta)、イエロー(Yellow)、キー・プレート(Key plate:黒)
それぞれ0 – 255の値
※この他のカラーコードは公式リファレンスをご参照ください。
第二引数は、描画する矩形の幅、高さを表します。
第三引数は、カラーを表します。デフォルトは黒です。RBGなら3つ、RGBAなら4つのの整数値を要素としたタプルを設定します。また、上記例のようにImageColorモジュールによって定義された文字列で表すこともできます。他にも例を示すと、、、
カラー:赤 RGBA値=(255, 0, 0, 255)の場合
>>> img = Image.new('RGBA', (200, 150), 'red')

カラー:紫 RGBA値=(128, 0, 128, 255)の場合
>>> img = Image.new('RGBA', (200, 150), 'purple')

画像の切り抜き

crop()メソッドを用います。切り取るエリアを座標で表して引数として渡します。
ここで、Pillowにで使われる座標系について簡単におさらいします。

  • 座標系: 画面左上が原点(0, 0)、x軸は画面横方向、y軸は画面縦方向
  • 座標の表し方:
    • 点 : (x, y)の2つタプル
    • 四角形: (左上のx, 左上のy, 右下のx, 右下のy)の4つのタプル
これらを踏まえ、上図のエリアを切り取るには、以下のように記述します。
>>> cropped_img = img.crop((250, 100, 400, 300))
塗りつぶした矩形だと面白くないので、具体的な例を以下に示します。
# 元画像をオープン
>>> img = Image.open('coffee.jpg')

# (40, 50)、(150, 130)で囲われたエリアを切り抜きます
>>> cropped_img = img.crop((40, 50, 150, 130))
 → 

画像のペースト

paste()メソッドを用います。第一引数にペーストしたい画像、第二引数にペーストする座標を設定します。
このメソッドは、元の画像(Imageオブジェクト)を直接変更します。元の画像を変更したくない場合はcopy()メソッドで画像を複製しておきます。
# 元画像をコピー
>>> copy_img = img.copy()
# コピーした画像に、上記で切り抜いた画像を(0, 0)の位置にペースト
>>> copy_img.paste(cropped_img, (0, 0))
>>> copy_img.show()

この他に、maskパラメータを使ってペーストする画像の境界線をぼやかすなどエフェクトを付けることも可能ですが、ここでは割愛します。

画像のリサイズ

resize()メソッドを用います。
以下は元画像の1/4のサイズにする場合の例です。
# 元画像
>>> img.size
(300, 200)
# 元画像のサイズ確認
>>> w, h = img.size

# リサイズ処理
>>> resized_img = img.resize((int(w/2), int(h/2)))
# リサイズ後のサイズ確認
>>> resized_img.size
(150, 100)

画像の回転

rotate()メソッドを使います。引数は、半時計回りの回転角度(degree)です。
90°回転の場合の例
>>> r90_img = img.rotate(90)

この設定だと、画像のサイズが変わらないまま回転してしまうので、画像がはみ出てしまっています。
元の画像全部が表示されるように回転させるには、オプション引数expand=Trueを設定します。
>>> r90_img = img.rotate(90, expand=True)

まとめ

PillowモジュールのImageオブジェクトを使った画像処理についてまとめました。
  • 画像データの呼び出し・保存
  • 画像のクリッピング、ペースト、回転といった基本的な操作