スキャナー本体のボタンを押したらメールで画像やPDFが届く。スキャンサーバーを有するオフィス複合機なんかであればちょいと設定すればできちゃう働きですが、これをお宅にあるサーバー機能のない白物スキャナーとRaspberry Piでやります。
イントロ
実際の動きのビデオをご覧ください。
スキャナのボタンを押すと画像が届くようにしました。メッセージはSlackでやり取りしてますが、ここはメールでもDiscordでもなんでも好きなのを設定できます。機材はCanoScan LiDE200というスキャナ、4つのボタンそれぞれに動きを割り当てました。スキャンボタンのついたUSBで動くスキャナならなんでも同じことができると思います。これは、SANEという今回お世話になるあらゆるスキャナを動かすAPIのおかげ。
この投稿ではscanbdことScanner Button Daemonという、SANEを使ってスキャナのボタンを監視するソフトを構成してボタン押下によって任意のスクリプトを発動し、そこでスキャンをするまでをまとめます。実用的な例として、連続したスキャンをまとめてひとつのPDFを送るーさらにはそのPDFにはOCRをかけてテキスト情報が含まれるーという応用をする場合のスクリプトを載せます。
対象読者はRaspberry Piを触ったことがある方。具体的には、ファイル操作・テキストエディタ・シェルスクリプトを使います。インターネットへの接続は無線がよく、SSHができると楽。このほか用意するものとして、スキャナをバスパワーで動かすのでしっかりした電源があると安心です。私が使ったのはRaspberry Pi 3ですがPi Zeroだとコンパクトでよさそう。品薄解消されてくれ。
最小構成でいくぞ
ここで説明する手順は最低限のものでして、Raspberry Pi とスキャナはボタンを押したらスキャンして特定の場所へ送信する、そのためだけの機械になります。SANEおよびscanbdをもっときちんと設定するとボタンを待ち受けるほかに、デスクトップ環境でGUIのスキャンソフトを使う、スキャンサーバーを構成してローカルネットで公開する、などが共存することもできるはずなのですが、めちゃめちゃ大変なのでやりません。ラズパイでやるからこそ単一の機能のために構成するのももったいなくないと思えるというもの。
インストールと設定
手順。まっさらな Raspberry Pi OS Lite で確認しています。
scanbdをインストール
sudo apt update
sudo apt install scanbd
scanbdをインストールすると、スキャナを動かすのに必要なSANEは依存として一緒にインストールされます。また、 scanbd も自動でsystemdのデーモンとして構成されるので、apt でのインストールで必要なソフトの導入と基本設定が完了します。そうしたらつぎはスキャナのボタンの設定をします。
ボタンの呼称を確認する
スキャナのボタンに呼び出したい動作を確実に割り当てるため、それぞれのボタンが内部でなんと呼ばれているか調べておきます。すでにデーモンとして起動しているscanbdは初期状態で想定されるいろんなボタンに反応してテストスクリプトが走るのでそのログをみてボタンの呼称を探します。
# /var/log/syslog の末尾をリアルタイム表示
less +F /var/log/syslog
そして、ボタンを押すと流れるログのなかにこんなのがあると思います。
Oct 22 06:31:26 raspberrypi scanbd[351]: /usr/sbin/scanbd: trigger action for copy for device genesys:libusb:001:004 with script test.script
これはスキャナの”COPY”というボタンを押したときのログ。 trigger action for
に続く copy
がボタンの呼ばれ方です。そのままやん。でも本体に刻まれたボタン名と内部の呼称とは必ずしも同一とはいかず、私のスキャナだとCOPY, SCAN, PDF, E-MAILのボタンはそれぞれcopy, scan, file, emailとなっていました。この次の割当設定のため、使いたいボタンの呼称はこの手順で調べてメモしておきます。
ボタンにアクションを割当
ボタンを押して呼びたい動作の名前とスクリプトを書いた設定ファイルを作成します。ここではcopy, scan, file, email のボタンでスクリプトを呼ぶ設定の例になります。管理者権限で作成・編集します。
global{
action pdfVertical {
filter = "^copy.*"
numerical-trigger {
from-value = 1
to-value = 0
}
desc = "COPY button is pressed, runs scan.sh."
script = "scan.sh"
}
action pdfHorizontal {
filter = "^scan.*"
numerical-trigger {
from-value = 1
to-value = 0
}
desc = "SCAN button is pressed, runs scan.sh."
script = "scan.sh"
}
action photoVertical {
filter = "^file.*"
numerical-trigger {
from-value = 1
to-value = 0
}
desc = "PDF button is pressed, runs scan.sh."
script = "scan.sh"
}
action photoHorizontal {
filter = "^email.*"
numerical-trigger {
from-value = 1
to-value = 0
}
desc = "E-MAIL button is pressed, runs scan.sh."
script = "scan.sh"
}
}
action
の名前はあとで割り当てたい処理の内容にしてます。 copy, scan, file, email のボタンごとに4つaction
を書きます。
filter
には先程調べたボタンの呼称を書きます。scabdは、ここに書いたキーワード(正規表現)でスキャナとの通信をフィルターして引っかかったらスクリプトを呼ぶという仕組みなんですねえ。 scanbdの設定ファイル(/etc/scanbd/scanbd.conf)にならってボタンの呼称の前に^
、後に.*
の記号を付けておきます。これにより、通信のなかで呼称が複数書いてあっても一度だけ引っかかるフィルタの設定となります。
numerical-trigger
の部分も scanbd.conf のテストスクリプトを呼ぶaction からコピーした内容を書きます。ボタンの状態が1から0になったらトリガーという事を書くようですね。
desc
の説明欄は必須ではないですが、本体ボタンとの対応がわかるようにメモしておきました。
script = "scan.sh
としてactionで実行させる予定のスクリプトファイル名を記載します。ファイルを指定するのであって、ここにコマンドと引数(ワンライナーとか)を書いても実行されません。そして、scanbdでは/etc/scanbd/scripts/
がスクリプトディレクトリとして読まれるので、そこに置くのであれば絶対パスでなくても平気。
また、上の例ではどのactionでも scan.shを指定していますが、ちゃんとそれぞれ違う処理ができるのでご安心を。actionが発動するとその名前が入ったSCANBD_ACTION
というグローバル変数が作られるので、スクリプトではこれを使って分岐させます。詳しくはサンプルスクリプト編で。
あとは、scanbd.confのしかるところにこの設定を読み込む設定を追記します。/etc/scanbd/scanbd.conf 末尾付近”#include”とある行の後にMy設定の読み込みについて書いてあるのでinclude("myscanner.conf")
と書き加えます。
# include
# include another file at this point. This may only occur outside general and devices blocks.
# an include statement may be relative to the direcory where scanbd.conf is located or absolute
# include("scanner.d/myscanner.conf")
# include("/my/long/path/myscanner.conf")
include("myscanner.conf")
動作確認
さきの設定で呼ぶことにしたscan.shにスキャンを一回するだけのスクリプトを書いて、ボタン押下でスキャナが動くか確認しましょう。
sudo mkdir /etc/scanbd/scripts/
echo -e '#!/bin/sh\nscanimage > /tmp/hoge.pnm' | sudo tee /etc/scanbd/scripts/scan.sh
sudo chmod a+x /etc/scanbd/scripts/scan.sh #実行権限を忘れずに
sudo service scanbd restart #一度scanbdをリスタート
scanbdの再起動を待ってからスキャナのボタンを押してスキャナが動き出せば成功。スキャナが動き出すまで5秒弱待つかも。/tmp/hoge.pnm は白黒で解像度が低すぎて訳のわからない画像となっているかと思いますが、これはあとでオプションを正しく設定することで改善します。
仮スクリプトの説明をすると、scanimage コマンドでスキャンを実行します。出力ファイル名を引数に渡すのでなく返り値としてバイナリが標準出力されるのをファイルにリダイレクトする、というふうに書く。.pnm 形式が初期値で、これはあまり聞きませんがマルチユースな画像形式らしい。
軽量化
さて、ボタンを押してスキャナが動き始めるまでにとても実用的ではない待ち時間が生じたと思います。これは、接続されたスキャナを使うためにSANEがライブラリから対応するすべてのスキャナのバックエンドを総当たりしているためです。そしてまた、「ボタンの呼称を確認する」で使ったデフォルトアクションが走るのにも少し時間がかかります。これらを必要なものだけに絞って発動までの時間を少しでも早めます。
不要なバックエンドをコメントアウト
/etc/scanbd/dll.conf
にはscanbdの使用するバックエンドがずらっと書いてあります。一種類のスキャナを使うとき、必要なのはこの中のひとつのバックエンドだけなので、探し出してそれ以外は削除またはコメンアウトします。
自分のスキャナがどのバックエンドを使っているか確認する方法はふたとおり。SANEのページ(“SANE: Backends (Drivers)”)で型番を検索するか、プロダクトIDを調べてSANEライブラリを検索するかです。webブラウザが使える環境ならばSANEのページを探す方法がわかりやすい。
lsusb
# → Bus 001 Device 004: ID 04a9:1905 Canon, Inc. CanoScan LiDE 200
# スキャナの型名の前にある`ID 0000:0000`というところのコロンの後がプロダクトID(前はベンダーID)
grep 1905 /etc/sane.d/*conf
# → /etc/sane.d/genesys.conf:usb 0x04a9 0x1909
CanoScan LiDE200の例。”genesys”というのがスキャナのバックエンドでした。/etc/scanbd/dll.conf
にはこれだけ残すようにします。
# Upon first installation, this file is filled with all supported SANE backends available at that time.# This is done to avoid having to ask the user to choose a backend. If you know what backend you want
# to use you can improve performance by deleting or commenting out all other backends here.
genesys
デフォルトアクションをコメントアウト
/etc/scanbd/scanbd.conf
の末に”# devices” につづくデバイスごとのアクションを読み込む初期設定が書いてあります。自分の設定はもうmyscanner.confに書きましたのでこれらはすべてコメントアウトしておきます。
# devices
# each device can have actions and functions, you can disable not relevant devices
#include(scanner.d/avision.conf)
#include(scanner.d/fujitsu.conf)
#include(scanner.d/hp.conf)
#include(scanner.d/pixma.conf)
#include(scanner.d/snapscan.conf)
#include(scanner.d/canon.conf)
#include(scanner.d/plustek.conf)
scanbdを再起動してもう一度動作確認すると、体感できるくらいのスピードアップがされているはず。
サンプル本番スクリプトと解説
scanbdの設定が終ったので、スキャナ本体の4つのボタンで
– 垂直方向の写真の取り込み
– 水平方向の写真の取り込み
– 垂直方向の連続したスキャンをPDFにまとめる
– 水平方向の連続したスキャンをPDFにまとめる
ーーそしてSlackへ送信、というのをするスクリプトを書きます。写真はスキャンした都度送信して、PDFではボタンを押すごとに待ち受け時間を設けて時間内にスキャンしたものを一つのファイルにして送信します。
本番スクリプト
#!/bin/bash
mkdir -p /tmp/scan/
cd /tmp/scan
datetime=`date +%F_%H%M%S`
file_count=`ls -1|wc -l`
function processIfLatestScan (){
file_count_when_called=`ls -1|wc -l`
sleep 15
file_count_now=`ls -1|wc -l`
if [ "${file_count_when_called}" = ${file_count_now} ]; then
for file in `find . -maxdepth 1 -name 'scan*pnm'`; do
tesseract -l jpn "${file}" "${file}" pdf
done
pdfunite scan*.pnm.pdf scan"${datetime}".pdf
postSlack scan"${datetime}".pdf
rm *
fi
}
function postSlack () {
curl -F file=@"${1}"\
-F "initial_comment=A file form CanoScan LiDE200."\
-F channels=CHANNELID\
-H "Authorization: Bearer xoxb-0000-0000-0000"\
https://slack.com/api/files.upload
}
case $SCANBD_ACTION in
pdfVertical)
scanimage --mode Color --resolution=150 > scan${file_count}.pnm
processIfLatestScan &
;;
pdfHorizontal)
scanimage --mode Color --resolution=150 > scan"${file_count}".pnm
convert scan"${file_count}".pnm -rotate -90 scan"${file_count}".pnm
processIfLatestScan &
;;
photoVertical)
scanimage --mode Color --resolution=150 --format=png > scan"${datetime}".png
postSlack scan"${datetime}".png
rm *
;;
photoHorizontal)
scanimage --mode Color --resolution=150 --format=png > scan"${datetime}".png
convert scan"${datetime}".png -rotate -90 scan"${datetime}".png
postSlack scan"${datetime}".png
rm *
;;
*)
echo This script is intended to be run by scanbd.
;;
esac
スクリプトのtips
SANE関係コマンドの補足や書きながら調べたものの説明。
ディレクトリが既存でも異常終了しない mkdir -p (2行目)
何度も発動されるこのスクリプトで/tmp/scansというディレクトリで処理したいときmkdirに-pオプションをつければ、もしすでにディレクトリがあっても怒られることもなく、なければないで作成してくれる。ディレクトリの存在を確認するような処理をしなくてよくなる。
mkdir --help
によると “-p, –parents no error if existing, make parent directories as needed” とのこと。/dir/dirのように階層を深く指定してもそのまま作ってくれるオプションでもある。
case $SCANBD_ACTION in (39)
scanbdで呼び出されたスクリプトではSCANBD_ACTIONというグローバル変数が使えます。この変数の中身は「ボタンにアクションを割当」で設定した”action” につづくアクション名の文字列なので、これでボタンごとの処理をするように分岐します。
scanimageコマンド (42, 他)
SANEのスキャナを動かすコマンド。scanimage --mode Color --resolution=150 > scan.pnm
とすることで実用的な画質のカラー画像が得られると思います。もしかすると、オプションがスキャナによって違うことがあるのでscanimage -A
で対応オプションと説明を見てから、その中の目的にあったオプションを選ぶようにしましょう。そのとき、scanbdがバックグラウンドで走ったままだと scanimage -A
が動かないのでsudo service scanbd stop
して停止してから行います。
--format
オプションが使えれば --format=png
のようにしてpnm以外の形式を指定できます(54, 60行目)。
“&”をつけてバックグラウンド処理 (43, 50)
コマンドの後に” &”をつけるとバックグラウンドで実行される。これを使い、複数枚のスキャンをしながら、裏で待受のタイムオーバーまでの時間をはかるという処理をしています。
バックグラウンドに投げられるprocessIfLatestScan関数には「呼ばれたときと15秒後にディレクトリ内のファイル数が変わっていなければ(新しいスキャンがされていなければ)、ディレクトリ内のPDFファイルをひとつのファイルにまとめて送信する」と書いてあります。これで、つぎつぎスキャンの待受をしつつ、その後にまとめる処理ができます。
別途必要なソフトウェアと設定
このサンプルスクリプトと同じことをする際に追加インストールが必要なソフトウェアと、設定の説明。
tesseract (23)
オープンソースのOCRで日本語の精度も高いTesseractを使うコマンド。使い方も簡単で、パッケージマネージャーでインストールsudo apt install tesseract-ocr tesseract-ocr-jpn
して、tesseract -l jpn in.pnm out pdf
とすればテキストの乗ったout.pdf ができる。入力には基本的な画像形式を受け取ってくれるけど、pdfは受け取られない。
pdfuniteで複数PDFを結合 (25)
複数のPDFファイルをくっつけるコマンド。何かしらPDFを操作するソフトを入れている場合すでにインストールされているかもですが、ない場合は追加でインストール。
はじめpdfuniteに行きつくまえ、使い慣れたconvert(ImageMagick6)でPDFを結合したら、OCRしたテキスト情報が消えてしまった。これはconvertではファイルが最適化される仕様のためらしい。pdfuniteだとリッチなデータが保たれます。
convert (ImageMagick6) のセキュリティ設定
convert (ImageMagick6) は実行可能たりえる画像形式を取り扱うには設定の変更が必要。別投稿を参照ください。
🔗「imagemagick セキュリティの許可」- Scrapbox
おわりに
近頃はめっきりスキャナーを触ることがなくなって、必要な時はスマホのカメラを用いたスキャンを使っていたのだけど、やっぱり専用の機械はいい。影とか白飛びとかを気にせず一発でスキャンできて快適でしてよ。
軽量化もしたけど、ボタンを押してスキャナが動くまでラグがあるのでボタンが効いているかわからないのがちょっと不便。ブザーを取り付けて、スクリプトが呼ばれたらまずビープ音が鳴るようにできたらいいかも。複数枚を続けて読む場合にもボタンが待ち受け状態になった時にビープ音が欲しい。Raspberry Piでscanbdを動かすハウツー投稿ではラズパイのLEDを明滅させて表示をする手順なんかも書いてあったりする。
作業の説明をしつつまとめたので長い投稿となってしまいましたが、Raspberry PiなどLinux触りはじめのひとにもわかりやすかったらよいです。
出典と参考
“SANE – Scanner Access Now Easy”
“mdengler/scanbd/README.txt” – GitHub
“Scanner Button Daemon” – ArchWiki
“Create a scan station with scanbd (Raspbian)” – SO Documentation
「Raspberry pi と scansnap で スタンドアローンなクラウドスキャナを作る」@fujiisoup – Qiita
“How to set up your scanner to work with Sane and PDF Studio under Fedora 27 Linux’s systemd” – PDF Studio Knowledge Base
余談:scanbdを構成することのいかに難儀か
scanbdとSANEによる他のスキャン機能の両立について、”大変なのでやりません ”などと言って”最短構成”なんてまとめましたが、実のところはめっちゃ試したんだけどできなかったんです。無念。
これは私に限ったことではなく、多くの人が折れている。Stack Overflowのscanbd関連投稿はどれも未解決となっておりまた、insaneというscanbdの構成に折れた人による代替ソフトウェアが生まれている(ユーザーも少なくなさそう)。それだけ大きな壁なのだと思います。絶望。
基本的にはscanbdのREADMEに書いてある手順(ArchWikiにも簡潔にまとめられてる)にならってudevの設定をしてnetバックエンドをプロキシとすればすべてうまくいくはず。ただ、何が難しいってスーパーデーモンのセキュリティが強まった昨今掻い潜るのが大変(なのだろうと読んでいる)。
systemdの場合socketなるファイアウォールめいた設定ができればいいはずなんだけど、わからんでした。スーパーデーモンへの理解が求められる。Linuxグルになった暁にはサクッと解決したいものです。