Add barcode reader to GR-PEACH Camera In
Quote:
Japanese version is available in lower part of this page.
このページの後半に日本語版が用意されています.
Modifying "GR-PEACH camera in" to bar code reader
Reading bar code using GR-PEACH
In this page I try to read bar code using GR-PEACH.
I found open source of bacode reader, ZBar that is written in C++. ZBar is licensed under the GNU GNU LGPL2.1. Therefore I use GR-PEACH_Camera_in as base project, because source code of all libraries used are opened.
Implementation ZBar
ZBar is implemented by following sequence.
- Go to http://zbar.sourceforge.net/.
- Click "download".
- Download "ZBar 0.10 source tarball".
- Expand "zbar-0.10.tar.bz2", Then "zbar-0.10.tar" will be generated.
- Expand "zbar-0.10.tar".
- Launch cygwin terminal.
- Move to zbar-0.10 directory.
- Input following command.
$ ./configure --without-qt --without-gtk --without-imagemagick --without-xv --without-jpeg --without-xshm
Then, we can use ZBar. This directory includes many files unused. Let's pick up files to use.
Basically we use core of this library (zbar-0.10\zbar) only.
- Files copy to zbar-0.10\zbar
- zbar-0.10\include\config.h
- zbar-0.10\include\zbar.h
- Files removed from zbar-0.10\zbar
- zbar-0.10\zbar\.deps
- zbar-0.10\zbar\convert.c
- zbar-0.10\zbar\debug.h
- zbar-0.10\zbar\jpeg.c
- zbar-0.10\zbar\libzbar.rc
- zbar-0.10\zbar\Makefile.am.inc
- zbar-0.10\zbar\processor.c
- zbar-0.10\zbar\processor.h
- zbar-0.10\zbar\svg.c
- zbar-0.10\zbar\video.c
- zbar-0.10\zbar\video.h
- zbar-0.10\zbar\window.c
- zbar-0.10\zbar\decoder\.deps
- zbar-0.10\zbar\decoder\pdf417.c
- zbar-0.10\zbar\decoder\pdf417.h
- zbar-0.10\zbar\decoder\pdf417_hash.h
- zbar-0.10\zbar\processor
- zbar-0.10\zbar\qrcode\.deps
- zbar-0.10\zbar\video
- zbar-0.10\zbar\window
Linking GR-PEACH Camera in and ZBar
Placing in the same directory
First, Import GR-PEACH Camera in.
Import programGR-PEACH_Camera_in
Camera in sample for GR-PEACH. This sample works on GR-LYCHEE besides GR-PEACH.
Modify the name of program "GR-PEACH_Camera_in_barcode"
Copy "zbar-0.10\zbar" to root directory of this program.
Modification of zbar\scan_iamge.c
The main routine of ZBar is in the file zbar\scan_iamge.c.
Modify this file.
- Modification of function name
There is main() function in this file, and is duplicated to main() function of GR-PEACH_Camera_in. The base program is GR-PEACH_Camera_in, Therefore, modify the name of ZBar function to zbar_main. And modify the argument of this function to (void* image_buff, int width, int height). The address and size of Image buffer can be changed by this. Change argument check. - Removing get_data function
get_data() function reads png file from file system and generates image buffer.
In this case we use captured image from camera, therefore get_data() function is not used.
We store the captured image to static memory.
ZBar only accepts grayscale images. 1 byte is used per 1 pixcel.
Modify zbar_main to use image_buff specified by argument.
Change settings of valiable "width" and "height" to be specified by argument.
Modification
/* obtain image data */ #if (1) // width, height, raw data are fixed void *raw = image_buff; #else int width = 0, height = 0; void *raw = NULL; get_data(argv[1], &width, &height, &raw); #endif
Modificatoin of main.cpp
- Modifying camera to CMOS
This is not needed who use analog cameras
To use GR-Audio/Camera Shield and CF0K82C with MT9V111, modify the value of VIDEO_INPUT_METHOD macro to VIDEO_CMOS_CAMERA. - Change image data format to YCbCr.
Change image data format to YCbCr that is easy to change gray-scale. Because ZBar accepts only gray-scale.
Modify the value of VIDEO_INPUT_FORMAT macro to VIDEO_YCBCR422. - Extracting the brightness from YCbCr format
Generate the subroutine to extract the brightness from YCbCr format.
static void yuv2gray(void * dst_buff, void * src_buff, uint32_t stride, uint32_t height ) { uint32_t count; uint32_t * src; uint32_t * dst; uint32_t data1; uint32_t data2; src = (uint32_t *)src_buff; dst = (uint32_t *)dst_buff; for( count = 0 ; count < stride * height -1 ; ) { data1 = *src++; data2 = *src++; *dst++ = ( (data1 & 0x000000ff) << 24 ) + ( (data1 & 0x00ff0000) << 0 ) + ( (data2 & 0x000000ff) << 8 ) + ( (data2 & 0x00ff0000) >> 16 ); count += 8; } } /* End of function yuv2gray() */
- Calling ZBar
Replace the file saving sequence to calling ZBar sequence.
/* Data save */ #if ( VIDEO_INPUT_FORMAT == VIDEO_YCBCR422 || VIDEO_INPUT_FORMAT == VIDEO_RGB565 ) #if (1) /* converting YCbCr to Grayscale and calling zbar_main */ yuv2gray(input_image_buff,save_buff_addr,VIDEO_BUFFER_STRIDE,VIDEO_BUFFER_HEIGHT); zbar_main(); #else /* Save ".bin" file */ sprintf(file_name, "/usb/video_%d.bin", file_name_index++); FILE * fp = fopen(file_name, "w"); save_file_size = fwrite(save_buff_addr, sizeof(char), (VIDEO_BUFFER_STRIDE * VIDEO_BUFFER_HEIGHT), fp); fclose(fp); #endif #else ... #endif
- Removing USB sequence
This program does not use USB. Therefore remove following:
- #include "USBHostMSD.h"
- #include "usb_host_setting.h"
- USBHostMSD msd("usb");
in main() function. - A while loop under the /* USB connect check */ comment in main() function.
- printf("file name %s, file size %d\n", file_name, save_file_size) in the end of main() function.
- Romove USBHost library from the program. (This is may not be.)
Compiling
Modify following to reduce compilation error.
- Remove #include <png.h> in zbar\scan_image.c
- Remove #include <unistd.h> in zbar\img_scanner.c
- Remove #include <sys/time.h> in zbar\img_scanner.c
- Remove followinig 3 lines at the top of zbar_scan_image() function in zbar\img_scanner.c.
struct timeval abstime;
gettimeofday(&abstime, NULL);
iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2;
- Remove followinig 2 lines that uses strdup() function in zbar\error.c.
if(!err->arg_str)
err->arg_str = strdup("<?>"); - Reneme zbar\debug.h to zbar\zbar_debug.h to avoid duplicate of the name of header file.
Modify #include "debug.h" to #include "zbar_debug.h". - There are not iconv() function.
I do not use iconv(), and use memcpy instead of iconv().
qrdectxt.c
#if (1) /* does not convert character code */ typedef void *iconv_t; #else #include <iconv.h> #endif
qrdectxt.c
#if (1) /* does not convert character code */ latin1_cd="ISO8859-1"; // dummy address #else latin1_cd=iconv_open("UTF-8","ISO8859-1"); #endif /*But this one is often used, as well.*/ #if (1) /* does not convert character code */ sjis_cd="SJIS"; // dummy address #else sjis_cd=iconv_open("UTF-8","SJIS"); #endif /*This is a trivial conversion just to check validity without extra code.*/ #if (1) /* does not convert character code */ utf8_cd="UTF-8"; // dummy address #else utf8_cd=iconv_open("UTF-8","UTF-8"); #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=utf8_cd==(iconv_t)-1|| iconv(utf8_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=iconv(enc_list[ei],&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=eci_cd==(iconv_t)-1|| iconv(eci_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=sjis_cd==(iconv_t)-1|| iconv(sjis_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ eci_cd=enc; #else eci_cd=iconv_open("UTF-8",enc); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(eci_cd!=(iconv_t)-1)iconv_close(eci_cd); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(eci_cd!=(iconv_t)-1)iconv_close(eci_cd); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(utf8_cd!=(iconv_t)-1)iconv_close(utf8_cd); if(sjis_cd!=(iconv_t)-1)iconv_close(sjis_cd); if(latin1_cd!=(iconv_t)-1)iconv_close(latin1_cd); #endif
Test
After compilation, let's run the program.
If we pushed USER_BUTTON 3 times, program will abort.
It coused by free() function in zbar/image.c (133). This line frees static memory. A pointer maintains dinamic memory is moved to illegal area. The original ZBar uses dinamic memory for image buffer in the get_data() function. We changed not to use get_data and to use static memory. After removing this free() function, program does not abort.
GR-PEACH can read barcode
GR-PEACH can read barcode.
The code below is read.
Following is the program.
Import programGR-PEACH_Camera_in_barcode
GR-PEACH barcode reader. This program uses ZBar bar code reader. ZBar is licensed under the GNU LGPL 2.1 to enable development of both open source and commercial projects.
Extra
- I modified zbar\scan_image.c to print "No Code detected." when no code is detected.
- There are many false recognitions of I2/5 code. When you modify the value of ENABLE_I25 macro to 0, this program does not analyze the I2/5 code.
Quote:
ここから日本語です。
GR-PEACH camera in を改造してバーコードリーダーにする
GR-PEACH でバーコードを読みたい
GR-PEACHで画像処理をしてみようということで、バーコードリーダー機能をつけてみます。
バーコードリーダーのオープンソースを探してみたところ、zbarがC++ベースなので使えそうです。ただ、zbarはGNU LGPL2.1ですね。
元々はGR-PEACH_WebCameraをベースにしようかと考えていたのですが、コード非公開のライブラリが含まれているので、コードが全開示されているGR-PEACH_Camera_inをベースにします。
zbarを組み込む
以下の手順でzbarを組み込みます。
- http://zbar.sourceforge.net/ に行きます。
- 「download」をクリックします。
- ZBar 0.10 source tarball をダウンロードします。
- zbar-0.10.tar.bz2 を展開します。zbar-0.10.tar が生成されます。
- zbar-0.10.tar を展開します。
- cygwinのterminalを起動します。
- 展開したzbar-0.10に移動します。
- 以下のコマンドを入力します。
$ ./configure --without-qt --without-gtk --without-imagemagick --without-xv --without-jpeg --without-xshm
これでプログラムが使用可能になりました。使う部分のみを抜き出します。
基本的にライブラリのコアの部分(zbar-0.10\zbar)しか使いません。
- zbar-0.10\zbarにコピーするファイル
- zbar-0.10\include\config.h
- zbar-0.10\include\zbar.h
- zbar-0.10\zbarから削除するファイル
- zbar-0.10\zbar\.deps
- zbar-0.10\zbar\convert.c
- zbar-0.10\zbar\debug.h
- zbar-0.10\zbar\jpeg.c
- zbar-0.10\zbar\libzbar.rc
- zbar-0.10\zbar\Makefile.am.inc
- zbar-0.10\zbar\processor.c
- zbar-0.10\zbar\processor.h
- zbar-0.10\zbar\svg.c
- zbar-0.10\zbar\video.c
- zbar-0.10\zbar\video.h
- zbar-0.10\zbar\window.c
- zbar-0.10\zbar\decoder\.deps
- zbar-0.10\zbar\decoder\pdf417.c
- zbar-0.10\zbar\decoder\pdf417.h
- zbar-0.10\zbar\decoder\pdf417_hash.h
- zbar-0.10\zbar\processor
- zbar-0.10\zbar\qrcode\.deps
- zbar-0.10\zbar\video
- zbar-0.10\zbar\window
GR-PEACH Camera in と zbarを合体させる
まずは同居
まず、GR-PEACH Camera inをインポートします。
Import programGR-PEACH_Camera_in
Camera in sample for GR-PEACH. This sample works on GR-LYCHEE besides GR-PEACH.
名前はGR-PEACH_Camera_in_barcodeとします。
このルートに、zbar-0.10\zbarをコピーします。
zbar\scan_iamge.cの改造
zbar側のメインルーチンは、zbar\scan_iamge.cにあるようです。
このファイルを改造します。
- 関数名変更
main関数がいらっしゃいますがGR-PEACH_Camera_in側のmain関数を使用したいため、zbar_mainに関数名を変えます。
また、引数をイメージバッファとその縦横のサイズとしました。引数チェックも変更します。 - get_data関数の削除
get_data関数はファイルシステムからpngファイルを読み、イメージバッファを作成するルーチンです。
今回はカメラからの入力画像をそのまま使用するため、使用しません。
カメラからの入力画像はメモリで受け渡しとします。
このサンプルはQVGAのため、画面サイズは320*240です。
zbarはグレースケールしか入力できないため、ドットあたり1バイトです。
入力画像バッファ
extern unsigned char input_image_buff[320*240]
これを使うようにzbar_mainのデータを設定している箇所を改造します。
入力データの変更
/* obtain image data */ #if (1) // width, height, raw data are fixed int width = 320, height = 240; void *raw = input_image_buff; #else int width = 0, height = 0; void *raw = NULL; get_data(argv[1], &width, &height, &raw); #endif
main.cppの改造
- 使用カメラをCMOSにする
この作業はアナログカメラを使用する方には不要です
手元の環境はGR-Audio/Camera ShieldにMT9V111を搭載したCF0K82Cを接続して使用するため、 VIDEO_INPUT_METHOD マクロの値を VIDEO_CMOS_CAMERA にします。 - メモリに展開するデータフォーマットをYCbCrにする
zbarはグレイスケール入力なので、データフォーマットを輝度抽出をしやすいYCbCrに変更します。
VIDEO_INPUT_FORMAT マクロの値を VIDEO_YCBCR422 にします。 - YCbCrから輝度を抽出する
YCbCrから輝度を抽出するサブルーチンを作成します。
static void yuv2gray(void * dst_buff, void * src_buff, uint32_t stride, uint32_t height ) { uint32_t count; uint32_t * src; uint32_t * dst; uint32_t data1; uint32_t data2; src = (uint32_t *)src_buff; dst = (uint32_t *)dst_buff; for( count = 0 ; count < stride * height -1 ; ) { data1 = *src++; data2 = *src++; *dst++ = ( (data1 & 0x000000ff) << 24 ) + ( (data1 & 0x00ff0000) << 0 ) + ( (data2 & 0x000000ff) << 8 ) + ( (data2 & 0x00ff0000) >> 16 ); count += 8; } } /* End of function yuv2gray() */
- グレイスケール画像をzbarに渡す
GR-PEACH_Camera_inでファイルを保存していたルーチンを使用せず、YCbCrからグレイスケールの変換をしzbarを呼び出すルーチンに置き換えます。
/* Data save */ #if ( VIDEO_INPUT_FORMAT == VIDEO_YCBCR422 || VIDEO_INPUT_FORMAT == VIDEO_RGB565 ) #if (1) /* converting YCbCr to Grayscale and calling zbar_main */ yuv2gray(input_image_buff,save_buff_addr,VIDEO_BUFFER_STRIDE,VIDEO_BUFFER_HEIGHT); zbar_main(); #else /* Save ".bin" file */ sprintf(file_name, "/usb/video_%d.bin", file_name_index++); FILE * fp = fopen(file_name, "w"); save_file_size = fwrite(save_buff_addr, sizeof(char), (VIDEO_BUFFER_STRIDE * VIDEO_BUFFER_HEIGHT), fp); fclose(fp); #endif #else ... #endif
- USB処理の削除
USBは使用しないため、以下の箇所を削除します。
- #include "USBHostMSD.h"
- #include "usb_host_setting.h"
- main関数内の
USBHostMSD msd("usb"); - main関数内の
/* USB connect check */
直下のwhile節全体 - main関数末尾の
printf("file name %s, file size %d\n", file_name, save_file_size); - プログラムからUSBHostライブラリを削除(しなくても問題ないです)
コンパイル
ようやくコンパイルです。エラーの対処を列挙します。
- zbar\scan_image.c の以下を削除します。
#include <png.h> - zbar\img_scanner.c の以下を削除します。
#include <unistd.h> - zbar\img_scanner.c の以下を削除します。
#include <sys/time.h> /* gettimeofday */ - zbar\img_scanner.c のzbar_scan_image関数冒頭の以下を削除します。
struct timeval abstime;
gettimeofday(&abstime, NULL);
iscn->time = (abstime.tv_sec * 1000) + ((abstime.tv_usec / 500) + 1) / 2;
- zbar\error.c の_zbar_error_string内でstrdupを使用している以下の箇所を削除します。
if(!err->arg_str)
err->arg_str = strdup("<?>"); - zbar\debug.hのヘッダファイル名が他と重複しているため、zbar\zbar_debug.hにファイル名変更します。
#includeしている箇所も変更します。 - iconv系の関数がないとのことで、qrdectxt.cをiconvの替わりにmemcopyを使う(文字コード変換を行わない)ように改造します。
qrdectxt.c
#if (1) /* does not convert character code */ typedef void *iconv_t; #else #include <iconv.h> #endif
qrdectxt.c
#if (1) /* does not convert character code */ latin1_cd="ISO8859-1"; // dummy address #else latin1_cd=iconv_open("UTF-8","ISO8859-1"); #endif /*But this one is often used, as well.*/ #if (1) /* does not convert character code */ sjis_cd="SJIS"; // dummy address #else sjis_cd=iconv_open("UTF-8","SJIS"); #endif /*This is a trivial conversion just to check validity without extra code.*/ #if (1) /* does not convert character code */ utf8_cd="UTF-8"; // dummy address #else utf8_cd=iconv_open("UTF-8","UTF-8"); #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=utf8_cd==(iconv_t)-1|| iconv(utf8_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=iconv(enc_list[ei],&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=eci_cd==(iconv_t)-1|| iconv(eci_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ err=(in == NULL) || ( out == NULL ) || inleft > outleft; if (inleft > outleft) inleft = outleft; memcpy(out, in, inleft); #else err=sjis_cd==(iconv_t)-1|| iconv(sjis_cd,&in,&inleft,&out,&outleft)==(size_t)-1; #endif
qrdectxt.c
#if (1) /* does not convert character code */ eci_cd=enc; #else eci_cd=iconv_open("UTF-8",enc); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(eci_cd!=(iconv_t)-1)iconv_close(eci_cd); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(eci_cd!=(iconv_t)-1)iconv_close(eci_cd); #endif
qrdectxt.c
#if (1) /* does not convert character code */ #else if(utf8_cd!=(iconv_t)-1)iconv_close(utf8_cd); if(sjis_cd!=(iconv_t)-1)iconv_close(sjis_cd); if(latin1_cd!=(iconv_t)-1)iconv_close(latin1_cd); #endif
動作確認
GR-PEACH_Camera_inと同様にボタンを押すとバーコードを読むはずですが、動きません。
3回ほど押すと、abortしてしまうようです。
調査したところ、zbar/image.c (133)のfree処理でstaticな領域をfreeしているのが原因でした。これにより、動的メモリを管理しているポインタに不正な値が書かれていました。流用元はデータ入力関数のget_dataで動的メモリを確保して解析する画像用の領域としていましたが、ここをget_data関数を使わずに静的アドレスと指定するように変更したことの影響です。該当のfree処理を削除したところ、abortしなくなりました。
できました
無事、バーコードが読めました。
ちなみに、読んだのは以下のコードです。
プログラムはこちら。
Import programGR-PEACH_Camera_in_barcode
GR-PEACH barcode reader. This program uses ZBar bar code reader. ZBar is licensed under the GNU LGPL 2.1 to enable development of both open source and commercial projects.
おまけ
- zbar\scan_image.c を改造して、バーコードを見つけられなかったときは「No Code detected.」と出るようにしました。
- やたらにI2/5コードを誤認識するので、zbar\config.h (14) ENABLE_I25 マクロの値を 0 にすると、I2/5コードの解析を行わないようにしました。
Please log in to post comments.