HoloLens开发手记 - Unity之Locatable camera 使用相机
Enabling the capability for Photo Video Camera 启用相机能力
为了使用摄像头,我们必须启用WebCam能力。
- 在Unity中打开Player settings
- 展开Windows Store标签页
- 在"Publishing Settings > Capabilities"部分勾选WebCam能力
同一时刻只能执行一次相机操作。为了识别当前相机处在哪种模式下(拍照还是视频),你可以通过UnityEngine.VR.WSA.WebCam.Mode API检查。
Photo Capture 捕获照片
命名空间: UnityEngine.VR.WSA.WebCam
类型: PhotoCapture
PhotoCapture类允许我们使用相机拍摄静态照片。正常的使用模式如下:
- 创建PhotoCapture对象
- 使用我们想要的设置来创建一个CameraParameters对象
- 调用StartPhotoModeAsync()方法开始拍照模式
- 拍摄想要的照片
- (可选项)进一步处理捕获的图像
- 关闭拍照模式并释放资源
Common Set Up for PhotoCapture 使用PhotoCapture的通用做法
对于任何拍照方式,开始步骤都是像下面这样:
创建PhotoCapture对象
PhotoCapture photoCaptureObject = null; void Start() { PhotoCapture.CreateAsync(false, OnPhotoCaptureCreated); }
存储对象,配置拍摄参数并开始拍照模式
void OnPhotoCaptureCreated(PhotoCapture captureObject) { photoCaptureObject = captureObject; Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); CameraParameters c = new CameraParameters(); c.hologramOpacity = 0.0f; c.cameraResolutionWidth = cameraResolution.width; c.cameraResolutionHeight = cameraResolution.height; c.pixelFormat = CapturePixelFormat.BGRA32; captureObject.StartPhotoModeAsync(c, false, OnPhotoModeStarted); }
最后关闭相机时需要使用同样的清理代码
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result) { photoCaptureObject.Dispose(); photoCaptureObject = null; }
完成这些步骤后,你可以挑选使用哪种方式捕获照片。
Capture a Photo to a File 捕获照片到文件
最简单的做法是直接将照片捕获到文件。照片可以被存储为PNG或JPG文件。
如果我们成功开始了拍照模式,需要拍照并将照片存储到磁盘上,做法如下:
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result) { if (result.success) { string filename = string.Format(@"CapturedImage{0}_n.jpg", Time.time); string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename); photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk); } else { Debug.LogError("Unable to start photo mode!"); } }
完成捕获照片到文件的操作后,需要退出拍照模式并清理资源
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result) { if (result.success) { Debug.Log("Saved Photo to disk!"); photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); } else { Debug.Log("Failed to save Photo to disk"); }
Capture a Photo to a Texture2D 捕获文件到Texture2D对象
我们可以把捕获的照片保存为Texture2D对象,做法和保存到文件类似。步骤如下:
在OnPhotoModeStarted()方法中,捕获一帧图像到内存中。
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result) { if (result.success) { photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory); } else { Debug.LogError("Unable to start photo mode!"); } }
我们需要把得到的结果赋给Texture2D对象,然后清理相机资源
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { if (result.success) { // 使用正确分辨率创建Texture2D对象 Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height); // 将图像数据拷贝到Texture2D对象中 photoCaptureFrame.UploadImageDataToTexture(targetTexture); // 进一步使用Texture2D对象,比如赋给材质神马的 } // 清理相机 photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); }
Capture a Photo and Interact with the Raw bytes 捕获照片并和原始数据交互
为了在内存中操作图像原始数据,需要的步骤和捕获图片到Texture2D类似,不同之处在于,OnCapturedPhotoToMemory()方法里可以获得图像原始数据并操作它们。
在本示例中,我们会创建一个List<Color>用来进一步处理或者直接通过SetPixels()方法来应用于Texture2D对象。
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { if (result.success) { List<byte> imageBufferList = new List<byte>(); // 复制原始 IMFMediaBuffer 数据到空的list photoCaptureFrame.CopyRawImageDataIntoBuffer(imageBufferList); //本例使用 BGRA32 格式捕获照片. int stride = 4; float denominator = 1.0f / 255.0f; List<Color> colorArray = new List<Color>(); for (int i = imageBufferList.Count - 1; i >= 0; i -= stride) { float a = (int)(imageBufferList[i - 0]) * denominator; float r = (int)(imageBufferList[i - 1]) * denominator; float g = (int)(imageBufferList[i - 2]) * denominator; float b = (int)(imageBufferList[i - 3]) * denominator; colorArray.Add(new Color(r, g, b, a)); } // 接下来可以把list用做进一步的处理 } photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); }
Video Capture 捕获视频
命名空间: UnityEngine.VR.WSA.WebCam
类型: VideoCapture
捕获视频的用法和捕获照片类似,不同的地方在于你必须指定一个帧率(FPS)并且你只能以MP4格式把视频直接存储到磁盘上。步骤如下:
- 创建VideoCapture对象
- 使用我们想要的设置来创建一个CameraParameters对象
- 调用 StartVideoModeAsync()方法开始视频捕获模式
- 开始录制视频
- 停止录制视频
- 停止视频捕获模式并释放相机资源
创建并配置VideoCapture对象
void Start () { VideoCapture.CreateAsync(false, OnVideoCaptureCreated); } void OnVideoCaptureCreated (VideoCapture videoCapture) { if (videoCapture != null) { m_VideoCapture = videoCapture; Resolution cameraResolution = VideoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); float cameraFramerate = VideoCapture.GetSupportedFrameRatesForResolution(cameraResolution).OrderByDescending((fps) => fps).First(); CameraParameters cameraParameters = new CameraParameters(); cameraParameters.hologramOpacity = 0.0f; cameraParameters.frameRate = cameraFramerate; cameraParameters.cameraResolutionWidth = cameraResolution.width; cameraParameters.cameraResolutionHeight = cameraResolution.height; cameraParameters.pixelFormat = CapturePixelFormat.BGRA32; m_VideoCapture.StartVideoModeAsync(cameraParameters, VideoCapture.AudioState.None, OnStartedVideoCaptureMode); } else { Debug.LogError("Failed to create VideoCapture Instance!"); } }
一旦配置完成VideoCapture对象,我们就开始录制视频
void OnStartedVideoCaptureMode(VideoCapture.VideoCaptureResult result) { if (result.success) { string filename = string.Format("MyVideo_{0}.mp4", Time.time); string filepath = System.IO.Path.Combine(Application.persistentDataPath, filename); m_VideoCapture.StartRecordingAsync(filepath, OnStartedRecordingVideo); } }
在开始录制后,我们需要更新UI或者行为来确保可以停止视频捕获。在这里我们只输出log。
void OnStartedRecordingVideo(VideoCapture.VideoCaptureResult result) { Debug.Log("Started Recording Video!"); // 我们将会通过一些方式来停止视频捕获,比如计时器或者点击手势等等 }
最后我们需要停止视频捕获,可以中国定时器或者其他输入方式来实现。
void StopRecordingVideo() { m_VideoCapture.StopRecordingAsync(OnStoppedRecordingVideo); }
一旦视频捕获停止后,需要及时退出视频捕获模式并释放相机资源。
void OnStoppedRecordingVideo(VideoCapture.VideoCaptureResult result) { Debug.Log("Stopped Recording Video!"); m_VideoCapture.StopVideoModeAsync(OnStoppedVideoCaptureMode); } void OnStoppedVideoCaptureMode(VideoCapture.VideoCaptureResult result) { m_VideoCapture.Dispose(); m_VideoCapture = null; }
Troubleshooting 问题诊断
-
获取不到分辨率
- 确保你在项目中启用了WebCam能力
- 全息图像不能捕获到图片或者视频中
- 未来的更新会支持捕获全息图像
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?