簡體   English   中英

OpenCV(Python 中的 cv2)VideoCapture 刪除后不釋放相機

[英]OpenCV (cv2 in Python) VideoCapture not releasing camera after deletion

我對 Python 比較陌生,剛剛在過去一個月左右的時間里學習了它,並根據我在網上找到的示例和其他人的代碼將其一起破解。

我得到了一個 Tkinter GUI 來將來自網絡攝像頭的提要顯示為畫布上不斷更新的圖像循環。 每隔一次退出 GUI 並重新運行腳本會導致此錯誤:

Exception in Tkinter callback
Traceback (most recent call last):
    File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
        return self.func(*args)
    File "C:\Python27\lib\lib-tk\Tkinter.py", line 495, in callit
        func(*args)
   File "C:\...\cv2_cam_v8.py", line 20, in update_video
        (self.readsuccessful,self.f) = self.cam.read()
SystemError: NULL object passed to Py_BuildValue

當錯誤發生時,不會讀取任何圖像,視頻源也不會收到任何圖像來更新畫布。 腳本第一次和第二次都正常運行,沒有錯誤。 從之前使用 cv2 模塊中的 VideoCapture 函數進行的測試中,我發現我必須刪除相機對象才能釋放它,以便后續運行能夠毫無問題地捕獲相機流。 通過在控制台中輸入who來檢查命名空間不顯示cam所以我知道它在 GUI 關閉后被正確刪除。 我不明白為什么 cv2 的 read 函數會出錯。 我認為它只會每兩次發生一次,因為當錯誤發生時,一些垃圾收集或錯誤處理會刪除或釋放與相機有關的東西,但我不知道這是什么......

這是我的代碼:

import cv2
import Tkinter as tk
from PIL import Image, ImageTk


class vid():      
    def __init__(self,cam,root,canvas):
        self.cam = cam
        self.root = root
        self.canvas = canvas

    def update_video(self):
        (self.readsuccessful,self.f) = self.cam.read()
        self.gray_im = cv2.cvtColor(self.f, cv2.COLOR_RGB2GRAY)
        self.a = Image.fromarray(self.gray_im)
        self.b = ImageTk.PhotoImage(image=self.a)
        self.canvas.create_image(0,0,image=self.b,anchor=tk.NW)
        self.root.update()
        self.root.after(33,self.update_video)


if __name__ == '__main__':
    root = tk.Tk()
    videoframe = tk.LabelFrame(root,text='Captured video')
    videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
    canvas = tk.Canvas(videoframe, width=640,height=480)
    canvas.grid(column=0,row=0)
    cam = cv2.VideoCapture(2)
    x = vid(cam,root,canvas)
    root.after(0,x.update_video)
    button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
    button.grid(column=0,row=1)
    root.mainloop()
    del cam

像這樣重構代碼:

def update_video(cam,root,canvas):
    (readsuccessful,f) = cam.read()
    gray_im = cv2.cvtColor(f, cv2.COLOR_RGB2GRAY)
    a = Image.fromarray(gray_im)
    b = ImageTk.PhotoImage(image=a)
    canvas.create_image(0,0,image=b,anchor=tk.NW)
    root.update()
    root.after(33,update_video(cam,root,canvas))

if __name__ == '__main__':
    root = tk.Tk()
    videoframe = tk.LabelFrame(root,text='Captured video')
    videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
    canvas = tk.Canvas(videoframe, width=640,height=480)
    canvas.grid(column=0,row=0)
    cam = cv2.VideoCapture(2)
    root.after(0,update_video(cam,root,canvas))
    button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
    button.grid(column=0,row=1)
    root.mainloop()
    del cam

不在 GUI 中顯示按鈕並在關閉窗口后出現此錯誤:

RuntimeError: Too early to create image

我有3個問題

1 - 如何防止任何異常? 更新:將“root.after(0,update_video(cam,root,canvas))”更改為“root.after(0,lambda: update_video(cam,root,canvas))”和“update_video(cam,root,canvas)” " 到 "update_video(cam,root,canvas,event=None)" 或使用以下格式將參數傳遞給回調: "root.after(time_to_wait, callback, arguments, master)" 修復了第二個錯誤(以及我做過的其他錯誤)不張貼)。 同樣正如 kobejohn 所指出的,添加一個 try: except 塊也修復了第二個錯誤。 有關更多詳細信息,請參閱他的回答。

2 - cv2 中是否有比 .read() 更快、更有效的函數? 編輯:有沒有辦法重構我的代碼以獲得更高的幀率? read 函數是文檔中唯一列出的函數,我只是在某處讀到,如果它不在文檔中,則它不可用。 這種方法只給我大約 5fps,其中 10-20fps 會更容易接受。 更新:從 kobejohn 的測試和我的不同相機的測試之間的差異來看,低幀率是網絡攝像頭質量差的結果。 質量更好的網絡攝像頭產生更高的幀率。

3 - 我一直在讀應該盡可能避免 update() 但是我如何讓畫布重新繪制圖像(或使用此代碼實現 update_idletasks() )? 我必須實現某種線程還是可以避免這種情況? 更新:我已經讓代碼在不使用 update() 方法的情況下工作,但無論如何必須考慮實現線程,因為當我從主 GUI 的按鈕開始錄制視頻時,它會凍結/變得無響應。

完成的程序將用於 Ubuntu 和 Windows(也可能在 Mac 上)。 我運行的是 Windows 7,IDE 是 Spyder 2.1.11(Python 2.7.3)。

在此先感謝您,任何建議和/或解決方案將不勝感激!

問候,

S. Chia

解決了! Python 中的 OpenCV 2.4.2/ cv2

由於一些奇怪的原因,我之前和其他論壇都找不到'release'方法,頁面特別提到opencv的python綁定不包含release方法。 也許這僅在使用“導入 cv”時適用。 我使用后者進行了最初的原型設計,但由於某種原因,我在尋找 ReleaseCapture 方法時錯過了 cv2 中的“release”方法。

剛剛在文檔中找到它: http : //docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html

import cv2

cam=cv2.VideoCapture(0)
cam.release

你能試試這個代碼,看看你得到了多少 FPS? 我包括了 FPS 計算,以便我們可以比較筆記。 (編輯:還有什么錯誤。我沒有得到你在原始代碼中得到的錯誤,我在下面的代碼中得到零錯誤)

我從頭開始只是想看看我是否想出了一些不同的東西。 有一些區別:

  1. 有一個(小?)錯誤:opencv 默認顏色通道是 BGR 而不是 RGB。 因此,從cv2.COLOR_RGB2GRAY --> cv2.COLOR_BGR2GRAY更改您的灰度轉換。 您可以在VideoCapture 示例中看到它們做了類似的事情。
  2. 我使用一個簡單的標簽來顯示圖像而不是畫布。 我以前沒有使用過畫布,所以我不確定你需要用它做什么。 使用簡單的標簽,您必須保留對正在顯示的圖像的引用,這樣它就不會被垃圾收集。 您可以在 update_image() 中看到這一點。
  3. 對於回調,我使用了帶參數的 lambdas(如您在評論中提到的)。 否則,當您使用參數進行函數調用時,您將立即運行回調而不是注冊它。 最終看起來它正在工作,但它並沒有像你想象的那樣做。 或者,如果您更喜歡打包參數並將其作為未調用的函數發送,則可以使用functools.partial
  4. 同樣對於回調,我添加了一個 try: except 塊,用於在 root 被銷毀后回調開始運行的情況。 我不知道這是否是“正確”的方法,但據我所知它有效。

使用此代碼,我在 Windows 7 上獲得 15 FPS 且沒有錯誤:

from collections import deque
import cv2
import Image, ImageTk
import time
import Tkinter as tk

def quit_(root):
    root.destroy()

def update_image(image_label, cam):
    (readsuccessful, f) = cam.read()
    gray_im = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    a = Image.fromarray(gray_im)
    b = ImageTk.PhotoImage(image=a)
    image_label.configure(image=b)
    image_label._image_cache = b  # avoid garbage collection
    root.update()


def update_fps(fps_label):
    frame_times = fps_label._frame_times
    frame_times.rotate()
    frame_times[0] = time.time()
    sum_of_deltas = frame_times[0] - frame_times[-1]
    count_of_deltas = len(frame_times) - 1
    try:
        fps = int(float(count_of_deltas) / sum_of_deltas)
    except ZeroDivisionError:
        fps = 0
    fps_label.configure(text='FPS: {}'.format(fps))


def update_all(root, image_label, cam, fps_label):
    update_image(image_label, cam)
    update_fps(fps_label)
    root.after(20, func=lambda: update_all(root, image_label, cam, fps_label))


if __name__ == '__main__':
    root = tk.Tk()
    # label for the video frame
    image_label = tk.Label(master=root)
    image_label.pack()
    # camera
    cam = cv2.VideoCapture(0)
    # label for fps
    fps_label = tk.Label(master=root)
    fps_label._frame_times = deque([0]*5)  # arbitrary 5 frame average FPS
    fps_label.pack()
    # quit button
    quit_button = tk.Button(master=root, text='Quit',
                            command=lambda: quit_(root))
    quit_button.pack()
    # setup the update callback
    root.after(0, func=lambda: update_all(root, image_label, cam, fps_label))
    root.mainloop()

在 opencv 中初始化相機對象之前設置環境變量。

os.environ['OPENCV_VIDEOIO_PRIORITY_MSMF'] = '0'

即使在我的代碼中關閉相機對象后,這也釋放了相機。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM