Я хочу сканировать как классические, так и инвертированные (светлые на темном) QR-коды. Что делать?

Я использую устаревший класс Camera. Я делаю обработку в методе onPreviewFrame(byte[] data, Camera camera). Сканер Zbar не имеет возможности запустить сканирование «попробовать инвертировать». Я понял, что могу установить отрицательный цветовой эффект на свою камеру Android, и он отлично работает для сканирования перевернутого QR-кода, но перестает обнаруживать обычные.

Я пытаюсь найти способ обмана, например, иметь 2 экземпляра одной и той же камеры, один с примененным отрицательным эффектом, а другой без, и отображать только один без эффекта, но это не позволит мне.

Приведенный ниже код вызывается каждый раз при отображении кадра.

private Camera.PreviewCallback previewCb = new Camera.PreviewCallback() {
    public void onPreviewFrame(byte[] data, Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        Camera.Size size = parameters.getPreviewSize();

        Image barcode = new Image(size.width, size.height, "Y800");
        barcode.setData(data);
        int result = scanner.scanImage(barcode);

И вот как я установил отрицательный эффект, о котором я говорю.

Camera.Parameters params = mCamera.getParameters();
params.setColorEffect(Camera.Parameters.EFFECT_NEGATIVE);
mCamera.setParameters(params);

Другой способ - обработать массив байтов YUV, который я получаю от обратного вызова предварительного просмотра, и применить отрицательный эффект, но я не уверен, как это сделать без тяжелых преобразований.

Любые идеи ?


person Someone    schedule 01.05.2015    source источник
comment
пожалуйста, предоставьте подробную информацию о вашей попытке.. это поможет всем ответить на этот вопрос..   -  person Kushal    schedule 01.05.2015
comment
Вы нашли решение для чтения QR-кодов на темном фоне?   -  person mz87    schedule 13.05.2015
comment
Нет, я поместил кнопку для переключения цветового эффекта камеры   -  person Someone    schedule 13.05.2015


Ответы (2)


Мне удалось заставить его работать, преобразовывая данные YUV туда и обратно, после нескольких недель, когда я не находил никакого ответа / вводил в заблуждение. В ZBarScannerView.java мой onPreviewFrame(..) выглядит так:

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    Camera.Parameters parameters = camera.getParameters();
    Camera.Size size = parameters.getPreviewSize();
    int width = size.width;
    int height = size.height;

    switcher = !switcher;


    if(DisplayUtils.getScreenOrientation(getContext()) == Configuration.ORIENTATION_PORTRAIT) {
        byte[] rotatedData = new byte[data.length];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++)
                rotatedData[x * height + height - y - 1] = data[x + y * width];
        }
        int tmp = width;
        width = height;
        height = tmp;
        data = rotatedData;
    }

    Image barcode = new Image(width, height, "Y800");

    if (switcher) {
        int[] pixels = applyGrayScale(data,width,height);
        Bitmap bm = Bitmap.createBitmap(pixels,width,height, Bitmap.Config.ARGB_8888);
        bm = MyUtils.createInvertedBitmap(bm, width, height);

        pixels = new int[width*height];
        bm.getPixels(pixels, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());

        encodeYUV420SP(data, pixels, bm.getWidth(), bm.getHeight());
    }


    barcode.setData(data);

    int result = mScanner.scanImage(barcode);

    if (result != 0) {
        stopCamera();
        if(mResultHandler != null) {
            SymbolSet syms = mScanner.getResults();
            Result rawResult = new Result();
            for (Symbol sym : syms) {
                String symData = sym.getData();
                if (!TextUtils.isEmpty(symData)) {
                    rawResult.setContents(symData);
                    rawResult.setBarcodeFormat(BarcodeFormat.getFormatById(sym.getType()));
                    break;
                }
            }
            mResultHandler.handleResult(rawResult);
        }
    } else {
        camera.setOneShotPreviewCallback(this);
    }
}

также добавьте это в класс (получено из здесь):

void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
    final int frameSize = width * height;

    int yIndex = 0;
    int uvIndex = frameSize;

    int a, R, G, B, Y, U, V;
    int index = 0;
    for (int j = 0; j < height; j++) {
        for (int i = 0; i < width; i++) {

            a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
            R = (argb[index] & 0xff0000) >> 16;
            G = (argb[index] & 0xff00) >> 8;
            B = (argb[index] & 0xff) >> 0;

            // well known RGB to YUV algorithm
            Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
            U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
            V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;

            // NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
            //    meaning for every 4 Y pixels there are 1 V and 1 U.  Note the sampling is every other
            //    pixel AND every other scanline.
            yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
            if (j % 2 == 0 && index % 2 == 0) {
                yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
                yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
            }

            index++;
        }
    }
}

это заботится о преобразовании массива int обратно в массив байтов после инверсии.

Также я использую эти фрагменты кода, которые я получил где-то на stackExchange (я слишком долго не мог вспомнить, где) с несколькими небольшими изменениями внутри служебного класса с именем MyUtils.java:

public class MyUtils {

public static Integer sizeWidth;
public static Integer sizeHeight;


public static Bitmap createInvertedBitmap(Bitmap src, Integer width, Integer height) {

    sizeWidth = width;
    sizeHeight = height;

    ColorMatrix colorMatrix_Inverted =
            new ColorMatrix(new float[] {
                    -1,  0,  0,  0, 255,
                    0, -1,  0,  0, 255,
                    0,  0, -1,  0, 255,
                    0,  0,  0,  1,   0});

    ColorFilter ColorFilter_Sepia = new ColorMatrixColorFilter(
            colorMatrix_Inverted);

    Bitmap bitmap = Bitmap.createBitmap(sizeWidth, sizeHeight,
            Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);

    Paint paint = new Paint();

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

    return bitmap;
}


public static int[] applyGrayScale(byte [] data, int width, int height) {
    int p;
    int size = width*height;
    int[] pixels = new int[size];
    for(int i = 0; i < size; i++) {
        p = data[i] & 0xFF;
        pixels[i] = 0xff000000 | p<<16 | p<<8 | p;
    }
    return pixels;
}

}

Наконец, добавьте Boolean switcher = true в класс в рамках класса ZBarScannerView. Переменная «переключатель» предназначена для переключения между проверкой инвертированных и неинвертированных кодов.

Пожалуйста, спросите, есть ли у вас какие-либо проблемы, я так долго боролся с этим, я чувствую, и ответ может потребовать некоторого редактирования для удобочитаемости.

person Versa    schedule 18.05.2015
comment
Я пробовал, работает отлично. Спасибо, Верса. - person Someone; 20.05.2015
comment
Это здорово слышать :) - person Versa; 21.05.2015

Я давно пытаюсь сделать то же самое. И до сих пор не увенчались полным успехом. Не могу сказать, отвечает ли это на вопрос или нет. Но этот код, помещенный в mBarcodeScannerView, и вызов его с помощью autoInvert() в mZBarScannerView, хотя и раздражает взгляд, позволит вам сканировать как обычные, так и инвертированные QR-коды. По сути, он просто время от времени переключается между эффектами камеры.

public void autoInvert() {
    // Don't think this line is needed.
    // autoInvertOn = !autoInvertOn;

    Runnable runAutoInvert = new Runnable() {
        @Override
        public void run() {

            android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            Camera.Parameters parameters = mCamera.getParameters();

            if (parameters.getSupportedColorEffects().contains(Camera.Parameters.EFFECT_NEGATIVE)) {
                while (mCamera != null && autoInvertOn) {
                    try {
                        parameters = mCamera.getParameters();
                        parameters.setColorEffect(Camera.Parameters.EFFECT_NEGATIVE);
                        mCamera.setParameters(parameters);

                        Thread.sleep(800);

                        parameters = mCamera.getParameters();
                        parameters.setColorEffect(Camera.Parameters.EFFECT_NONE);
                        mCamera.setParameters(parameters);

                        Thread.sleep(800);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (Exception e1) {
                        mCamera = null;
                    }
                }
            }
        }
    };

    Thread autoInvertThread = new Thread(runAutoInvert);
    autoInvertThread.start();

}

Чтобы это не мешало моим глазам в темных условиях, я также добавляю черный, несколько прозрачный вид поверх предварительного просмотра, когда он инвертируется. Для меня это решение, пока я работаю над поиском лучшего.

person Versa    schedule 15.05.2015
comment
Посмотрите на мой другой ответ, который отвечает на вопрос и подтверждается его работой, оставив это, поскольку это может решить чей-то связанный с этим вопрос. - person Versa; 21.05.2015