Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to Preview and AnalysisImage resolution selection, aspect ratio and convertFromImage #497

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -125,13 +128,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")
)
Expand All @@ -144,7 +154,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(
Expand Down Expand Up @@ -196,13 +211,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(
Expand All @@ -217,7 +239,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) {
Expand Down Expand Up @@ -260,12 +287,14 @@ 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)

}
}

Expand Down Expand Up @@ -442,5 +471,8 @@ data class CameraXState(
"RATIO_1_1" -> Rational(1, 1)
else -> Rational(3, 4)
}
if (imageAnalysisBuilder != null){
imageAnalysisBuilder!!.updateAspectRatio(newAspectRatio)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand All @@ -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?,
Expand Down Expand Up @@ -54,6 +60,7 @@ class ImageAnalysisBuilder private constructor(
format,
widthOrDefault,
height.toInt(),
aspectRatio,
executor,
maxFramesPerSecond = maxFps,
)
Expand All @@ -63,7 +70,15 @@ class ImageAnalysisBuilder private constructor(
@SuppressLint("RestrictedApi")
fun build(): ImageAnalysis {
countDownLatch.reset()
val imageAnalysis = ImageAnalysis.Builder().setTargetResolution(Size(width, 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 ->
Expand Down Expand Up @@ -120,6 +135,15 @@ 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
}
height = (width * (1 / analysisAspectRatio)).toInt()
}

private fun cropRect(imageProxy: ImageProxy): Map<String, Any> {
return mapOf(
"left" to imageProxy.cropRect.left,
Expand Down
1 change: 1 addition & 0 deletions example/lib/preview_overlay_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class _CameraPageState extends State<CameraPage> {
aspectRatio: CameraAspectRatios.ratio_16_9,
),
previewFit: CameraPreviewFit.fitWidth,
previewAlignment: Alignment.center,
onMediaTap: (mediaCapture) {
mediaCapture.captureRequest
.when(single: (single) => single.file?.open());
Expand Down
10 changes: 2 additions & 8 deletions example/lib/widgets/barcode_preview_overlay.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class BarcodePreviewOverlay extends StatefulWidget {
}

class _BarcodePreviewOverlayState extends State<BarcodePreviewOverlay> {
late Size _screenSize;
late Rect _scanArea;

// The barcode that is currently in the scan area (one at a time)
Expand Down Expand Up @@ -64,7 +63,7 @@ class _BarcodePreviewOverlayState extends State<BarcodePreviewOverlay> {
// 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,
Expand All @@ -74,8 +73,6 @@ class _BarcodePreviewOverlayState extends State<BarcodePreviewOverlay> {

@override
Widget build(BuildContext context) {
_screenSize = MediaQuery.of(context).size;

return IgnorePointer(
ignoring: true,
child: Stack(children: [
Expand Down Expand Up @@ -163,10 +160,7 @@ class _BarcodePreviewOverlayState extends State<BarcodePreviewOverlay> {
// 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
Expand Down
36 changes: 18 additions & 18 deletions lib/src/orchestrator/analysis/analysis_to_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,29 @@ 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),
) *
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,
);
var offset = Offset(
(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);
return offset;
}

Expand Down
Loading