だらだらやるよ。

こげつのIT技術メモ

zxingを使用してQRコードを読み込むんだけど重すぎてくそだったので修正した話

.NET CFでQRコードの実装探してたんですよ、
ぐぐったらOpenNETCFで使えるとか書いてるけど見つからず。なんだったんだろう。。。


というわけでわりとメジャなライブラリであるところのzxingを使うことにしました。
で、CF環境でzxingを使う場合のメモ

  • ソースはアーカイブからじゃなくてリポジトリから落とさないとC#のソースが入っていない
  • ソースはそのままだとCFでコンパイルが通らないので、機械的にCF用に直してやる。
  • パフォーマンスがまったくでなくてつらいのでめげない。


って書いたんですけど正直心がめげました。


というわけでライブラリの重い部分に手を入れて高速化することに。
ファイルで言うとRGBLuminanceSource.csですね。
ここのコンストラクタを以下のように修正しました。

    public RGBLuminanceSource(Bitmap d, int W, int H)
        : base(W, H)
    {
        int width = __width = W;
        int height = __height = H;
        // In order to measure pure decoding speed, we convert the entire image to a greyscale array
        // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
        luminances = new sbyte[width * height];
        //if (format == PixelFormat.Format8bppIndexed)
        {
            
            BitmapData bmpData = d.LockBits(new Rectangle(0, 0, d.Width, d.Height),
                ImageLockMode.ReadWrite,
                PixelFormat.Format24bppRgb);
            IntPtr bitmapPtr = bmpData.Scan0;
            byte[] b = new byte[width*height*3];
            Marshal.Copy(bitmapPtr, b, 0, d.Width * d.Height*3);
            int cnt = 0;
            for(int i=0;i<b.Length;i+=3){
                luminances[cnt] = (sbyte)b[i];
                cnt++;

            }
            d.UnlockBits(bmpData);

        }
    }

まあいつものようにC#で画像処理するとGetPixelでのbyte配列への変換が重くてひどいのでMarshal.Copyしようぜっていうあれですね。
ちゃんと時間はかる前にチューニングやっちゃったけど速度的には10分の1程度にはなったかな。。。
これでもコード読んで不思議だったんだけど、なんでsbyteにしてるんだろう。。。

一応使う側のコード

            CameraCaptureDialog dialog = new CameraCaptureDialog();
            dialog.StillQuality = CameraCaptureStillQuality.Low;

            if (dialog.ShowDialog() != DialogResult.OK) {
                return;
            }


            Reader reader = new MultiFormatReader();


            Bitmap bmp = new Bitmap(dialog.FileName);
            HybridBinarizer binarizer = new com.google.zxing.common.HybridBinarizer(new RGBLuminanceSource(bmp, bmp.Width, bmp.Height));
            BinaryBitmap binBmp = new BinaryBitmap(binarizer);
            Result decodedString = null;
            try {
                decodedString = reader.decode(binBmp);
            } finally {
                if (decodedString != null) {
                    MessageBox.Show(decodedString.Text);
                }
            }