Home > Blockchain >  Tkinter : problem to udpate a grayscale histogram of video
Tkinter : problem to udpate a grayscale histogram of video

Time:11-11

I've already succeded to plot a grayscale histogram of a video : for each image of the video, the histogram was updated to correspond to the current image. For this program I used the classic way, with the functions subplots, plot, set_ydata etc. I only had 2 windows : one with the video and one figure with the histogram, and now what I'm trying to do is to have only one window with the video and the histogram on it, and add buttons like "pause", "play" or "restart". With research I saw that Tkinter could be a way to do that, so I started to use it.

I configured all my window (with buttons, displaying the video and the histogram) and video is shown normally, but I can't update my histogram, my program just plot the first histogram (of the first image) and nothing else. I've already tried several things, like the tkinter animation, or to put an ax clear and a draw() in my function calc_hist() (with the function draw() I have an error "draw_wrapper() missing 1 required positional argument: 'renderer'", I didnt find what it corresponded to), but it's not working. Maybe I misused theses functions, so maybe you cand find what's going wrong with my code.

Here's my class App which configure the window and supposed to display the histogram (I delete useless part for my problem like functions and declaration of button to reduce the code) :

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)

and here's the second part of my code with the video class to initialize it, I put you the code just in case but I think it's useless to look it, nothing matter to my problem in it :

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")

And here's my final interface : Screenshot of my complete window

CodePudding user response:

When you update the y data, you need to refresh the graph using self.canvas_hist.draw().

However self.canvas_hist (instance of FigureCanvasTkAgg()) is overwritten by the line:

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

So suggest to change the above line to:

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

Then add self.canvas_hist.draw() at the end of calc_hist():

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