python - OSX पर अलग प्रक्रिया में वेबकैम कैसे पढ़ सकता है?




macos opencv (2)

मैं OSX पर एक वेबकैम पढ़ रहा हूँ, जो इस सरल स्क्रिप्ट के साथ ठीक काम करता है:

import cv2
camera = cv2.VideoCapture(0)

while True:
    try:
        (grabbed, frame) = camera.read()  # grab the current frame
        frame = cv2.resize(frame, (640, 480))  # resize the frame
        cv2.imshow("Frame", frame)  # show the frame to our screen
        cv2.waitKey(1)  # Display it at least one ms before going to the next frame
    except KeyboardInterrupt:
        # cleanup the camera and close any open windows
        camera.release()
        cv2.destroyAllWindows()
        print "\n\nBye bye\n"
        break

अब मैं एक अलग प्रक्रिया में वीडियो को पढ़ना चाहता हूं जिसके लिए मुझे स्क्रिप्ट है जो बहुत लंबा है और जो लिनक्स पर एक अलग प्रक्रिया में वीडियो को सही तरीके से पढ़ता है:

import numpy as np
import time
import ctypes
import argparse

from multiprocessing import Array, Value, Process
import cv2


class VideoCapture:
    """
    Class that handles video capture from device or video file
    """
    def __init__(self, device=0, delay=0.):
        """
        :param device: device index or video filename
        :param delay: delay between frame captures in seconds(floating point is allowed)
        """
        self._cap = cv2.VideoCapture(device)
        self._delay = delay

    def _proper_frame(self, delay=None):
        """
        :param delay: delay between frames capture(in seconds)
        :param finished: synchronized wrapper for int(see multiprocessing.Value)
        :return: frame
        """
        snapshot = None
        correct_img = False
        fail_counter = -1
        while not correct_img:
            # Capture the frame
            correct_img, snapshot = self._cap.read()
            fail_counter += 1
            # Raise exception if there's no output from the device
            if fail_counter > 10:
                raise Exception("Capture: exceeded number of tries to capture the frame.")
            # Delay before we get a new frame
            time.sleep(delay)
        return snapshot

    def get_size(self):
        """
        :return: size of the captured image
        """
        return (int(self._cap.get(int(cv2.CAP_PROP_FRAME_HEIGHT))),
                int(self._cap.get(int(cv2.CAP_PROP_FRAME_WIDTH))), 3)

    def get_stream_function(self):
        """
        Returns stream_function object function
        """

        def stream_function(image, finished):
            """
            Function keeps capturing frames until finished = 1
            :param image: shared numpy array for multiprocessing(see multiprocessing.Array)
            :param finished: synchronized wrapper for int(see multiprocessing.Value)
            :return: nothing
            """
            # Incorrect input array
            if image.shape != self.get_size():
                raise Exception("Capture: improper size of the input image")
            print("Capture: start streaming")
            # Capture frame until we get finished flag set to True
            while not finished.value:
                image[:, :, :] = self._proper_frame(self._delay)
            # Release the device
            self.release()

        return stream_function

    def release(self):
        self._cap.release()


def main():
    # Add program arguments
    parser = argparse.ArgumentParser(description='Captures the video from the webcamera and \nwrites it into the output file with predefined fps.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-output', dest="output",  default="output.avi", help='name of the output video file')
    parser.add_argument('-log', dest="log",  default="frames.log", help='name of the log file')
    parser.add_argument('-fps', dest="fps",  default=25., help='frames per second value')

    # Read the arguments if any
    result = parser.parse_args()
    fps = float(result.fps)
    output = result.output
    log = result.log

    # Initialize VideoCapture object and auxilary objects
    cap = VideoCapture()
    shape = cap.get_size()
    stream = cap.get_stream_function()

    # Define shared variables(which are synchronised so race condition is excluded)
    shared_array_base = Array(ctypes.c_uint8, shape[0] * shape[1] * shape[2])
    frame = np.ctypeslib.as_array(shared_array_base.get_obj())
    frame = frame.reshape(shape[0], shape[1], shape[2])
    finished = Value('i', 0)

    # Start processes which run in parallel
    video_process = Process(target=stream, args=(frame, finished))
    video_process.start()  # Launch capture process

    # Sleep for some time to allow videocapture start working first
    time.sleep(2)

    # Termination function
    def terminate():
        print("Main: termination")
        finished.value = True
        # Wait for all processes to finish
        time.sleep(1)
        # Terminate working processes
        video_process.terminate()

    # The capturing works until keyboard interrupt is pressed.
    while True:
        try:
            # Display the resulting frame
            cv2.imshow('frame', frame)
            cv2.waitKey(1)  # Display it at least one ms before going to the next frame
            time.sleep(0.1)

        except KeyboardInterrupt:
            cv2.destroyAllWindows()
            terminate()
            break

if __name__ == '__main__':
    main()

यह लिनक्स पर ठीक काम करता है, लेकिन ओएसएक्स पर मुझे परेशानी हो रही है क्योंकि इसे cv2.VideoCapture(device) पर बनाया गया cv2.VideoCapture(device) .read() नहीं कर सकता। cv2.VideoCapture(device) ऑब्जेक्ट (var self._cap में संग्रहीत)।

कुछ खोज के बाद मुझे यह इतना जवाब मिला, जो कि बिलियर्ड का उपयोग करने का सुझाव देता है, अजगर बहुप्रोसेसिंग के लिए एक प्रतिस्थापन जो माना जाता है कि इसमें कुछ बहुत ही उपयोगी सुधार हैं। इसलिए फाइल के शीर्ष पर मैंने अपने पिछले बहुप्रोसेनिंग आयात (प्रभावशाली रूप से अधिभावी multiprocessing.Process ) के बाद आयात जोड़ लिया है:

from billiard import Process, forking_enable

और video_process चर के video_process होने से पहले मैं निम्नलिखित के लिए forking_enable उपयोग करता forking_enable :

forking_enable(0)  # Supposedly this is all I need for billiard to do it's magic
video_process = Process(target=stream, args=(frame, finished))

तो इस संस्करण में ( पेस्टबिन पर ) मैं फिर से फाइल फिर से चला, जो मुझे यह त्रुटि देता है:

pickle.PicklingError: अचार नहीं कर सकता: यह मुख्य के रूप में नहीं मिला है। stream_function

उस त्रुटि की खोज ने मुझे जवाब देने की एक लंबी सूची के साथ एक ऐसे प्रश्न का नेतृत्व किया जिसके एक ने मुझे यह समाधान करने के लिए डिल धारावाहिककरण लिब का उपयोग करने का सुझाव दिया। कि lib हालांकि, पैथोस बहु प्रक्रमण कांटा के साथ प्रयोग किया जाना चाहिए। इसलिए मैं बस से मेरी मल्टी प्रसंस्करण आयात लाइन बदलने की कोशिश की

from multiprocessing import Array, Value, Process

सेवा मेरे

from pathos.multiprocessing import Array, Value, Process

लेकिन pathos.multiprocessing , Value और Process में से कोई भी pathos.multiprocessing है। pathos.multiprocessing पैकेज।

और इस बिंदु से मैं पूरी तरह से खो गया हूं। मैं उन चीजों की खोज कर रहा हूं जिनके बारे में मेरे पास पर्याप्त जानकारी है, और मुझे यह भी पता नहीं है कि मुझे किस दिशा में अब खोज या डीबग करना चाहिए।

तो क्या मेरे द्वारा किसी भी उज्ज्वल आत्मा से मुझे एक अलग प्रक्रिया में वीडियो प्राप्त करने में मदद मिल सकती है? सभी सुझावों का स्वागत है!


आपकी पहली समस्या यह थी कि आप एक forked प्रक्रिया में वेबकैम तक नहीं पहुंच सके। कई मुद्दे तब उठते हैं जब बाहरी पुस्तकालयों का उपयोग fork साथ किया जाता fork क्योंकि फोर्क ऑपरेशन मूल प्रक्रिया द्वारा खुली सभी फाइल डिस्क्रिप्टर को साफ नहीं करता है, जिससे अजीब व्यवहार होता है। लिनक्स पर इस प्रकार की समस्या के लिए लाइब्रेरी अक्सर और अधिक मजबूत होती है लेकिन यह cv2.VideoCapture 2 जैसे आईओ ऑब्जेक्ट को साझा करने का एक अच्छा विचार नहीं है। 2 प्रक्रिया के बीच cv2.VideoCapture

जब आप billard.forking_enabled उपयोग billard.forking_enabled और इसे False सेट करते हैं, तो आप लाइब्रेरी से पूछते हैं कि नई प्रक्रिया को अंडे देने के लिए fork का उपयोग नहीं करें, लेकिन spawn या forkserver तरीके, जो क्लीनर हैं, क्योंकि वे सभी फाइल डिस्क्रिप्टर बंद करते हैं लेकिन शुरू करने के लिए धीमी गति से नहीं हैं, यह नहीं होना चाहिए आपके मामले में एक मुद्दा हो। यदि आप python3.4+ का उपयोग कर रहे हैं, तो आप उदाहरण के लिए multiprocessing.set_start_method('forkserver') का उपयोग करके ऐसा कर सकते हैं।

जब आप इनमें से किसी एक विधि का उपयोग करते हैं, तो लक्ष्य फ़ंक्शन और तर्कों को बाल प्रक्रिया में पारित करने के लिए क्रमबद्ध होने की आवश्यकता होती है। pickle , pickle का उपयोग करके डिफ़ॉल्ट रूप से किया जाता है, जिसमें कई प्रवाह होते हैं, जैसा कि आपने उल्लेख किया है, जो स्थानीय रूप से परिभाषित ऑब्जेक्ट और cv2.VideoCapture भी क्रमबद्ध नहीं कर पा रहा था। वीडियो cv2.VideoCapture लेकिन आप अपने प्रोग्राम को चुनने योग्य Process लिए सभी तर्क बनाने के लिए अपने प्रोग्राम को सरल बना सकते हैं। आपकी समस्या को हल करने के लिए यहाँ एक अस्थायी है:

import numpy as np
import time
import ctypes

from multiprocessing import set_start_method
from multiprocessing import Process, Array, Value
import cv2


class VideoCapture:
    """
    Class that handles video capture from device or video file
    """
    def __init__(self, device=0, delay=0.):
        """
        :param device: device index or video filename
        :param delay: delay between frame captures in seconds(float allowed)
        """
        self._delay = delay
        self._device = device
        self._cap = cv2.VideoCapture(device)
        assert self._cap.isOpened()

    def __getstate__(self):
        self._cap.release()
        return (self._delay, self._device)

    def __setstate__(self, state):
        self._delay, self._device = state
        self._cap = cv2.VideoCapture(self._device)
        assert self._cap.grab(), "The child could not grab the video capture"

    def _proper_frame(self, delay=None):
        """
        :param delay: delay between frames capture(in seconds)
        :param finished: synchronized wrapper for int
        :return: frame
        """
        snapshot = None
        correct_img = False
        fail_counter = -1
        while not correct_img:
            # Capture the frame
            correct_img, snapshot = self._cap.read()
            fail_counter += 1
            # Raise exception if there's no output from the device
            if fail_counter > 10:
                raise Exception("Capture: exceeded number of tries to capture "
                                "the frame.")
            # Delay before we get a new frame
            time.sleep(delay)
        return snapshot

    def get_size(self):
        """
        :return: size of the captured image
        """
        return (int(self._cap.get(int(cv2.CAP_PROP_FRAME_HEIGHT))),
                int(self._cap.get(int(cv2.CAP_PROP_FRAME_WIDTH))), 3)

    def release(self):
        self._cap.release()


def stream(capturer, image, finished):
    """
    Function keeps capturing frames until finished = 1
    :param image: shared numpy array for multiprocessing
    :param finished: synchronized wrapper for int
    :return: nothing
    """
    shape = capturer.get_size()

    # Define shared variables
    frame = np.ctypeslib.as_array(image.get_obj())
    frame = frame.reshape(shape[0], shape[1], shape[2])

    # Incorrect input array
    if frame.shape != capturer.get_size():
        raise Exception("Capture: improper size of the input image")
    print("Capture: start streaming")
    # Capture frame until we get finished flag set to True
    while not finished.value:
        frame[:, :, :] = capturer._proper_frame(capturer._delay)

    # Release the device
    capturer.release()


def main():

    # Initialize VideoCapture object and auxilary objects
    cap = VideoCapture()
    shape = cap.get_size()

    # Define shared variables
    shared_array_base = Array(ctypes.c_uint8, shape[0] * shape[1] * shape[2])
    frame = np.ctypeslib.as_array(shared_array_base.get_obj())
    frame = frame.reshape(shape[0], shape[1], shape[2])
    finished = Value('i', 0)

    # Start processes which run in parallel
    video_process = Process(target=stream,
                            args=(cap, shared_array_base, finished))
    video_process.start()  # Launch capture process

    # Sleep for some time to allow videocapture start working first
    time.sleep(2)

    # Termination function
    def terminate():
        print("Main: termination")
        finished.value = True
        # Wait for all processes to finish
        time.sleep(1)
        # Terminate working processes
        video_process.join()

    # The capturing works until keyboard interrupt is pressed.
    while True:
        try:
            # Display the resulting frame
            cv2.imshow('frame', frame)
            # Display it at least one ms before going to the next frame
            time.sleep(0.1)
            cv2.waitKey(1)

        except KeyboardInterrupt:
            cv2.destroyAllWindows()
            terminate()
            break


if __name__ == '__main__':
    set_start_method("spawn")
    main()

मैं इसे मैक पर परीक्षण नहीं कर सका था इसलिए यह बॉक्स से बाहर काम नहीं करेगा, लेकिन संबंधित त्रुटियों को multiprocessing नहीं करनी चाहिए कुछ नोट:

  • मैं नए बच्चे में cv2.VideoCapture ऑब्जेक्ट इन्स्तांत करता cv2.VideoCapture और कैमरे को cv2.VideoCapture हूं क्योंकि कैमरे से केवल एक प्रक्रिया पढ़नी चाहिए।
  • हो सकता है कि आपका पहला कार्यक्रम cv2.VideoCapture साथ ही साझा किया गया cv2.VideoCapture कारण हो। cv2.VideoCapture और इसे stream फ़ंक्शन में पुनः बनाने से आपकी समस्या हल हो जाएगी।
  • आप बच्चे को नीच आवरण को नहीं पारित कर सकते, क्योंकि यह mp.Array बफर को साझा नहीं करेगा (यह वास्तव में अजीब है और यह मुझे कुछ समय निकालने के लिए ले गया)। आपको स्पष्ट रूप से Array से गुजारें और आवरण को फिर से बनाना होगा।
  • हो सकता है कि आपका पहला कार्यक्रम cv2.VideoCapture साथ ही साझा किया गया cv2.VideoCapture कारण हो। cv2.VideoCapture और इसे stream फ़ंक्शन में पुनः बनाने से आपकी समस्या हल हो जाएगी।

  • मुझे लगता है कि आप अपना कोड python3.4+ में चला रहे थे python3.4+ इसलिए मैंने billard इस्तेमाल नहीं किया, लेकिन forking_enabled(False) बजाय forking_enabled(False) का set_start_method करना समान होना चाहिए।

मुझे यह काम पता है अगर!


multiprocessing साथ मुख्य चुनौती अलग स्मृति पता रिक्त स्थान के मामले में स्मृति मॉडल को समझना है।

पायथन ने चीजों को और अधिक भ्रामक बना दिया है क्योंकि यह कई मापदंडों में कई मासूम दिखने वाले एपीआई के तहत छुपाए गए इन पहलुओं में से बहुत से हैं।

जब आप यह तर्क लिखते हैं:

# Initialize VideoCapture object and auxilary objects
cap = VideoCapture()
shape = cap.get_size()
stream = cap.get_stream_function()

...

# Start processes which run in parallel
video_process = Process(target=stream, args=(frame, finished))
video_process.start()  # Launch capture process

आप Process stream_function से गुजर रहे हैं जो stream_function क्लास ( stream_function आंतरिक घटकों का संदर्भ VideoCapture , लेकिन अधिक महत्वपूर्ण बात, जो शीर्ष स्तर समारोह के रूप में उपलब्ध नहीं है।

बच्चे की प्रक्रिया आवश्यक वस्तु का पुन: निर्माण करने में सक्षम नहीं होगी क्योंकि यह एक फ़ंक्शन का नाम है। यह मुख्य मॉड्यूल में इसलिए इसे देखने की कोशिश करता है:

pickle.PicklingError: अचार नहीं कर सकता: यह मुख्य के रूप में नहीं मिला है। stream_function

मुख्य प्रक्रिया main.stream_function रूप में मुख्य मॉड्यूल में फ़ंक्शन को हल करने की कोशिश कर रही है और लुकअप विफल रहता है।

मेरा पहला सुझाव आपके तर्क को बदलने के लिए होगा ताकि आप बाल प्रक्रिया को पारित कर रहे हैं, जिसकी प्रक्रिया वापस हो रही है stream_function

video_process = Process(target=cap.get_stream_function, args=(...))

फिर भी आप अभी भी समस्याओं का सामना कर सकते हैं क्योंकि आप दो प्रक्रियाओं के बीच राज्य को साझा कर रहे हैं।

मैं आमतौर पर लोगों को सुझाव देता हूं कि जब वे पायथन में बहु-प्रिक्रया मानदंडों का दृष्टिकोण करते हैं तो प्रक्रियाओं के बारे में सोचना है जैसे कि वे अलग-अलग मशीनों में चल रहे थे। इन मामलों में यह स्पष्ट रूप से स्पष्ट होगा कि आपकी वास्तुकला समस्याग्रस्त है।

मैं आपको दो प्रक्रियाओं की जिम्मेदारियों को अलग करने की सिफारिश करता हूं ताकि सुनिश्चित किया जा सके कि एक प्रक्रिया (बच्चे) वीडियो के पूरे कैप्चरिंग के साथ काम कर रही है और दूसरे (माता-पिता या तीसरी प्रक्रिया) फ्रेम के प्रसंस्करण के साथ काम कर रही है।

इस प्रतिमान को निर्माता और उपभोक्ता समस्या के रूप में जाना जाता है और यह आपके सिस्टम के लिए बहुत अनुकूल है। वीडियो कैप्चरिंग प्रक्रिया निर्माता और दूसरा उपभोक्ता होगा एक सरल multiprocessing.Pipe या multiprocessing.Queue ये सुनिश्चित करेगा कि जैसे ही वे तैयार हैं multiprocessing.Queue फ़्रेम उत्पादक से उपभोक्ता को स्थानांतरित कर दिए जाते हैं।

छद्म-कोड में एक उदाहरण जोड़ना क्योंकि मुझे वीडियो कैप्चरिंग एपीआई नहीं पता है। यह मुद्दा उपभोक्ता से अलग होने वाले निर्माता प्रक्रिया में पूरे वीडियो कैप्चरिंग तर्क के साथ काम कर रहा है। केवल उपभोक्ता को जानना चाहिए कि यह पाइप के माध्यम से एक फ़्रेम ऑब्जेक्ट प्राप्त करता है।

def capture_video(writer):
    """This runs in the producer process."""
    # The VideoCapture class wraps the video acquisition logic
    cap = VideoCapture()

    while True:
        frame = cap.get_next_frame()  # the method returns the next frame
        writer.send(frame)  # send the new frame to the consumer process

def main():
    reader, writer = multiprocessing.Pipe(False)

    # producer process
    video_process = Process(target=capture_video, args=[writer])
    video_process.start()  # Launch capture process

    while True:
        try:
            frame = reader.recv()  # receive next frame from the producer
            process_frame(frame)
        except KeyboardInterrupt:
            video_process.terminate()
            break

ध्यान दें प्रक्रियाओं के बीच कोई साझा स्थिति नहीं है (कोई भी सरणी साझा करने की कोई आवश्यकता नहीं है)। संचार पाइप्स के माध्यम से जाता है और यह तर्क सरल बनाने के लिए यूनिडायरेक्शनल है। जैसा कि मैंने ऊपर कहा, यह तर्क विभिन्न मशीनों में भी काम करेगा। आपको बस एक सॉकेट के साथ पाइप को बदलने की आवश्यकता होगी

आप निर्माता प्रक्रिया के लिए एक क्लीनर समाप्ति दृष्टिकोण चाहते हैं मैं आपको एक multiprocessing.Event का उपयोग करने का सुझाव देता हूं। बस KeyboardInterrupt while not event.is_set() ब्लॉक में माता-पिता से इसे सेट करें और प्रत्येक पुनरावर्ती पर बच्चे की स्थिति की जांच करें ( while not event.is_set() )।





pathos