android-vision icon indicating copy to clipboard operation
android-vision copied to clipboard

Can't read QRCode with different colors

Open Nom4d3 opened this issue 8 years ago • 12 comments

The regular qrcodes are black with white background. They are working like a charm.

But I have here hundred of cards with a white qrcode on a blue background. And I can't make the android vision recognize these qrcodes with different colors. Every qrcode reader from the Google Play works perfectly. But I can't read them using android vision api.

I've played with different colors here and the problem seems to be the with background. Every qrcode with dark color with white background works. But if i change the white background, it stops working.

Any solution for this? Any parameters that I can change? I'm trying to avoid using an external lib as we have the native support.

Sorry if this is not a issue, but I've searched a lot without success and I can read without problems using other apps. But I'm not sure if they are using the new api. Thanks!

Nom4d3 avatar Jan 15 '16 12:01 Nom4d3

Right, the barcode API generally doesn't support color-inverted codes. There's no parameter or option to control this at the moment. Though some APIs support them, I don't believe it's a common feature.

For a workaround, you could preprocess the colors in the bitmap before passing them to the barcode API (perhaps inverting colors on alternate frames).

Would you mind sharing an example image of the QR codes you are trying to read?

pchx-zz avatar Jan 15 '16 15:01 pchx-zz

I was about to start the approach you suggested. I'm just worried about the performance. Ideas how to do this on the fly without losing performance?

Here are an example of the qrcode I'm testing. Image qrcode1 Image qrcode2

Nom4d3 avatar Jan 15 '16 15:01 Nom4d3

I'd suggest trying to preprocess the image in Java first. If that isn't fast enough for your needs, try JNI or RenderScript.

pchx-zz avatar Jan 15 '16 17:01 pchx-zz

Hi,

Will this be included in any release? The workaround that pchx suggested may work but it disables black on white qrs.

Fancy QRs (inverted colour included) are becoming increasingly common.

keith-redbite avatar Dec 09 '16 16:12 keith-redbite

For now, as my application is in hands of few users, I've included an option to turn on the camera negative effect during the capture.

It's not the best workaround, but we need something fast and processing the image on the fly was not a good option.

Nom4d3 avatar Dec 09 '16 16:12 Nom4d3

@Nom4d3 not sure if you need this still but this does the job.

class MyQRDetector extends Detector<Barcode> {
    private Detector<Barcode> mDelegate;

    MyQRDetector(Detector<Barcode> delegate) {
        mDelegate = delegate;
    }

    public Bitmap invertBitmap(Bitmap src)
    {
        int height = src.getHeight();
        int width = src.getWidth();

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();

        ColorMatrix matrixGrayscale = new ColorMatrix();
        matrixGrayscale.setSaturation(0);

        ColorMatrix matrixInvert = new ColorMatrix();
        matrixInvert.set(new float[]
                {
                        -1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
                        0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
                        0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
                        0.0f, 0.0f, 0.0f, 1.0f, 0.0f
                });
        matrixInvert.preConcat(matrixGrayscale);

        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrixInvert);
        paint.setColorFilter(filter);

        canvas.drawBitmap(src, 0, 0, paint);

        return bitmap;
    }

    public Bitmap createBlackAndWhite(Bitmap src) {
        int width, height;
        height = src.getHeight();
        width = src.getWidth();

        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmpGrayscale);
        Paint paint = new Paint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(src, 0, 0, paint);
        return bmpGrayscale;
    }

    public SparseArray<Barcode> detect(Frame frame) {
        // *** add your custom frame processing code here
        SparseArray<Barcode> detections;

        YuvImage yuvImage = new YuvImage(frame.getGrayscaleImageData().array(), ImageFormat.NV21, frame.getMetadata().getWidth(), frame.getMetadata().getHeight(), null);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0, 0, frame.getMetadata().getWidth(), frame.getMetadata().getHeight()), 100, byteArrayOutputStream);
        byte[] jpegArray = byteArrayOutputStream.toByteArray();
        Bitmap bitmap = BitmapFactory.decodeByteArray(jpegArray, 0, jpegArray.length);


        if (bitmap != null) {
            Bitmap BWBitmap = createBlackAndWhite(bitmap);
            frame = new Frame.Builder()
                    .setBitmap(BWBitmap)
                    .setRotation(frame.getMetadata().getRotation())
                    .build();
            detections = mDelegate.detect(frame);

            Bitmap InvertedBitmap = invertBitmap(BWBitmap);
            if (detections.size() == 0) {
                frame = new Frame.Builder()
                        .setBitmap(InvertedBitmap)
                        .setRotation(frame.getMetadata().getRotation())
                        .build();
            }
        }

        detections = mDelegate.detect(frame);
        return detections;
    }

    public boolean isOperational() {
        return mDelegate.isOperational();
    }

    public boolean setFocus(int id) {
        return mDelegate.setFocus(id);
    }
}

doodlesbykumbi avatar Nov 26 '17 16:11 doodlesbykumbi

I found a faster solution for invert colors (8ms vs 200ms) https://gist.github.com/jacklt/4627f9e773aaebea12f2bce357446dc8

In Kotlin: Invert ByteArray color

frame.grayscaleImageData.array().let { b ->
    val max = 0xFFFFFFFF
    ByteArray(b.size) { (max - b[it].toInt()).toByte() }
}

and wrap in ByteBuffer!

#prefmatters (Colt McAnlis) ;)

omarmiatello avatar Feb 15 '18 18:02 omarmiatello

I improved googles example app "barcode-reader" to detect both inverted colored barcodes and regular ones. I did so by editing "CameraSource" class, package: "com.google.android.gms.samples.vision.barcodereader.ui.camera". I added a parameter: private boolean isInverted = false; and changed function void setNextFrame(byte[] data, Camera camera):

void setNextFrame(byte[] data, Camera camera) {
            synchronized (mLock) {
                if (mPendingFrameData != null) {
                    camera.addCallbackBuffer(mPendingFrameData.array());
                    mPendingFrameData = null;
                }

                if (!mBytesToByteBuffer.containsKey(data)) {
                    Log.d(TAG,
                            "Skipping frame.  Could not find ByteBuffer associated with the image " +
                                    "data from the camera.");
                    return;
                }

                mPendingTimeMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
                mPendingFrameId++;
                if (!isInverted){
                    for (int y = 0; y < data.length; y++) {
                        data[y] = (byte) ~data[y];
                    }
                    isInverted = true;
                } else {
                    isInverted = false;
                }
                mPendingFrameData = mBytesToByteBuffer.get(data);

                // Notify the processor thread if it is waiting on the next frame (see below).
                mLock.notifyAll();
            }
        }

It solves the problem perfectly!

hadaspeled12 avatar Apr 11 '18 05:04 hadaspeled12

Can you read this colored QR Code ? WhatsApp Image 2019-03-20 at 11 00 44 PM

kopilim96 avatar Mar 20 '19 15:03 kopilim96

I can't read the inverted DataMatrix

not read: not read

Read: read

Is it possible to fix it?

skambenner avatar Oct 05 '20 21:10 skambenner

Let me know if someone told you a solution I'm trying that in flutter, same problem with datamatrix

mhilalsh avatar Sep 23 '21 14:09 mhilalsh

Let me know if someone told you a solution I'm trying that in flutter, same problem with datamatrix

I haven't found a solution. I started using a physical scanner.

skambenner avatar Sep 23 '21 15:09 skambenner