Windows-universal-samples 摄像头人脸检测示例:用 MediaCapture 与 Face Detection effect 构建完整相机应用

发布时间:2026/9/25 3:03:50
Windows-universal-samples 摄像头人脸检测示例:用 MediaCapture 与 Face Detection effect 构建完整相机应用 示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载archived/CameraFaceDetection是 Windows-universal-samples 仓库中一个基于 JavaScriptWinJS的归档相机示例它展示了如何使用Windows.Media.Capture命名空间编写一个端到端的相机应用从MediaCapture对象的生命周期管理、前置摄像头获取、预览镜像到设备旋转处理与拍照方向元数据再到调用 Windows 内置的 Face Detection effect 并在预览上叠加人脸边界框。读完本文你将掌握该示例的完整架构、人脸检测效果的创建/配置/注销全流程以及预览坐标到 UI 坐标换算的关键算法并能在 Visual Studio 中构建运行它或在Samples/CameraFaceDetection下查看其维护中的 C/C# 对应版本。示例定位与仓库内文件结构该示例基于仓库中的基础相机示例 CameraStarterKit在其之上叠加了人脸检测功能。示例说明中强调它实现了贴近真实相机应用体验的自定义 UI因此面向开发者的提示信息都输出到调试控制台而非界面弹窗。归档示例位于archived/CameraFaceDetection仅保留 JavaScriptWinJS版本核心文件包括camera.js全部相机与人脸检测逻辑约 942 行camera.html预览video元素、人脸叠加canvas以及拍照/录像/人脸检测三个WinJS.UI.AppBarCommand按钮Package.appxmanifest包清单声明了picturesLibrary、webcam、microphone三个能力目标最低系统版本为10.0.14393.0CameraFaceDetection.sln 与 CameraFaceDetection.jsprojVisual Studio 解决方案与 JS 项目文件。需要说明的是这是归档archived示例仓库中仍在维护的 C/C#/VB 版本位于 Samples/CameraFaceDetection两者架构一致下文会指出对应实现的位置便于交叉对照。示例要解决的八个核心问题原文档列出了该示例演示的全部能力这也是理解整个代码结构的骨架管理 MediaCapture 对象贯穿应用生命周期与导航事件初始化、清理、最小化/恢复获取设备特定侧的摄像头示例优先获取前置摄像头Panel.front启动与停止预览包括对前置摄像头的镜像处理拍摄普通照片并保存到文件计入设备方向的旋转元数据处理旋转事件同时处理设备在空间中的转动和页面在屏幕上的方向变化并修正预览流旋转处理 MediaCapture 的 Failed 事件出错时清理 MediaCapture 实例管理 Face Detection effect创建、配置、启用/停用、注册事件、清理渲染人脸边界框作为叠加层绘制在相机预览上且要考虑镜像与旋转。MediaCapture 生命周期管理应用启动时app.onactivated调用initializeCameraAsync()恢复挂起时resuming事件同样重新初始化oncheckpoint即将挂起与onunload关闭时调用cleanupCameraAsync()。这一成对管理在 camera.js 中实现oMediaCapture new Capture.MediaCapture(); // 注册错误与录制时长上限通知 oMediaCapture.addEventListener(recordlimitationexceeded, mediaCapture_recordLimitationExceeded); oMediaCapture.addEventListener(failed, mediaCapture_failed); var settings new Capture.MediaCaptureInitializationSettings(); settings.videoDeviceId camera.id; settings.streamingCaptureMode Capture.StreamingCaptureMode.audioAndVideo; return oMediaCapture.initializeAsync(settings) .then(function () { isInitialized true; startPreview(); updateCaptureControls(); }, function (error) { console.log(error.message); });初始化流程为找到摄像头 → 创建MediaCapture→ 注册failed与recordlimitationexceeded事件 → 通过MediaCaptureInitializationSettings指定videoDeviceId和streamingCaptureMode音频视频→initializeAsync成功后启动预览。清理逻辑camera.js按序处理若正在录像则先stopRecordingAsync()保存录像若存在人脸检测效果则先cleanUpFaceDetectionEffectAsync()停止预览WinJS.Promise.join等所有任务完成后解除事件监听并调用oMediaCapture.close()。这种先停流、再摘效果、最后关设备的顺序是避免底层媒体管道报脏的关键。维护中的 C 版本同样实现了CleanupCameraAsync中先清理人脸检测效果再停止预览的顺序见 MainPage.xaml.cpp。按面板位置选择摄像头并决定镜像策略示例优先获取前置摄像头实现于findCameraDeviceByPanelAsync()camera.jsreturn DeviceInformation.findAllAsync(DeviceClass.videoCapture) .then(function (devices) { devices.forEach(function (cameraDeviceInfo) { if (cameraDeviceInfo.enclosureLocation ! null cameraDeviceInfo.enclosureLocation.panel panel) { deviceInfo cameraDeviceInfo; return; } }); // 找不到匹配面板时退化为返回列表中的第一个设备 if (!deviceInfo devices.length 0) { deviceInfo devices.getAt(0); } return deviceInfo; });拿到设备后还要判断它是否为内置摄像头若enclosureLocation为空或面板为unknown则视为外接摄像头externalCamera true——外接摄像头不随设备转动因此拍照时不需要附加设备旋转只有内置前摄像头才开启预览镜像mirroringPreview。这个标志后续会贯穿预览镜像、按钮旋转修正和人脸框镜像三处逻辑。预览启动、旋转校正与镜像startPreview()camera.js先通过DisplayRequest.requestActive()阻止屏幕休眠再把MediaCapture通过URL.createObjectURL绑定到video元素并播放若需镜像则对预览元素应用scale(-1, 1)变换。playing事件触发后读取预览流的编码属性并调用setPreviewRotationAsync()。旋转校正是相机应用中容易出错的部分。示例维护两套方向状态oDeviceOrientationSimpleOrientationSensor报告的设备在空间中的朝向和oDisplayOrientationDisplayInformation报告的页面在屏幕上的朝向。setPreviewRotationAsync()将显示方向换算成角度注意预览被镜像时角度要取反var rotationDegrees convertDisplayOrientationToDegrees(oDisplayOrientation); // 预览镜像时需要反转旋转方向 if (mirroringPreview) { rotationDegrees (360 - rotationDegrees) % 360; } var props oMediaCapture.videoDeviceController .getMediaStreamProperties(Capture.MediaStreamType.videoPreview); props.properties.insert(RotationKey, rotationDegrees); return oMediaCapture.setEncodingPropertiesAsync(Capture.MediaStreamType.videoPreview, props, null);其中RotationKey是媒体属性 GUIDC380465D-2271-428C-9B83-ECEA3B4A85C1即MF_MT_VIDEO_ROTATION把旋转以元数据形式写入预览流可保证渲染与取帧时宽高比一致。displayInformation_orientationChanged事件页面旋转例如AutoRotationPreferences无法生效时会再次调用该方法并同步更新按钮角度。setupUiAsync()camera.js还做了两件体验相关的设置用DisplayInformation.autoRotationPreferences landscape锁定页面方向避免CaptureElement随页面旋转以及把拍照/录像保存目录定位到图片库KnownLibraryId.pictures不可用时回退到ApplicationData.current.localFolder。设备方向的处理还有一个值得注意的细节camera.js当传感器报告faceup/facedown设备平行于地面时保留上一次的方向值这样用户可以先摆好方向再把相机对准桌面文件FaceUp或天花板FaceDown拍照。getCameraOrientation()camera.js则集中处理了两类修正竖屏优先portrait-first设备上传感器天然有 90° 安装偏移需要整体偏移一次方向前置镜像情况下 90°/270° 互换。这些换算的 C 版本可对照 MainPage.xaml.cpp 中的GetCameraOrientation相关实现。Face Detection effect 的创建、配置与事件这是示例区别于基础相机应用的核心部分。人脸检测以视频效果的形式插入到预览流中创建流程在createFaceDetectionEffectAsync()camera.jsfunction createFaceDetectionEffectAsync() { // 创建效果定义包含初始化设置 var definition new Windows.Media.Core.FaceDetectionEffectDefinition(); // 为保证预览流畅不阻塞同步等待输入样本 definition.synchronousDetectionEnabled false; // 本场景下检测速度优先于精度 definition.detectionMode Windows.Media.Core.FaceDetectionMode.highPerformance; // 将效果添加到预览流 return oMediaCapture.addVideoEffectAsync(definition, Capture.MediaStreamType.videoPreview) .then(function (extension) { oFaceDetectionEffect extension; // 注册人脸检测事件 oFaceDetectionEffect.addEventListener(facedetected, faceDetectionEffect_FaceDetected); // 选择最短的检测事件间隔 oFaceDetectionEffect.desiredDetectionInterval 33; // 毫秒 // 开始检测人脸 oFaceDetectionEffect.enabled true; }); }三个配置参数的取舍在注释中交代得很清楚可归纳为参数取值含义synchronousDetectionEnabledfalse异步检测不延迟预览帧到达保证预览流畅detectionModehighPerformance速度优先于精度desiredDetectionInterval33毫秒约 30fps检测事件的最小间隔C 版本中同样的逻辑可见于 MainPage.xaml.cpp其中DesiredDetectionInterval是TimeSpan类型33 毫秒写作duration.Duration 330000单位为 100 纳秒阅读源码时注意这个单位差异。效果的停用与清理在cleanUpFaceDetectionEffectAsync()camera.js中按相反顺序进行enabled false→ 移除facedetected事件 → 清空画布上残留的方框 →oMediaCapture.removeEffectAsync(effect)→ 将成员变量置空。效果只加在videoPreview流上因此不影响拍照与录像的输出。UI 上faceDetectionButton_tapped()根据效果是否存在/是否启用在创建与清理两个动作间切换camera.js并通过updateCaptureControls()同步按钮图标contact/contact2。人脸边界框叠加层坐标换算与镜像FaceDetected事件回调拿到的是预览分辨率坐标系下的faceBox而绘制目标是一个覆盖整个窗口的canvascamera.html 中的#facesCanvas两者分辨率不同且预览控件可能因缩放模式Uniform出现 letterbox黑边因此需要两步换算。第一步getPreviewStreamRectInControl()camera.js计算预览流在控件内实际占用的矩形横屏方向下宽高直接取流分辨率竖屏方向下先交换宽高再比较窗口宽高比与流宽高比决定 letterbox 出现在左右还是上下并据此求出x/y/width/height。第二步convertPreviewToUiRectangle()camera.js按线性比例把方框从流坐标映射到 UI 坐标// 将预览流坐标缩放为窗口坐标 result.width (faceBoxInPreviewCoordinates.width / streamWidth) * previewInUI.width; result.height (faceBoxInPreviewCoordinates.height / streamHeight) * previewInUI.height; result.x (faceBoxInPreviewCoordinates.x / streamWidth) * previewInUI.width; result.y (faceBoxInPreviewCoordinates.y / streamHeight) * previewInUI.height;highlightDetectedFaces()camera.js在每次事件到达时把画布拉伸到窗口尺寸、平移预览内容区域的偏移、清屏后逐个描边线宽 3 像素第一张人脸用蓝色#0000FF高亮、其余用#00BFFF若预览正在镜像画布整体再应用scale(-1, 1)使方框与镜像后的预览对齐。这就是原文档第 8 点考虑镜像和旋转渲染人脸边界框的完整实现。拍照与录像中的方向元数据拍照流程camera.js先经capturePhotoToStreamAsync把 JPEG 拍到内存流再用reencodeAndSavePhotoAsync()重编码写文件——重编码的目的一直是写入方向元数据var properties new Imaging.BitmapPropertySet(); properties.insert(System.Photo.Orientation, new Imaging.BitmapTypedValue(orientation, Windows.Foundation.PropertyType.uint16)); return bitmapEncoder.bitmapProperties.setPropertiesAsync(properties);其中orientation由convertOrientationToPhotoOrientation(getCameraOrientation())计算把SimpleOrientation映射为PhotoOrientation的normal/rotate90/rotate180/rotate270设备与显示两层修正外接摄像头、portrait-first 偏移、前置镜像取反都已在getCameraOrientation()中集中完成。录像侧startRecordingAsync()则把旋转角度直接写进 MP4 编码 profilevar rotationAngle 360 - convertDeviceOrientationToDegrees(getCameraOrientation()); var encodingProfile MediaProperties.MediaEncodingProfile .createMp4(VideoEncodingQuality.auto); encodingProfile.video.properties.insert(RotationKey, rotationAngle); return oMediaCapture.startRecordToStorageFileAsync(encodingProfile, file);另外两处体验细节拍照按钮在拍照瞬间会依据mediaCaptureSettings.concurrentRecordAndPhotoSupported决定录像按钮是否仍可点击部分相机不支持边录边拍手机设备上还注册了Windows.Phone.UI.Input.HardwareButtons.CameraPressed事件使物理相机键也能触发拍照。应用最小化通过SystemMediaTransportControls的 mute 状态推断时调用cleanupCameraAsync()恢复时重新初始化——这是把 MediaCapture 管理与导航/前后台事件挂钩的具体做法。构建与运行系统要求沿用原文档硬件摄像头Camera客户端Windows 10服务器Windows Server 2016 Technical Preview手机Windows 10 Phone构建步骤若下载的是整个样本集 ZIP务必解压全部文件不要只解压目标示例文件夹否则会丢失共享依赖启动 Visual Studio原文档基于 Visual Studio 2017选择FileOpenProject/Solution在解压目录的对应语言子文件夹下双击.sln文件——本示例为archived/CameraFaceDetection/js/CameraFaceDetection.sln按 CtrlShiftB或选择BuildBuild Solution。运行步骤仅部署选择BuildDeploy Solution部署并运行按 F5调试运行或 CtrlF5不调试运行对应菜单为DebugStart Debugging/Start Without Debugging。由于该示例依赖真实摄像头与方向传感器建议在带前置摄像头的 Windows 10 桌面设备或模拟器启用相机模拟上运行若findCameraDeviceByPanelAsync找不到前置摄像头它会退化为使用列表中第一个相机此时镜像逻辑不生效。关联资源基础相机示例本示例的蓝本archived/CameraStarterKit维护中的多语言版本C/C#/VBC 人脸检测实现见 MainPage.xaml.cppSamples/CameraFaceDetection仓库根目录说明README.md从源码结构看归档 JS 版与Samples/CameraFaceDetection的 C 版在关键路径上完全同构FindCameraDeviceByPanelAsync、CreateFaceDetectionEffectAsync、ConvertPreviewToUiRectangle等函数一一对应因此本文基于 JS 版源码的坐标换算与效果管理分析同样适用于阅读 C 实现。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐MCP Toolbox for Databases 的 bigtable-list-logical-views 工具列出 Bigtable 逻辑视图的配置、调用链与源码解析MCP Toolbox for Databases 的 bigtable list logical views 工具列出 Bigtable 逻辑视图的配置、调示例工程UWP 人脸检测实战基于 FaceDetector 在 Windows-universal-samples 中检测照片与摄像头帧中的人脸UWP 人脸检测实战基于 FaceDetector 在 Windows universal samples 中检测照片与摄像头帧中的人脸 本文以 Window示例工程Chrome Apps 摄像头捕获示例camera-capture详解用 getUserMedia 与 videoCapture 权限构建相机应用Chrome Apps 摄像头捕获示例camera capture详解用 getUserMedia 与 videoCapture 权限构建相机应用 导读示例工程上一篇螺旋矩阵 II 四边界收缩解法详解以 LeetCode-Book 的 lc_59 实现为例下一篇从0到1构建Windows包仓库GitHub_Trending/wi/winget-pkgs私有部署指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考