Raspberry Piのプログラムコード置き場

植物栽培に関係したコードをまとめました。中にはメモとして残した物もあるので実際に使えるかは不明です。自分でまとめたコードもあればChatGPTと対話した結果のものや、それぞれを継ぎ接ぎしたものもあります。何かのヒントになればと思って記録を残します。

実際に使っているコードに関しては、各記事のリンクを貼るのでそちらを参考にしてください。

プログラミングの試行錯誤のたびに追記していく予定です。

コードを作成する上での注意点(自戒)

なるべく端的にまとめて、1つ1つの挙動を確認できるようにする。

長い処理文にすると不具合の元で、修正すればするほど不具合が生えてくる危険性が上がる。

シェルスクリプト

カメラ位置の調整用コード

仕様
  • ビューファインダー(カメラのプレビュー)を表示しっぱなしにする
  • 撮影は行われず、何かのキーが押されたら終了する
#!/bin/bash

raspistill --nopreview --fullscreen --timeout 0 &

# キーボード入力待ち
read -n1 -s -r -p "Press any key to exit..."

# プレビュープロセスを終了
killall raspistill

カメラを設置して最初に実行するプログラムです。これで画角や距離の調整を行います

カメラ撮影用コード

仕様
  • 緑字の各部分を変えることで“保存場所とファイル名を変更可能
  • 解像度は1920×1080ピクセルで撮影
    (カメラモジュールごとに解像度上限が異なる)
#!/bin/bash

# 保存先パスとファイル名
save_path="/path/to/save"
default_name="image"

# 現在の日時を取得
datetime=$(date +"%Y-%m-%d_%H-%M-%S")

# ファイル名を作成
filename="$save_path/$default_name-$datetime.jpg"

# 画像を撮影してファイルに保存
raspistill -w 1920 -h 1080 -o "$filename"

最後の一文を「 raspistill -o “$filename” 」に変えると、モジュールのデフォルトの解像度で撮影します。使用しているモジュールだと2592×1944ピクセルになってしまい、画像サイズが大きいのでサイズを落としています。

撮影した画像に余白を作成し、テキスト形式の情報を追加する

仕様
  • この緑字の部分を変える“ことで以下の項目を変更できる
    それぞれの保存先やファイル名
    上下左右の塗りつぶし領域の位置(※どれか一箇所のみ)
    塗りつぶし領域の色
    塗りつぶし領域の幅と高さ(使わない場合の数値は無視される)
    テキストの描画開始位置
  • 写真の解像度は1820×1080で、100×1080ピクセルの余白を右側に追加して合計で1920×1080ピクセルの画像を出力する
  • このコードで扱うフォルダは2つで、
    「通常の画像が保存されるフォルダ」
    「塗りつぶし領域が追加された画像が保存されるフォルダ」
  • 塗りつぶし領域には日時情報が追加される(その他情報は要カスタマイズ)
#!/bin/bash

# デフォルトのパスやファイル名、条件を設定
default_path="/path/to/save"
default_edited_path="/path/to/save"
default_name="image"

# [up,down,left,right]から塗りつぶし領域を作る上下左右の方向と幅・高さを指定
padding_direction= "up"
padding_width = 100
padding_height = 0

# 塗りつぶし領域の色と幅と高さを指定
fill_color="white"

# 現在の日時を取得
datetime=$(date +"%Y-%m-%d_%H-%M-%S")

# ファイル名を作成
filename="$default_path/$default_name-$datetime.jpg"

# 画像を撮影してファイルに保存
raspistill -w 1820 -h 1080 -o "$filename"

# 入力ファイルと出力ファイルのパスを指定
input_file="$filename"
output_file="$default_edited_path/$default_name-$datetime-output.jpg"

# 画像の幅と高さを取得
width=$(identify -format "%w" "$input_file")
height=$(identify -format "%h" "$input_file")

# 塗りつぶし領域を追加した画像の幅と高さを計算
if [ "$padding_direction" = "up" ] || [ "$padding_direction" = "down" ]; then
  new_width="$width"
  new_height=$((height + padding_height))
elif [ "$padding_direction" = "left" ] || [ "$padding_direction" = "right" ]; then
  new_width=$((width + padding_width))
  new_height="$height"
else
  new_width="$width"
  new_height="$height"
fi

# 塗りつぶし領域を追加して画像を生成
convert -size "${new_width}x${new_height}" xc:"$fill_color" "$input_file" -geometry "+0+0" -composite "$output_file"

# 画像の右側から85、上から20ピクセルの位置にに日時を書き込む
convert "$output_file" -pointsize 24 -fill black -annotate +$(($width - 85)) +20 "$datetime" "$output_file"

成長記録をタイムラプスに変換する際に、画像内に各種情報を載せる余白があった方が編集の手間が省けると思って作成しました。後でよくよく考えると、

  • 余白を追加することで使いにくい解像度になってしまう
  • 別に画像中の下の部分は植物の栽培的に情報が増える部分ではないので、そこに情報を書き込んでも問題ない
  • 1920×1080ピクセルの比率を維持して画面の下などに情報が少ないらなくてもコードの「# 白塗り領域を右に追加して画像を生成」の下の部分のコードを書く記述に変更すると向きを変えられます。

保存された画像を繋げてタイムラプス動画にするコード

仕様
  • この緑字の部分を変える“ことでそれぞれの保存先やファイル名を指定できる
  • このコードで扱うフォルダは3つで、
    「編集する画像が保存されているフォルダ」
    「タイムラプス動画が保存されるフォルダ」
    「編集済み画像が保存されるフォルダ」
  • フレームレートは24fps
#!/bin/bash

# 編集元の画像フォルダのパス
unprocessed_folder="/path/to/unprocessed"

# タイムラプス動画フォルダのパス
timelapse_folder="/path/to/timelapse"

# 編集済み画像の移動先フォルダのパス
edited_folder="/path/to/edited"

# 現在の日時を取得
datetime=$(date +"%Y-%m-%d_%H-%M-%S")

# タイムラプス未加工画像フォルダ内の画像をタイムラプス動画に変換
ffmpeg -framerate 24 -pattern_type glob -i "$unprocessed_folder/*.jpg" -c:v libx264 -r 24 "$timelapse_folder/$datetime.mp4"

# タイムラプス未加工画像フォルダの画像を編集済み画像フォルダに移動
mv "$unprocessed_folder"/*.jpg "$edited_folder/"

24fpsは1秒間に24枚の画像を使用します。画像の枚数が少ない場合はフレームレートを減らす必要があります。

1日15枚の写真を撮った時の各フレームレートごとの再生時間(秒)

1週間1ヶ月2ヶ月3ヶ月6ヶ月
10fps10.545135270
15fps73090180
20fps5.2522.567.5135
24fps4.37518.7556.25112.5
25fps4.21854108
30fps3.5154590

魔改造コード

仕様
  • シェルスクリプトから1820×1080の解像度の写真を撮る(普通)
  • 撮った写真に100×1080の余白を右側に追加して、1920×1080の画像にする(まだ普通)
  • 余白に日時、気温と湿度、水分量を書き込みたいので、データをPythonをシェルスクリプト内で無理やり使って取得する(カオス)
  • 右から指定の位置に情報を記述(普通)
#!/bin/bash

# デフォルトのパスやファイル名、条件を設定
default_path="/home/pi/イチジク/完成プログラム/record"
default_edited_path="/home/pi/イチジク/完成プログラム/padding_record"
default_name="image"

# 塗りつぶし領域の幅・高さを指定
padding_width=200
padding_height=0

# 塗りつぶし領域の色と幅と高さを指定
fill_color="white"

# 現在の日時を取得
datetime=$(date +"%Y-%m-%d_%H-%M-%S")

# ファイル名を作成
filename="$default_path/$default_name-$datetime.jpg"

# 画像を撮影してファイルに保存
raspistill -w 1720 -h 1080 -o "$filename"

# 入力ファイルと出力ファイルのパスを指定
input_file="$filename"
output_file="$default_edited_path/$datetime-output-edited.jpg"

# 画像の幅と高さを取得
width=$(identify -format "%w" "$input_file")
height=$(identify -format "%h" "$input_file")

# 塗りつぶし領域を追加した画像の幅と高さを計算
new_width=$((width + padding_width))
new_height="$height"

# 塗りつぶし領域を追加して画像を生成
convert -size "${new_width}x${new_height}" xc:"$fill_color" "$input_file" -geometry "+0+0" -composite "$output_file"

# Pythonスクリプトの実行とデータの取得
sensor_data=$(python3 - <<EOF
import spidev
from datetime import datetime

num_sensor = 3
moist_offsets = [26, 0, 0]

spi = spidev.SpiDev()
spi.open(0, 0)

def read_adc(channel):
    adc = spi.xfer2([1, (8 + channel) << 4, 0])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data

def calculate_moisture_percent(sensor_value, offset):
    dry_value = 850
    wet_value = 485
    sensor_value += offset
    moisture_range = dry_value - wet_value
    if sensor_value > dry_value:
        sensor_value = dry_value
    elif sensor_value < wet_value:
        sensor_value = wet_value
    moisture_percent = 100 - ((sensor_value - wet_value) / moisture_range) * 100
    return round(moisture_percent, 2)

try:
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    sensor_data = []
    sensor_data.append("date: %s" % timestamp)
    sensor_data.append("temp: 00C, humid: 00%")
    sensor_data.append("moisture")

    for channel in range(num_sensor):
        sensor_value = read_adc(channel)
        offset = moist_offsets[channel]
        moisture_percent = calculate_moisture_percent(sensor_value, offset)

        if channel == 0:
            sensor_data.append("Top: %02.0f%%" % moisture_percent)
        elif channel == 1:
            sensor_data.append("Middle: %02.0f%%" % moisture_percent)
        elif channel == 2:
            sensor_data.append("Bottom: %02.0f%%" % moisture_percent)

except Exception as e:
    print("エラーが発生しました:", str(e))

finally:
    spi.close()
    print('\n'.join(sensor_data))
EOF
)

# 画像の右側から85、上から20ピクセルの位置にに日時と水分量を書き込む
convert "$output_file" -pointsize 24 -fill black -annotate +$(($width - 85)) +20 "$sensor_data" "$output_file"

crontabの設定について

仕様
0 5-19/1 * * * /path/to/capture.sh
0 0 * * 1 /path/to/timelapse.sh

仕様

コメント

タイトルとURLをコピーしました