我正在使用 google MLkit 条码扫描仪和cameraX api 开发一个简单的条码扫描仪。但边界框没有正确绘制在条形码上。我已经尝试了很多东西,例如变换坐标等,但没有用:
附上图片。
代码:
条形码扫描仪活动
public class BarcodeScannerActivity extends AppCompatActivity {
private static final int REQUEST_CAMERA_PERMISSION = 1001;
private ExecutorService cameraExecutor;
private static final String TAG = "BarcodeScannerActivity";
private int currentOrientation = 0; // 0: portrait, 1: landscape
private BarcodeOverlay barcodeOverlay;
private PreviewView previewView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_scanner);
previewView = findViewById(R.id.previewView);
barcodeOverlay = findViewById(R.id.barcodeOverlay);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCamera();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
}
cameraExecutor = Executors.newSingleThreadExecutor();
// Set up orientation listener
OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
@Override
public void onOrientationChanged(int orientation) {
// Update orientation state based on the device's orientation
if ((orientation >= 0 && orientation < 45) || (orientation >= 315)) {
currentOrientation = 0; // Portrait
} else if (orientation >= 135 && orientation < 225) {
currentOrientation = 1; // Landscape
}
}
};
orientationEventListener.enable();
}
private void startCamera() {
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindCameraUseCases(cameraProvider);
} catch (Exception e) {
Log.e(TAG, "Camera provider failed.", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void bindCameraUseCases(@NonNull ProcessCameraProvider cameraProvider) {
// PreviewView previewView = findViewById(R.id.previewView);
// Camera selector for the back camera
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
// Preview use case
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// Image analysis use case
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
.build();
BarcodeScanner scanner = BarcodeScanning.getClient(options);
imageAnalysis.setAnalyzer(cameraExecutor, image -> processImageProxy(scanner, image));
// Bind to lifecycle
cameraProvider.bindToLifecycle(
(LifecycleOwner) this,
cameraSelector,
preview,
imageAnalysis
);
}
private void processImageProxy(BarcodeScanner scanner, ImageProxy imageProxy) {
@SuppressWarnings("UnsafeOptInUsageError")
InputImage image = InputImage.fromMediaImage(imageProxy.getImage(), imageProxy.getImageInfo().getRotationDegrees());
scanner.process(image)
.addOnSuccessListener(barcodes -> {
if (!barcodes.isEmpty()) {
// Map all barcode bounding boxes to the view coordinates
List<Rect> mappedBoundingBoxes = new ArrayList<>();
for (com.google.mlkit.vision.barcode.common.Barcode barcode : barcodes) {
Rect boundingBox = barcode.getBoundingBox();
if (boundingBox != null) {
Rect rect = mapBoundingBoxToView(boundingBox, imageProxy);
mappedBoundingBoxes.add(rect);
}
}
// Update the overlay with all mapped bounding boxes
barcodeOverlay.updateBarcodes(mappedBoundingBoxes);
}
})
.addOnFailureListener(e -> Log.e(TAG, "Barcode detection failed.", e))
.addOnCompleteListener(task -> imageProxy.close());
}
public Rect mapBoundingBoxToView(Rect boundingBox, ImageProxy imageProxy) {
int imageWidth = imageProxy.getWidth();
int imageHeight = imageProxy.getHeight();
int viewWidth = previewView.getWidth();
int viewHeight = previewView.getHeight();
float scaleX = (float) viewWidth / imageHeight;
float scaleY = (float) viewHeight / imageWidth;
float offsetX = (viewWidth - imageHeight * scaleX) / 2;
float offsetY = (viewHeight - imageWidth * scaleY) / 2;
int left = (int) (boundingBox.left * scaleX + offsetX);
int top = (int) (boundingBox.top * scaleY + offsetY);
int right = (int) (boundingBox.right * scaleX + offsetX);
int bottom = (int) (boundingBox.bottom * scaleY + offsetY);
return new Rect(left, top, right, bottom);
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraExecutor.shutdown();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startCamera();
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
}
}
}
}
BarcodeOverlay 类
public class BarcodeOverlay extends View {
private final Paint paint = new Paint();
private List<Rect> barcodeRects = new ArrayList<>();
public BarcodeOverlay(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5f);
}
public void updateBarcodes(List<Rect> newBarcodeRects) {
barcodeRects = newBarcodeRects;
invalidate(); // Trigger a redraw
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Rect rect : barcodeRects) {
canvas.drawRect(rect, paint);
}
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<com.invenits.barcodescannerinvenits.BarcodeOverlay
android:id="@+id/barcodeOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
请帮忙!
我想扫描条形码并绘制边界框,但我的框没有正确绘制在条形码上。
相机馈送和叠加之间的坐标映射可能很棘手。虽然您可以调整
mapBoundingBoxToView
逻辑,但如果这是用于生产应用程序,您可能需要考虑使用商业解决方案。例如,Scanbot SDK 提供开箱即用的边界框覆盖,这可以节省您调试这些坐标映射问题的时间。您可以在 https://docs.scanbot.io/barcode-scanner-sdk/android/barcode-scanner/ready-to-use-ui/#ar-overlay 查看他们的文档(完整披露:我与扫描机器人)。