スマホで撮影した写真を白黒二値化してサーマルプリンタに出力する

スマホで撮影した写真を白黒二値化してサーマルプリンタに出力する:


はじめに

@yukisato1987 さんの『「OKグーグル領収書印刷して!」をobnizとGoogleホームで作る』記事を読んでサーマルプリンタ(DP-EH600)から何か出力したい衝動に駆られ、Amazonでこれをポチったのが切っ掛けです。

さらに、DP-EH600 をググっていたら、「Maker向けサーマルプリンタ『DP-EH600』を爆速化する」記事を見つけ、イメージ出力にも十分耐える性能が出せることが分かり、この記事に述べる制作に取り掛かることにしました。


制作するもの

スマホで撮影、画像データを白黒二値化、obnizを通してサーマルプリンタ(DP-EH600)にイメージデータを出力 という流れです。

作業としては、大きく下記の3つ。

  1. DP-EH600の高速化
  2. スマホで撮影、画像データを白黒二値化、obnizへ出力する一連のHTMLとJavaScriptを書く
  3. obnizからDP-EH600をアクセスするためのライブラリの作成(どうせなのでイメージ出力専用ではなく、汎用的なライブラリを作成し、レシートプリンタのように、テキストやバーコードも出力できるようにするつもり)


DP-EH600の高速化

大量のイメージデータをプリンタに送り込むため、可能な限りプリンタ出力の高速化が必要だと思います。

前出の記事によると、UARTのボーレートを9600bpsから115200bpsに上げることによりトータルのプリンタ性能を劇的に上げる方法が詳しく説明されています。9600bpsでは64秒かかったものが115200bpsでわずか11秒に。さらにサーマルヘッド関係のパラメタをチューニングすることで7秒にまで高速化できたというものです。

文字通り爆速化により速度を9倍にできたというから、やらない手はないと思います。

この記事で使われているUSBシリアル変換器と同型の物を持ち合わせていたので、作業を始めてからわずか数分で115200bps化が完了しました。ボーレートとの変更と同時に使用する文字セットをUTF-8に変更したせいかテストプリントの内容が変わりました。



68747470733a2f2f71696974612d696d6167652d


前出のAmazonの商品説明には「日本語は使用できません」としっかり書かれていますが、UTF-8に変更したことにより、日本語もちゃんと出ました。(しかし、'・'(中点)は印字できなかったので、すべての日本語(漢字)が出る分けではないのかも)
文字がかすれ気味なのはサーマルヘッド関係の設定をちゃんとしていないからです。



68747470733a2f2f71696974612d696d6167652d


通信レートが上がったことでプリンタの印字速度を上回ってしまうため、このプリンタの基板からRTS信号を引き出してフロー制御する必要がでてきました。前出の「『DP-EH600』を爆速化する」では、

RTSピンを引き出すには、もちろん分解して半田付けしてもいいけれど、ひとまずサンハヤトのスルーホール用テストワイヤをぶっ刺した。
と、説明されていますが、私が購入したプリンタは、この穴がハンダで完全に埋められていました。仕方なく分解してジャンパー線をハンダ付けしました。

以上で高速化は完了です。


HTMLとJavaScriptを書く

DP-EH600で出力できるイメージの横幅は384ピクセルのため、画像データの短辺が384pxになるようにイメージデータをリサイズします。次にイメージデータを白黒二値化します。誤差拡散法というアルゴルズムを使うのが一般的なようですが、まずは単純に閾値判定による二値化で進めます。後ほど体力があれば誤差拡散法にも挑戦したいと思います。

HTML5になって、スマホのカメラ起動から画像データを得ることが、下記のようにinputタグ一つですごく簡単に書けます。同じHTMLをPCで開くと、イメージデータ選択ダイアログとして動く優れもの。

スマホのカメラ起動
<h1>Camera</h1> 
<form action="" method="post" enctype="multipart/form-data"> 
  <input type="file" id="ufile" name="capture" accept="image/*" capture="camera" /> 
</form> 


DP-EH600アクセスライブラリ

どうせなのでイメージ出力専用ではなく、普通のプリンタのようにテキストやバーコードも出力できるようにして、将来、obnizのパーツライブラリに取り込める形で、汎用的な機能を盛り込みます。

主な機能は以下のとおり。

method function
wired({vcc, gnd,} tx, rx {, gnd2}, baud {, cts}) DP-EH600と接続します
init(heatTime, heatInterval, heatingDots) heatingDots:ヘッドの加熱一回あたりの印刷ドット数、

heatingTime:ヘッド加熱時間、

heatingInterval:ヘッド加熱間隔
reset()
offline()
online()
wakeup()
hasPaper()
fontA() 12x24 font A
fontB() 9x17 font B
linefeed(number=1)
align(alignment='L') 'L':左寄せ、'C':中央、'R':左寄せ
boldOn()
boldOff()
underlineOn()
underlineOff()
inverseOn()
inverseOff()
upsideDownOn()
upsideDownOff()
barcodeChar(pos=2) コード印字位置

1:Abovebarcode、

2:Below、

3:Both、

0:Not printed
barcodeHeight(height=162) 1-255:バーコードの高さ
barcodeWidth(width=3) 2-6:バーコードの幅
printBarcode(type, code) バーコードを出力
printText(msg) テキストを出力
printImage(imageData) イメージデータを出力
テキストの出力例
//Javascript 
const pr = obniz.wired('DPEH600', {gnd:0, vcc:1, gnd2:9, tx:10, rx:11, baud:115200, cts:7}); 
pr.printText("Hellow!"); 
pr.printText(["こんにちは。", "日本語も出力できました。", "書体が中華風ですが..."]); 
pr.linefeed(2); 


まとめ(中間)

白黒二値化の方法として、一旦グレーススケール値を求めそれを閾値との比較で白・黒と判定する単純なアルゴリズムとしました。

白黒二値化アルゴリズム
const grayScale = (0.299 * imageData[n + 0] + 0.587 * imageData[n + 1] + 0.114 * imageData[n + 2]); 
black_and_white_pixels[index] = (grayScale < this.black_threshold) ? 0 : 1; 
閾値(black_threshold)を48で試した結果が、次の画像(家の中を撮った写真)です。まあ輪郭は分かるかな程度の精度です。出力時間を計測することを忘れましたが、ものの数秒でした。



68747470733a2f2f71696974612d696d6167652d


閾値の調整でもう少し見映えが良くなる可能性がありますが、やはりもっと高度なアルゴリズムにする必要がありそうです。今回は「まとめ(中間)」として、今後改善してこの記事をアップデートしたいと思います。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)