バーコードリーダーで写真撮影ができるのをご存知でしょうか??
Honywell製のバーコードリーダーはバーコードを読み取りするだけではなく、デジタルカメラと同じように画像を撮影することができます。
バーコードリーダーの取り扱い説明書には、「イメージコマンド」を送信すれば画像の取り込み処理を行うことができると記載があります。
参考サイト:ユーザーガイド(9-1参照)
ここに記載している内容を参考にして画像の撮影をやってみましょう。
[chat face=”icon.png” name=”名無し君” align=”left” border=”blue” bg=”none” style=”maru”]バーコードリーダーはコマンドを送信すれば画像撮影が可能です。[/chat]
この記事では、C#のシリアル通信を使ってバーコードリーダーで写真を撮影する方法を紹介しています。是非最後まで読んでみてください。
バーコードリーダーの仕組み
バーコードリーダーは、横方向に情報を持つ1次元バーコード(JANコードやCODE39等)や横方向と縦方向に情報を持つ2次元バーコード(QRコード・データマトリックス)を読み取りして、バーコードのデータを出力することができます。
バーコードリーダーには赤色LED(発光ダイオード)があり、読み取り時にバーコードへ照射します。そして画像センサーが撮影して、バーコードの黒い部分と白い部分を解析し、文字列(キャラクタ)に変換します。
画像センサーを使うことで、バーコードとバーコードリーダーの距離が多少あっても読み取りすることができるのです。
コントラストとは
バーコードリーダーはコントラスト(白・黒)を捉えて読み取りをします。CMOSに入光する光量が多いと白と捉えます。CMOSに入光する光量が少ないと黒と捉えます。この白黒の明暗度合をコントラストと言います。
バーコードリーダーで読み取りをする場合、通常は赤色LEDを照射します。もし、赤色LEDが無かったら、どうなるのでしょうか?
CMOSに入光する白い部分の光量が少なくなってしまい、バーコードがぼやけてしまいます。コントラストが低いと読み取りが不安定になります。
赤色LEDを照射すると、バーコードの白い部分が反射し、CMOSに入光する光量が多くなります。バーコード黒い部分は光を吸収し、CMOSに入光する光量が少なくなります。結果、バーコードがはっきりと表示される事になります。
LEDの色が赤色である理由は、赤と黒は波長の差異大きいので光を発しにくく、コンストラクトが高くなるからです。
だいぶ脱線してしまいましたが、ここで言いたかったのは撮影される画像は白黒になるということです。
バーコードリーダーで写真を撮影する方法
バーコードリーダーで写真を撮影するには、「イメージコマンド」を送信して画像データを受信します。
コマンドやデータの送受信を行う為に、パソコンとバーコードリーダーをシリアル通信で接続しておく必要があります。
以下の記事で紹介している「C#でシリアル通信をする方法」を元にプログラム作成していますので参照してみてください。
この記事では以下の開発環境でプログラムを作成します。
開発環境 | Visual Studio 2019 |
開発プラットフォーム | WPF |
フレームワーク | .NET Framework 4.8 |
イメージコマンドを使う
写真を撮影するコマンドは、「IMGSNP」です。
このコマンドを実行する度に画像データがバーコードリーダーから送信されます。
このコマンドを使うにはメニューコマンドシンタックス(構文)を先頭に付けておく必要があります。これがないと、バーコードリーダーはコマンドを認識してくれないので注意してください。
参考サイト:ユーザーガイド(12-1参照)
メニューコマンドシンタックス:SYN M CR
※分かりやすくする為に半角スペースを入れているので、実際コマンドを送信する際は不要です。
※3つのASCIIのキャラクタです。
※16進数にすると、「\x16\x4D\x0D」です。
上記の内容を組み合わせると次のコードになります。
//バーコードリーダーへ送信するコマンド
var command = "\x16\x4D\x0DIMGSNP1L;IMGSHP.";
イメージコマンドを送信する
続いて、コマンドを送信する処理を作成します。
バーコードリーダーへ送信するコマンドですが、ASCIIコードと文字が入り交ざっています。
WriteLineメソッドを使う場合は、指定した文字列を勝手に変換してから、送信バッファーに書き込んで送信します。送信バッファーへの書き込み時に、SerialPort.NewLineが末尾に追加されています。
SerialPortのWriteLineメソッドで送信する処理
public void Send()
{
if (!sport.IsOpen) return; //ポートが閉じている場合は戻す
try
{
sport.WriteLine("\x16\x4D\x0DIMGSNP1L;IMGSHP."); //データ書き込み
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
Writeメソッドを使う場合は、byte配列で送信バッファーへ書き込みます。ASCIIコードと文字が入り交ざったコマンドをbyte配列へ変換しなければなりません。
ここでは、コマンドを16進数へ変換し、更に16進数からバイト配列に変換する方法を紹介しています。
16進数へ変換する処理
public string StringToHexadecimal(string str)
{
var hexText = "";
while (true)
{
if (0 == str.Length) break;
var c = str[0];
var removelenth = 1; // デフォルト値:1 1文字削除
if (c == '[')
{
var result = Regex.Match(str, @"(?<=\[).+?(?=\])");
hexText += result;
removelenth = string.Format("[{0}]", result).Length;
}
else
{
hexText += ((int)c).ToString("X2");
}
str = str.Remove(0, removelenth);
}
return hexText;
}
バイト配列へ変換する処理
public byte[] StringToBytes(string str)
{
var bytes = new List();
for (int i = 0; i < str.Length / 2; i++)
{
bytes.Add(Convert.ToByte(str.Substring(i * 2, 2), 16));
}
return bytes.ToArray();
}
Writeメソッドの第1引数はポートに書き込むバイト配列、第2引数はポートへ書き込み開始するインデックス番号の開始位置、第3引数は書き込みするバイト数です。
SerialPortのWriteメソッドで送信する処理
public bool Send(Byte[] bytes)
{
if (!sport.IsOpen) return; //ポートが閉じている場合は戻す
//文字とASCIIを→16進数→バイト配列に変換
var command = "\x16\x4D\x0DIMGSNP1L;IMGSHP.";
var bytes = StringToBytes(StringToHexadecimal(command));
try
{
sport.Write(bytes, 0, bytes.Length);
}
catch (System.Exception)
{
MessageBox.Show("Write failed.");
}
return true;
}
画像データを受信する
バーコードリーダーへコマンドを送信すると、画像データが受信バッファへ入力されます。受信バッファにデータが入力されたらSerialPort.DataReceivedイベントを発生させます。
受信した画像データは後でBitmapImageに変換するので、Readメソッドを使ってbyte配列で受け取ります。
画像データは1回のイベントで全て取得できるわけではなく、複数回に分けて受信されるので、ここではReadメソッドで取得したbyte[]はList<byte>に蓄積させています。
SerialPortのReadメソッドで画像データを受信する処理
List<byte> Buffer = new List<byte>();
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
var IndexBuffer = sport.BytesToRead;
var ByteBuffer = new byte[IndexBuffer];
sport.Read(ByteBuffer, 0, IndexBuffer);
ListBuffer .AddRange(ByteBuffer);
if (ByteBuffer.Length == 0 && ListBuffer .Count != 0)
{
Dispatcher.BeginInvoke(new Action(() =>
{
ImgSnapshot.Source = LoadFromBytes(ListBuffer .Skip(36).ToArray());
Buffer.Clear();
}));
}
}
次はListBufferに蓄積された画像データをBitmapImageに変換する方法です。
まず、ListBufferをbyte配列に変換してMemoryStreamに書き込みます。MemoryStreamをBitmapImageのStreamSourceとして設定します。あとはWPFのImageコントロールのSourceプロパティにBitmapImageを設定すれば画面に撮影した画像が反映されます。
画像データを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();
}
}
動作確認
バーコードリーダーから画像が取得できるか確認してみましょう。
今回もアプリケーションの画面は「Material Design」で仕上げました。「Material Design」について詳しく知りたい方はこの記事を参考にしてみてください。
バーコードリーダーのCOMポートを選択して、歯車ボタンをクリックするとバーコードリーダーとシリアル通信が開始されます。
この状態で画像取得ボタンをクリックすれば、バーコードリーダーで撮影した画像が画面に表示されます。
こんな感じで画像の取得ができました。冒頭で述べたように取得した画像は白黒になります。
このアプリケーションに他に機能を追加するとしたら、取得した画像データを保存する機能なんかがあればいいかもしれませんね。
今回使用したバーコードリーダーは、Xenon1950でした。機会があれば是非やってみてください。
以上、最後まで読んでいただきありがとうございました。