Не удается прочитать символ FNC1 в первой позиции штрих-кода GS1 128 с использованием библиотеки Zbar?

Я разработал приложение для декодирования штрих-кода в Android, используя библиотеку Google vision для матрицы данных GS1 и библиотеку Zbar для штрих-кода GS1 128. Невозможно прочитать символ FNC1 в первой позиции штрих-кода GS1 128 с использованием библиотеки Zbar.

Библиотека Zbar не может отображать символы FNC1 в начале штрих-кода!

Любые решения. . . .

Мгновенная помощь заметна. . .

Ниже моя активность сканера ZBar

 @SuppressWarnings("deprecation")
 public class ZBarFirstScannerActivity extends AppCompatActivity{

//TextView tv;
ImageView iv;
LinearLayout ll;
private Camera mCamera;
private CameraPreview mPreview;
private Handler autoFocusHandler;
private ImageScanner scanner;
private boolean barcodeScanned = false;
private boolean previewing = true;
TextView tv;

static {
    System.loadLibrary("iconv");
}
static {
    System.loadLibrary("zbarjni");
}



public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);



    setContentView(R.layout.barcode_capture1d);


    tv = (TextView) findViewById(R.id.textVertical);
    tv.setRotation(90);

    initToolbar();


    autoFocusHandler = new Handler();
    mCamera = getCameraInstance();
    // Instance barcode scanner

    scanner = new ImageScanner();
    scanner.setConfig(0, Config.X_DENSITY, 1);
    scanner.setConfig(0, Config.Y_DENSITY, 1);
    scanner.setConfig(Symbol.CODE128, Config.ENABLE,1);
    scanner.setConfig(Symbol.EAN13, Config.ENABLE,1);

    mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
    FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
    preview.addView(mPreview);


}

private void initToolbar() {

    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    final ActionBar actionBar = getSupportActionBar();

    if (actionBar != null) {


        actionBar.setHomeButtonEnabled(true);
        actionBar.setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.abc_ic_ab_back_mtrl_am_alpha));

        actionBar.setDisplayHomeAsUpEnabled(true);
    }
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance()
{
    Camera c = null;
    try
    {
        c = Camera.open();
    } catch (Exception e)
    {
        //nada
    }
    return c;
}

private void releaseCamera()
{
    if (mCamera != null)
    {
        previewing = false;
        mCamera.setPreviewCallback(null);
        mCamera.release();
        mCamera = null;
    }
}

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

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

        int result = scanner.scanImage(barcode);
        if (result != 0)
        {
            previewing = false;
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            SymbolSet syms = scanner.getResults();
            for (Symbol sym : syms)
            {
                barcodeScanned = true;

                Intent returnIntent = new Intent();
                returnIntent.putExtra("BARCODE", sym.getData());
                setResult(MainActivity.BAR_CODE_TYPE_128,returnIntent);
                releaseCamera();
                finish();
                break;
            }
        }
    }
};

// Mimic continuous auto-focusing
AutoFocusCallback autoFocusCB = new AutoFocusCallback()
{
    public void onAutoFocus(boolean success, Camera camera)
    {
        autoFocusHandler.postDelayed(doAutoFocus, 3000);
    }
};

private Runnable doAutoFocus = new Runnable()
{
    public void run()
    {
        if (previewing)
            mCamera.autoFocus(autoFocusCB);
    }
};

public void onPause() {
    super.onPause();
    releaseCamera();
}

public void onResume(){
    super.onResume();
    new ZBarFirstScannerActivity();

}

@Override
public void onBackPressed() {

    releaseCamera();

    finish();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == android.R.id.home) {
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

Ниже моя активность Google Scanner

public final class GoogleScannerActivity extends AppCompatActivity {
private static final String TAG = "Barcode-reader";

// intent request code to handle updating play services if needed.
private static final int RC_HANDLE_GMS = 9001;

// permission request codes need to be < 256
private static final int RC_HANDLE_CAMERA_PERM = 2;

// constants used to pass extra data in the intent
public static final String AutoFocus = "AutoFocus";
public static final String UseFlash = "UseFlash";
public static final String BarcodeObject = "Barcode";
Bitmap bmp;
FileOutputStream fos = null;
private Camera c;

Switch aSwitch;
private CameraSource mCameraSource;
private CameraSourcePreview mPreview;
private GraphicOverlay<BarcodeGraphic> mGraphicOverlay;

// helper objects for detecting taps and pinches.
private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;

/**
 * Initializes the UI and creates the detector pipeline.
 */
@Override
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.barcode_capture2d);
    initToolbar();

    ActivitySource.caller = this;
    mPreview = (CameraSourcePreview) findViewById(R.id.preview);
    mGraphicOverlay = (GraphicOverlay<BarcodeGraphic>) findViewById(R.id.graphicOverlay);

    boolean autoFocus = true;
    boolean useFlash = false;

    // Check for the camera permission before accessing the camera.  If the
    // permission is not granted yet, request permission.
    int rc = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
    if (rc == PackageManager.PERMISSION_GRANTED) {
        createCameraSource(autoFocus, useFlash);
    } else {
        requestCameraPermission();
    }

    gestureDetector = new GestureDetector(this, new CaptureGestureListener());
    scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());

    /*Snackbar.make(mGraphicOverlay, "Tap to capture. Pinch/Stretch to zoom",
            Snackbar.LENGTH_LONG)
            .show();*/
}

private void initToolbar() {

    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    final ActionBar actionBar = getSupportActionBar();

    if (actionBar != null) {


        actionBar.setHomeButtonEnabled(true);
        actionBar.setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.abc_ic_ab_back_mtrl_am_alpha));

        actionBar.setDisplayHomeAsUpEnabled(true);
    }
}

private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters){
    Camera.Size bestSize = null;
    List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();

    bestSize = sizeList.get(0);

    for(int i = 1; i < sizeList.size(); i++){
        if((sizeList.get(i).width * sizeList.get(i).height) >
                (bestSize.width * bestSize.height)){
            bestSize = sizeList.get(i);
        }
    }
    return bestSize;
}
/**
 * Handles the requesting of the camera permission.  This includes
 * showing a "Snackbar" message of why the permission is needed then
 * sending the request.
 */
private void requestCameraPermission() {
    Log.w(TAG, "Camera permission is not granted. Requesting permission");

    final String[] permissions = new String[]{Manifest.permission.CAMERA};

    if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_CAMERA_PERM);
        return;
    }

    final Activity thisActivity = this;

    View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            ActivityCompat.requestPermissions(thisActivity, permissions,
                    RC_HANDLE_CAMERA_PERM);
        }
    };

    Snackbar.make(mGraphicOverlay, R.string.permission_camera_rationale,
            Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.ok, listener)
            .show();
}

@Override
public boolean onTouchEvent(MotionEvent e) {
    boolean b = scaleGestureDetector.onTouchEvent(e);

    boolean c = gestureDetector.onTouchEvent(e);

    return b || c || super.onTouchEvent(e);
}

/**
 * Creates and starts the camera.  Note that this uses a higher resolution in comparison
 * to other detection examples to enable the barcode detector to detect small barcodes
 * at long distances.
 *
 * Suppressing InlinedApi since there is a check that the minimum version is met before using
 * the constant.
 */
@SuppressLint("InlinedApi")
private void createCameraSource(boolean autoFocus, boolean useFlash) {
    Context context = getApplicationContext();

    // A barcode detector is created to track barcodes.  An associated multi-processor instance
    // is set to receive the barcode detection results, track the barcodes, and maintain
    // graphics for each barcode on screen.  The factory is used by the multi-processor to
    // create a separate tracker instance for each barcode.

    BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(context).setBarcodeFormats(Barcode.CODE_128 | Barcode.DATA_MATRIX | Barcode.QR_CODE).build();
    BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay);
    barcodeDetector.setProcessor(
            new MultiProcessor.Builder<>(barcodeFactory).build());

    if (!barcodeDetector.isOperational()) {
        // Note: The first time that an app using the barcode or face API is installed on a
        // device, GMS will download a native libraries to the device in order to do detection.
        // Usually this completes before the app is run for the first time.  But if that
        // download has not yet completed, then the above call will not detect any barcodes
        // and/or faces.
        //
        // isOperational() can be used to check if the required native libraries are currently
        // available.  The detectors will automatically become operational once the library
        // downloads complete on device.
        Log.w(TAG, "Detector dependencies are not yet available.");

        // Check for low storage.  If there is low storage, the native library will not be
        // downloaded, so detection will not become operational.
        IntentFilter lowstorageFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
        boolean hasLowStorage = registerReceiver(null, lowstorageFilter) != null;

        if (hasLowStorage) {
            Toast.makeText(this, R.string.low_storage_error, Toast.LENGTH_LONG).show();
            Log.w(TAG, getString(R.string.low_storage_error));
        }
    }

    // Creates and starts the camera.  Note that this uses a higher resolution in comparison
    // to other detection examples to enable the barcode detector to detect small barcodes
    // at long distances.
    CameraSource.Builder builder = new CameraSource.Builder(getApplicationContext(), barcodeDetector)
            .setFacing(CameraSource.CAMERA_FACING_BACK)
            .setRequestedPreviewSize(1100, 844)
            .setRequestedFps(15.0f);
    // make sure that auto focus is an available option
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
        builder = builder.setFocusMode(
                autoFocus ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE : null);
    }

    mCameraSource = builder
            .setFlashMode(useFlash ? Camera.Parameters.FLASH_MODE_TORCH : null)
            .build();
}


/**
 * Restarts the camera.
 */
@Override
protected void onResume() {
    super.onResume();
    startCameraSource();
}

/**
 * Stops the camera.
 */
@Override
protected void onPause() {
    super.onPause();
    if (mPreview != null) {
        mPreview.stop();
    }
}

/**
 * Releases the resources associated with the camera source, the associated detectors, and the
 * rest of the processing pipeline.
 */
@Override
protected void onDestroy() {
    super.onDestroy();
    if (mPreview != null) {
        mPreview.release();
    }
}


@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode != RC_HANDLE_CAMERA_PERM) {
        Log.d(TAG, "Got unexpected permission result: " + requestCode);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        return;
    }

    if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        Log.d(TAG, "Camera permission granted - initialize the camera source");
        // we have permission, so create the camerasource
        boolean autoFocus = getIntent().getBooleanExtra(AutoFocus,false);
        boolean useFlash = getIntent().getBooleanExtra(UseFlash, false);
        createCameraSource(autoFocus, useFlash);
        return;
    }

    Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
            " Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));

    DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            finish();
        }
    };

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Multitracker sample")
            .setMessage(R.string.no_camera_permission)
            .setPositiveButton(R.string.ok, listener)
            .show();
}

/**
 * Starts or restarts the camera source, if it exists.  If the camera source doesn't exist yet
 * (e.g., because onResume was called before the camera source was created), this will be called
 * again when the camera source is created.
 */
private void startCameraSource() throws SecurityException {
    // check that the device has play services available.
    int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
            getApplicationContext());
    if (code != ConnectionResult.SUCCESS) {
        Dialog dlg =
                GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS);
        dlg.show();
    }

    if (mCameraSource != null) {
        try {
            mPreview.start(mCameraSource, mGraphicOverlay);
        } catch (IOException e) {
            Log.e(TAG, "Unable to start camera source.", e);
            mCameraSource.release();
            mCameraSource = null;
        }
    }
}

/**
 * onTap is called to capture the oldest barcode currently detected and
 * return it to the caller.
 *
 * @param rawX - the raw position of the tap
 * @param rawY - the raw position of the tap.
 * @return true if the activity is ending.
 */

private boolean onTap(float rawX, float rawY) {
    //TODO: use the tap position to select the barcode.
    BarcodeGraphic graphic = mGraphicOverlay.getFirstGraphic();
    Barcode barcode = null;
    if (graphic != null) {
        barcode = graphic.getBarcode();
        if (barcode != null) {
            Intent data = new Intent();
            data.putExtra(BarcodeObject, barcode);
            setResult(CommonStatusCodes.SUCCESS, data);
            finish();
        }
        else {
            Log.d(TAG, "barcode data is null");
        }
    }
    else {
        Log.d(TAG,"no barcode detected");
    }
    return barcode != null;
}

private class CaptureGestureListener extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {

        return onTap(e.getRawX(), e.getRawY()) || super.onSingleTapConfirmed(e);
    }
}

private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {

    /**
     * Responds to scaling events for a gesture in progress.
     * Reported by pointer motion.
     *
     * @param detector The detector reporting the event - use this to
     *                 retrieve extended info about event state.
     * @return Whether or not the detector should consider this event
     * as handled. If an event was not handled, the detector
     * will continue to accumulate movement until an event is
     * handled. This can be useful if an application, for example,
     * only wants to update scaling factors if the change is
     * greater than 0.01.
     */
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        return false;
    }

    /**
     * Responds to the beginning of a scaling gesture. Reported by
     * new pointers going down.
     *
     * @param detector The detector reporting the event - use this to
     *                 retrieve extended info about event state.
     * @return Whether or not the detector should continue recognizing
     * this gesture. For example, if a gesture is beginning
     * with a focal point outside of a region where it makes
     * sense, onScaleBegin() may return false to ignore the
     * rest of the gesture.
     */
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    /**
     * Responds to the end of a scale gesture. Reported by existing
     * pointers going up.
     * <p/>
     * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
     * and {@link ScaleGestureDetector#getFocusY()} will return focal point
     * of the pointers remaining on the screen.
     *
     * @param detector The detector reporting the event - use this to
     *                 retrieve extended info about event state.
     */
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        mCameraSource.doZoom(detector.getScaleFactor());
    }
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == android.R.id.home) {
        onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}
}

person Akash Dubey    schedule 24.08.2016    source источник


Ответы (1)


При сканировании символа GS1-128 FNC1 в первой позиции действует как символ флага, указывающий на наличие данных стандартного формата идентификатора приложения GS1, и намеренно опускается в сканируемых данных, в то время как любой символ форматирования FNC1 между полями передается в GS ( ASCII 29).

Неявный ведущий FNC1 можно вывести, если ваш считыватель настроен на выдачу идентификаторов символов в начале отсканированных данных. В этом случае ваши отсканированные данные GS1-128 будут начинаться с ]C1, а не ]C0 для общего кода 128.

К сожалению, похоже, что ни библиотека ZBar, ни библиотека Google Vision не могут быть настроены для возврата идентификаторов символов, что является разочаровывающим ограничением. Кроме того, библиотека Google Vision ошибочно возвращает ведущий GS1, представляющий FNC1 на первой позиции.

Чтение данных в формате GS1 подробно описано в этом ответе.

В частности, спецификация символики штрих-кода ISO/IEC 15417 — Code 128 гласит:

«Любое приложение, которое использует символы Code 128 с FNC1 в первой или второй позиции данных, должно требовать включения передачи идентификаторов символики. Когда FNC1 используется в первой или второй позиции, она не должна быть представлена ​​в передаваемом сообщении, хотя ее присутствие указывается использованием значений модификатора 1 или 2 соответственно в идентификаторе символики».

person Terry Burton    schedule 25.08.2016
comment
Вы предполагаете, что мы знаем о вашем заявлении больше, чем вы представляете здесь. Вам нужно предоставить более релевантную информацию, если вы ожидаете мгновенного и полезного обходного пути. Как получается, что ваш рабочий процесс должен видеть символ FNC1, который по определению должен быть невидимым? Чем ваш ввод ZBar для GS1-128 Essential отличается от ввода Google Vision Library для GS1 Data Matrix? - person Terry Burton; 26.08.2016
comment
Для сканирования матрицы данных GS1 я использую библиотеку видения Google, которая предоставляет GS в первой позиции, но ZBar не предоставляет ее при сканировании штрих-кода GS1 128. - person Akash Dubey; 16.09.2016
comment
Из вашего описания может показаться, что библиотека Google Vision неверна, поскольку на самом деле она не должна предоставлять начальный GS1, представляющий FNC1 в первой позиции. Однако для того, чтобы быть полезными для ваших целей, каждая библиотека должна предоставлять средства для идентификации подтипа символа (GS1 DataMatrix по сравнению с DataMatrix и т. д.), из которого были считаны данные, либо путем предоставления идентификатора символики, соответствующего стандартам, либо с помощью предоставление подсказки об обнаружении структурированных данных GS1 в той или иной форме. Беглый просмотр справочника API для каждого проекта не указывает на наличие таких функций. - person Terry Burton; 17.09.2016
comment
Можете ли вы предложить мне любую библиотеку с открытым исходным кодом, которая предоставляет символ FNC1 в первой позиции для штрих-кода GS1 128 ?? - person Akash Dubey; 19.09.2016
comment
Вы необоснованно просите библиотеку, которая имеет ошибку, совместимую с вашими неверными ожиданиями, т.е. преобразует ведущую FNC1 в GS вопреки спецификации. Даже если вы найдете такую ​​библиотеку, ваше решение будет ненадежным, поскольку в конечном итоге ошибка может быть исправлена, что приведет к поломке будущих сборок вашего приложения. - person Terry Burton; 19.09.2016
comment
Вместо этого (при отсутствии каждой библиотеки, указывающей на наличие данных GS1) вам следует обойти ошибку Google Vision, удалив начальный символ GS из отсканированных данных. Это даст вам паритет между двумя библиотеками. Затем вы должны использовать эвристический или явный синтаксический анализатор, чтобы определить, имеют ли отсканированные данные формат GS1, и отклонить сканирование, если оно не является допустимыми структурированными данными. Если для вашей последующей обработки данных требуется начальный символ GS, вы можете повторно ввести его после этой начальной фазы проверки. - person Terry Burton; 19.09.2016
comment
Если вы решите проанализировать входные данные, чтобы определить, являются ли они действительными данными GS1, то следующее поможет вам написать этот код. stackoverflow.com/a/31760872/2568535 - person Terry Burton; 19.09.2016
comment
Здесь. Необходимо получить FNC1 на первой позиции, чтобы доказать, что штрих-код имеет символику GS1 128. Вот почему мне нужна библиотека, которая предоставляет мне информацию о том, закодирован ли FNC1 или нет в штрих-коде, и если он закодирован, то он должен быть в декодированном значении в соответствии с ]c или любым ascii 29 или чем-либо, что указывает на наличие FNC1 на первой позиции - person Akash Dubey; 20.09.2016
comment
Правильно, и я говорю, что вам не повезло в этом отношении, поэтому вам придется прибегнуть к эвристике. Или расширить/улучшить библиотеки самостоятельно... - person Terry Burton; 20.09.2016
comment
Можете ли вы предложить мне что-нибудь по этому поводу!! Потому что я даже не знаю с чего начать!! - person Akash Dubey; 21.09.2016
comment
Вы не продемонстрировали попытку понять или решить проблему самостоятельно, учитывая то, что вы здесь узнали. Никто, скорее всего, не напишет ваш код за вас, поэтому вам лучше найти кого-то с такой способностью и заплатить ему за его опыт. С точки зрения, не связанной с программированием, вы всегда можете сообщить об ошибках в этих проектах, указав на это обсуждение для контекста. 1: GV не должен передавать ведущий FNC1. 2/3: API-интерфейсы GV/ZBar должны возвращать идентификатор символов, указывающий данные в формате GS1 (сначала FNC1). - person Terry Burton; 21.09.2016