バーコードリーダーで写真撮影ができるのをご存知でしょうか??
Honywell製のバーコードリーダーはバーコードを読み取りするだけではなく、デジタルカメラと同じように画像を撮影することができます。
バーコードリーダーの取り扱い説明書には、「イメージコマンド」を送信することで画像の取り込み処理を行えると記載があります。参考サイト:ユーザーガイド(9-1参照)
ここに記載している内容を参考にして画像の撮影をやってみましょう。
この記事では、C#のシリアル通信を使ってバーコードリーダーで写真を撮影する方法を紹介しています。ぜひ最後まで読んでみてください。
オススメの参考書
C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。
バーコードリーダーの仕組み
バーコードリーダーは、横方向に情報を持つ1次元バーコード(JANコードや CODE39等)や横方向と縦方向に情報を持つ2次元バーコード(QRコード・データマトリックス)を読み取りして、バーコードのデータを出力することができます。
バーコードリーダーには赤色 LED(発光ダイオード)があり、読み取り時にバーコードへ照射します。そして画像センサーが撮影して、バーコードの黒い部分と白い部分を解析し、文字列(キャラクタ)に変換します。
画像センサーを使うことで、バーコードとバーコードリーダーの距離が多少あっても読み取りすることができるのです。
コントラストとは
バーコードリーダーはコントラスト(白・黒)を捉えて読み取りをします。CMOS に入光する光量が多いと白と捉えます。CMOSに入光する光量が少ないと黒と捉えます。この白黒の明暗度合をコントラストと言います。
バーコードリーダーで読み取りをする場合、通常は赤色 LED を照射します。もし、赤色LEDが無かったら、どうなるのでしょうか?
CMOS に入光する白い部分の光量が少なくなってしまい、バーコードがぼやけてしまいます。コントラストが低いと読み取りが不安定になります。
赤色 LED を照射すると、バーコードの白い部分が反射し、CMOS に入光する光量が多くなります。バーコード黒い部分は光を吸収し、CMOS に入光する光量が少なくなります。結果、バーコードがはっきりと表示される事になります。
LED の色が赤色である理由は、赤と黒は波長の差異大きいので光を発しにくく、コンストラクトが高くなるからです。
だいぶ脱線してしまいましたが、ここで言いたかったのは撮影される画像は白黒になるということです。
バーコードリーダーで写真を撮影する方法
バーコードリーダーで写真を撮影するには、「イメージコマンド」を送信して画像データを受信します。
コマンドやデータの送受信を行う為に、パソコンとバーコードリーダーをシリアル通信で接続しておく必要があります。
以下の記事で紹介している「C#でシリアル通信をする方法」を元にプログラム作成していますので、参照してみてください。
この記事では以下の開発環境でプログラムを作成します。
開発環境
- OS:Windows 11
- 統合開発環境:Visual Studio 2022
- .NETバージョン:.NET 6
イメージコマンドを使う
バーコードリーダーで画像を撮影するコマンドは「IMGSNP」で、撮影した画像を送信するコマンドは、「IMGSHP」です。この2つのコマンドを組み合わせることで、”写真を撮影”して”撮影した画像を送信”することができます。
このコマンドを使うにはメニューコマンドシンタックスを先頭に付けておく必要があります。これがないと、バーコードリーダーはコマンドを認識してくれないので注意してください。
参考サイト:ユーザーガイド(12-1参照)
メニューコマンドシンタックス:SYN M CR
※3つのASCIIのキャラクタです。上記の16進数にすると、「\x16\x4D\x0D」です。
上記の内容を組み合わせると次のコードになります。
//バーコードリーダーへ送信するコマンド
var command = "\x16\x4D\x0DIMGSNP1L;IMGSHP.";
イメージコマンドを送信する
続いて、コマンドを送信する処理を作成します。
コマンドはバイト配列に変換して、バーコードリーダーへシリアル通信で送信します。
コマンドをbyte配列に変換する
コマンドを1文字ずつ取得し、16進数 → byte に変換してリストに格納します。全て変換が終わったら、リストに格納された byte をbyte 配列に直して返します。
private byte[] ConvertStringToByteArray(string str)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
var bytes = new List<byte>();
for (int i = 0; i < str.Length; i++)
{
var value = ((int)str[i]).ToString("X2");
bytes.Add(Convert.ToByte(value, 16));
}
return bytes.ToArray();
}
SerialPortのWriteメソッドで送信する
byte 配列のコマンドを SerialPort クラスのWrite
メソッドでバーコードリーダーに送信します。
SerialPort クラスのWrite
メソッドは第1引数に送信バッファへ書き込む byte 配列、第2引数はインデックスの開始位置、第3引数に送信バッファへ書き込むバイト数です。
public bool Send(byte[] bytes)
{
if (!IsOpen || bytes == null)
{
return false;
}
var result = false;
try
{
serialPort.Write(bytes, 0, bytes.Length);
result = true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return result;
}
SerialPortのReadメソッドで受信する
バーコードリーダーへコマンドを送信すると、画像データが受信バッファへ入力されます。 SerialPort クラスのBytesToRead
メソッドで受信バッファにデータが入力されるまでループさせ、受信バッファにデータが入力されたらReadByte
メソッドで受信します。
ReadByte
メソッドは1文字ずつbyte で取得するので、リストbufferList
に追加します。受信が完了したらリストに格納された byte をbyte 配列に直して返します。
public byte[] Receive()
{
// 受信バッファ
var bufferList = new List<byte>();
if (!IsOpen) { return null; }
try
{
// 受信データ待機
while (serialPort.BytesToRead == 0)
{
Thread.Sleep(1500);
}
// 受信
while (serialPort.BytesToRead > 0)
{
// 1 バイト受信してバッファに格納
var buffer = (byte)serialPort.ReadByte();
bufferList.Add(buffer);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
SerialPortErrorOccurred?.Invoke(this, new EventArgs());
}
return bufferList.ToArray();
}
画像データをBitmapImageに変換する
バーコードリーダーから受信した画像データ(byte 配列)をBitmapImage
に変換します。
byte 配列に変換してMemoryStream
に書き込みます。MemoryStream
をBitmapImage
のStreamSource
として設定します。あとは WPF のImage
コントロールのSource
プロパティにBitmapImage
を設定すれば画面に撮影した画像が反映されます。
private BitmapImage LoadFromBytes(byte[] bytes)
{
try
{
using (var stram = new MemoryStream(bytes))
{
stram.Seek(0, SeekOrigin.Begin);
var image = new BitmapImage();
image.BeginInit();
image.StreamSource = stram;
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.EndInit();
return image;
}
}
catch (Exception ex)
{
return new BitmapImage();
}
}
使用例
上記のメソッドを使用してバーコードリーダーで画像を撮影するコードは以下になります。
バーコードリーダーから受信したデータにはコマンドデータ+画像データが含まれています。このデータは0x29(グループ区切り)で区切られています。区切り文字以降の画像データをLoadFromBytes
メソッドの引数に渡します。
private void Snap()
{
var command = "\x16\x4D\x0DIMGSNP1L;IMGSHP.";
var commandBytes = ConvertStringToByteArray(command);
var data = serialPortModel.Query(commandBytes);
var index = data.ToList().FindIndex(x => x == 29);
if (data == null || data.Length < index)
{
return;
}
var length = data.Length - index;
var imageData = new byte[length];
Array.Copy(data, index + 1, imageData, 0, imageData.Length - 1);
ImageCapture = LoadFromBytes(imageData);
}
GitHub上にサンプルのソースコードを公開しています。ソースコードのダウンロードが可能ですので、Honeywell製のバーコードリーダーをお持ちの方はぜひ実行してみてください。
動作確認
バーコードリーダーから画像が取得できるか確認してみましょう。
今回もアプリケーションの画面は「Material Design」で仕上げました。「Material Design」について詳しく知りたい方はこの記事を参考にしてみてください。
バーコードリーダーの COM ポートを選択して、歯車ボタンをクリックするとバーコードリーダーとシリアル通信が開始されます。
この状態で画像取得ボタンをクリックすれば、バーコードリーダーで撮影した画像が画面に表示されます。
こんな感じで画像の取得ができました。冒頭で述べたように取得した画像は白黒になります。
このアプリケーションに他に機能を追加するとしたら、取得した画像データを保存する機能なんかがあればいいかもしれませんね。
今回使用したバーコードリーダーは、Xenon1950 でした。機会があれば是非やってみてください。
以上、最後まで読んでいただきありがとうございました。