You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 21:13:22