Sunday 26 March 2023

MacOS capture of still image from AVFoundation camera silently fails

Problem: I am trying to use a python function, capture_image(unique_id, dest_filename), for MacOS that is supposed to capture a still image from a video camera using the AVFoundation framework and the pyobjc-framework-AVFoundation framework.

Expected result: Given an AVFoundation unique_id and a dest_filename passed as parameters, a JPG still image should be captured from the camera with AVFoundation unique id. The image should be written to the JPG file with the name dest_filename.

In my test I called this function 5 times and I expected to see this output:

writing /tmp/images/image_1.jpg
writing /tmp/images/image_2.jpg
writing /tmp/images/image_3.jpg
writing /tmp/images/image_4.jpg
writing /tmp/images/image_5.jpg
list_of_image_files_written=['image_1.jpg','image_2.jpg','image_3.jpg','image_4.jpg','image_5.jpg']

Process finished with exit code 0

Observed result: The function silently fails to write the captured image to the desired file.

No Runtime errors are thrown.

In my test I called this function 5 times and I actually saw output (note that image directory is actually empty - no JPGs were actually written:

writing /tmp/images/image_1.jpg
writing /tmp/images/image_2.jpg
writing /tmp/images/image_3.jpg
writing /tmp/images/image_4.jpg
writing /tmp/images/image_5.jpg
list_of_image_files_written=[]

Process finished with exit code 0

The Pycharm IDE does show these compile time errors and warnings:

  • Unresolved reference 'NSData': 31
  • Unresolved reference 'NSDataWritingAtomic': 32
  • Cannot find reference 'AVCaptureSession' in '__init__py | __init__py: 8
  • Cannot find reference 'AVCaptureDevice' in '__init__py | __init__py: 9
  • Cannot find reference 'AVMediaTypeVideo' in '__init__py | __init__py: 9
  • Cannot find reference 'AVCaptureDeviceInput' in '__init__py | __init__py: 17
  • Cannot find reference 'AVCaptureStillImageOutput' in '__init__py | __init__py: 18
  • Cannot find reference 'AVMediaTypeVideo' in '__init__py | __init__py: 27
  • Cannot find reference 'AVVideoCodecKey' in '__init__py | __init__py: 28
  • Cannot find reference 'AVVideoCodecTypeJPEG' in '__init__py | __init__py: 28
  • Cannot find reference 'AVCaptureStillImageOutput' in '__init__py | __init__py: 31
  • Local variable 'output_settings' value is not used: 28
  • PEP 8: E128 continuation line under_indented for visual indent: 30
  • PEP 8: E128 continuation line under_indented for visual indent: 31

I am running on MacOS Ventura 13.2.1 using PyCharm.

I have searched the AVFoundation webpages as well as OpenStack and google, but without success in finding a better example.

The code:

import os

import AVFoundation
import time


def capture_image(unique_id, dest_filename):
    # Set up AVFoundation capture session
    session = AVFoundation.AVCaptureSession.alloc().init()
    devices = AVFoundation.AVCaptureDevice.devicesWithMediaType_(AVFoundation.AVMediaTypeVideo)
    device = None
    for dev in devices:
        if dev.uniqueID() == unique_id:
            device = dev
            break
    if device is None:
        raise ValueError("No camera found with unique ID: " + unique_id)
    input_session = AVFoundation.AVCaptureDeviceInput.deviceInputWithDevice_error_(device, None)[0]
    output_session = AVFoundation.AVCaptureStillImageOutput.alloc().init()
    session.addInput_(input_session)
    session.addOutput_(output_session)
    session.startRunning()

    # Wait for the capture to be ready
    time.sleep(2)

    # Capture the image
    connection = output_session.connectionWithMediaType_(AVFoundation.AVMediaTypeVideo)
    output_settings = {AVFoundation.AVVideoCodecKey: AVFoundation.AVVideoCodecTypeJPEG}
    output_session.captureStillImageAsynchronouslyFromConnection_completionHandler_(connection,
        lambda buffer, error:
            NSData.dataWithData_(AVFoundation.AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation_(buffer))
            .writeToFile_options_error_(dest_filename, NSDataWritingAtomic, None))

    # Stop the session
    session.stopRunning()
    print(f'writing {dest_filename}')
    return dest_filename


if __name__ == '__main__':
    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',
    }
    for the_unique_id in images_dict:
        capture_image(the_unique_id, images_dict[the_unique_id])
    list_of_image_files_written = os.listdir('/tmp/images')
    print(f'{list_of_image_files_written=}')


from MacOS capture of still image from AVFoundation camera silently fails

No comments:

Post a Comment