2011-02-03 4 views
10

Я пытаюсь захватить один кадр из камеры Apple iSight, встроенный в Macbook Pro, используя Python (версия 2.7 или 2.6) и PyObjC (версия 2.2).Как захватить кадры из Apple iSight с помощью Python и PyObjC?

В качестве отправной точки, я использовал this old StackOverflow вопрос. Чтобы убедиться, что это имеет смысл, я перекрестно ссылаюсь на пример Apple's MyRecorder, на котором, похоже, основывается. К сожалению, мой скрипт не работает.

Моих большие вопросы:

  • Am I инициализация камеры правильно?
  • Правильно ли я начинаю цикл событий?
  • Была ли какая-нибудь другая настройка, которую я должен был делать?

В приведенном ниже примере сценария предполагается, что после вызова функции startImageCapture() я должен начать печатать сообщения «Получил фрейм ...» из CaptureDelegate. Тем не менее, свет камеры никогда не включается, и обратный вызов делегата никогда не выполняется.

Кроме того, во время startImageCapture() нет сбоев, все функции утверждают, что они преуспели, и он успешно находит устройство iSight. Анализ объекта сеанса в pdb показывает, что он имеет допустимые объекты ввода и вывода, выход имеет назначенный делегат, устройство не используется другими процессами, а сеанс помечен как запущенный после вызова функции startRunning().

Вот код:

#!/usr/bin/env python2.7 

import sys 
import os 
import time 
import objc 
import QTKit 
import AppKit 
from Foundation import NSObject 
from Foundation import NSTimer 
from PyObjCTools import AppHelper 
objc.setVerbose(True) 

class CaptureDelegate(NSObject): 
    def captureOutput_didOutputVideoFrame_withSampleBuffer_fromConnection_(self, captureOutput, 
                      videoFrame, sampleBuffer, 
                      connection): 
     # This should get called for every captured frame 
     print "Got a frame: %s" % videoFrame 

class QuitClass(NSObject): 
    def quitMainLoop_(self, aTimer): 
     # Just stop the main loop. 
     print "Quitting main loop." 
     AppHelper.stopEventLoop() 


def startImageCapture(): 
    error = None 

    # Create a QT Capture session 
    session = QTKit.QTCaptureSession.alloc().init() 

    # Find iSight device and open it 
    dev = QTKit.QTCaptureDevice.defaultInputDeviceWithMediaType_(QTKit.QTMediaTypeVideo) 
    print "Device: %s" % dev 
    if not dev.open_(error): 
     print "Couldn't open capture device." 
     return 

    # Create an input instance with the device we found and add to session 
    input = QTKit.QTCaptureDeviceInput.alloc().initWithDevice_(dev) 
    if not session.addInput_error_(input, error): 
     print "Couldn't add input device." 
     return 

    # Create an output instance with a delegate for callbacks and add to session 
    output = QTKit.QTCaptureDecompressedVideoOutput.alloc().init() 
    delegate = CaptureDelegate.alloc().init() 
    output.setDelegate_(delegate) 
    if not session.addOutput_error_(output, error): 
     print "Failed to add output delegate." 
     return 

    # Start the capture 
    print "Initiating capture..." 
    session.startRunning() 


def main(): 
    # Open camera and start capturing frames 
    startImageCapture() 

    # Setup a timer to quit in 10 seconds (hack for now) 
    quitInst = QuitClass.alloc().init() 
    NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(10.0, 
                      quitInst, 
                      'quitMainLoop:', 
                      None, 
                      False) 
    # Start Cocoa's main event loop 
    AppHelper.runConsoleEventLoop(installInterrupt=True) 

    print "After event loop" 


if __name__ == "__main__": 
    main() 

Спасибо за любую помощь вы можете предоставить!

ответ

15

ОК, я провел день, погружаясь в глубины PyObjC и получил его работу.

Для будущей записи причина, по которой код в вопросе не работает: переменная область и сбор мусора. Операция была удалена, когда она выпала из области действия, которая произошла до запуска процессора событий. Что-то нужно сделать, чтобы сохранить его, чтобы он не освободился, прежде чем у него будет время для запуска.

Перемещение всего в класс и создание сеанса переменная класса запустила обратные вызовы. Кроме того, приведенный ниже код демонстрирует получение данных пикселя кадра в формате растрового изображения и сохранение его через вызовы Cocoa, а также как его копирование в мирное представление Python в виде буфера или строки.

Скрипт ниже будет захватывать отдельные кадры

#!/usr/bin/env python2.7 
# 
# camera.py -- by Trevor Bentley (02/04/2011) 
# 
# This work is licensed under a Creative Commons Attribution 3.0 Unported License. 
# 
# Run from the command line on an Apple laptop running OS X 10.6, this script will 
# take a single frame capture using the built-in iSight camera and save it to disk 
# using three methods. 
# 

import sys 
import os 
import time 
import objc 
import QTKit 
from AppKit import * 
from Foundation import NSObject 
from Foundation import NSTimer 
from PyObjCTools import AppHelper 

class NSImageTest(NSObject): 
    def init(self): 
     self = super(NSImageTest, self).init() 
     if self is None: 
      return None 

     self.session = None 
     self.running = True 

     return self 

    def captureOutput_didOutputVideoFrame_withSampleBuffer_fromConnection_(self, captureOutput, 
                      videoFrame, sampleBuffer, 
                      connection): 
     self.session.stopRunning() # I just want one frame 

     # Get a bitmap representation of the frame using CoreImage and Cocoa calls 
     ciimage = CIImage.imageWithCVImageBuffer_(videoFrame) 
     rep = NSCIImageRep.imageRepWithCIImage_(ciimage) 
     bitrep = NSBitmapImageRep.alloc().initWithCIImage_(ciimage) 
     bitdata = bitrep.representationUsingType_properties_(NSBMPFileType, objc.NULL) 

     # Save image to disk using Cocoa 
     t0 = time.time() 
     bitdata.writeToFile_atomically_("grab.bmp", False) 
     t1 = time.time() 
     print "Cocoa saved in %.5f seconds" % (t1-t0) 

     # Save a read-only buffer of image to disk using Python 
     t0 = time.time() 
     bitbuf = bitdata.bytes() 
     f = open("python.bmp", "w") 
     f.write(bitbuf) 
     f.close() 
     t1 = time.time() 
     print "Python saved buffer in %.5f seconds" % (t1-t0) 

     # Save a string-copy of the buffer to disk using Python 
     t0 = time.time() 
     bitbufstr = str(bitbuf) 
     f = open("python2.bmp", "w") 
     f.write(bitbufstr) 
     f.close() 
     t1 = time.time() 
     print "Python saved string in %.5f seconds" % (t1-t0) 

     # Will exit on next execution of quitMainLoop_() 
     self.running = False 

    def quitMainLoop_(self, aTimer): 
     # Stop the main loop after one frame is captured. Call rapidly from timer. 
     if not self.running: 
      AppHelper.stopEventLoop() 

    def startImageCapture(self, aTimer): 
     error = None 
     print "Finding camera" 

     # Create a QT Capture session 
     self.session = QTKit.QTCaptureSession.alloc().init() 

     # Find iSight device and open it 
     dev = QTKit.QTCaptureDevice.defaultInputDeviceWithMediaType_(QTKit.QTMediaTypeVideo) 
     print "Device: %s" % dev 
     if not dev.open_(error): 
      print "Couldn't open capture device." 
      return 

     # Create an input instance with the device we found and add to session 
     input = QTKit.QTCaptureDeviceInput.alloc().initWithDevice_(dev) 
     if not self.session.addInput_error_(input, error): 
      print "Couldn't add input device." 
      return 

     # Create an output instance with a delegate for callbacks and add to session 
     output = QTKit.QTCaptureDecompressedVideoOutput.alloc().init() 
     output.setDelegate_(self) 
     if not self.session.addOutput_error_(output, error): 
      print "Failed to add output delegate." 
      return 

     # Start the capture 
     print "Initiating capture..." 
     self.session.startRunning() 


    def main(self): 
     # Callback that quits after a frame is captured 
     NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(0.1, 
                       self, 
                       'quitMainLoop:', 
                       None, 
                       True) 

     # Turn on the camera and start the capture 
     self.startImageCapture(None) 

     # Start Cocoa's main event loop 
     AppHelper.runConsoleEventLoop(installInterrupt=True) 

     print "Frame capture completed." 

if __name__ == "__main__": 
    test = NSImageTest.alloc().init() 
    test.main() 
+0

Прохладный человек, кстати, вы должны пометить свой собственный, принятый как «принятый» :) –

+0

Этот скрипт отлично работает, но не работает при записи файла байта. Вы должны изменить open ('filename', 'w'), чтобы открыть ('filename', 'wb'), чтобы открыть файл в режиме байта, затем он работает. – andli

0

QTKit устарела и PyObjC является большой зависимостью (и, кажется, сложно построить, если вы хотите его в Homebrew). Плюс PyObjC не имеет большого количества AVFoundation, поэтому я создал a simple camera extension for Python, который использует AVFoundation для записи видео или снимка, для этого не требуется никаких зависимостей (промежуточные файлы Cython предназначены для того, чтобы избежать необходимости иметь Cython для большинства пользователей).

Должна быть обеспечена возможность построить его так:

pip install -e git+https://github.com/dashesy/pyavfcam.git 

Тогда мы можем использовать его для take a picture:

import pyavfcam 

# Open the default video source 
cam = pyavfcam.AVFCam(sinks='image') 
frame = cam.snap_picture('test.jpg') # frame is a memory buffer np.asarray(frame) can retrieve 

Не связанные с этим вопросом, но если AVFCam класс суб- классифицированные, переопределенные методы будут вызываться с результатом.

+0

Ошибка установки протокола для меня: '' pip install -e git + https: //github.com/dashesy/pyavfcam.git --editable = git + https: //github.com/dashy/pyavfcam. git - неправильный формат; он должен иметь # egg = Package'' – DanHickstein