2011-02-03 13 views
10

Próbuję uchwycić pojedynczą klatkę z kamery iSight wbudowanej w Jabłko MacBook Pro przy użyciu Python (wersja 2.7 lub 2.6) i PyObjC (wersja 2.2).Jak przechwytywać ramki z Apple iSight za pomocą Python i PyObjC?

Jako punkt wyjścia, użyłem this old StackOverflow pytanie. Aby sprawdzić, czy ma to sens, odwołałem się do przykładu Apple's MyRecorder, który wydaje się być oparty na. Niestety mój skrypt nie działa.

Moje wielkie pytania:

  • Am I inicjalizacji aparat poprawnie?
  • Czy poprawnie uruchamiam pętlę zdarzeń?
  • Czy istnieje jakakolwiek inna konfiguracja miałem zrobić?

W poniższym skrypcie przykładowym zamierzona operacja polega na tym, że po wywołaniu funkcji startImageCapture() powinienem rozpocząć drukowanie wiadomości "Mam ramkę ..." z CaptureDelegate. Jednak lampka aparatu nigdy się nie włącza, a wywołanie zwrotne uczestnika nie jest nigdy wykonywane.

Ponadto, nie ma żadnych uszkodzeń podczas startImageCapture(), wszystkie funkcje twierdzą, że się uda, i to z powodzeniem wyszukuje urządzenie iSight. Analizowanie obiektu sesji w pdb pokazuje, że ma on poprawne obiekty wejściowe i wyjściowe, dane wyjściowe mają przypisanego delegata, urządzenie nie jest używane przez inne procesy, a sesja jest oznaczana jako działająca po wywołaniu funkcji startRunning().

Oto kod:

#!/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() 

Dzięki za wszelką pomoc można zapewnić!

Odpowiedz

15

OK, spędziłem dzień nurkowania w głąb PyObjC i dostał pracę.

Dla przyszłego zapisu, dlaczego kod w pytaniu nie zadziałał: zakres zmiennych i wywóz śmieci. Zmieniono zmienną , która wypadła z zakresu, który nastąpił przed uruchomieniem procesora zdarzeń. Trzeba coś zrobić, aby go zatrzymać, aby nie został uwolniony, zanim zdąży się uruchomić.

Przeniesienie wszystkiego na klasę i utworzenie zmiennej spowodowało, że połączenia zwrotne zaczęły działać. Dodatkowo, poniższy kod demonstruje pobranie danych pikseli klatki do formatu bitmapy i zapisanie ich za pomocą wywołań Cocoa, a także jak skopiować go z powrotem do widoku światowego Pythona jako bufor lub ciąg znaków.

Poniższy skrypt przechwycić pojedynczą klatkę

#!/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

Cool man, btw należy oznaczyć własną akceptację jako "accept" :) –

+0

Ten skrypt działa świetnie, ale kończy się niepowodzeniem podczas zapisywania pliku bajtowego. Powinieneś zmienić otwarty ("filename", "w"), aby otworzyć ("filename", "wb"), aby otworzyć plik w trybie bajtowym, to działa. – andli

0

QTKit jest nieaktualna i PyObjC jest duża zależność (i wydaje się być trudne do zbudowania, jeśli chcesz go w homebrew). Plus PyObjC nie miał większości AVFoundation więc stworzyłem a simple camera extension for Python który używa AVFoundation, aby nagrać film lub zrobić zdjęcie, nie wymaga żadnych zależności (Cython pliki pośrednie zostały popełnione w celu uniknięcia konieczności posiadania Cython dla większości użytkowników).

Powinno być możliwe, aby zbudować go tak:

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

Wtedy możemy użyć go do 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 

nie związane z tym pytanie, ale jeśli klasa AVFCam jest pod- zaklasyfikowane, nadpisane metody zostaną wywołane z wynikiem.

+0

Instalacja pip nie powiedzie się dla mnie: '' pip install -e git + https: //github.com/dashesy/pyavfcam.git --editable = git + https: //github.com/dashesy/pyavfcam. git nie jest właściwym formatem; musi mieć # egg = Package'' – DanHickstein