今回はバーコード認識になります。
バーコードと言っても、商品についている1次元バーコードから2次元バーコードまでサポートされています。
サポートされているバーコードの種類は以下のようになっています。
(こんなにバーコードに種類があること自体知りませんでした)

環境設定

Barcode detectionもMobile Visionの一部なので、以下のようにapp/build.gradleに依存関係を追加することが設定が完了します。 app/build.gradle
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.google.android.gms:play-services-vision:11.0.0'    // ★追加
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:recyclerview-v7:25.3.1'
}
これを設定することで、Mobile Vision APIが使用可能となります。

実装

動画については、先の顔認識のブログにも書かせてもらったように、動画をJCodecでフレーム分割して認識させるだけなので、今回は静止画とカメラからの入力映像についてのみ記載します。

静止画に対するバーコード認識

実装の流れは「Mobile Vision API - Face Detect(静止画)」と同じように以下のようになります。
  1. BarcodeDetector.Builderインスタンスを生成し、それからバーコード認識条件が設定されたBarcodeDetectorインスタンスを生成
  2. Frame.Builderインスタンスを生成し、それから静止画像が設定されたFrameインスタンスを生成
  3. BarcodeDetectorインスタンスにFrameインスタンスを設定して、バーコード認識を実施
バーコード認識が完了すると、BarcodeSparseArrayに格納されてきます。

1. BarcodeDetector.Builderインスタンスを生成し、それからバーコード認識条件が設定されたBarcodeDetectorインスタンスを生成

顔認識のFaceDetectorとFaceDetector.Buildと同じような構成がとられています。2017.09現在、設定可能なバーコード認識条件はバーコードのフォーマットの指定のみとなります。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromPhotoFragment.java - BarcodeDetectTask.onPreExecute
BarcodeDetector.Builder builder = new BarcodeDetector.Builder(activity);

// barcode format
Integer selectedFormat = (Integer) ((Spinner) activity.findViewById(R.id.spnrBarcodeFormat)).getSelectedItem();
if (selectedFormat == null) {
    selectedFormat = Barcode.ALL_FORMATS;
}
builder.setBarcodeFormats(selectedFormat);

// rotation
Spinner rotationSpinner = (Spinner) activity.findViewById(R.id.spnrRotation);
mSelectedRotation = (Integer) rotationSpinner.getSelectedItem();

mBarcodeDetector = builder.build();

2. Frame.Builderインスタンスを生成し、それから静止画像が設定されたFrameインスタンスを生成

この辺は「Mobile Vision API - Face Detect(静止画)」と同じになります。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromPhotoFragment.java - BarcodeDetectTask.doInBackground
Frame fullImageFrame = new Frame.Builder().setBitmap(fullImage).setRotation(rotation).build();

3. BarcodeDetectorインスタンスにFrameインスタンスを設定して、バーコード認識を実施

この辺も「Mobile Vision API - Face Detect(静止画)」と同じになります。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromPhotoFragment.java - BarcodeDetectTask.doInBackground
SparseArray<Barcode> detectedBarcodeArray = mBarcodeDetector.detect(fullImageFrame);
これで、個々のバーコードが取得できました。

カメラからの入力映像に対するバーコード認識

実装の流れは「Mobile Vision API - Face Detect(カメラ)」と同じように以下のようになります。
  1. カメラプレビューを表示するためのSurfaceViewを用意
  2. BarcodeDetectorインスタンスを生成
  3. Processorインスタンスを生成し、BarcodeDetectorに設定
  4. CameraSourceインスタンスを生成し、startメソッドをコール

1. カメラプレビューを表示するためのSurfaceViewを用意

厳密には必ずしもSurfaceViewである必要はありませんが、SurfaceHolderが必要になるので、多分SurfaceViewを使用するのが簡単でしょう。
後述するCameraSourceにSurfaceHolderを指定することで、カメラからの入力を、指定されたSurfaceに描画してくれます。
ただし、カメラプレビューを表示したくない場合は、SurfaceViewは不要です。
※ カメラプレビューを表示しない場合は、文字認識開始時にSurfaceHolderを指定しないCameraSource.start(引数なし)メソッドをコールすることになります。

2. BarcodeDetectorインスタンスを生成

2017.09現在、BarcodeDetector.BuilderはsetBarcodeFormatsとbuildメソッドしか公開されていないので、以下のように両メソッドをコールするのみとなります。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromCameraFragment.java - initBarcodeDetector
  BarcodeDetector.Builder builder = new BarcodeDetector.Builder(activity);

  // barcode format
  Integer selectedFormat = (Integer) ((Spinner) activity.findViewById(R.id.spnrBarcodeFormat)).getSelectedItem();
  if (selectedFormat == null) {
      selectedFormat = Barcode.ALL_FORMATS;
  }
  builder.setBarcodeFormats(selectedFormat);

  mBarcodeDetector = builder.build();

3. Processorインスタンスを生成し、BarcodeDetectorに設定

FocusingProcessor.selectFocusで返却する値は、以下のパラメータで指定されたDetector.Detectionsから取得できるSparseArrayのkeyを返却する必要があります。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromCameraFragment.java - initBarcodeDetector
// use multi processor
SwitchCompat tempSwitch = (SwitchCompat) activity.findViewById(R.id.scUseMultiProcessor);
if (tempSwitch.isChecked()) {
    MultiProcessor.Builder<Barcode> multiProcessorBuilder = new MultiProcessor.Builder<Barcode>(mMultiProcessFactory);
    mBarcodeDetector.setProcessor(multiProcessorBuilder.build());
} else {
    FocusingProcessor<Barcode> focusingProcessor = new FocusingProcessor<Barcode>(mBarcodeDetector, new BarcodeTracker()) {
        @Override
        public int selectFocus(Detector.Detections detections) {
            SparseArray<Barcode> detectedItems = detections.getDetectedItems();
            int selectedItem = 0;
            int largestSize = 0;
            for(int i=0, size=detectedItems.size(); i<size; i++){
                Barcode detectedItem = detectedItems.valueAt(i);
                Rect bounds = detectedItem.getBoundingBox();
                int tempBoundsSize = bounds.width() * bounds.height();
                if (largestSize < tempBoundsSize) {
                    largestSize = tempBoundsSize;
                    selectedItem = detectedItems.keyAt(i);
                }
            }

            return selectedItem;
        }
    };
    mBarcodeDetector.setProcessor(focusingProcessor);
}

4. CameraSourceインスタンスを生成し、startメソッドをコール

カメラの制御を隠蔽してくれるCameraSourceインスタンスを生成します。生成時にBarcodeDetectorをリンクするので、カメラからの入力映像がそのままBarcodeDetectorに渡され、逐次解析されていきます。
app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromCameraFragment.java - initCameraSource
CameraSource.Builder builder = new CameraSource.Builder(activity, mBarcodeDetector);

// previewサイズ
builder.setRequestedPreviewSize(mCameraPreview.getWidth(), mCameraPreview.getHeight());

// auto focus
SwitchCompat tempSwitch = (SwitchCompat) activity.findViewById(R.id.scAutoFocus);
builder.setAutoFocusEnabled(tempSwitch.isChecked());

// facing
tempSwitch = (SwitchCompat) activity.findViewById(R.id.scFacing);
if (tempSwitch.isChecked()) {
    builder.setFacing(CameraSource.CAMERA_FACING_FRONT);
} else {
    builder.setFacing(CameraSource.CAMERA_FACING_BACK);
}

// detect fps
try {
    EditText etDetectFps = (EditText) activity.findViewById(R.id.etDetectFps);
    float detectFps = Float.parseFloat(etDetectFps.getText().toString());
    builder.setRequestedFps(detectFps);
} catch (NumberFormatException e) {

}

mCameraSource = builder.build();

app/src/main/java/jp/eq_inc/testmobilevision/fragment/BarcodeDetectFromCameraFragment.java - start
try {
    // プレビューを表示したいSurfaceViewのSurfaceHolderを指定
    mCameraSource.start(mCameraPreview.getHolder());
    mRealPreviewSize = mCameraSource.getPreviewSize();
    ret = true;
} catch (IOException e) {
    e.printStackTrace();
}
これで、カメラの入力映像でバーコードを認識すると、Processorに指定したTrackerが動作します。
private class BarcodeTracker extends Tracker<Barcode> {
    private Paint mLinePaint;
    private boolean mVisible = false;

    public BarcodeTracker() {
        super();
        mLinePaint = new Paint();
        mLinePaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.face_line_width));
    }

    @Override
    public void onNewItem(int i, Barcode barcode) {
        LogUtil.d("", "onNewItem: " + barcode.displayValue);

        super.onNewItem(i, barcode);
        mVisible = true;
        mDrawBarcodeMap.put(this, barcode);
        mPreviewOverlay.postInvalidate();

        mLinePaint.setARGB(100, 255, 0, 0);
    }

    @Override
    public void onUpdate(Detector.Detections<Barcode> detections, Barcode barcode) {
        super.onUpdate(detections, barcode);

        mVisible = true;
        mDrawBarcodeMap.put(this, barcode);
        mPreviewOverlay.postInvalidate();
    }

    @Override
    public void onMissing(Detector.Detections<Barcode> detections) {
        if (mDrawBarcodeMap.containsKey(this)) {
            LogUtil.d("", "onMissing: " + mDrawBarcodeMap.get(this).displayValue);
        }

        super.onMissing(detections);
        mVisible = false;
        mPreviewOverlay.postInvalidate();
    }

    @Override
    public void onDone() {
        if (mDrawBarcodeMap.containsKey(this)) {
            LogUtil.d("", "onDone: " + mDrawBarcodeMap.get(this).displayValue);
        }

        super.onDone();
        mDrawBarcodeMap.remove(this);
    }
}

サンプルアプリ

ここにサンプルアプリを置きました。 試してみる場合は、以下の手順で確認してみてください。
  1. ここから環境をクローン
  2. タグ"v0.4.2"をチェックアウト
  3. ビルドしてAPKをインストール
  4. "Barcode Detect from Photo"または"Barcode Detect from Movie"または"Barcode Detect from Camera"を選択
    • "Barcode Detect from Photo"を選択した場合:
    • デバイスに格納されている静止画が表示されるので、バーコード認識したい静止画をタッチ
    • "Barcode Detect from Movie"を選択した場合:
    • デバイスに格納されている動画が表示されるので、バーコード認識したい動画をタッチ
      タッチすると、動画を1秒間隔のフレームに分割して表示するので、バーコード認識したいフレームをタッチ
    • "Barcode Detect from Camera"を選択した場合:
    • カメラプレビューが表示されるので、バーコードを表示

参考サイト

Barcode API Overview

コメントの投稿