MacOS下基于AVFoundation的Python图像捕获函数静默失败求助
MacOS下PyObjC调用AVFoundation捕获图像失败的解决方案
修复后的完整代码
import os import AVFoundation import Foundation import time from threading import Semaphore def capture_image(unique_id, dest_filename): # 初始化信号量,用于等待异步捕获完成 semaphore = Semaphore(0) # 设置AVFoundation捕获会话 session = AVFoundation.AVCaptureSession.alloc().init() # 获取所有设备并过滤视频设备(替代废弃的devicesWithMediaType_) devices = AVFoundation.AVCaptureDevice.devices() device = None for dev in devices: if dev.hasMediaType_(AVFoundation.AVMediaTypeVideo) and dev.uniqueID() == unique_id: device = dev break if device is None: raise ValueError(f"No camera found with unique ID: {unique_id}") # 创建设备输入并处理错误 input_session, error = AVFoundation.AVCaptureDeviceInput.deviceInputWithDevice_error_(device, None) if error is not None: raise RuntimeError(f"Failed to create capture input: {error}") output_session = AVFoundation.AVCaptureStillImageOutput.alloc().init() # 应用JPG编码输出设置 output_settings = {AVFoundation.AVVideoCodecKey: AVFoundation.AVVideoCodecTypeJPEG} output_session.setOutputSettings_(output_settings) session.addInput_(input_session) session.addOutput_(output_session) session.startRunning() # 等待相机启动就绪(可根据实际情况调整时长) time.sleep(1) # 定义捕获完成回调 def capture_completion(buffer, error): if error is not None: print(f"Capture error: {error}") semaphore.release() return # 从缓冲区提取JPG数据并写入文件 image_data = AVFoundation.AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation_(buffer) success, write_error = image_data.writeToFile_options_error_(dest_filename, Foundation.NSDataWritingAtomic, None) if not success and write_error is not None: print(f"Failed to write image: {write_error}") semaphore.release() # 异步捕获图像 connection = output_session.connectionWithMediaType_(AVFoundation.AVMediaTypeVideo) output_session.captureStillImageAsynchronouslyFromConnection_completionHandler_(connection, capture_completion) # 等待捕获逻辑执行完毕 semaphore.acquire() # 释放会话资源 session.stopRunning() session.release() output_session.release() input_session.release() print(f'writing {dest_filename}') return dest_filename if __name__ == '__main__': # 确保目标目录存在 os.makedirs('/tmp/images', exist_ok=True) images_dict = { '0x143141300bda5829': '/tmp/images/image_1.jpg', '0x143141100bda5829': '/tmp/images/image_2.jpg', '0x143200000bda5829': '/tmp/images/image_3.jpg', '0x143300000bda5829': '/tmp/images/image_4.jpg', '0x143121200bda5829': '/tmp/images/image_5.jpg', } list_of_image_files_written = [] for the_unique_id, dest_path in images_dict.items(): try: captured_path = capture_image(the_unique_id, dest_path) if os.path.exists(captured_path): list_of_image_files_written.append(os.path.basename(captured_path)) except Exception as e: print(f"Failed to capture image for {the_unique_id}: {e}") print(f'{list_of_image_files_written=}')
关键问题修复说明
1. 缺失Foundation框架导入
原代码使用NSData和NSDataWritingAtomic但未导入Foundation框架,直接导致运行时失败。添加import Foundation即可解决该问题。
2. 异步捕获未等待完成
captureStillImageAsynchronouslyFromConnection_completionHandler_是异步执行的,原代码调用后立即停止会话,此时捕获逻辑还未执行就被终止,导致图像无法写入。使用Semaphore信号量等待回调执行完毕后再停止会话,确保捕获流程完成。
3. 输出设置未生效
原代码定义了output_settings但未调用output_session.setOutputSettings_()绑定到输出会话,导致JPG编码设置未生效。
4. 废弃API替换
AVCaptureDevice.devicesWithMediaType_在新版本AVFoundation中已废弃,改用devices()获取所有设备后,通过hasMediaType_过滤视频设备。
5. 错误处理补充
添加了设备输入创建失败、捕获错误、文件写入错误的处理逻辑,便于排查具体失败原因。
6. PyCharm未解析引用问题
PyCharm对PyObjC的动态Objective-C绑定支持有限,出现的未解析引用提示不影响运行。若要消除提示,可安装类型存根包:
pip install pyobjc-stubs
7. 摄像头权限检查
确保运行程序的进程(如PyCharm)拥有摄像头访问权限:
- 打开系统设置 → 隐私与安全性 → 摄像头
- 确认PyCharm已在列表中并勾选权限
额外注意事项
- 多次调用捕获函数时,需确保前一个会话资源完全释放后再创建新会话,避免设备资源冲突。
time.sleep()的时长可根据相机启动速度调整,无需固定2秒。- 提前创建目标目录,避免因目录不存在导致写入失败。
内容的提问来源于stack exchange,提问作者mcgregor94086
相关产品推荐
相关产品推荐

