资讯详情

资讯详情

Flet Camera 图像流事件 CameraImageEvent 完全指南:从帧回调到图像数据解析

Flet Camera 图像流事件 CameraImageEvent 完全指南从帧回调到图像数据解析【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet导读CameraImageEvent是 Fletflet-camera扩展包中承载实时摄像头图像帧的核心事件类型。当应用通过Camera.start_image_stream()开启图像流后每一帧画面都会以CameraImageEvent的形式通过Camera.on_stream_image回调送达 Python 侧其中既包含帧的尺寸、原始像素格式等元数据也包含可直接用于显示或保存的编码图像字节。本文以该事件类型为线索结合仓库源码完整讲解其全部字段语义、底层数据流与实战用法帮助你在 Flet 应用中实现实时相机预览分析、扫码、帧处理等能力。事件类型定位flet_camera.CameraImageEventCameraImageEvent定义于 sdk/python/packages/flet-camera/src/flet_camera/types.py是一个继承自ft.Event[Camera]的dataclassdataclass class CameraImageEvent(ft.Event[Camera]): Image data produced by the camera stream. width: int height: int format: Optional[ImageFormatGroup] encoded_format: str bytes: bytes lens_aperture: Optional[ft.Number] None sensor_exposure_time: Optional[int] None sensor_sensitivity: Optional[ft.Number] None它通过on_stream_image事件处理器对外暴露。在 camera.py 中声明如下on_stream_image: Optional[ft.EventHandler[CameraImageEvent]] None Fires when an image frame is available while streaming.一句话概括只要on_stream_image被赋值底层每当产生一帧图像就会构造一个CameraImageEvent并回调没有赋值处理器时帧数据会被底层直接丢弃见下文源码分析不会产生任何跨端开销。字段详解每个属性代表什么CameraImageEvent共 8 个字段前 5 个为必填后 3 个为可选依赖硬件能力。完整对照表如下字段类型必填含义widthint✅帧的像素宽度heightint✅帧的像素高度formatOptional[ImageFormatGroup]✅源帧的原始像素格式分组如jpeg、bgra8888encoded_formatstr✅bytes中实际字节的编码格式当前固定为jpegbytesbytes✅已编码的图像字节可直接展示或落盘lens_apertureOptional[ft.Number]❌该帧对应的镜头光圈值sensor_exposure_timeOptional[int]❌传感器曝光时间单位为纳秒sensor_sensitivityOptional[ft.Number]❌传感器感光度ISO逐项说明如下帧尺寸width与height以像素为单位的原始帧尺寸直接映射自 FlutterCameraImage.width/height。注意它们与预览控件的显示尺寸不一定相同——帧尺寸由初始化时指定的resolution_preset如ResolutionPreset.MEDIUM决定。实际应用中常用来换算显示比例示例代码中就是用它们计算Image控件的变换见下文实战小节。原始格式format表示源帧的像素格式分组对应 ImageFormatGroup 枚举。仓库 types.py 定义了 5 种取值BGRA8888BGRA 8888 原始格式JPEGJPEG 压缩格式推荐优先使用可零转换直出NV21NV21 YUV 格式Android 常见YUV420YUV420 planar 格式UNKNOWN未知或不支持的格式。该字段描述的是相机产出的原始数据长什么样而encoded_format描述的是你拿到的bytes是什么编码。两者在非 JPEG 源帧下是不同的。输出编码encoded_format与bytesbytes是事件中唯一承载像素数据的字段encoded_format说明这段字节的编码方式。从 Flutter 侧实现看当前实现会将所有帧统一编码为 JPEG 输出因此encoded_format恒为jpeg。见 utils/camera.dartMapString, dynamic cameraImageToMap( CameraImage image, Uint8List encodedBytes) { return { width: image.width, height: image.height, format: image.format.group.name, encoded_format: jpeg, lens_aperture: image.lensAperture, sensor_exposure_time: image.sensorExposureTime, sensor_sensitivity: image.sensorSensitivity, bytes: encodedBytes, }..removeWhere((_, v) v null); }这意味着在 Python 侧你可以放心地把e.bytes直接当作 JPEG 数据使用例如赋给Image.src或写入.jpg文件无需再关心底层是 BGRA、NV21 还是 YUV420。硬件元数据lens_aperture、sensor_exposure_time、sensor_sensitivity这三个可选字段透传相机传感器的曝光信息可用于实现自动曝光分析、HDR 判定或调试日志lens_aperture光圈值f-numbersensor_exposure_time曝光时长单位是纳秒源码 docstring 明确注明 Exposure time in nanoseconds换算毫秒需除以 1_000_000sensor_sensitivityISO 感光度。它们并非所有平台、所有帧都可用不可用时为NoneDart 端通过removeWhere((_, v) v null)剔除空值Python 端对应可选默认值。事件从哪来图像流的完整工作流程CameraImageEvent只是结果理解它的前提是掌握图像流的开启与关闭。整个链路涉及 Python 端 API 与 Flutter 端实现两层Python 端camera.py提供三个相关方法async def supports_image_streaming(self) - bool: Indicates whether image streaming is supported on the current platform. return await self._invoke_method(supports_image_streaming) async def start_image_stream(self): Begins streaming camera image frames. await self._invoke_method(start_image_stream) async def stop_image_stream(self): Stops streaming camera image frames. await self._invoke_method(stop_image_stream)Flutter 端camera.dart的核心实现如下Futurevoid _startImageStream() async { final controller _requireController(); await controller.startImageStream((CameraImage image) { if (!widget.control.hasEventHandler(stream_image)) { return; // 未注册 Python 回调直接丢弃帧 } if (_processingImage) { return; // 上一帧仍在处理中跳过本帧 } _processingImage true; unawaited(_processStreamImage(image)); }); }这里有三个值得注意的工程细节按需回调hasEventHandler(stream_image)检查 Python 侧是否注册了on_stream_image。没有注册处理器时即使start_image_stream()被调用帧也会被静默丢弃——因此可以放心地在不需要帧时只关闭回调而不停流当然更推荐显式stop_image_stream()释放资源。反压保护_processingImage标志位确保同一时刻只处理一帧若上一帧尚未处理完例如编码较慢新帧会被跳过避免事件无限堆积拖垮 UI 线程。后台编码非 JPEG 源帧的编码转换通过Isolate.run提交到独立 isolate 执行避免阻塞 Flutter UI 线程。事件经triggerEvent(stream_image, cameraImageToMap(image, encoded))回传Python 侧由ft.EventHandler[CameraImageEvent]反序列化为CameraImageEvent实例。底层揭秘源帧是如何变成 JPEG 字节的_processStreamImagecamera.dart中有一段关键分支final Uint8List encoded image.format.group ImageFormatGroup.jpeg ? encodeCameraImage(image) : await Isolate.run( () encodeCameraImagePayload(cameraImageToPayload(image)));即源帧本身是 JPEG 时直接透传 plane 字节零拷贝开销否则把原始像素转换成 JPEG。编码函数定义在 utils/camera.dartUint8List encodeCameraImage(CameraImage image) { switch (image.format.group) { case ImageFormatGroup.bgra8888: case ImageFormatGroup.nv21: case ImageFormatGroup.yuv420: return encodeCameraImagePayload(cameraImageToPayload(image)); case ImageFormatGroup.jpeg: if (image.planes.isEmpty) return Uint8List(0); return image.planes.first.bytes; default: return Uint8List(0); } }对应的像素转换逻辑_encodeBgra8888Payload、_encodeNv21Payload、_encodeYuv420Payload见 utils/camera.dart会按各自的 plane 布局bytes_per_row、bytes_per_pixel逐像素完成 YUV→RGB 或 BGRA→RGB 转换再交给img.encodeJpg输出。给开发者的直接启示初始化时若明确指定image_format_groupfc.ImageFormatGroup.JPEG可以规避帧帧转换的开销让bytes走直通路径这在追求高帧率时尤为重要。另外注意若某帧编码后为空字节如Uint8List(0)Flutter 端会直接跳过该帧if (encoded.isEmpty) return;Python 侧不会收到空数据事件。实战用on_stream_image做实时画面展示仓库自带的官方示例 sdk/python/examples/extensions/camera/camera_playground/main.py 完整演示了流式帧的标准用法。核心链路如下第一步初始化并确认流能力示例 L243-L256await preview.initialize( descriptionstate.selected_camera, resolution_presetfc.ResolutionPreset.MEDIUM, enable_audioTrue, image_format_groupfc.ImageFormatGroup.JPEG, # 优先 JPEG走直通编码 ) if not page.web: try: await preview.lock_capture_orientation() except RuntimeError as ex: logging.warning(Could not lock capture orientation: %s, ex) state.is_streaming_supported await preview.supports_image_streaming()第二步注册事件处理器示例 L379-L391def on_stream_image(e: fc.CameraImageEvent): try: state.last_frame_width e.width state.last_frame_height e.height last_image.src e.bytes # JPEG 字节直接作为图片源 apply_last_image_transform(e.width, e.height) # 按帧尺寸换算显示变换 last_photo_label.visible True last_image_frame.visible True page.update() except Exception as ex: logging.exception(Failed to render stream frame: %s, ex) preview.on_stream_image on_stream_image第三步启停图像流示例 L357-L377async def start_streaming(): if not state.is_initialized: status.value Initialize camera first; page.update(); return if not state.is_streaming_supported: status.value Image streaming is not supported by this camera; page.update(); return await preview.start_image_stream() state.is_streaming True sync_action_buttons(); page.update() async def stop_streaming(): if not state.is_initialized: return await preview.stop_image_stream() state.is_streaming False sync_action_buttons(); page.update()几点实战建议先查supports_image_streaming()部分平台或机型不支持图像流示例中先探测再启动避免运行时报错帧频控制回调频率取决于设备输出帧率配合示例中的_processingImage机制Python 侧处理器应尽量轻量如只更新Image.src避免在回调内做耗时计算拖慢整体响应事件与take_picture()的关系CameraImageEvent来自连续流而Camera.take_picture()返回的是单次快门的高清 JPEG 字节见 camera.py两者用途不同——需要连拍/分析用流需要最高质量快照用take_picture()。平台支持与使用前提flet-camera目前仅支持 Android、iOS 与 Web 平台见 website/docs/controls/camera/index.md。camera.py 的before_update()会在其他平台显式抛出FletUnsupportedPlatformExceptionif not ( self.page.web or self.page.platform in [ft.PagePlatform.ANDROID, ft.PagePlatform.IOS] ): raise ft.FletUnsupportedPlatformException( Camera is currently only supported on Android, iOS and Web platforms. )此外在移动平台使用相机含麦克风若录制带音频视频前需要申请相应权限可借助PermissionHandler或flet build的跨平台权限捆绑详见 Camera 控制文档。图像流能力本身与平台相关——例如ImageFormatGroup.NV21是 Android 常见格式、JPEG 直通只在源帧即 JPEG 时生效这些都会反映在CameraImageEvent.format字段中解析帧数据前应先判断该字段。小结CameraImageEvent是flet-camera图像流功能的数据出口8 个字段覆盖了帧尺寸、原始格式、输出编码、像素字节与曝光元数据五个维度元数据width/height/format帮你了解帧的形态与来源数据本体bytesencoded_format永远是统一的 JPEG 编码可直接消费曝光信息lens_aperture/sensor_exposure_time/sensor_sensitivity为更高级的帧分析留出空间。掌握supports_image_streaming() → start_image_stream() → on_stream_image 回调 → stop_image_stream()这条完整链路再结合初始化时指定ImageFormatGroup.JPEG的优化技巧你就能在 Flet 中稳定、高效地实现实时相机取流应用。进一步了解相关类型可继续阅读 ImageFormatGroup 与 CameraStateEvent 文档并在仓库 camera_playground 示例 中查看完整可运行代码。【免费下载链接】fletBuild realtime web, mobile and desktop apps in Python only. No frontend experience required.项目地址: https://gitcode.com/gh_mirrors/fl/flet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →