.NET MAUI Android中EmguCV Mat对象为空且ImageGrabbed事件不触发
.NET MAUI中使用Emgu.CV捕获相机帧失败:Mat为空且ImageGrabbed事件不触发
我在.NET MAUI的Android平台上用Emgu.CV捕获相机帧,已经安装了对应平台的Emgu.CV运行时。能正常访问相机,但Mat对象始终为空,无法获取画面;同时_capture.ImageGrabbed += _capture_ImageGrabbed;注册的事件完全不触发。相同代码在Windows Forms和WPF里能正常运行。
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_VIDEO" /> <queries> <intent> <action android:name="android.media.action.IMAGE_CAPTURE" /> </intent> <intent> <action android:name="android.media.action.VIDEO_CAPTURE" /> </intent> </queries> </manifest>
MainPage.xaml.cs
public partial class MainPage : ContentPage { private VideoCapture _capture = null; //private bool _captureInProgress; private Mat _frame; public MainPage() { InitializeComponent(); //CvInvoke.UseOpenCL = false; CvInvokeAndroid.Init(); _capture = new VideoCapture(0, VideoCapture.API.Android); _capture.ImageGrabbed += _capture_ImageGrabbed; _frame = new Mat(); } private void captureButton_Clicked(object sender, EventArgs e) { _capture.Start(); } private void _capture_ImageGrabbed(object sender, EventArgs e) { _capture.Retrieve(_frame, 0); } private void ManuelFrame_Clicked(object sender, EventArgs e) { _capture.Retrieve(_frame, 0); if (_frame.IsEmpty) { statusLabel.Text = "frame is null"; } else statusLabel.Text = "frame is not null"; } }
相关截图



可以确认相机能被访问,但调用Retrieve后Mat还是空。试过切换到Android 13(API 33)、手动触发获取帧,都没用。
更新
我照搬了EmguCV官方MAUI示例代码,但ImageGrabbed事件依旧不触发,Mat还是空。示例代码如下:
public partial class MainPage : ContentPage { private VideoCapture _capture = null; //private bool _captureInProgress; private Mat _mat; private Mat _renderMat; private string _defaultButtonText = "Start Camera"; protected string _StopCameraButtonText = "Stop Camera"; private string _deaultImage; ButtonTextImagePage buttonPage = new ButtonTextImagePage(); private IProcessAndRenderModel _model; public MainPage() { InitializeComponent(); //CvInvoke.UseOpenCL = false; CvInvokeAndroid.Init(); InitVideoCapture(); } private void _capture_ImageGrabbed(object sender, EventArgs e) { if (_mat == null) _mat = new Mat(); _capture.Retrieve(_mat); if (_renderMat == null) _renderMat = new Mat(); _mat.CopyTo(_renderMat); } protected virtual async void captureButton_Clicked(object sender, EventArgs e) { var button = buttonPage.GetButton(); if(captureButton.Text.Equals(_StopCameraButtonText)) { #if __ANDROID__ && __USE_ANDROID_CAMERA2__ StopCapture(); //AndroidImageView.Visibility = ViewStates.Invisible; #else _capture.Stop(); _capture.Dispose(); _capture = null; #endif captureButton.Text = _defaultButtonText; return; } Mat[] images; if(_deaultImage == null) { images = new Mat[0]; } else { images = await buttonPage.LoadImages(new string[] { _deaultImage }); if (images == null || (images.Length > 0 && images[0] == null)) return; } if(images.Length == 0) { #if __ANDROID__ && __USE_ANDROID_CAMERA2__ StartCapture(async delegate (Object captureSender, Mat m) { //Skip the frame if busy, //Otherwise too many frames arriving and will eventually saturated the memory. if (!_isBusy) { _isBusy = true; try { String message = String.Empty; await Task.Run(() => { if (_renderMat == null) _renderMat = new Mat(); using (InputArray iaImage = m.GetInputArray()) { iaImage.CopyTo(_renderMat); } message = _model.ProcessAndRender(m, _renderMat); }); SetImage(_renderMat); SetMessage(message); } finally { _isBusy = false; } } }); #else //Handle video if (_capture == null) { InitVideoCapture(); } if (_capture != null) _capture.Start(); #endif captureButton.Text = _StopCameraButtonText; } else { if (_renderMat == null) _renderMat = new Mat(); images[0].CopyTo(_renderMat); } } private void InitVideoCapture() { var openCVConfigDict = CvInvoke.ConfigDict; bool haveVideoio = (openCVConfigDict["HAVE_OPENCV_VIDEOIO"] != 0); if (haveVideoio && Microsoft.Maui.Devices.DeviceInfo.Platform == DevicePlatform.Android) { #if __ANDROID__ && __USE_ANDROID_CAMERA2__ return true; #else if (CvInvoke.Backends.Length > 0) { if (Microsoft.Maui.Devices.DeviceInfo.Platform == DevicePlatform.Android) { _capture = new VideoCapture(0, VideoCapture.API.Android); } else { _capture = new VideoCapture(); } if (_capture.IsOpened) { _capture.ImageGrabbed += _capture_ImageGrabbed; return; } else { _capture.Dispose(); _capture = null; } } #endif } return; } }
解决方法
1. 动态申请相机权限
Android 6.0+要求动态申请相机权限,仅在Manifest声明无效。在启动相机前添加权限检查:
private async Task CheckCameraPermission() { var status = await Permissions.CheckStatusAsync<Permissions.Camera>(); if (status != PermissionStatus.Granted) { status = await Permissions.RequestAsync<Permissions.Camera>(); } if (status != PermissionStatus.Granted) { statusLabel.Text = "相机权限未授权"; return; } }
修改captureButton_Clicked方法,先执行权限检查:
private async void captureButton_Clicked(object sender, EventArgs e) { await CheckCameraPermission(); if (_capture != null && _capture.IsOpened) { _capture.Start(); } }
2. 修正VideoCapture初始化逻辑
- 检查
_capture.IsOpened是否为true,如果为false,尝试更换设备索引(比如用1访问前置相机):_capture = new VideoCapture(1, VideoCapture.API.Android); - 或者不指定设备索引,让系统自动选择可用相机:
_capture = new VideoCapture(VideoCapture.API.Android);
3. 改用手动轮询获取帧
如果ImageGrabbed事件始终无法触发,用定时器手动轮询替代:
private Timer _frameTimer; // 在MainPage构造函数或权限检查通过后初始化 _frameTimer = new Timer(async (state) => { if (_capture == null || !_capture.IsOpened) return; using var tempMat = new Mat(); if (_capture.Read(tempMat) && !tempMat.IsEmpty) { MainThread.BeginInvokeOnMainThread(() => { tempMat.CopyTo(_frame); statusLabel.Text = "frame is not null"; // 这里可以添加帧渲染到UI的逻辑 }); } }, null, 0, 30); // 30ms间隔,约30fps
停止相机时记得释放定时器:
private void StopCamera() { _capture?.Stop(); _capture?.Dispose(); _capture = null; _frameTimer?.Dispose(); }
4. 确认Emgu.CV版本与编译选项
- 确保安装的是
Emgu.CV.Maui包,且版本为最新稳定版; - 如果尝试使用Camera2 API,在Android项目的编译符号中添加
__USE_ANDROID_CAMERA2__(右键项目→属性→生成→条件编译符号)。
内容的提问来源于stack exchange,提问作者CaptainTaro
相关产品推荐
相关产品推荐

