如何在新版Android设备上用Xamarin.Forms自定义相机拍摄高分辨率照片
问题:Xamarin.Forms CameraX拍照分辨率受限(多摄像头Android设备)
我基于netstandard2.0框架开发Xamarin.Forms跨平台应用,部署目标为Android和iOS双平台,核心需求是实现自定义相机,捕获后置摄像头的高分辨率照片并发送至电脑分析。
iOS端已实现全分辨率拍照,但Android端在不同设备上表现不一致:
- 三星SM-A510F(Android 7.0 API 24):可拍摄12MP照片,与原生相机效果一致
- 三星SM-A415F(Android 10.0 API 29,配备3个后置摄像头:5MP、8MP、48MP):拍照分辨率被限制为3.5MP(2560*1440)
我通过自定义CameraPreview视图并绑定CameraPreviewRenderer实现跨平台视图关联,相关代码如下:
共享代码:CameraPreview
using System; using System.Collections.Generic; using System.Text; using Xamarin.Forms; namespace XamPOC.CustomRenderer { public enum CameraOptions { Rear, Front } public class CameraPreview : View { public static readonly BindableProperty CameraProperty = BindableProperty.Create(propertyName: "Camera", returnType: typeof(CameraOptions), declaringType: typeof(CameraPreview), defaultValue: CameraOptions.Rear); public CameraOptions Camera { get { return (CameraOptions)GetValue(CameraProperty); } set { SetValue(CameraProperty, value); } } public void CapturePhoto() { MessagingCenter.Send(this, "CapturePhoto"); } } }
Android平台:CameraPreviewRenderer
using Android.Content; using Android.Hardware.Camera2; using Android.Util; using AndroidX.Camera.Core; using AndroidX.Camera.Lifecycle; using AndroidX.Core.Content; using Java.Util.Concurrent; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; using XamPOC; using XamPOC.Droid; using XamPOC.CustomRenderer; using System; using Android.App; using Android.Graphics; using Android.Hardware.Camera2.Params; using AndroidX.Camera.Camera2.InterOp; using AndroidX.Lifecycle; using Java.Lang; using XamPOC.Services; using XamPOC.CustomRenderer.Droid; using AndroidX.Camera.Core.Impl; using AndroidX.Camera.Camera2.Internal.Compat; using Android.Net.Wifi.Aware; [assembly: ExportRenderer(typeof(CameraPreview), typeof(CameraPreviewRenderer))] namespace XamPOC.Droid { public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview> { private UICameraPreview _uiCameraPreview; private IExecutorService _cameraExecutor; private ImageCapture _imageCapture; private CameraManager _cameraManager; public CameraPreviewRenderer(Context context) : base(context) { _cameraManager = (CameraManager) Context.GetSystemService(Context.CameraService); _cameraManager.GetCameraIdList(); } protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e) { base.OnElementChanged(e); if (e.OldElement != null) { MessagingCenter.Unsubscribe<CameraPreview>(this, "CapturePhoto"); } if (e.NewElement != null) { if (Control == null) { _uiCameraPreview = new UICameraPreview(Context); SetNativeControl(_uiCameraPreview); _cameraExecutor = Executors.NewSingleThreadExecutor(); StartCamera(); } MessagingCenter.Subscribe<CameraPreview>(this, "CapturePhoto", async (sender) => TakePhoto()); } } private void StartCamera() { var cameraProviderFuture = ProcessCameraProvider.GetInstance(Context); cameraProviderFuture.AddListener(new Runnable(() => { var cameraProvider = (ProcessCameraProvider)cameraProviderFuture.Get(); var preview = new Preview.Builder().Build(); preview.SetSurfaceProvider(_uiCameraPreview.previewView.SurfaceProvider); var cameraSelector = new CameraSelector.Builder() .RequireLensFacing(CameraSelector.LensFacingBack) .Build(); _imageCapture = new ImageCapture.Builder() .SetCaptureMode(ImageCapture.CaptureModeMaximizeQuality) .Build(); try { cameraProvider.UnbindAll(); var list = cameraProvider.AvailableCameraInfos; foreach (var cameraInfo in list) { var c = cameraInfo; } var camera = cameraProvider.BindToLifecycle(GetLifecycleOwner(), cameraSelector, preview, _imageCapture); } catch (System.Exception ex) { Log.Error("CameraPreviewRenderer", "Use case binding failed", ex); } }), ContextCompat.GetMainExecutor(Context)); } private ILifecycleOwner GetLifecycleOwner() { if (Context is ILifecycleOwner lifecycleOwner) { return lifecycleOwner; } if (Context is Activity activity) { var lifecycleRegistry = new LifecycleRegistry((ILifecycleOwner)activity); lifecycleRegistry.SetCurrentState(Lifecycle.State.Started); return (ILifecycleOwner)lifecycleRegistry; } throw new InvalidOperationException("Context does not implement ILifecycleOwner"); } public void TakePhoto() { if (_imageCapture == null) return; var fileService = DependencyService.Get<IFileService>(); var folder = fileService.GetPicturesFolder(); var photoFile = new Java.IO.File(System.IO.Path.Combine(folder, $"CCube{DateTime.Now:yyyyMMdd_HHmmss}.jpg")); var outputOptions = new ImageCapture.OutputFileOptions.Builder(photoFile) .Build(); _imageCapture.TakePicture(outputOptions, _cameraExecutor, new ImageCaptureCallback(photoFile)); } private class ImageCaptureCallback : Java.Lang.Object, ImageCapture.IOnImageSavedCallback { private Java.IO.File _photoFile; public ImageCaptureCallback(Java.IO.File photoFile) { _photoFile = photoFile; } public void OnError(ImageCaptureException exception) { Log.Error("CameraPreviewRenderer", "Photo capture failed: " + exception.Message); } public void OnImageSaved(ImageCapture.OutputFileResults outputFileResults) { Log.Debug("CameraPreviewRenderer", "Photo capture succeeded: " + _photoFile.AbsolutePath); byte[] ImageBytes = System.IO.File.ReadAllBytes(_photoFile.AbsolutePath); MessagingCenter.Send(this, "PhotoArray", ImageBytes); } } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _cameraExecutor?.Shutdown(); } } } }
CameraPage布局代码
<?xml version="1.0" encoding="utf-8" ?> <ContentPage x:Class="XamPOC.Views.CameraPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:XamPOC.CustomRenderer" xmlns:vm="clr-namespace:XamPOC.ViewModels" Title="CameraPage" x:DataType="vm:CameraViewModel" NavigationPage.HasNavigationBar="true"> <Grid Padding="0" BackgroundColor="Black"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition x:Name="ImageRow" Height="10*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <local:CameraPreview x:Name="ImageView" Grid.Row="1" Grid.RowSpan="11" Grid.Column="0" Grid.ColumnSpan="12" BindingContext="{Binding CameraPreview}" /> <ImageButton Grid.Row="8" Grid.RowSpan="3" Grid.Column="4" Grid.ColumnSpan="4" Margin="10" BackgroundColor="Transparent" Clicked="Button_Clicked" Source="Camera.png" /> <FlexLayout Grid.ColumnSpan="12" AlignContent="Center" AlignItems="Center" Direction="Row" JustifyContent="SpaceEvenly"> <Label HorizontalTextAlignment="Center" Text="CLI" TextColor="White" WidthRequest="85" /> <Switch Toggled="Switch_Toggled" /> <Label HorizontalTextAlignment="Center" Text="DERM" TextColor="White" WidthRequest="85" /> </FlexLayout> </Grid> </ContentPage>
我也尝试过使用CameraXBasics仓库,遇到了同样的分辨率不足问题,想知道这是否是Xamarin.Forms自定义相机的限制?
内容的提问来源于stack exchange,提问作者Pablo Arrieumerlou
相关产品推荐
相关产品推荐

