From 024129c0835e83941faa06fc1c85545c06eb3c7d Mon Sep 17 00:00:00 2001 From: Almie Date: Fri, 19 Jul 2024 15:51:20 -0700 Subject: [PATCH 1/6] fixing preview aspect ratio and alignment calculation --- .../camerawesome/cameraX/CameraAwesomeX.kt | 2 +- .../camerawesome/cameraX/CameraXState.kt | 74 +++++++++++++++---- .../cameraX/ImageAnalysisBuilder.kt | 31 +++++++- example/lib/preview_overlay_example.dart | 1 + .../lib/widgets/barcode_preview_overlay.dart | 10 +-- .../analysis/analysis_to_image.dart | 29 +++++--- .../widgets/preview/awesome_preview_fit.dart | 32 +++++--- 7 files changed, 136 insertions(+), 43 deletions(-) diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt index 1968f4af..0dc1ea49 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt @@ -190,7 +190,7 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { cameraState.apply { try { imageAnalysisBuilder = ImageAnalysisBuilder.configure( - aspectRatio ?: AspectRatio.RATIO_4_3, + aspectRatio ?: aspectRatio!!, when (format.uppercase()) { "YUV_420" -> OutputImageFormat.YUV_420_888 "NV21" -> OutputImageFormat.NV21 diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt index efd01954..d030331f 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt @@ -2,6 +2,7 @@ package com.apparence.camerawesome.cameraX import android.annotation.SuppressLint import android.app.Activity +import android.graphics.ImageFormat import android.hardware.camera2.CameraCharacteristics import android.util.Log import android.util.Rational @@ -11,6 +12,8 @@ import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat import androidx.camera.camera2.internal.compat.quirk.CamcorderProfileResolutionQuirk import androidx.camera.camera2.interop.Camera2CameraInfo import androidx.camera.core.* +import androidx.camera.core.resolutionselector.AspectRatioStrategy +import androidx.camera.core.resolutionselector.ResolutionSelector import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.video.* import androidx.core.content.ContextCompat @@ -89,6 +92,7 @@ data class CameraXState( @SuppressLint("RestrictedApi", "UnsafeOptInUsageError") fun updateLifecycle(activity: Activity) { previews = mutableListOf() + Log.d("KOTLIN", "Aspect ratio: $aspectRatio") imageCaptures.clear() videoCaptures.clear() if (cameraProvider.isMultiCamSupported() && sensors.size > 1) { @@ -125,13 +129,20 @@ data class CameraXState( // }) // .build() - - val preview = if (aspectRatio != null) { - Preview.Builder().setTargetAspectRatio(aspectRatio!!) - .setCameraSelector(cameraSelector).build() + val resolutionSelector = if (aspectRatio != null){ + ResolutionSelector.Builder() + .setAspectRatioStrategy( + AspectRatioStrategy(aspectRatio!!, AspectRatioStrategy.FALLBACK_RULE_AUTO) + ) + .build() } else { - Preview.Builder().setCameraSelector(cameraSelector).build() + ResolutionSelector.Builder() + .setAllowedResolutionMode(ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION) + .build() } + val preview = Preview.Builder() + .setResolutionSelector(resolutionSelector) + .setCameraSelector(cameraSelector).build() preview.setSurfaceProvider( surfaceProvider(executor(activity), sensor.deviceId ?: "$index") ) @@ -144,7 +155,12 @@ data class CameraXState( .apply { //photoSize?.let { setTargetResolution(it) } if (rational.denominator != rational.numerator) { - setTargetAspectRatio(aspectRatio ?: AspectRatio.RATIO_4_3) + val resolutionSelector = ResolutionSelector.Builder(). + setAspectRatioStrategy( + AspectRatioStrategy(aspectRatio ?: AspectRatio.RATIO_4_3, AspectRatioStrategy.FALLBACK_RULE_AUTO) + ) + .build() + setResolutionSelector(resolutionSelector) } setFlashMode( @@ -196,13 +212,20 @@ data class CameraXState( if (sensors.first().position == PigeonSensorPosition.FRONT) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA // Preview if (currentCaptureMode != CaptureModes.ANALYSIS_ONLY) { + val resolutionSelector = if (aspectRatio != null){ + ResolutionSelector.Builder() + .setAspectRatioStrategy( + AspectRatioStrategy(aspectRatio!!, AspectRatioStrategy.FALLBACK_RULE_AUTO) + ) + .build() + } else { + ResolutionSelector.Builder() + .setAllowedResolutionMode(ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION) + .build() + } previews!!.add( - if (aspectRatio != null) { - Preview.Builder().setTargetAspectRatio(aspectRatio!!) - .setCameraSelector(cameraSelector).build() - } else { - Preview.Builder().setCameraSelector(cameraSelector).build() - } + Preview.Builder().setResolutionSelector(resolutionSelector) + .setCameraSelector(cameraSelector).build() ) previews!!.first().setSurfaceProvider( @@ -217,7 +240,12 @@ data class CameraXState( .apply { //photoSize?.let { setTargetResolution(it) } if (rational.denominator != rational.numerator) { - setTargetAspectRatio(aspectRatio ?: AspectRatio.RATIO_4_3) + val resolutionSelector = ResolutionSelector.Builder(). + setAspectRatioStrategy( + AspectRatioStrategy(aspectRatio ?: AspectRatio.RATIO_4_3, AspectRatioStrategy.FALLBACK_RULE_AUTO) + ) + .build() + setResolutionSelector(resolutionSelector) } setFlashMode( when (flashMode) { @@ -260,12 +288,27 @@ data class CameraXState( .build() concurrentCamera = null + val useCaseGroup : UseCaseGroup = useCaseGroupBuilder.build() previewCamera = cameraProvider.bindToLifecycle( activity as LifecycleOwner, cameraSelector, - useCaseGroupBuilder.build(), + useCaseGroup, ) previewCamera!!.cameraControl.enableTorch(flashMode == FlashMode.ALWAYS) + useCaseGroup.getUseCases()?.forEach{ + Log.d("KOTLIN", "Use case ${it.javaClass.kotlin} surface resolution: ${it.attachedSurfaceResolution}") + } + + } + val characteristics = CameraCharacteristicsCompat.toCameraCharacteristicsCompat( + Camera2CameraInfo.extractCameraCharacteristics(previewCamera!!.getCameraInfo()), + Camera2CameraInfo.from(previewCamera!!.getCameraInfo()).cameraId + ) + val streamConfigMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + val resolutions = streamConfigMap?.getOutputSizes(ImageFormat.YUV_420_888) + val previewSizesList = previewSizes() + resolutions?.forEach{ + Log.d("KOTLIN", "Preview available size: $it") } } @@ -442,5 +485,8 @@ data class CameraXState( "RATIO_1_1" -> Rational(1, 1) else -> Rational(3, 4) } + if (imageAnalysisBuilder != null){ + imageAnalysisBuilder!!.updateAspectRatio(newAspectRatio) + } } } \ No newline at end of file diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt index 15f012b1..adff090e 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt @@ -3,10 +3,15 @@ package com.apparence.camerawesome.cameraX import android.annotation.SuppressLint import android.graphics.Rect import android.util.Size +import android.util.Log import androidx.camera.core.AspectRatio import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy +import androidx.camera.core.resolutionselector.AspectRatioStrategy +import androidx.camera.core.resolutionselector.ResolutionSelector +import androidx.camera.core.resolutionselector.ResolutionStrategy import androidx.camera.core.internal.utils.ImageUtil +import android.hardware.camera2.params.StreamConfigurationMap import com.apparence.camerawesome.utils.ResettableCountDownLatch import io.flutter.plugin.common.EventChannel import kotlinx.coroutines.* @@ -21,7 +26,8 @@ enum class OutputImageFormat { class ImageAnalysisBuilder private constructor( private val format: OutputImageFormat, private val width: Int, - private val height: Int, + private var height: Int, + private var aspectRatio: Int, private val executor: Executor, var previewStreamSink: EventChannel.EventSink? = null, private val maxFramesPerSecond: Double?, @@ -48,12 +54,14 @@ class ImageAnalysisBuilder private constructor( AspectRatio.RATIO_4_3 -> 4f / 3 else -> 16f / 9 } + Log.d("KOTLIN", "Analysis Aspect Ratio: $aspectRatio ($analysisAspectRatio)") val height = widthOrDefault * (1 / analysisAspectRatio) val maxFps = if (maxFramesPerSecond == 0.0) null else maxFramesPerSecond return ImageAnalysisBuilder( format, widthOrDefault, height.toInt(), + aspectRatio, executor, maxFramesPerSecond = maxFps, ) @@ -63,7 +71,16 @@ class ImageAnalysisBuilder private constructor( @SuppressLint("RestrictedApi") fun build(): ImageAnalysis { countDownLatch.reset() - val imageAnalysis = ImageAnalysis.Builder().setTargetResolution(Size(width, height)) + Log.d("KOTLIN", "Building image analysis at target resolution ${width}x${height}") + val imageAnalysisResolutionSelector = ResolutionSelector.Builder() + .setAspectRatioStrategy( + AspectRatioStrategy(aspectRatio, AspectRatioStrategy.FALLBACK_RULE_AUTO) + ) + .setResolutionStrategy( + ResolutionStrategy(Size(width, height), ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER) + ) + .build() + val imageAnalysis = ImageAnalysis.Builder().setResolutionSelector(imageAnalysisResolutionSelector) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888).build() imageAnalysis.setAnalyzer(Dispatchers.IO.asExecutor()) { imageProxy -> @@ -120,6 +137,16 @@ class ImageAnalysisBuilder private constructor( return imageAnalysis } + fun updateAspectRatio(newAspectRatio: String){ + aspectRatio = if (newAspectRatio == "RATIO_16_9") 1 else 0 + val analysisAspectRatio = when (aspectRatio) { + AspectRatio.RATIO_4_3 -> 4f / 3 + else -> 16f / 9 + } + Log.d("KOTLIN", "Analysis Aspect Ratio: $aspectRatio ($analysisAspectRatio)") + height = (width * (1 / analysisAspectRatio)).toInt() + } + private fun cropRect(imageProxy: ImageProxy): Map { return mapOf( "left" to imageProxy.cropRect.left, diff --git a/example/lib/preview_overlay_example.dart b/example/lib/preview_overlay_example.dart index c9e23d93..2e2e4e80 100644 --- a/example/lib/preview_overlay_example.dart +++ b/example/lib/preview_overlay_example.dart @@ -48,6 +48,7 @@ class _CameraPageState extends State { aspectRatio: CameraAspectRatios.ratio_16_9, ), previewFit: CameraPreviewFit.fitWidth, + previewAlignment: Alignment.center, onMediaTap: (mediaCapture) { mediaCapture.captureRequest .when(single: (single) => single.file?.open()); diff --git a/example/lib/widgets/barcode_preview_overlay.dart b/example/lib/widgets/barcode_preview_overlay.dart index a54bd90e..33ae4864 100644 --- a/example/lib/widgets/barcode_preview_overlay.dart +++ b/example/lib/widgets/barcode_preview_overlay.dart @@ -64,7 +64,7 @@ class _BarcodePreviewOverlayState extends State { // including the clipping that may be needed to respect the current // aspectRatio. _scanArea = Rect.fromCenter( - center: widget.preview.rect.center, + center: widget.preview.rect.center + widget.preview.offset, // In this example, we want the barcode scan area to be a fraction // of the preview that is seen by the user, so we use previewRect width: widget.preview.rect.width * 0.7, @@ -152,6 +152,9 @@ class _BarcodePreviewOverlayState extends State { bottomRightOffset.toOffset(), img, ); + debugPrint( + 'BARCODE $topLeftOffset $bottomRightOffset, $topLeftOff, $bottomRightOff'); + debugPrint('SCAN AREA ${widget.preview.rect.top}'); _barcodeRect = Rect.fromLTRB( topLeftOff.dx, @@ -163,10 +166,7 @@ class _BarcodePreviewOverlayState extends State { // Approximately detect if the barcode is in the scan area by checking // if the center of the barcode is in the scan area. if (_scanArea.contains( - _barcodeRect!.center.translate( - (_screenSize.width - widget.preview.previewSize.width) / 2, - (_screenSize.height - widget.preview.previewSize.height) / 2, - ), + _barcodeRect!.center, )) { // Note: for a better detection, you should calculate the area of the // intersection between the barcode and the scan area and compare it diff --git a/lib/src/orchestrator/analysis/analysis_to_image.dart b/lib/src/orchestrator/analysis/analysis_to_image.dart index c7d76dce..23cfecad 100644 --- a/lib/src/orchestrator/analysis/analysis_to_image.dart +++ b/lib/src/orchestrator/analysis/analysis_to_image.dart @@ -41,27 +41,32 @@ class Preview { }) { num imageDiffX; num imageDiffY; + num imgToNativeScaleX; + num imgToNativeScaleY; final shouldflipXY = flipXY ?? img.flipXY(); if (Platform.isIOS) { imageDiffX = img.size.width - img.croppedSize.width; imageDiffY = img.size.height - img.croppedSize.height; + imgToNativeScaleX = nativePreviewSize.width / img.croppedSize.width; + imgToNativeScaleY = nativePreviewSize.height / img.croppedSize.height; } else { // Width and height are inverted on Android imageDiffX = img.size.height - img.croppedSize.width; imageDiffY = img.size.width - img.croppedSize.height; + imgToNativeScaleX = nativePreviewSize.width / img.croppedSize.width; + imgToNativeScaleY = nativePreviewSize.height / img.croppedSize.height; } - var offset = (Offset( - (shouldflipXY ? point.dy : point.dx).toDouble() - - (imageDiffX / 2), - (shouldflipXY ? point.dx : point.dy).toDouble() - - (imageDiffY / 2), - ) * - scale) - .translate( - // If screenSize is bigger than croppedSize, move the element to half the difference - (previewSize.width - (img.croppedSize.width * scale)) / 2, - (previewSize.height - (img.croppedSize.height * scale)) / 2, - ); + debugPrint('IMAGE DIFF X: $imageDiffX Y: $imageDiffY'); + debugPrint('PREVIEW SCALE: $scale $previewSize $nativePreviewSize'); + debugPrint( + 'IMAGE TO NATIVE SCALE X: $imgToNativeScaleX, Y: $imgToNativeScaleY'); + debugPrint('PREVIEW OFFSET: ${this.offset.dx}x${this.offset.dy}'); + var offset = Offset( + (shouldflipXY ? point.dy : point.dx).toDouble() - (imageDiffX / 2), + (shouldflipXY ? point.dx : point.dy).toDouble() - (imageDiffY / 2), + ) + .scale(imgToNativeScaleX * scale, imgToNativeScaleY * scale) + .translate(this.offset.dx, this.offset.dy); return offset; } diff --git a/lib/src/widgets/preview/awesome_preview_fit.dart b/lib/src/widgets/preview/awesome_preview_fit.dart index a82cddbd..cc3df88d 100644 --- a/lib/src/widgets/preview/awesome_preview_fit.dart +++ b/lib/src/widgets/preview/awesome_preview_fit.dart @@ -45,6 +45,7 @@ class _AnimatedPreviewFitState extends State { previewFit: widget.previewFit, previewSize: widget.previewSize, constraints: widget.constraints, + previewAlignment: widget.alignment, ); sizeCalculator!.compute(); maxSize = sizeCalculator!.maxSize; @@ -66,11 +67,13 @@ class _AnimatedPreviewFitState extends State { previewFit: oldWidget.previewFit, previewSize: oldWidget.previewSize, constraints: oldWidget.constraints, + previewAlignment: oldWidget.alignment, ); sizeCalculator = PreviewSizeCalculator( previewFit: widget.previewFit, previewSize: widget.previewSize, constraints: widget.constraints, + previewAlignment: widget.alignment, ); oldsizeCalculator.compute(); sizeCalculator!.compute(); @@ -116,7 +119,7 @@ class _AnimatedPreviewFitState extends State { previewFit: widget.previewFit, previewSize: widget.previewSize, scale: ratio, - maxSize: maxSize!, + maxSize: currentSize, child: child!, ); }, @@ -163,7 +166,7 @@ class PreviewFitWidget extends StatelessWidget { scaleEnabled: false, constrained: false, panEnabled: false, - alignment: FractionalOffset.topLeft, + alignment: Alignment.topLeft, clipBehavior: Clip.antiAlias, child: Align( alignment: Alignment.topLeft, @@ -185,6 +188,7 @@ class PreviewSizeCalculator { final CameraPreviewFit previewFit; final PreviewSize previewSize; final BoxConstraints constraints; + final Alignment previewAlignment; Size? _maxSize; double? _zoom; @@ -194,6 +198,7 @@ class PreviewSizeCalculator { required this.previewFit, required this.previewSize, required this.constraints, + required this.previewAlignment, }); void compute() { @@ -222,6 +227,12 @@ class PreviewSizeCalculator { return _offset!; } + Offset _computeOffset(num wDiff, num hDiff) { + final wMult = (previewAlignment.x + 1) / 2; + final hMult = (previewAlignment.y + 1) / 2; + return Offset(wDiff * wMult, hDiff * hMult); + } + Size _computeMaxSize() { var nativePreviewSize = previewSize.toSize(); Size maxSize; @@ -230,38 +241,40 @@ class PreviewSizeCalculator { final nativeHeightProjection = constraints.maxHeight * 1 / zoom; final hDiff = nativePreviewSize.height - nativeHeightProjection; + debugPrint( + 'COMPUTE PREVIEW SIZE: $nativeWidthProjection $nativeHeightProjection $nativePreviewSize, ZOOM: $zoom'); switch (previewFit) { case CameraPreviewFit.fitWidth: maxSize = Size(constraints.maxWidth, nativePreviewSize.height * zoom); - _offset = Offset(0, constraints.maxHeight - maxSize.height); + _offset = _computeOffset(0, constraints.maxHeight - maxSize.height); break; case CameraPreviewFit.fitHeight: maxSize = Size(nativePreviewSize.width * zoom, constraints.maxHeight); - _offset = Offset(constraints.maxWidth - maxSize.width, 0); + _offset = _computeOffset(constraints.maxWidth - maxSize.width, 0); break; case CameraPreviewFit.cover: maxSize = Size(constraints.maxWidth, constraints.maxHeight); if (constraints.maxWidth / constraints.maxHeight > previewSize.width / previewSize.height) { - _offset = Offset((hDiff * zoom) * 2, 0); + _offset = _computeOffset((hDiff * zoom) * 2, 0); // _offset = Offset(0, constraints.maxHeight - maxSize.height); } else { - _offset = Offset(0, (wDiff * zoom)); + _offset = _computeOffset(0, (wDiff * zoom)); // _offset = Offset(constraints.maxWidth - maxSize.width, 0); } break; case CameraPreviewFit.contain: maxSize = Size( nativePreviewSize.width * zoom, nativePreviewSize.height * zoom); - _offset = Offset( + _offset = _computeOffset( constraints.maxWidth - maxSize.width, constraints.maxHeight - maxSize.height, ); break; } - + debugPrint('MAX PREVIEW SIZE $maxSize $_offset'); return maxSize; } @@ -308,7 +321,8 @@ class PreviewSizeCalculator { runtimeType == other.runtimeType && previewFit == other.previewFit && constraints == other.constraints && - previewSize == other.previewSize; + previewSize == other.previewSize && + previewAlignment == other.previewAlignment; @override int get hashCode => previewSize.hashCode ^ previewSize.hashCode; From 28c5d91a385581bd83bcc3dd2afd4961d16f34b3 Mon Sep 17 00:00:00 2001 From: Almie Date: Fri, 19 Jul 2024 16:57:47 -0700 Subject: [PATCH 2/6] removing debug prints --- .../apparence/camerawesome/cameraX/CameraXState.kt | 14 -------------- .../camerawesome/cameraX/ImageAnalysisBuilder.kt | 3 --- example/lib/widgets/barcode_preview_overlay.dart | 3 --- .../orchestrator/analysis/analysis_to_image.dart | 5 ----- lib/src/widgets/preview/awesome_preview_fit.dart | 3 --- 5 files changed, 28 deletions(-) diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt index d030331f..216f825d 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraXState.kt @@ -92,7 +92,6 @@ data class CameraXState( @SuppressLint("RestrictedApi", "UnsafeOptInUsageError") fun updateLifecycle(activity: Activity) { previews = mutableListOf() - Log.d("KOTLIN", "Aspect ratio: $aspectRatio") imageCaptures.clear() videoCaptures.clear() if (cameraProvider.isMultiCamSupported() && sensors.size > 1) { @@ -295,21 +294,8 @@ data class CameraXState( useCaseGroup, ) previewCamera!!.cameraControl.enableTorch(flashMode == FlashMode.ALWAYS) - useCaseGroup.getUseCases()?.forEach{ - Log.d("KOTLIN", "Use case ${it.javaClass.kotlin} surface resolution: ${it.attachedSurfaceResolution}") - } } - val characteristics = CameraCharacteristicsCompat.toCameraCharacteristicsCompat( - Camera2CameraInfo.extractCameraCharacteristics(previewCamera!!.getCameraInfo()), - Camera2CameraInfo.from(previewCamera!!.getCameraInfo()).cameraId - ) - val streamConfigMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) - val resolutions = streamConfigMap?.getOutputSizes(ImageFormat.YUV_420_888) - val previewSizesList = previewSizes() - resolutions?.forEach{ - Log.d("KOTLIN", "Preview available size: $it") - } } private fun buildVideoCapture(videoOptions: AndroidVideoOptions?): VideoCapture { diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt index adff090e..48ad069c 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/ImageAnalysisBuilder.kt @@ -54,7 +54,6 @@ class ImageAnalysisBuilder private constructor( AspectRatio.RATIO_4_3 -> 4f / 3 else -> 16f / 9 } - Log.d("KOTLIN", "Analysis Aspect Ratio: $aspectRatio ($analysisAspectRatio)") val height = widthOrDefault * (1 / analysisAspectRatio) val maxFps = if (maxFramesPerSecond == 0.0) null else maxFramesPerSecond return ImageAnalysisBuilder( @@ -71,7 +70,6 @@ class ImageAnalysisBuilder private constructor( @SuppressLint("RestrictedApi") fun build(): ImageAnalysis { countDownLatch.reset() - Log.d("KOTLIN", "Building image analysis at target resolution ${width}x${height}") val imageAnalysisResolutionSelector = ResolutionSelector.Builder() .setAspectRatioStrategy( AspectRatioStrategy(aspectRatio, AspectRatioStrategy.FALLBACK_RULE_AUTO) @@ -143,7 +141,6 @@ class ImageAnalysisBuilder private constructor( AspectRatio.RATIO_4_3 -> 4f / 3 else -> 16f / 9 } - Log.d("KOTLIN", "Analysis Aspect Ratio: $aspectRatio ($analysisAspectRatio)") height = (width * (1 / analysisAspectRatio)).toInt() } diff --git a/example/lib/widgets/barcode_preview_overlay.dart b/example/lib/widgets/barcode_preview_overlay.dart index 33ae4864..6e80f97e 100644 --- a/example/lib/widgets/barcode_preview_overlay.dart +++ b/example/lib/widgets/barcode_preview_overlay.dart @@ -152,9 +152,6 @@ class _BarcodePreviewOverlayState extends State { bottomRightOffset.toOffset(), img, ); - debugPrint( - 'BARCODE $topLeftOffset $bottomRightOffset, $topLeftOff, $bottomRightOff'); - debugPrint('SCAN AREA ${widget.preview.rect.top}'); _barcodeRect = Rect.fromLTRB( topLeftOff.dx, diff --git a/lib/src/orchestrator/analysis/analysis_to_image.dart b/lib/src/orchestrator/analysis/analysis_to_image.dart index 23cfecad..c0362a59 100644 --- a/lib/src/orchestrator/analysis/analysis_to_image.dart +++ b/lib/src/orchestrator/analysis/analysis_to_image.dart @@ -56,11 +56,6 @@ class Preview { imgToNativeScaleX = nativePreviewSize.width / img.croppedSize.width; imgToNativeScaleY = nativePreviewSize.height / img.croppedSize.height; } - debugPrint('IMAGE DIFF X: $imageDiffX Y: $imageDiffY'); - debugPrint('PREVIEW SCALE: $scale $previewSize $nativePreviewSize'); - debugPrint( - 'IMAGE TO NATIVE SCALE X: $imgToNativeScaleX, Y: $imgToNativeScaleY'); - debugPrint('PREVIEW OFFSET: ${this.offset.dx}x${this.offset.dy}'); var offset = Offset( (shouldflipXY ? point.dy : point.dx).toDouble() - (imageDiffX / 2), (shouldflipXY ? point.dx : point.dy).toDouble() - (imageDiffY / 2), diff --git a/lib/src/widgets/preview/awesome_preview_fit.dart b/lib/src/widgets/preview/awesome_preview_fit.dart index cc3df88d..8239b009 100644 --- a/lib/src/widgets/preview/awesome_preview_fit.dart +++ b/lib/src/widgets/preview/awesome_preview_fit.dart @@ -241,8 +241,6 @@ class PreviewSizeCalculator { final nativeHeightProjection = constraints.maxHeight * 1 / zoom; final hDiff = nativePreviewSize.height - nativeHeightProjection; - debugPrint( - 'COMPUTE PREVIEW SIZE: $nativeWidthProjection $nativeHeightProjection $nativePreviewSize, ZOOM: $zoom'); switch (previewFit) { case CameraPreviewFit.fitWidth: @@ -274,7 +272,6 @@ class PreviewSizeCalculator { ); break; } - debugPrint('MAX PREVIEW SIZE $maxSize $_offset'); return maxSize; } From 16896021b1bfd7917ea26c6c48b0d827f168e4f2 Mon Sep 17 00:00:00 2001 From: Almie Date: Fri, 19 Jul 2024 17:36:49 -0700 Subject: [PATCH 3/6] removed unused variable --- example/lib/widgets/barcode_preview_overlay.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/example/lib/widgets/barcode_preview_overlay.dart b/example/lib/widgets/barcode_preview_overlay.dart index 6e80f97e..8c55ffac 100644 --- a/example/lib/widgets/barcode_preview_overlay.dart +++ b/example/lib/widgets/barcode_preview_overlay.dart @@ -23,7 +23,6 @@ class BarcodePreviewOverlay extends StatefulWidget { } class _BarcodePreviewOverlayState extends State { - late Size _screenSize; late Rect _scanArea; // The barcode that is currently in the scan area (one at a time) @@ -74,8 +73,6 @@ class _BarcodePreviewOverlayState extends State { @override Widget build(BuildContext context) { - _screenSize = MediaQuery.of(context).size; - return IgnorePointer( ignoring: true, child: Stack(children: [ From a5bf7d67b2210f0132779cf52bd26e99b1f04522 Mon Sep 17 00:00:00 2001 From: Almie Date: Fri, 19 Jul 2024 17:37:30 -0700 Subject: [PATCH 4/6] now accounting for stretching in analysis image with 1:1 aspect ratio --- .../orchestrator/analysis/analysis_to_image.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/orchestrator/analysis/analysis_to_image.dart b/lib/src/orchestrator/analysis/analysis_to_image.dart index c0362a59..941d6606 100644 --- a/lib/src/orchestrator/analysis/analysis_to_image.dart +++ b/lib/src/orchestrator/analysis/analysis_to_image.dart @@ -39,26 +39,26 @@ class Preview { AnalysisImage img, { bool? flipXY, }) { - num imageDiffX; - num imageDiffY; + num imageStretchX; + num imageStretchY; num imgToNativeScaleX; num imgToNativeScaleY; final shouldflipXY = flipXY ?? img.flipXY(); if (Platform.isIOS) { - imageDiffX = img.size.width - img.croppedSize.width; - imageDiffY = img.size.height - img.croppedSize.height; + imageStretchX = img.size.width / img.croppedSize.width; + imageStretchY = img.size.height / img.croppedSize.height; imgToNativeScaleX = nativePreviewSize.width / img.croppedSize.width; imgToNativeScaleY = nativePreviewSize.height / img.croppedSize.height; } else { // Width and height are inverted on Android - imageDiffX = img.size.height - img.croppedSize.width; - imageDiffY = img.size.width - img.croppedSize.height; + imageStretchX = img.size.height / img.croppedSize.width; + imageStretchY = img.size.width / img.croppedSize.height; imgToNativeScaleX = nativePreviewSize.width / img.croppedSize.width; imgToNativeScaleY = nativePreviewSize.height / img.croppedSize.height; } var offset = Offset( - (shouldflipXY ? point.dy : point.dx).toDouble() - (imageDiffX / 2), - (shouldflipXY ? point.dx : point.dy).toDouble() - (imageDiffY / 2), + (shouldflipXY ? point.dy : point.dx).toDouble() / imageStretchX, + (shouldflipXY ? point.dx : point.dy).toDouble() / imageStretchY, ) .scale(imgToNativeScaleX * scale, imgToNativeScaleY * scale) .translate(this.offset.dx, this.offset.dy); From cef947c7784036fa28bcaa5174ee31cc22af0e93 Mon Sep 17 00:00:00 2001 From: Almie Date: Fri, 19 Jul 2024 20:40:07 -0700 Subject: [PATCH 5/6] fixed preview alignment and offset calculation when using CameraPreviewFit.fitHeight or cover --- .../widgets/preview/awesome_preview_fit.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/src/widgets/preview/awesome_preview_fit.dart b/lib/src/widgets/preview/awesome_preview_fit.dart index 8239b009..888f4608 100644 --- a/lib/src/widgets/preview/awesome_preview_fit.dart +++ b/lib/src/widgets/preview/awesome_preview_fit.dart @@ -155,6 +155,12 @@ class PreviewFitWidget extends StatelessWidget { Widget build(BuildContext context) { final transformController = TransformationController() ..value = (Matrix4.identity()..scale(scale)); + final wDiff = + max(previewSize.width * scale - constraints.maxWidth, 0) / scale; + final hDiff = + max(previewSize.height * scale - constraints.maxHeight, 0) / scale; + transformController.value.translate( + wDiff * -((alignment.x + 1) / 2), hDiff * -((alignment.y + 1) / 2)); return Align( alignment: alignment, child: SizedBox( @@ -236,11 +242,6 @@ class PreviewSizeCalculator { Size _computeMaxSize() { var nativePreviewSize = previewSize.toSize(); Size maxSize; - final nativeWidthProjection = constraints.maxWidth * 1 / zoom; - final wDiff = nativePreviewSize.width - nativeWidthProjection; - - final nativeHeightProjection = constraints.maxHeight * 1 / zoom; - final hDiff = nativePreviewSize.height - nativeHeightProjection; switch (previewFit) { case CameraPreviewFit.fitWidth: @@ -252,14 +253,14 @@ class PreviewSizeCalculator { _offset = _computeOffset(constraints.maxWidth - maxSize.width, 0); break; case CameraPreviewFit.cover: - maxSize = Size(constraints.maxWidth, constraints.maxHeight); - if (constraints.maxWidth / constraints.maxHeight > previewSize.width / previewSize.height) { - _offset = _computeOffset((hDiff * zoom) * 2, 0); + maxSize = Size(constraints.maxWidth, nativePreviewSize.height * zoom); + _offset = _computeOffset(0, constraints.maxHeight - maxSize.height); // _offset = Offset(0, constraints.maxHeight - maxSize.height); } else { - _offset = _computeOffset(0, (wDiff * zoom)); + maxSize = Size(nativePreviewSize.width * zoom, constraints.maxHeight); + _offset = _computeOffset(constraints.maxWidth - maxSize.width, 0); // _offset = Offset(constraints.maxWidth - maxSize.width, 0); } break; From 1cfd0138327c37b46a3dc38fc4f2536d53294c08 Mon Sep 17 00:00:00 2001 From: Almie Date: Wed, 24 Jul 2024 10:45:52 -0700 Subject: [PATCH 6/6] fixed force casting null aspectRatio --- .../kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt index 0dc1ea49..1968f4af 100644 --- a/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt +++ b/android/src/main/kotlin/com/apparence/camerawesome/cameraX/CameraAwesomeX.kt @@ -190,7 +190,7 @@ class CameraAwesomeX : CameraInterface, FlutterPlugin, ActivityAware { cameraState.apply { try { imageAnalysisBuilder = ImageAnalysisBuilder.configure( - aspectRatio ?: aspectRatio!!, + aspectRatio ?: AspectRatio.RATIO_4_3, when (format.uppercase()) { "YUV_420" -> OutputImageFormat.YUV_420_888 "NV21" -> OutputImageFormat.NV21