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.

  1. Go to http://zbar.sourceforge.net/.
  2. Click "download".
  3. Download "ZBar 0.10 source tarball".
  4. Expand "zbar-0.10.tar.bz2", Then "zbar-0.10.tar" will be generated.
  5. Expand "zbar-0.10.tar".
  6. Launch cygwin terminal.
  7. Move to zbar-0.10 directory.
  8. 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.

  1. 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.
  2. 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

  1. 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.
  2. 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.
  3. 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() */
  1. 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
  1. 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.

  1. Remove #include <png.h> in zbar\scan_image.c
  2. Remove #include <unistd.h> in zbar\img_scanner.c
  3. Remove #include <sys/time.h> in zbar\img_scanner.c
  4. 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;
  5. Remove followinig 2 lines that uses strdup() function in zbar\error.c.
    if(!err->arg_str)
    err->arg_str = strdup("<?>");
  6. 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".
  7. 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.
/media/uploads/RyoheiHagimoto/barcode-readed.png
The code below is read.
/media/uploads/RyoheiHagimoto/qrcode.gif
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を組み込みます。

  1. http://zbar.sourceforge.net/ に行きます。
  2. 「download」をクリックします。
  3. ZBar 0.10 source tarball をダウンロードします。
  4. zbar-0.10.tar.bz2 を展開します。zbar-0.10.tar が生成されます。
  5. zbar-0.10.tar を展開します。
  6. cygwinのterminalを起動します。
  7. 展開したzbar-0.10に移動します。
  8. 以下のコマンドを入力します。
$ ./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にあるようです。
このファイルを改造します。

  1. 関数名変更
    main関数がいらっしゃいますがGR-PEACH_Camera_in側のmain関数を使用したいため、zbar_mainに関数名を変えます。
    また、引数をイメージバッファとその縦横のサイズとしました。引数チェックも変更します。
  2. 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の改造

  1. 使用カメラをCMOSにする
    この作業はアナログカメラを使用する方には不要です
    手元の環境はGR-Audio/Camera ShieldにMT9V111を搭載したCF0K82Cを接続して使用するため、 VIDEO_INPUT_METHOD マクロの値を VIDEO_CMOS_CAMERA にします。
  2. メモリに展開するデータフォーマットをYCbCrにする
    zbarはグレイスケール入力なので、データフォーマットを輝度抽出をしやすいYCbCrに変更します。
    VIDEO_INPUT_FORMAT マクロの値を VIDEO_YCBCR422 にします。
  3. 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() */
  1. グレイスケール画像を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
  1. 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ライブラリを削除(しなくても問題ないです)

コンパイル

ようやくコンパイルです。エラーの対処を列挙します。

  1. zbar\scan_image.c の以下を削除します。
    #include <png.h>
  2. zbar\img_scanner.c の以下を削除します。
    #include <unistd.h>
  3. zbar\img_scanner.c の以下を削除します。
    #include <sys/time.h> /* gettimeofday */
  4. 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;
  5. zbar\error.c の_zbar_error_string内でstrdupを使用している以下の箇所を削除します。
    if(!err->arg_str)
    err->arg_str = strdup("<?>");
  6. zbar\debug.hのヘッダファイル名が他と重複しているため、zbar\zbar_debug.hにファイル名変更します。
    #includeしている箇所も変更します。
  7. 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しなくなりました。

できました

無事、バーコードが読めました。
/media/uploads/RyoheiHagimoto/barcode-readed.png
ちなみに、読んだのは以下のコードです。
/media/uploads/RyoheiHagimoto/qrcode.gif
プログラムはこちら。

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.