Android ML Kit 仅在屏幕中央扫描二维码
Android MLKit scan QRcode only on center of screen
我实现了 firebase MLKit 来扫描 QRCode,它正在扫描,但它正在扫描屏幕上的所有 QRcode。
我只需要扫描中心捕获的二维码(中心有箭头 ImageView),我该怎么做?
我尝试在 Analisys 函数上裁剪(在 analysisUseCase?.setAnalyzer 内)
imageProxy.cropRect()
我尝试在 processImageProxy 函数上裁剪。
但是没有成功,我认为我不能用这个
class QrcodeScanner(
private val onQrCapture: Barcode.() -> Unit,
private val onFailure: Throwable.() -> Unit,
private val lifecycleOwner: LifecycleOwner,
private val context: Context,
private val previewView: PreviewView
) {
private var cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
private var cameraProvider: ProcessCameraProvider? = null
private var previewUseCase: Preview? = null
private var analysisUseCase: ImageAnalysis? = null
fun startCamera() {
val cameraProviderFuture =
ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener(
{
runCatching {
val provider = cameraProviderFuture.get()
cameraProvider = provider
startPreview()
startAnalysis()
}.onFailure {
onFailure(it)
}
},
ContextCompat.getMainExecutor(context)
)
}
private fun startPreview() {
if (previewUseCase != null) {
cameraProvider?.unbind(previewUseCase)
}
previewUseCase = Preview.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
previewUseCase?.setSurfaceProvider(previewView.surfaceProvider)
runCatching {
cameraProvider?.bindToLifecycle(lifecycleOwner,
cameraSelector,
previewUseCase
)
}.onFailure {
onFailure(it)
}
}
private fun startAnalysis() {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
val barcodeScanner: BarcodeScanner = BarcodeScanning.getClient(options)
if (cameraProvider == null) {
return
}
if (analysisUseCase != null) {
cameraProvider?.unbind(analysisUseCase)
}
analysisUseCase = ImageAnalysis.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
val cameraExecutor = Executors.newSingleThreadExecutor()
analysisUseCase?.setAnalyzer(cameraExecutor, ImageAnalysis.Analyzer { imageProxy ->
processImageProxy(barcodeScanner, imageProxy)
})
runCatching {
cameraProvider?.bindToLifecycle(lifecycleOwner,
cameraSelector,
analysisUseCase
)
}.onFailure {
onFailure(it)
}
}
@SuppressLint("UnsafeExperimentalUsageError")
private fun processImageProxy(
barcodeScanner: BarcodeScanner,
imageProxy: ImageProxy
) {
runCatching {
val img = imageProxy.image
if (img != null) {
val inputImage =
InputImage.fromMediaImage(img, imageProxy.imageInfo.rotationDegrees)
barcodeScanner.process(inputImage)
.addOnSuccessListener { barcodes ->
barcodes.forEach {
onQrCapture(it)
}
}
.addOnFailureListener {
onFailure(it)
}.addOnCompleteListener {
imageProxy.close()
}
} else {
throw Exception("Falha ao processar a imagem")
}
}.onFailure {
onFailure(it)
}
}
}
imageProxy.cropRect() 只在图片中放入元数据,不做裁剪操作。对于静态图像,您可以将其转换为位图并进行裁剪。更好的方法是在所有返回的条码都带有检测到的条码边界框时过滤掉结果。
在 MLKit 中,我们正在添加对图像裁剪的支持。
您可以在BarcodeScannerProcessor
中修改onSuccess()
class来查看条形码的四个角位置。如果在扫描区域内,return true from onSuccess()
,如下:
protected void onSuccess(List<Barcode> barcodes, GraphicOverlay graphicOverlay) {
if (barcodes.isEmpty()) {
Log.v(MANUAL_TESTING_LOG, "No barcode has been detected");
}
for (int i = 0; i < barcodes.size(); ++i) {
Barcode barcode = barcodes.get(i);
BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode);
// check whether within valid scan area, translateX and translateY is to convert to screen position in pixel
if (barcode.getBoundingBox() != null &&
withinScanArea(
barcodeGraphic.translateY(barcode.getBoundingBox().top),
barcodeGraphic.translateX(barcode.getBoundingBox().left),
barcodeGraphic.translateY(barcode.getBoundingBox().bottom),
barcodeGraphic.translateX(barcode.getBoundingBox().right)
)
) {
graphicOverlay.add(barcodeGraphic);
if (barcode.getRawValue() != null && !barcode.getRawValue().isEmpty()) {
exchangeScannedData.sendScannedCode(barcode.getRawValue());
}
}
}
}
scanAreaTop
等是根据您的扫描视图取值的局部变量
private boolean withinScanArea(float top, float left, float bottom, float right) {
return top > scanAreaTop &&
left > scanAreaLeft &&
bottom < scanAreaBottom &&
right < scanAreaRight;
}
我实现了 firebase MLKit 来扫描 QRCode,它正在扫描,但它正在扫描屏幕上的所有 QRcode。 我只需要扫描中心捕获的二维码(中心有箭头 ImageView),我该怎么做?
我尝试在 Analisys 函数上裁剪(在 analysisUseCase?.setAnalyzer 内)
imageProxy.cropRect()
我尝试在 processImageProxy 函数上裁剪。 但是没有成功,我认为我不能用这个
class QrcodeScanner(
private val onQrCapture: Barcode.() -> Unit,
private val onFailure: Throwable.() -> Unit,
private val lifecycleOwner: LifecycleOwner,
private val context: Context,
private val previewView: PreviewView
) {
private var cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
private var cameraProvider: ProcessCameraProvider? = null
private var previewUseCase: Preview? = null
private var analysisUseCase: ImageAnalysis? = null
fun startCamera() {
val cameraProviderFuture =
ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener(
{
runCatching {
val provider = cameraProviderFuture.get()
cameraProvider = provider
startPreview()
startAnalysis()
}.onFailure {
onFailure(it)
}
},
ContextCompat.getMainExecutor(context)
)
}
private fun startPreview() {
if (previewUseCase != null) {
cameraProvider?.unbind(previewUseCase)
}
previewUseCase = Preview.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
previewUseCase?.setSurfaceProvider(previewView.surfaceProvider)
runCatching {
cameraProvider?.bindToLifecycle(lifecycleOwner,
cameraSelector,
previewUseCase
)
}.onFailure {
onFailure(it)
}
}
private fun startAnalysis() {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
val barcodeScanner: BarcodeScanner = BarcodeScanning.getClient(options)
if (cameraProvider == null) {
return
}
if (analysisUseCase != null) {
cameraProvider?.unbind(analysisUseCase)
}
analysisUseCase = ImageAnalysis.Builder()
.setTargetRotation(previewView.display.rotation)
.build()
val cameraExecutor = Executors.newSingleThreadExecutor()
analysisUseCase?.setAnalyzer(cameraExecutor, ImageAnalysis.Analyzer { imageProxy ->
processImageProxy(barcodeScanner, imageProxy)
})
runCatching {
cameraProvider?.bindToLifecycle(lifecycleOwner,
cameraSelector,
analysisUseCase
)
}.onFailure {
onFailure(it)
}
}
@SuppressLint("UnsafeExperimentalUsageError")
private fun processImageProxy(
barcodeScanner: BarcodeScanner,
imageProxy: ImageProxy
) {
runCatching {
val img = imageProxy.image
if (img != null) {
val inputImage =
InputImage.fromMediaImage(img, imageProxy.imageInfo.rotationDegrees)
barcodeScanner.process(inputImage)
.addOnSuccessListener { barcodes ->
barcodes.forEach {
onQrCapture(it)
}
}
.addOnFailureListener {
onFailure(it)
}.addOnCompleteListener {
imageProxy.close()
}
} else {
throw Exception("Falha ao processar a imagem")
}
}.onFailure {
onFailure(it)
}
}
}
imageProxy.cropRect() 只在图片中放入元数据,不做裁剪操作。对于静态图像,您可以将其转换为位图并进行裁剪。更好的方法是在所有返回的条码都带有检测到的条码边界框时过滤掉结果。
在 MLKit 中,我们正在添加对图像裁剪的支持。
您可以在BarcodeScannerProcessor
中修改onSuccess()
class来查看条形码的四个角位置。如果在扫描区域内,return true from onSuccess()
,如下:
protected void onSuccess(List<Barcode> barcodes, GraphicOverlay graphicOverlay) {
if (barcodes.isEmpty()) {
Log.v(MANUAL_TESTING_LOG, "No barcode has been detected");
}
for (int i = 0; i < barcodes.size(); ++i) {
Barcode barcode = barcodes.get(i);
BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode);
// check whether within valid scan area, translateX and translateY is to convert to screen position in pixel
if (barcode.getBoundingBox() != null &&
withinScanArea(
barcodeGraphic.translateY(barcode.getBoundingBox().top),
barcodeGraphic.translateX(barcode.getBoundingBox().left),
barcodeGraphic.translateY(barcode.getBoundingBox().bottom),
barcodeGraphic.translateX(barcode.getBoundingBox().right)
)
) {
graphicOverlay.add(barcodeGraphic);
if (barcode.getRawValue() != null && !barcode.getRawValue().isEmpty()) {
exchangeScannedData.sendScannedCode(barcode.getRawValue());
}
}
}
}
scanAreaTop
等是根据您的扫描视图取值的局部变量
private boolean withinScanArea(float top, float left, float bottom, float right) {
return top > scanAreaTop &&
left > scanAreaLeft &&
bottom < scanAreaBottom &&
right < scanAreaRight;
}