你好我正在使用没有 firebase 和 androidx 的 android mlkit 条码扫描器,我遵循这个代码https://medium.com/@surya.n1447/google-vision-ml-kit-with-camerax-64bbbfd4c6fd 我扫描二维码的时候太慢了,我不知道如何提高扫描速度,有没有什么技巧之类的?还是改用 zxing 或 Google vision 更好? 我用的是小米 10 t pro
ScanPersonFragment 类:片段(){
private var processingBarcode = AtomicBoolean(false)
private var mediaPlayer: MediaPlayer? = null
private lateinit var cameraExecutor: ExecutorService
private lateinit var scanBarcodeViewModel: ScanPersonViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
cameraExecutor = Executors.newSingleThreadExecutor()
scanBarcodeViewModel = ViewModelProvider(this).get(ScanPersonViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.fragment_scan_person_destination, container, false)
mediaPlayer = MediaPlayer.create(context, R.raw.beep)
scanBarcodeViewModel.progressState.observe(viewLifecycleOwner, {
v.fragment_scan_person_barcode_progress_bar.visibility = if (it) View.VISIBLE else View.GONE
})
scanBarcodeViewModel.navigation.observe(viewLifecycleOwner, { navDirections ->
navDirections?.let {
findNavController().navigate(navDirections)
scanBarcodeViewModel.doneNavigating()
}
})
return v
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (allPermissionsGranted()) {
startCamera()
} else {
requestPermissions(
REQUIRED_PERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
}
}
override fun onResume() {
super.onResume()
processingBarcode.set(false)
}
private fun startCamera() {
// Create an instance of the ProcessCameraProvider,
// which will be used to bind the use cases to a lifecycle owner.
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
//.setTargetResolution(Size(400, 400))
.build()
// Add a listener to the cameraProviderFuture.
// The first argument is a Runnable, which will be where the magic actually happens.
// The second argument (way down below) is an Executor that runs on the main thread.
cameraProviderFuture.addListener({
// Add a ProcessCameraProvider, which binds the lifecycle of your camera to
// the LifecycleOwner within the application's life.
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Initialize the Preview object, get a surface provider from your PreviewView,
// and set it on the preview instance.
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(
fragment_scan_person_barcode_preview_view.surfaceProvider
)
}
// Setup the ImageAnalyzer for the ImageAnalysis use case
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
.also {
it.setAnalyzer(cameraExecutor, BarcodeAnalyzer { barcode ->
if (processingBarcode.compareAndSet(false, true)) {
mediaPlayer?.start()
searchBarcode(barcode)
}
})
}
// Select back camera
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind any bound use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to lifecycleOwner
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
} catch (e: Exception) {
Log.e("PreviewUseCase", "Binding failed! :(", e)
}
}, ContextCompat.getMainExecutor(requireContext()))
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it
) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(
requireContext(),
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
).show()
}
}
}
private fun searchBarcode(barcode: String) {
scanBarcodeViewModel.searchBarcode(barcode)
}
override fun onDestroy() {
cameraExecutor.shutdown()
super.onDestroy()
}
companion object {
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
private const val REQUEST_CODE_PERMISSIONS = 10
}
类 BarcodeAnalyzer(私有 val barcodeListener:BarcodeListener):ImageAnalysis.Analyzer {
@SuppressLint("UnsafeExperimentalUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val options = BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build()
val scanner = BarcodeScanning.getClient(options)
// Pass image to the scanner and have it do its thing
scanner.process(image)
.addOnSuccessListener { barcodes ->
// Task completed successfully
for (barcode in barcodes) {
barcodeListener(barcode.rawValue ?: "")
}
}
.addOnFailureListener {
// You should really do something about Exceptions
}
.addOnCompleteListener {
// It's important to close the imageProxy
imageProxy.close()
}
}
}
}
总结一些答案:
对于非常大的图像,比如 108 兆像素的相机,降低分辨率很有帮助。对于典型用途,我们发现 1280x720 或 1920x1080 分辨率就足够了。
近期尝试使用条码模型V2的“捆绑”版条码SDK:
implementation 'com.google.mlkit:barcode-scanning:16.1.0'
Barcode V2 实施速度更快、更准确,但作为“捆绑”模型,它会增加大约 2.2 MB 的应用程序大小。团队致力于将其引入 Google Play 服务版本(即非捆绑),并在未来几个月内消除应用程序捆绑 2.2 MB 模型的需要。
有关捆绑版本和非捆绑版本之间的更多信息,请查看本页顶部的表格。
首先将图像代理转换为位图。 其次不要忘记关闭图像代理。然后它会提高性能。
if (image.image != null && image.format == ImageFormat.YUV_420_888) {
scanner.process(InputImage.fromBitmap(image.toBitmap(), image.imageInfo.rotationDegrees))
.addOnSuccessListener { barcodes ->
if (barcodes.isNotEmpty()) {
if (firstCall){
for (barcode in barcodes) {
// Handle received barcodes...
Log.d("ValueXXXX ",barcode.format.toString())
if (barcode.format==Barcode.FORMAT_QR_CODE){
var data = barcode.displayValue.toString().replace("\\", "")
//var json=org.json.JSONObject(data)
Log.d("TAG","DATA1111 "+data)
if(Utils.urlPattern.matcher(data).matches()) {
firstCall=false
showPopupForInvalidQRCode(R.string.DISPLAY_INVALID_QR_CODE,context)
break
}
if (!data.isNullOrEmpty() && Utils.isJSONValid(data)){
firstCall=false
try {
val json = JSONObject(data)
Log.d("TAG","json "+json)
val vibrator = context?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(VibrationEffect.createOneShot(300, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(300)
}
//parse your json here
break
}catch (e:Exception){
e.printStackTrace()
showPopupForInvalidQRCode(R.string.DISPLAY_INVALID_QR_CODE,context)
}
}
break
}
}
}
} else {
// Remove bounding rect
barcodeBoxView.setRect(RectF())
}
}
.addOnFailureListener {
firstCall=true
Log.d("EXCEPTION",it.message.toString())
//image.close()
}
}
}
这对我来说很重要!
请注意在成功或失败后关闭 ImageProxy 和 Image否则代码将只运行一次,它只触发一次而忘记。
image.image?.close()
image.close()