1

i am trying to use the following code to create an interactive GUI which changes the color-space(HSV, RBG, grayscale etc) on the event of button click. Display an OpenCV video in tkinter using multiprocessing Being new to python i am having problems with multiprocessing and my attempts on making the GUI which would change it's color space on button clicks hangs the entire system. Any help on it's implementation will be highly appreciated. Thankyou. Below is my code:

import cv2
from PIL import Image,ImageTk
import Tkinter as tk
import numpy as np
from multiprocessing import Process , Queue

def quit_it(root,process):
    root.destroy()
    process.terminate()

def black_andwhite(root,process):
    process.terminate
    p=Process(target=capture_image, args=(5,queue, ))
    p.start()
    root.after(0, func=lambda: update_all(root, image_label, queue))

def update_image(image_label, queue):
    frame = queue.get()
    a = Image.fromarray(frame)
    b = ImageTk.PhotoImage(image=a)
    image_label.configure(image=b)
    image_label._image_cache = b
    root.update()

def update_all(root, image_label, queue):
    update_image(image_label, queue)
    root.after(0, func=lambda: update_all(root, image_label, queue))

def capture_image(var,queue):
    vidFile = cv2.VideoCapture(0)
    while True:
       try:
          flag, frame=vidFile.read()
          if flag==0:
             break
          if(var==5):
             frame1=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
          else:
             frame1=cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
          queue.put(frame1)
          cv2.waitKey(20)
       except:
          continue


if __name__ == '__main__':
    queue=Queue();
    root=tk.Tk()
    root.geometry("1500x1200+2+2")
    image_label=tk.Label(master=root)
    image_label.pack()
    p=Process(target=capture_image, args=(7,queue, ))
    p.start()

    quit_button=tk.Button(master=root, text='Quit', command=lambda:quit_it(root,p))
    quit_button.pack()

    bandw_button=tk.Button(master=root, text='black_and_white',command=lambda:black_andwhite(root,p))
    bandw_button.pack()

    root.after(5, func=lambda: update_all(root, image_label, queue,))
    root.mainloop()
    p.terminate()
Community
  • 1
  • 1
user5106036
  • 45
  • 1
  • 5

1 Answers1

0

Add some logging to the code to see which parts are executed when. Multithreaded code is always hard to debug (because things happen while the debugger waits for you). Logging will give you a report of what happened when and that will allow you to track down unexpected behavior.$

In this case, I see two problems:

except: continue

will silently ignore any problems in the frame capture loop. So if something goes wrong, no image will be pushed to the queue. But that should cause queue.get() to throw an Empty exception.

The second problem is that you install update_all() twice. So you will have one process/loop which adds one image per N milliseconds to the queue but two callbacks in the main event loop that try to get an image. That could cause Python to lock up.

Again, use logging to see when processes are started, when images are put into the queue and taken from it and how many callbacks are registered to process images in the queue.

[EDIT] If the original code works, then use a different approach:

Instead of installing a second callback with after() set a flag. When the flag is set, change the colorspace. Otherwise, leave it alone. When the button is clicked, toggle the flag.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • thanks for the suggestion, nut i have logged my original code to track down the executed steps. I tried using some delay to update the process in the 'root.after' function, but it did not help much. :( and i did not understand your comment "process.terminate doesn't do anything; use process.terminate() instead to call the method". Inside the process black_andwhite(root,process): i checked if the process was alive and it returned a true value. I checked for a wait-state for process, but didn't find any....is there any method to force the former process into a wait or blocked state? – user5106036 Jul 13 '15 at 15:33
  • The first line of `black_andwhite()` doesn't do anything since you forgot the `()` after `process.terminate`. – Aaron Digulla Jul 13 '15 at 19:29
  • `root.after(0, ...` is problematic since it could hog the main loop. Use a small delay just to be safe. – Aaron Digulla Jul 13 '15 at 19:31
  • But my main concern is that you invoke `root.after()` twice but there is only a single process adding to the queue. The callback might starve the queue. Call it only once. – Aaron Digulla Jul 13 '15 at 19:33
  • There is no method to make a process wait without special code. What you can do is create another queue and use a blocking `get()` with a timeout. But I doubt that this would help here. – Aaron Digulla Jul 13 '15 at 19:34
  • I have overlooked process.terminate(), thankyou so much for making me realize it. I adjusted some delay and followed your suggestion and it really helped me. Thankyou very much once again! :) – user5106036 Jul 16 '15 at 02:46