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

# はじめに

@yukisato1987 さんの[『「OKグーグル領収書印刷して！」をobnizとGoogleホームで作る』](https://qiita.com/yukisato1987/items/cd7276cb36286762ea1c)記事を読んで、「自分もこのサーマルプリンタ(DP-EH600)から何か出力したい」衝動に駆られ、Amazonで[ポチった](https://www.amazon.co.jp/gp/product/B07DTGXCHD/)のが切っ掛けです。
さらに、**DP-EH600** をググっていたら、[「Maker向けサーマルプリンタ『DP-EH600』を爆速化する」](https://www.shumi-tech.online/entry/2018/07/03/Maker%E5%90%91%E3%81%91%E3%82%B5%E3%83%BC%E3%83%9E%E3%83%AB%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF%E3%80%8EDP-EH600%E3%80%8F%E3%82%92%E7%88%86%E9%80%9F%E5%8C%96%E3%81%99%E3%82%8B)記事を見つけ、イメージ出力にも十分耐える性能が出せることが分かり、この記事に述べる制作に取り掛かることにしました。


# 制作するもの

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

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

1. DP-EH600の高速化
1. スマホで撮影、画像データを白黒二値化、obnizへ出力する一連のHTMLとJavaScriptを書く
1. obnizからDP-EH600をアクセスするためのライブラリの作成


# DP-EH600の高速化

白黒とはいえ多量のイメージデータをプリンタに送り込むため、可能な限りプリンタ出力の高速化が必要だと思います。
前出の記事によると、UARTのボーレートを9600bpsから115200bpsに上げることによりトータルのプリンタ性能を劇的に上げる方法が詳しく説明されています。9600bpsでは**64秒**かかったものが115200bpsでわずか**11秒**に。さらにサーマルヘッド関係のパラメタをチューニングすることで**7秒**にまで高速化できたというものです。
文字通り爆速化により速度を**9倍**にできたというから、やらない手はないと思います。

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

<img src="https://qiita-image-store.s3.amazonaws.com/0/283293/1b1dce20-9bf7-a824-8c4c-ee96b5511dee.jpeg" width=80%>

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

<img src="https://qiita-image-store.s3.amazonaws.com/0/283293/95c90b92-54f0-0b09-6df9-ed0b7042239f.jpeg" width=60%>


通信レートが上がったことでプリンタの印字速度を上回ってしまうため、このプリンタの基板からRTS信号を引き出してフロー制御に利用する必要がでてきました。前出の爆速化の記事では、
> RTSピンを引き出すには、もちろん分解して半田付けしてもいいけれど、ひとまずサンハヤトのスルーホール用テストワイヤをぶっ刺した。

と、説明されていますが、私が購入したプリンタは、この穴がハンダで完全に埋められていたため、仕方なく分解してジャンパー線をハンダ付けしました。

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


# HTMLとJavaScriptを書く

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

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

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

> ＜2018.12.16追加＞
> 上記のinputタグの記述は間違いではないですが、これまで通りのinputタグ(type=file)でもカメラで撮影やライブラリから選択が普通にできます。


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

どうせなのでイメージ出力専用ではなく、普通のプリンタのようにテキストやバーコードも出力できるようにして、将来、obnizの[パーツライブラリ](https://docs.obniz.com/ja/sdk/parts)に取り込める形で、汎用的な機能を盛り込みます。
以下、使用例です。


```javascript:テキストの出力例
//Javascript
const pr = obniz.wired('DPEH600', {gnd:0, vcc:1, gnd2:9, tx:10, rx:11, baud:115200, cts:7});
pr.init();
pr.printText("Hellow!");
pr.printText(["こんにちは。", "日本語も出力できました。", "書体が中華風ですが..."]);
pr.linefeed(2);
```

```javascript:ビットマップの出力例
//Javascript
const pr = obniz.wired('DPEH600', {gnd:0, vcc:1, gnd2:9, tx:10, rx:11, baud:115200, cts:7});
pr.init();
const canvas = $("#canvas");
const context = canvas[0].getContext('2d');
const imageData = context.getImageData(0, 0, width, height).data;
const bwBitmap = dithering(imageData, width, height);//白黒二値化
pr.printBitmap(bwBitmap, width, height, true); //白黒反転
pr.linefeed(2);
```


# まとめ（中間）

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

```javascript:白黒二値化アルゴリズム
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**で試した結果が、次の画像（家の中を撮った写真）です。まあ輪郭は分かるかな程度の精度ですね。

<img src="https://qiita-image-store.s3.amazonaws.com/0/283293/db70e029-a5ca-1cf5-bbe4-21b0cb5450b6.jpeg" width=40%> ![IMG_4950.gif](https://qiita-image-store.s3.amazonaws.com/0/283293/38bca869-926e-b710-af98-ac2fcb572f66.gif)


今回の出力時間はおよそ13秒でした。
obnizにつながっている灰色のジャンパー線がプリンタから引き出したRTS信号で、obnizのuartインタフェースのctsピンに指定しています。


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


> ＜2018.12.16追加＞
> **誤差拡散法**に変更した続編は[こちら](https://qiita.com/nak435/items/ff1ab1708f026de5e74a)。