I am a beginner developer. I'm sorry for my english. I am trying to make a barcode reader application. I use MLKit and CameraX. I want to analyze only the part of the preview that is in the rectangle. Now the preview is being analyzed in full. I only want to analize what is in the rectangle. I tried to use the ViewPort, but it seems I didn't quite understand what it was for, because it could not solve the problem. I looked for solutions on the Internet, but my problem remained relevant. I think that before analysis it is necessary to crop the image and only then analyze it, but is this true? My layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:background="@android:color/white"
tools:context=".ui.BarcodeScanningFragment">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.t_ovchinnikova.android.scandroid_2.ui.ViewFinderOverlay
android:id="@+id/overlay"
android:layerType="software"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Camera class:
class CameraSource (private val overlay: ViewFinderOverlay) {
private lateinit var cameraExecutor: ExecutorService
val preview : Preview = Preview.Builder()
.build()
fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(overlay.context)
cameraProviderFuture.addListener(Runnable {
//Используется для привязки жизненного цикла камер к владельцу жизненного цикла
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
bindCameraUseCases(cameraProvider)
}, ContextCompat.getMainExecutor(overlay.context))
}
@SuppressLint("UnsafeOptInUsageError")
private fun bindCameraUseCases(cameraProvider: ProcessCameraProvider) {
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(overlay.width, overlay.height))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
val orientationEventListener = object : OrientationEventListener(overlay.context) {
override fun onOrientationChanged(orientation : Int) {
// Monitors orientation values to determine the target rotation value
val rotation : Int = when (orientation) {
in 45..134 -> Surface.ROTATION_270
in 135..224 -> Surface.ROTATION_180
in 225..314 -> Surface.ROTATION_90
else -> Surface.ROTATION_0
}
imageAnalysis.targetRotation = rotation
}
}
orientationEventListener.enable()
cameraExecutor = Executors.newSingleThreadExecutor()
val analyzer = BarcodeAnalyzer()
imageAnalysis.setAnalyzer(cameraExecutor, analyzer)
var cameraSelector : CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val useCaseGroup = UseCaseGroup.Builder()
.addUseCase(preview)
.addUseCase(imageAnalysis)
.build()
cameraProvider.bindToLifecycle(overlay.context as LifecycleOwner, cameraSelector, useCaseGroup)
}
}
class ViewFinderOverlay:
class ViewFinderOverlay(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val boxPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_stroke)
style = Paint.Style.STROKE
strokeWidth = context.resources.getDimensionPixelOffset(R.dimen.barcode_stroke_width).toFloat()
}
private val scrimPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_background)
}
private val eraserPaint: Paint = Paint().apply {
strokeWidth = boxPaint.strokeWidth
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private val boxCornerRadius: Float =
context.resources.getDimensionPixelOffset(R.dimen.barcode_reticle_corner_radius).toFloat()
var boxRect: RectF? = null
fun setViewFinder() {
val overlayWidth = width.toFloat()
val overlayHeight = height.toFloat()
val boxWidth = overlayWidth * 80 /100
val boxHeight = overlayHeight * 36 / 100
val cx = overlayWidth / 2
val cy = overlayHeight / 2
boxRect = RectF(cx - boxWidth / 2, cy - boxHeight / 2, cx + boxWidth / 2, cy + boxHeight / 2)
invalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
boxRect?.let {
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), scrimPaint)
eraserPaint.style = Paint.Style.FILL
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
eraserPaint.style = Paint.Style.STROKE
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
// Draws the box.
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, boxPaint)
}
}
}
ViewFinderOverlay - a view that is superimposed on the camera preview. Screen: [1]: https://i.stack.imgur.com/cYvFP.jpg
Image analyzer:
@SuppressLint("UnsafeOptInUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val scanner = BarcodeScanning.getClient()
scanner.process(image)
.addOnSuccessListener { barcodes ->
barcodes?.firstOrNull().let { barcode ->
val rawValue = barcode?.rawValue
rawValue?.let {
}
}
}
imageProxy.close()
}
}
Help me please. I would be glad to any advice.