1

I am using Tkinter to make a gui to display the output from a usb camera. It is for a microscopy experiment with the idea being that the gui shows a low resolution live stream, but at the click of a button a high resolution image is taken. I have been able to get the code working with the inbuilt webcam of my laptop, (VideoCapture(0)) but when I try and use the code with the intended webcam (https://www.leopardimaging.com/uploads/LI-OV5640-USB-72_datasheet.pdf - VideoCapture(1)) it crashes. The code is this:

import Tkinter as tk
import cv2
import cv2.cv as cv
import numpy as np
from PIL import Image, ImageTk

global counter
counter = 0
global save_dir
save_dir = "C:/Users/etc..."
global runner
runner = 50
global run_num
run_num = "50"
##########################################################################
global hi_w, hi_h
global lo_w, lo_h
hi_w, hi_h = 640,480 # Camera intended resolution 2592,1944
lo_w, lo_h = 320,240 # Camera intended resolution 640,480
cap = cv2.VideoCapture(1)
cap.set(3, lo_w)
cap.set(4, lo_h)
cap.set(5,15)
##########################################################################
# Define the Tkinter functions

#-- QUIT_ --#
#-----------#
def quit_(root):
    root.destroy()    
#---------------------
#-- FUNCTION1 --#
#---------------#
def function1(root):
    global counter
    counter = 1    
#---------------------
#-- FUNCTION2 --#
#---------------#
def function2(root):
    global counter
    counter = 2    
#---------------------
#-- FUNCTION3 --#
#---------------#
def function3(root):
    global counter
    counter = 3    
#---------------------
def capture(filename):
    print 'capturing'
    global hi_w, hi_h, lo_w, lo_h
    cap.set(3, hi_w)
    cap.set(4, hi_h)
    flag2, frame2 = cap.read()
    frame2 = cv2.flip(frame2, 1)
    print 'writing'
    cv2.imwrite(filename, frame2)
    print 'resetting'
    cap.set(3, lo_w)
    cap.set(4, lo_h)
    del flag2, frame2
    global counter
    counter = 0

def show_frame():
    #Set up dummy frame
    global counter, save_dir, runner, run_num
    if counter == 1:
        flag,frame = cap.read()
        filename = save_dir + "z01_" + run_num + ".jpeg"
        capture(filename)
    elif counter == 2:
        flag, frame = cap.read()
        filename = save_dir + "z02_" + run_num + ".jpeg"
        capture(filename)
    elif counter == 3:
        flag, frame = cap.read()
        filename = save_dir + "z03_" + run_num + ".jpeg"
        capture(filename)
        runner = runner + 1
        run_num = '{0:02d}'.format(runner)
        counter = 0
    else:
            flag, frame = cap.read()
            frame = cv2.flip(frame, 1)

    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    img = Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    lmain.after(10, show_frame)


if __name__ == '__main__':
    root = tk.Tk()
    lmain = tk.Label(master=root)
    lmain.grid(column=0, rowspan=4, padx=5, pady=5)

    button1 = tk.Button(master=root, text='Function 1', command=lambda: function1(root))
    button1.grid(column=1, columnspan=2, row=0, padx=5, pady=5)
    button2 = tk.Button(master=root, text='Function 2', command=lambda: function2(root))
    button2.grid(column=1, columnspan=2, row=1, padx=5, pady=5)
    button3 = tk.Button(master=root, text='Function 3', command=lambda: function3(root))
    button3.grid(column=1, columnspan=2, row=2, padx=5, pady=5)
    quit_button = tk.Button(master=root, text='Quit',bg="red3", fg="white", command=lambda: quit_(root))
    quit_button.grid(column=1, row=3, padx=5, pady=5)

    show_frame()
    root.mainloop()
    cap.release()

The program crashes after the first button press with the following error

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1486, in __call__
    return self.func(*args)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 533, in callit
    func(*args)
  File "C:/Users/.../LI_USB_GUI_RR_worksWithInBuiltCam2.py", line 109, in show_frame
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
error: ..\..\..\..\opencv\modules\imgproc\src\color.cpp:3648: error: (-215) scn == 3 || scn == 4 in function cv::cvtColor

The file that is meant to have been written is zero bytes in size. The frame that is supposed to be there to feed the display has become empty, although the camera is still on and controllable through shell. I am really perplexed as to why the inbuilt camera will work but a USB won't with the same code.

Please help...

Chris
  • 31
  • 8
  • The object `frame` in your line `cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)` is `None`. You might have to check the values that `cap.read()` is returning. Or if `frame = cv2.flip(frame, 1)` is returning a `None` object. – Steven Correia Aug 28 '15 at 13:40
  • Hi, I'm unfortunately away from my desk for a couple of days so I can't do any definitive checking, but as memory serves you are right. `cap.read()` does return None when I try to resize the feed, but the fed remains active, and when it crashes I can still control the camera in shell. It's all rather odd! – Chris Aug 30 '15 at 21:35
  • I did a little digging and found this previous [answer](http://stackoverflow.com/a/8046886/5066845). It looks like there is an issue with OpenCV regarding webcams. Is it possible to deactivate the built-in webcam? I have a different hardware set-up so I cannot reproduce this behavior. – Steven Correia Aug 31 '15 at 14:24
  • Interesting, I'll have a look. One other thing that I thought of is that I haven't defined a frame rate for the display, therefore when the camera is producing the high resolution image and it is then written to file, the program may be asking for the next frame which the camera is unable to give, returns a `None` and the whole thing hangs. If I define a frame rate and store the last small res frame, I can write a case to handle the exception if the camera returns `None` and display the last valid frame. I'll report later in the week if that fixes it. – Chris Aug 31 '15 at 21:26
  • See my edited question - I think I have solved my issue – Chris Sep 01 '15 at 14:37
  • If that answers your question, post it as the answer :) – Steven Correia Sep 01 '15 at 14:38
  • Ooops, didn't see the answer button at the bottom of the page - Done! – Chris Sep 02 '15 at 09:15

1 Answers1

2

OK, I think I've solved the issue. Basically the program was continually trying to ask the camera for the next frame to display before it was ready after changing the resolution. It therefore returned a None and the program crashed. I have solved it by making a dummy frame the same size as the preview image and I fill it with the last valid frame from the camera. While the program is recording the high resolution still image, this last frame is displayed. Also, to give the program enough time to record the image I have set up a while loop to give the camera a set period of time to get itself configured and record the image. If there is a fault and it times out I can create a way to close the program safely. The new code is below, except for handling the exceptions - boilerplate stuff that will close the application safely can go in there.

import Tkinter as tk
import cv2
import cv2.cv as cv
import numpy as np
from PIL import Image, ImageTk
import time

global save_dir
save_dir = "C:/Users/.../"
global runner
runner = 00
global run_num
run_num = "00"

##########################################################################
##########################################################################
global hi_w, hi_h
global lo_w, lo_h
hi_w, hi_h = 2592,1944
lo_w, lo_h = 640,480
#Set up dummy frame
global last_frame
last_frame = np.zeros((lo_h, lo_w, 3), dtype=np.uint8)
global cap
cap = cv2.VideoCapture(1)
cap.set(3, lo_w)
cap.set(4, lo_h)

##########################################################################
##########################################################################
# Define the Tkinter functions

#-- QUIT_ --#
#-----------#
def quit_(root):
    root.destroy()

#---------------------


#-- FUNCTION1 --#
#---------------#
def function1(root):
    global save_dir, run_num
    filename = save_dir + "z01_" + run_num + ".jpeg"
    capture(filename)

#---------------------


#-- FUNCTION2 --#
#---------------#
def function2(root):
    global save_dir, run_num
    filename = save_dir + "z02_" + run_num + ".jpeg"
    capture(filename)

#---------------------


#-- FUNCTION3 --#
#---------------#
def function3(root):
    global save_dir, runner, run_num
    filename = save_dir + "z03_" + run_num + ".jpeg"
    capture(filename)
    runner = runner + 1
    run_num = '{0:02d}'.format(runner)

#---------------------

def capture(filename):
    print 'capturing'
    cap.set(3, hi_w)
    cap.set(4, hi_h)
    print time.time()

    timeout = time.time() + 30
    while time.time() < timeout:
        flag2, frame2 = cap.read()
        if flag2:
            frame2 = cv2.flip(frame2,1)
            cv2.imwrite(filename, frame2)
            cap.set(3, lo_w)
            cap.set(4, lo_h)
            time.sleep(1)
            global counter
            counter = 0
            break
        else:
            time.sleep(1)
    else:
        <code to handle timeout appropriately>

#---------------------       

def show_frame():
    global counter, save_dir, runner, run_num
    flag, frame = cap.read()
    frame = cv2.flip(frame, 1)
    if flag is None:
        print "Major error!"
        <code to handle exception>
    elif flag:
        global last_frame
        last_frame = frame.copy()
    else:
        print "Cant process the image"
        <code to handle exception>

    cv2image = cv2.cvtColor(last_frame, cv2.COLOR_BGR2RGBA)
    img = Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    lmain.after(10, show_frame)



##########################################################################
##########################################################################


if __name__ == '__main__':
    root = tk.Tk()
    lmain = tk.Label(master=root)
    lmain.grid(column=0, rowspan=4, padx=5, pady=5)

    button1 = tk.Button(master=root, text='Function 1', command=lambda: function1(root))
    button1.grid(column=1, columnspan=2, row=0, padx=5, pady=5)
    button2 = tk.Button(master=root, text='Function 2', command=lambda: function2(root))
    button2.grid(column=1, columnspan=2, row=1, padx=5, pady=5)
    button3 = tk.Button(master=root, text='Function 3', command=lambda: function3(root))
    button3.grid(column=1, columnspan=2, row=2, padx=5, pady=5)
    quit_button = tk.Button(master=root, text='Quit',bg="red3", fg="white", command=lambda: quit_(root))
    quit_button.grid(column=1, row=3, padx=5, pady=5)


    show_frame()
    root.mainloop()
    cap.release()

Thanks to everyone who helped.

Chris
  • 31
  • 8