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_hist
(FigureCanvasTkAgg()
的实例)被以下行覆盖:
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()
我已经成功绘制了视频的灰度直方图:对于视频的每个图像,直方图都已更新以对应于当前图像。对于这个程序,我使用了 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_hist
(FigureCanvasTkAgg()
的实例)被以下行覆盖:
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()