raspi2pngが超便利!ラズパイのCUI環境でフレームバッファのスクリーンショットを撮る方法 +BGRA→RGB変換プログラム
Raspbian Liteを入れてCUI環境で動かしているRaspberry Pi Zero W。
軽快にさくさく動いて良いのですが、デスクトップ環境(X)がないのでスクリーンショットを撮るには一筋縄ではいきません。
なかなかマニアックな、CUI環境でフレームバッファのデータからスクリーンショット(PNG画像)を作成する方法を紹介します。
スクリーンショットを撮る(超簡単な方法)
フレームバッファをrawデータとして保存する方法は簡単に見つかるのですが、そこから一般的な画像形式(pngとか)にする方法が見つからず、色々探す事小1時間。
完璧な「raspi2png」を発見しました!
githubからcloneしてきます。
※初めての方はgitコマンドをインストール。
$ sudo apt-get install git
$ mkdir ~/git
$ cd ~/git
$ git clone https://github.com/AndrewFromMelbourne/raspi2png.git
一応バイナリが入っているのですが、自分環境で作り直します。
コンパイルにはlibpngが必要です。
$ sudo apt-get install libpng12-dev
$ cd raspi2png
$ make
完了したら、キャプチャしてみましょう。
$ ./raspi2png -p ss.png
これで、あっさりとスクリーンショットが撮れました!
どこからでも使えるようにパスが通る場所に設置しておきます。
$ sudo install -m 0755 ./raspi2png /usr/local/bin/raspi2png
途中で行き詰まった方法
ググるとたくさん出てくるフレームバッファを直接ファイルに保存する方法を試してみました。
フレームバッファをraw画像ファイルに書き出します。
$ cat /dev/fb0 > ss.raw
出来上がったファイルはRGBにAlphaを加えた32bitのデータになります。(width x height x 4byte)
rawtoppmというコマンドで、raw画像をPPM形式に変換します。画面の縦横サイズが必要なので予めfbsetで調べておきます。
$ fbset
mode "1920x1200"
geometry 1920 1200 1920 1200 32
timings 0 0 0 0 0 0 0
rgba 8/16,8/8,8/0,8/24
endmode
コマンドが入っていない場合は、インストールします。
$ sudo apt-get install netpbm
PPM形式に変換します。
$ rawtoppm 1920 1200 ss.raw > ss.ppm
そして、ppmをconvertコマンド(ImageMagick)でpngに変換します。convertコマンドが使えるようにimageMagickを入れておきます(最初だけ)。
$ sudo apt-get install imagemagick
変換します。
$ convert -depth 8 -size 1920x1200 ss.ppm ss.png
できあがったファイルをPCで開いて見ると、、、
ぐちゃぐちゃ!
rawtoppmが24bitデータ(RGB)にしか対応していないのが原因と思われます。
最初にできたrawデータ(ss.raw)のサイズをls -l で見てみると、
9216000
となっています。
画面の横幅 x 画面の縦幅 x 4byteを計算すると、
1920x1200x4(byte)=9216000
ちょうどピッタリ。
ということで、4byte(BGRA)を3byte(RGB)に変換してからrawtoppm→convertとすれば良さそうです。
- 参考
上手くいった方法 →【フレームバッファのBGRAデータをRGBに変換する自作プログラム】
データをppm形式に変換するところでつまずいたので、フレームバッファのrawデータであるBGRA(各8bitRGBカラーデータにαを加えた32bitのデータ)からRGBを抜き出してくるプログラムを作りました。
変換プログラム(C言語編)
実行スピードを考慮して、C言語で作りました。
- フレームバッファ(画面サイズ)を小さくしてデータ量を少なくしています(方法は最後に記載)。
- 例外処理を入れ始めるととても長くなるので、全部決め打ちしています。
- ファイル名やサイズを後から指定する場合は、引数として与えて参照するように修正すると良いです。
$ vi argb2rgb.c
#include <stdio.h>
#define BUFSIZE1 (1280*720*4)
#define BUFSIZE2 (1280*720*3)
#define _FIN_ "ss.raw"
#define _FOUT_ "ss.dat"
int main (void){
FILE *fin, *fout;
unsigned char ibuf[BUFSIZE1], obuf[BUFSIZE2];
unsigned int i, size, num;
fin=fopen(_FIN_,"rb");
fout=fopen(_FOUT_, "wb");
size = fread(ibuf, sizeof(unsigned char), BUFSIZE1, fin);
num = (int)(size/4);
printf("size: %d, num: %d\n", size, num);
for (i=0; i<num; i++){
obuf[3*i+0] = ibuf[4*i+2];
obuf[3*i+1] = ibuf[4*i+1];
obuf[3*i+2] = ibuf[4*i+0];
}
fwrite(obuf, sizeof(unsigned char), BUFSIZE2, fout);
fclose(fin);
fclose(fout);
return 0;
}
※最初に定義しているバッファサイズはfbsetで確認した値を使います。
$ fbset
mode "1280x720"
geometry 1280 720 1280 720 32
timings 0 0 0 0 0 0 0
rgba 8/16,8/8,8/0,8/24
endmode
入力用には×4、出力用には×3しています。
※ファイルサイズを取得して動的にやれば良いのですが、手抜き、もとい、要点だけを分かり易くするために最小限にしています。
コンパイルします。
$ gcc -o argb2rgb argb2rgb.c
あとは、同じフォルダにss.rawデータを置いて実行すればOKです。
(詳細は変換手順にて)
変換プログラム(Python編)
Python3でも変換するコードを書いてみました。
#!/usr/bin/env python3
# coding: utf-8
import sys
with open(sys.argv[1], 'rb') as fin: # バイナリ・リードモードで開く
data = fin.read() # データを一括読み込み
num = int(len(data)/4) # BGRAがいくつあるか計算
print('len: {}, num: {}'.format(len(data), num)) # 確認用
with open(sys.argv[2], 'wb') as fout: # バイナリ・ライトモードで開く
odata = bytearray() # 空っぽのbytearrayを作成
for i in range(num): # numだけ回す。Aだけ抜いてRGBとして格納
odata.append(data[4*i+2]) # R
odata.append(data[4*i+1]) # G
odata.append(data[4*i+0]) # B
fout.write(odata) # ファイルに書き出して完了
C言語より恐ろしくシンプル。。。
実行権限を付けておきます。
$ chmod +x argb2rgb.py
変換手順
フレームバッファをrawデータとして保存します。
$ cat /dev/fb0 > ss.raw
変換プログラムを実行します。
$ ./argb2rgb
または、
$ ./argb2rgb.py ss.raw ss.dat
pythonの場合は引数に入力ファイルと出力ファイルを指定します。
※注)ラズパイzeroだとかなり時間がかかります。
出来上がったss.datをppm形式に変換します。
$ rawtoppm -rgb 1280 720 ss.dat > ss.ppm
png形式に変換します。
$ convert -depth 8 -size 1280x720 ss.ppm ss.png
これでスクリーンショット画像の出来上がり!
C vs Python 変換時間の比較
当初Pythonでやっていたのですが、あまりにも変換に時間がかかったのでCで書き直しました。どれくらい違うかをラズパイZero Wで比較してみました。
まずはPythonで実行。
$ time ./argb2rgb.py ss.raw ss.dat
len: 3686400, num: 921600
real 0m39.166s
user 0m39.020s
sys 0m0.110s
次にコンパイル済みのCで実行。
$ time ./argb2rgb
size: 3686400, num: 921600
real 0m0.195s
user 0m0.100s
sys 0m0.080s
40秒 vs 0.2秒 ということで、200倍の差が付きました^^;
おまけ(フレームバッファのサイズを変更)
接続しているディスプレイによっては解像度が高すぎて、スクリーンショットが大きくなってしまいます(文字が小さい!)
config.txtを編集して適度なサイズに変更することで対応します。
$ cd /boot
$ sudo cp config.txt config.txt.org
$ vim config.txt
フレームバッファの縦横サイズのコメントアウトを消します。
# uncomment to force a console size. By default it will be display's size minus
# overscan.
framebuffer_width=1280
framebuffer_height=720
保存して終了し、再起動後に無事に表示されたら完了です。
まとめ
ラズパイのCUI環境でスクリーンショットを撮る方法を紹介しました。
世の中には便利な物を公開してくださる方がおられ、ありがたい限りです^^
また、自作プログラムで変換する方法も開拓できました。
画像処理の初歩的な理解にも役立ちそうです。
ディスカッション
コメント一覧
まだ、コメントがありません