helloworld12345
helloworld12345

Reputation: 127

How to display an image from OpenCV in a Tkinter interface?

I am trying to continuously display and replace an image in a Tkinter interface taken from OpenCV's VideoCapture. However, I am getting the following error that I think is a result of improper formatting of the image numpy array:

TypeError: unhashable type: 'numpy.ndarray'

How can I reformat it to all it to display properly? Below is my code:

import tkinter as tk
import cv2
import numpy as np
from PIL import ImageTk, Image

main = tk.Tk()
main.title("Hole Pattern Recognition")
main.geometry("300x300")

frame = tk.Frame(main)
frame.pack()

def startScan():
    global main, frame
    #begins utilizing webcam for scan
    cap = cv2.VideoCapture(0)
    while(True):
        ret,img = cap.read()
        img = ImageTk.PhotoImage(img)
        panel = tk.Label(main, image = img)
        panel.pack(side = "bottom", fill = "both", expand = "yes")
        ch = cv2.waitKey(1)
        if ch == ord('q'):
            break
        
    cap.release()
    cv2.destroyAllWindows()

    
    
startButton = tk.Button(frame, 
                   text="Start Scan", 
                   fg="blue",
                   command=startScan)

startButton.pack(side=tk.TOP)
main.mainloop()

Upvotes: 0

Views: 1318

Answers (1)

acw1668
acw1668

Reputation: 46669

First you need to use PIL.Image.fromarray() to convert the captured image to format supported by tkinter.

Second better not use while loop in main thread as it will block the tkinter mainloop. Use after() instead.

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

cap = None

main = tk.Tk()
main.title('Hole Pattern Recognition')
#main.geometry('300x300')

frame = tk.Frame(main)
frame.pack()

def startScan():
    global cap

    def scan():
        ret, img = cap.read()
        if ret:
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(img)
            tkimg = ImageTk.PhotoImage(img)
            panel.config(image=tkimg)
            panel.tkimg = tkimg # save a reference to the image to avoid garbage collection
        panel.after(25, scan) # change 25 to other value to adjust FPS

    if cap is None:
        cap = cv2.VideoCapture(0)
        scan() # start the capture loop
    else:
        print('capture already started')

startButton = tk.Button(frame, text='Start Scan', fg='blue', command=startScan)
startButton.pack()

panel = tk.Label(main)
panel.pack()

main.mainloop()

if cap:
    cap.release()

Upvotes: 1

Related Questions