11

Python (バージョン 2.7 または 2.6) と PyObjC (バージョン 2.2) を使用して、Macbook Pro に組み込まれた Apple iSight カメラから単一のフレームをキャプチャしようとしています。

出発点として、この古い StackOverflow の質問を使用しました。それが理にかなっていることを確認するために、基になっていると思われるApple の MyRecorder の例と相互参照しました。残念ながら、私のスクリプトは機能しません。

私の大きな質問は次のとおりです。

  • カメラを正しく初期化していますか?
  • イベントループを正しく開始していますか?
  • 私がすることになっていた他のセットアップはありましたか?

以下に貼り付けたスクリプトの例では、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()

ご協力いただきありがとうございます。

4

2 に答える 2

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()
于 2011-02-04T21:22:40.020 に答える
0

QTKitは推奨さPyObjCれておらず、大きな依存関係があります (HomeBrew でビルドするのは難しいようです)。PlusPyObjCにはほとんどの機能がなかったので、ビデオを録画したり写真を撮ったりするために使用するPython 用の単純なカメラ拡張機能AVFoundationを作成しました。依存関係は必要ありません (ほとんどのユーザーが Cython を使用する必要がないように、Cython 中間ファイルがコミットされています)。AVFoundation

次のようにビルドできるはずです。

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

次に、それを使用して写真を撮ることができます。

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クラスがサブクラス化されている場合、オーバーライドされたメソッドが結果で呼び出されます。

于 2015-09-14T21:40:37.890 に答える