Tkinter:更新视频灰度直方图的问题

Tkinter : problem to udpate a grayscale histogram of video

我已经成功绘制了视频的灰度直方图:对于视频的每个图像,直方图都已更新以对应于当前图像。对于这个程序,我使用了 classic 方式,具有子图、绘图、set_ydata 等函数。我只有 2 个 windows:一个是视频,一个是直方图,以及现在我要做的是只有一个 window 带有视频和直方图,并添加“暂停”、“播放”或“重新启动”等按钮。通过研究,我发现 Tkinter 可能是一种做到这一点的方法,所以我开始使用它。

我配置了所有 window(带有按钮,显示视频和直方图)并且视频显示正常,但我无法更新我的直方图,我的程序只绘制第一个直方图(的第一张图片),仅此而已。我已经尝试了几件事,比如 tkinter 动画,或者在我的函数 calc_hist() 中放置一个 ax clear 和一个 draw() (使用函数 draw() 我有一个错误“draw_wrapper() 缺少 1 个必需的位置参数:'renderer'",我没有找到它对应的内容),但它不起作用。也许我误用了这些函数,所以也许你能找到我的代码出了什么问题。

这是我的 class 应用程序,它配置 window 并应该显示直方图(我删除了对我的问题无用的部分,例如功能和按钮声明以减少代码):

import tkinter
import cv2
import PIL.Image, PIL.ImageTk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import matplotlib.pyplot as plt

class App:
    def __init__(self, window, window_title, video_source=0):
        self.window = window
        self.window.title(window_title)
        self.video_source = video_source
        
        self.vid = MyVideoCapture(self.video_source)
        
        #Video
        self.canvas = tkinter.Canvas(window, width = 640, height = 480)
        self.canvas.grid(row=0, column = 0)

        #Histogram
        self.frame_hist = tkinter.Frame(window)
        self.frame_hist.grid(row=0, column = 1)
        self.figure = plt.Figure(figsize=(5,4), dpi = 100)
        self.ax = self.figure.add_subplot()
        self.canvas_hist = FigureCanvasTkAgg(self.figure, self.frame_hist)
        self.canvas_hist.get_tk_widget().pack(fill = tkinter.BOTH, side = tkinter.TOP)
        self.ax = self.figure.gca()
        x = np.linspace(0, 255, 256)
        y = np.linspace(10, 100000, 256)
        self.canvas_hist, = self.ax.plot(x,y)
        self.ax.set_ylabel('Nombre pixel', fontsize = 15)
        self.ax.set_xlabel('Valeur pixel', fontsize = 15)
        self.ax.set_yscale('log')
        
        self.delay = 15
        self.update()
        
        self.window.mainloop()
     
    def update(self):
        ret, frame = self.vid.get_frame()
        if ret :
            
            self.gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            self.smaller_image = cv2.resize(self.gris,(640,480))
            self.photo = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(self.smaller_image))
            self.canvas.create_image(0, 0, image = self.photo, anchor = tkinter.NW)
            
            self.calc_hist(self.gris)
            
            self.window.after(self.delay, self.update)
    
    def calc_hist(self, gris) :
        self.histogram = cv2.calcHist([gris], [0], None, [256], [0, 256])
        self.canvas_hist.set_ydata(self.histogram)

这是我的代码的第二部分,视频 class 用于初始化它,我把代码给你以防万一,但我认为看它没用,这与我的问题无关:

class MyVideoCapture:
    def __init__(self, video_source=0):
        # Open the video source
        self.vid = cv2.VideoCapture(video_source)
        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)
             
            # Get video source width and height
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
     
    def get_frame(self):
        if self.vid.isOpened():
            ret, frame = self.vid.read()
            if ret:
                # Return a boolean success flag and the current frame converted to BGR
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (ret, None)
     
    # Release the video source when the object is destroyed
    def __del__(self):
        if self.vid.isOpened():
            self.vid.release()
     
# Create a window and pass it to the Application object
App(tkinter.Tk(), "Tkinter and OpenCV", "output.avi")

这是我的最终界面:

更新y数据时,需要使用self.canvas_hist.draw()刷新图表。

但是 self.canvas_histFigureCanvasTkAgg() 的实例)被以下行覆盖:

self.canvas_hist, = self.ax.plot(x, y)

所以建议把上面一行改成:

self.graph, = self.ax.plot(x, y)

然后在calc_hist()末尾加上self.canvas_hist.draw()

def calc_hist(self, gris):
    histogram = cv2.calcHist([gris], [0], None, [256], [0, 256])
    self.graph.set_ydata(histogram)
    self.canvas_hist.draw()