libzebra を使って Objective-C でバーコードを読み取る

Obejctvie-C ほとんど関係ありませんが。

iPhone のアプリケーションでバーコードを読み取りたいなーと思って調べていると、Zebra っちゅうのがあったので(もうすぐ名前が変わるみたい)、これ使って実現できないかやってみました。

MacPorts には zebra が用意されていないようなので、自分でダウンロードしてきてコンパイルします。
[Masahiro@masahiro.local:~/Documents] $ tar xzvf zebra-0.6.tar.gz
[Masahiro@masahiro.local:~/Documents] $ ./zebra-0.6
[Masahiro@masahiro.local:~/Documents/zebra-0.6] $ ./configure
...
configure: error: test for video support failed!
rebuild your kernel to include video4linux support or
configure --disable-video to skip building video support.
See `config.log' for more details.

なんつって怒られてしまいました。video4linux を無効にして再チャレンジ。

./configure --disable-video
...
No package 'ImageMagick++' found

ImageMagick 入ってないと。MacPorts で ImageMagick インストールして再々チャレンジ。

./configure --disable-video
...
No package 'gtk+-2.0' found

きりがないわ、っちゅうことで ./configure -h でオプション見て必要なさそうなものを外します。

./configure --prefix=/Users/Masahiro/tmp/libzebra --disable-video --without-gtk --without-qt --without-xv --without-xshm --without-x --without-python
...
please verify that the detected configuration matches your expectations:
------------------------------------------------------------------------
X                 --with-x=no
pthreads          --enable-pthread=yes
v4l               --enable-video=no
        => zebracam video scanner will *NOT* be built
jpeg              --with-jpeg=no
        => JPEG image conversions will *NOT* be supported
Magick++          --with-imagemagick=yes
Python            --with-python=no
GTK+              --with-gtk=no
        => the GTK+ widget will *NOT* be built
Qt4               --with-qt=no
        => the Qt4 widget will *NOT* be built

自分のホームの tmp/libzebra に入れておくことにして、make & make install でインストール。

で、次は Objective-C でこのライブラリを使ってバーコードを読み取るコードを書いてみます。テストで使うバーコードはここから持ってきました。

#import <Foundation/Foundation.h>
#import <zebra.h>
 
#define fourcc(a, b, c, d) \
    ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
    ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
 
void dataHandler(zebra_image_t* zimage, const void* userdata) {
    NSLog(@"succeeded to scan barcode");
    const zebra_symbol_t* symbol = zebra_image_first_symbol(zimage);
    zebra_symbol_type_t type = zebra_symbol_get_type(symbol);
    const char* data = zebra_symbol_get_data(symbol);
    NSLog(@"type: %d, data: %s", type, data);
}
 
void cleanupHandler(zebra_image_t* zimage) {
    NSLog(@"cleanup called");
}
 
CGImageRef createCGImageFromData(NSData* data) {
    CGImageRef imageRef = NULL;
    CGImageSourceRef sourceRef = CGImageSourceCreateWithData((CFDataRef)data, NULL);
    if (sourceRef) {
        imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
        CFRelease(sourceRef);
    }
    return imageRef;
}
 
int main(int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
    //
    // ファイルから画像を読み込みビットマップに変換する
    //
    NSData* data = [[[NSData alloc] initWithContentsOfFile:@"/Users/Masahiro/Desktop/Zbar.114.png"] autorelease];
    CGImageRef image = createCGImageFromData(data);
    if (!image) {
        NSLog(@"faild to execute createCGImageFromData");
        exit(1);
    }
    int width = CGImageGetWidth(image);
    int height = CGImageGetHeight(image);
    CGRect rect = CGRectMake(0, 0, width, height);
    int bitsPerComponent = 8;
    int bytesPerRow = width;
    int bitmapByteCount = bytesPerRow * height;
    void* bitmap = malloc(bitmapByteCount);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
    CGContextRef c = CGBitmapContextCreate(bitmap,
                                           width,
                                           height,
                                           bitsPerComponent,
                                           bytesPerRow,
                                           colorspace,
                                           kCGImageAlphaNone);
    CGColorSpaceRelease(colorspace);
    CGContextDrawImage(c, rect, image);
    CGContextRelease(c);
 
    //
    // バーコードを読み取る
    //
    zebra_image_scanner_t* scanner = zebra_image_scanner_create();
    zebra_image_scanner_set_data_handler(scanner, dataHandler, NULL);
    zebra_image_t* zimage = zebra_image_create();
    zebra_image_set_format(zimage, fourcc('Y', '8', '0', '0'));
    zebra_image_set_size(zimage, width, height);
    zebra_image_set_data(zimage, bitmap, bitmapByteCount, cleanupHandler);
    zebra_scan_image(scanner, zimage);
    zebra_image_destroy(zimage);
    zebra_image_scanner_destroy(scanner);
 
    [pool drain];
    return 0;
}

エラー処理丁寧に入れていませんが、こんな感じ。Xcode でビルドするときに気をつけないといけないのは、ヘッダのパス(/Users/Masahiro/tmp/libzebra/include)を追加することと、ライブラリを追加すること(今回はスタティックライブラリを使うことにして /Users/Masahiro/tmp/libzebra/lib/libzebra.a)。Xcode でリンクするライブラリの追加はファイルをドラッグ&ドロップなんですね。それくらいっすかね。ああ、あとプロジェクトをテスト用に Foundation Tool で作っていまして、CGImage を扱うために ApplicationServices フレームワークを追加してあげないといけませんでした。普通に Cocoa Application だったりすると必要ないのかもしれません。

で、実行してみると、

2009-03-20 13:18:51.084 ObjC Test[98413:10b] succeeded to scan barcode
2009-03-20 13:18:51.086 ObjC Test[98413:10b] type: 13, data: 9876543210128
2009-03-20 13:18:51.087 ObjC Test[98413:10b] cleanup called

こんな感じで、タイプが 13(zebra.h の zebra_symbol_type_e で ZEBRA_EAN13)、バーコードが 9876543210128 と読み取れております。素晴らしい。


About this entry