User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

PiCamera streaming on Tkinter label -Help

Fri Jan 04, 2019 3:42 pm

I have a script that display streaming from a webcam in a tkinter label , I want to do the same but from a PiCamera, but after a lot of googling I did not find any solution to this, I need the streaming on a tkinter label because I need buttons to control actions of my raspberry pi. All the solutions I found is the displaying on a frame separatelly. Here is a summary of the script that run with a WebCam. Can some one please guide me to make the changes necessary to work with the Pi Camera. Many thanks in advance

Code: Select all


from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import re
import subprocess
import urllib
import RPi.GPIO as GPIO
import threading


class Application:
    def __init__(self, output_path = "./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0) # capture video frames, 0 is your default video camera

        self.vs.set(cv2.CAP_PROP_FRAME_WIDTH, 864)
        self.vs.set(cv2.CAP_PROP_FRAME_HEIGHT, 486)
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera
        self.root = tk.Tk()  # initialize root window
        defaultbg = self.root.cget('bg') # set de default grey color to use in labels background
        w = 900 # width for the Tk root
        h = 545   # height for the Tk root
        self.root .resizable(0, 0)
        ws = self.root .winfo_screenwidth() # width of the screen
        hs = self.root .winfo_screenheight() # height of the screen
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        self.root .geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("     LA  SELVA - SAFETY CONTROL UNIT     ")  # set window title
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=10, column=8, columnspan=25, padx=4, pady=6)        

        self.botQuit = tk.Button(self.root,width=6,font=('arial narrow', 18, 'normal'), text="CLOSE", activebackground="#00dfdf")
        self.botQuit.grid(row=10,column=32)
        self.botQuit.configure(command=self.destructor)        
                
        self.video_loop()
        
    def video_loop(self):
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image
        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds
       

    def destructor(self):
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./",
    help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()









gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Fri Jan 04, 2019 4:30 pm

If you run "sudo modprobe bcm2835-v4l2" the pi should then see the pi camera as video0, and work with your code.

User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

Re: PiCamera streaming on Tkinter label -Help

Fri Jan 04, 2019 4:45 pm

So easy!!! Thanks a lot.
Have a nice Friday evenning
Cheers

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Fri Jan 04, 2019 5:45 pm

You could add it into your python code

Code: Select all

  # initialise pi camera v4l2
if not os.path.exists('/dev/video0'):
   rpistr = "sudo modprobe bcm2835-v4l2"
   p = subprocess.Popen(rpistr, shell=True, preexec_fn=os.setsid)
   time.sleep(1)
 

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Fri Jan 04, 2019 7:22 pm

A few weeks ago, I raised the following thread.

viewtopic.php?f=32&t=228251&p=1399963#p1399963

Although there were some helpful answers, I did not believe that any really satisfied what I had intended.
If I had read this new thread correctly though, it appears to be exactly what I wanted.

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Sat Jan 05, 2019 2:31 pm

I spoke too soon in my previous post!

I am getting the following error:

Code: Select all

ImportError: cannot import name 'ImageTk'
if I REM (by use of #) out the first line, just to see how far it runs, I get another error

Code: Select all

No module named 'cv2'
The first thing that came to mind was, Is this a Python2 program, that I am trying to run under my much preferred, Python3 but I cannot get it to run under Python2 either.

Can anyone please suggest a solution to these errors.

User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

Re: PiCamera streaming on Tkinter label -Help

Sat Jan 05, 2019 2:40 pm

I have been changing the script and runs perfectly, but I have a new challenge to resolve , I want to take a snapshot , taking from a frame is not problem, the streaming is allways running, but because the frame resolution is very low I try to use instead the full picamera resolution with picamera.capture and I must release the camera to avoid error "out of resources" , and I can not find how to restart the streaming( if it is possible).
Thanks again for any help .

Code: Select all

# Created i La Selva 18-mars-04 15:15
# Modified i La Selva 19-Januari-04  20:19

from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import re
import subprocess
import urllib
import RPi.GPIO as GPIO
import threading
import picamera

# initialise pi camera v4l2
if not os.path.exists('/dev/video0'):
   rpistr = "sudo modprobe bcm2835-v4l2"
   p = subprocess.Popen(rpistr, shell=True, preexec_fn=os.setsid)
   time.sleep(1)
   
def do_picamera(app):
    camera = picamera.PiCamera()
    #camera.awb_mode = 'auto'
    camera.brightness = 50
    #camera.rotation= 90
    camera.resolution = (2592, 1944)
    data = time.strftime("%Y-%b-%d_(%H%M%S)")
    texte = "picture take:" + data
    camera.start_preview()
    camera.capture('%s.jpg' % data)
    camera.stop_preview()

    
class Application:
    def __init__(self, output_path = "./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0) # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera
        self.root = tk.Tk()  # initialize root window
        defaultbg = self.root.cget('bg') # set de default grey color to use in labels background
        w = 650 # width for the Tk root
        h = 550 # height for the Tk root
        self.root .resizable(0, 0)
        ws = self.root .winfo_screenwidth() # width of the screen
        hs = self.root .winfo_screenheight() # height of the screen
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        self.root .geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("     LA  SELVA - SAFETY CONTROL UNIT     ")  # set window title
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=10, column=8, columnspan=25, padx=4, pady=6)

        self.botShoot2 = tk.Button(self.root,width=18, font=('arial', 14, 'normal'),  text="PICAM-SHOOT" ,anchor="w")
        self.botShoot2.grid(row=10, column=20,columnspan=6)
        self.botShoot2.configure(command=self.picam)        

        self.botShoot1= tk.Button(self.root,width=10, font=('arial', 14, 'normal'),  text="CV2-SHOOT" ,anchor="w")
        self.botShoot1.grid(row=10, column=26,columnspan=5)
        self.botShoot1.configure(command=self.snapshot)

        
        self.botQuit = tk.Button(self.root,width=6,font=('arial narrow', 14, 'normal'), text="CLOSE", activebackground="#00dfdf")
        self.botQuit.grid(row=10,column=32)
        self.botQuit.configure(command=self.destructor)        
                
        self.video_loop()
        
    def video_loop(self):
        global test
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            test = cv2image
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image
        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds

    def snapshot(self):
        imageName = str(time.strftime("%Y-%m-%d %H_%M")) + '.jpg'
        cv2.imwrite(imageName,test)

    def picam(self):
        self.vs.release()
        t = threading.Thread(target=do_picamera, args=(self,))
        t.start()
        
    def destructor(self):
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./Pictures",
help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()









gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Sat Jan 05, 2019 5:21 pm

I tried to run your original code but similar to RDS got errors. Are you using Python2 or 3?

User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

Re: PiCamera streaming on Tkinter label -Help

Sat Jan 05, 2019 5:53 pm

Python 2.7

User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

Re: PiCamera streaming on Tkinter label -Help

Sun Jan 06, 2019 8:08 am

At the end I got the solution, adding " camera.close" to release the picamera after taken a picture, and "vs.open(0)" to restart the streaming.

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 9:19 am

gordon77 wrote:
Sat Jan 05, 2019 5:21 pm
I tried to run your original code but similar to RDS got errors. Are you using Python2 or 3?
sudo apt-get install python-imaging python-imaging-tk

fixed the error

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 11:10 am

joseplaselva wrote:
Sun Jan 06, 2019 8:08 am
At the end I got the solution, adding " camera.close" to release the picamera after taken a picture, and "vs.open(0)" to restart the streaming.
where did you add these items ?

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 2:22 pm

I cannot get past the error:

Code: Select all

No module named 'cv2'
The system just reports that:

Code: Select all

Could not find any downloads that satisfy the requirement cv2
I am obviously doing something wrong but cannot fathom out what it is!

I have got the other version (different thread on this Forum) working now for still pictures.

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 2:28 pm

RDS wrote:
Mon Jan 07, 2019 2:22 pm
I cannot get past the error:

Code: Select all

No module named 'cv2'
The system just reports that:

Code: Select all

Could not find any downloads that satisfy the requirement cv2
I am obviously doing something wrong but cannot fathom out what it is!

I have got the other version (different thread on this Forum) working now for still pictures.
Have you installed opencv? Use sudo apt-get install python-opencv

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 2:37 pm

@gordon77
Thank you very much. It works great.
I can't believe it was so simple, as I was sure I had tried that.

User avatar
joseplaselva
Posts: 79
Joined: Tue Oct 04, 2016 4:45 am
Location: Kvilla , Sweden

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 3:59 pm

Here comes the final script if it can be useful for some one . The scripts take a full resolution jpg image from the PiCamera ( video streaming just frezze for 4 seconds). Then visualize the picture in a little window, and you can decide if send by mail, delete or save the file . To avoid freezing the video streaming while it is sent by mail ( because is a heavy file to send) I use the thread function.

Code: Select all

# -*- coding: utf-8 -*-
# Created i La Selva 18-mars-04 15:15
# Modified i La Selva 19-Januari-07  16:40

from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import re
import shutil
import smtplib
import subprocess
import urllib
import RPi.GPIO as GPIO
import threading
import picamera

from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import encoders
from email.mime.image import MIMEImage

fromaddr = "[email protected]"
toaddr = "[email protected]"
mailpass = "xxxxxxxxxxxxxxx"

device = "TEST_Kam "

path = "/home/pi/"
moveto = "/home/pi/Pictures/saved/"


bilder = "noimage.jpg"
flag = 0
delete_flag = 0


os.system("sudo modprobe bcm2835-v4l2") # to recognize PiCamera as video0

def do_picam(app):
    global shot
    global texte
    global texmed
    global delete_flag
    global txt_display
    camera = picamera.PiCamera()
    #camera.awb_mode = 'auto'
    camera.brightness = 50
    #camera.rotation= 90
    camera.resolution = (2592, 1944)
    data = time.strftime("%y-%b-%d_(%H%M%S)")
    texte = "picture take at: " + data    
    camera.start_preview()
    camera.capture('%s.jpg' % data)
    camera.stop_preview()
    camera.close() # close Picamera to free resources  to restart the video stream
    shot = '%s.jpg' % data
    dagtid = time.strftime("%y-%b-%d (%H:%M)")
    texte = "picture take:" + time.strftime("%y-%b-%d_(%H%M%S)")
    texmed = device + dagtid
    app.showImg()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,shot)
    app.textBox.configure(bg="dodgerblue")
    app.textBox.update_idletasks()
    delete_flag = 1
    app.vs.open(0) # restarting video stream from Pi Camera
    app.enable_buttons()
    txt_display = " " + shot[0:18]

def do_sendMail(app):   
    mail = MIMEMultipart()
    mail['Subject'] =   str(texmed)
    mail['From'] = fromaddr
    mail['To'] = toaddr
    mail.attach(MIMEText(texte, 'plain'))

    attachment = open(shot, 'rb')
    image = MIMEImage(attachment.read())
    attachment.close()
    mail.attach(image)
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login(fromaddr, mailpass)
    text = mail.as_string()
    server.sendmail(fromaddr, toaddr, text)
    server.quit()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,txt_display + "  MAILED OK")
    app.textBox.update_idletasks() 
    app.enable_buttons()
    
class Application:
    def __init__(self, output_path = "./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0) # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera
        self.root = tk.Tk()  # initialize root window
        defaultbg = self.root.cget('bg') # set de default grey color to use in labels background
        w = 930 # width for the Tk root
        h = 535 # height for the Tk root
        self.root .resizable(0, 0)
        ws = self.root .winfo_screenwidth() # width of the screen
        hs = self.root .winfo_screenheight() # height of the screen
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        self.root .geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("     LA  SELVA - REMOT VIDEO SNAP SHOT     ")  # set window title
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=15, column=8, columnspan=25, padx=4, pady=6)

        self.labPic= tk.Label(self.root, bg="grey70")  # initialize image panel
        self.labPic.grid(row=5, column=34,  padx=4, pady=6)

        self.textBox = tk.Text(self.root, height=1, width=28, font=('arial narrow', 10, 'bold'), bg="green",fg="white")
        self.textBox.grid(row=6, rowspan=2, column=34, padx=3)
        self.textBox.insert(tk.END, "READY")
        
        self.botMail = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SEND PICTURE BY MAIL", activebackground="#00dfdf" )
        self.botMail.grid(row=8, column=34, pady= 1)
        self.botMail.configure(command=self.sendMail,state = "disabled")

        self.botRadera = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="DELETE PICTURE", activebackground="#00dfdf" )
        self.botRadera.grid(row=9, column=34, pady=1)
        self.botRadera.configure(command=self.radera, state = "disabled")

        self.botSave = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SAVE PICTURE", activebackground="#00dfdf" )
        self.botSave.grid(row=10, column=34, pady=1)
        self.botSave.configure(command=self.movepic,state = "disabled")
        
        self.botShoot = tk.Button(self.root,width=24, font=('arial', 14, 'normal'),  text="PICAM-SHOOT", activebackground="#00dfdf" )
        self.botShoot.grid(row=15, column=20)
        self.botShoot.configure(command=self.picam)        

        self.botQuit = tk.Button(self.root,width=6,font=('arial narrow', 14, 'normal'), text="CLOSE", activebackground="#00dfdf")
        self.botQuit.grid(row=15,column=32)
        self.botQuit.configure(command=self.destructor)              
        self.video_loop()
        
    def video_loop(self):
        global test
        global flag
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            test = cv2image
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image

        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds
        if flag ==0:
            flag = 1
            self.show_thumb()
            
    def snapshot(self):
        imageName = 'cv-' + time.strftime("%Y-%b-%d_(%H%M%S)")+ '.jpg'
        cv2.imwrite(imageName,test)

    def picam(self):        
        self.disable_buttons()
        self.vs.release() # release the camera to get all resources
        t = threading.Thread(target=do_picam, args=(self,))
        t.start()
        
    def showImg(self):
        image = Image.open(shot)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def show_thumb(self) :
        image = Image.open(bilder)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def sendMail(self):
        t = threading.Thread(target=do_sendMail, args=(self,))
        t.start()
        self.disable_buttons()
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,"SENDING PICTURE BY MAIL")
        self.textBox.update_idletasks()        


    def movepic(self):
        global delete_flag
        pic= shot
        src = path+pic
        dst = moveto+pic
        shutil.move(src,dst)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "   SAVED")
        self.textBox.configure(bg="darkorange")
        self.textBox.update_idletasks()
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        delete_flag = 2
                            
    def radera(self):
        global delete_flag
        pic = shot
        os.remove(pic)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "  DELETED ")
        self.textBox.configure(bg="red")
        self.textBox.update_idletasks()
        delete_flag = 2
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
                            
    def disable_buttons(self):
        self.botShoot.configure(state="disabled")
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        self.botQuit.configure(state="disabled")

    def enable_buttons(self):
        self.botShoot.configure(state="normal")
        self.botMail.configure(state="normal")
        self.botSave.configure(state="normal")
        self.botRadera.configure(state="normal")
        self.botQuit.configure(state="normal")
        
    def destructor(self):
        if delete_flag == 1 : self.radera()
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./Pictures",
help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()









RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 07, 2019 10:22 pm

@joseplaselva
Thank you very much for sharing this program.

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Sun Jan 13, 2019 7:19 pm

I downloaded the program posted by joseplaselva at 4:59pm on the 7th January (above) and it is virtually exactly what I have been looking for.

I have tried without success so far to convert it to Python3 but I cannot get past the cv2 error.
The program is good enough that I will continue to run it using Python2.

The next feature I would like to add, is to combine it with a Pan/Tilt Hat to allow movement of the camera. The way my camera mounts, the main video is shown upside down. I have added the following line:

Code: Select all

camera.rotation = 180
in the procedure 'do_picam(app)' and this works perfectly for the still picture that can be grabbed from the video but it does not rotate the video picture. There does not seem to be anywhere else that camera.rotation is used.
Could someone please advise how I can rotate the video picture that plays in the main window.

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 11:16 am

try this..

Code: Select all

# -*- coding: utf-8 -*-
# Created i La Selva 18-mars-04 15:15
# Modified i La Selva 19-Januari-07  16:40

from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import re
import shutil
import smtplib
import subprocess
import urllib
import RPi.GPIO as GPIO
import threading
import picamera
import imutils

from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import encoders
from email.mime.image import MIMEImage

fromaddr = "[email protected]"
toaddr = "[email protected]"
mailpass = "xxxxxxxxxxxxxxx"

device = "TEST_Kam "

path = "/home/pi/"
moveto = "/home/pi/Pictures/saved/"


bilder = "noimage.jpg"
flag = 0
delete_flag = 0


os.system("sudo modprobe bcm2835-v4l2") # to recognize PiCamera as video0

def do_picam(app):
    global shot
    global texte
    global texmed
    global delete_flag
    global txt_display
    camera = picamera.PiCamera()
    #camera.awb_mode = 'auto'
    camera.brightness = 50
    #camera.rotation= 90
    camera.resolution = (2592, 1944)
    camera.rotation = 180
    data = time.strftime("%y-%b-%d_(%H%M%S)")
    texte = "picture take at: " + data    
    camera.start_preview()
    camera.capture('%s.jpg' % data)
    camera.stop_preview()
    camera.close() # close Picamera to free resources  to restart the video stream
    shot = '%s.jpg' % data
    dagtid = time.strftime("%y-%b-%d (%H:%M)")
    texte = "picture take:" + time.strftime("%y-%b-%d_(%H%M%S)")
    texmed = device + dagtid
    app.showImg()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,shot)
    app.textBox.configure(bg="dodgerblue")
    app.textBox.update_idletasks()
    delete_flag = 1
    app.vs.open(0) # restarting video stream from Pi Camera
    app.enable_buttons()
    txt_display = " " + shot[0:18]

def do_sendMail(app):   
    mail = MIMEMultipart()
    mail['Subject'] =   str(texmed)
    mail['From'] = fromaddr
    mail['To'] = toaddr
    mail.attach(MIMEText(texte, 'plain'))

    attachment = open(shot, 'rb')
    image = MIMEImage(attachment.read())
    attachment.close()
    mail.attach(image)
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login(fromaddr, mailpass)
    text = mail.as_string()
    server.sendmail(fromaddr, toaddr, text)
    server.quit()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,txt_display + "  MAILED OK")
    app.textBox.update_idletasks() 
    app.enable_buttons()
    
class Application:
    def __init__(self, output_path = "./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0) # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera
        self.root = tk.Tk()  # initialize root window
        defaultbg = self.root.cget('bg') # set de default grey color to use in labels background
        w = 930 # width for the Tk root
        h = 535 # height for the Tk root
        self.root .resizable(0, 0)
        ws = self.root .winfo_screenwidth() # width of the screen
        hs = self.root .winfo_screenheight() # height of the screen
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        self.root .geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("     LA  SELVA - REMOT VIDEO SNAP SHOT     ")  # set window title
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=15, column=8, columnspan=25, padx=4, pady=6)

        self.labPic= tk.Label(self.root, bg="grey70")  # initialize image panel
        self.labPic.grid(row=5, column=34,  padx=4, pady=6)

        self.textBox = tk.Text(self.root, height=1, width=28, font=('arial narrow', 10, 'bold'), bg="green",fg="white")
        self.textBox.grid(row=6, rowspan=2, column=34, padx=3)
        self.textBox.insert(tk.END, "READY")
        
        self.botMail = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SEND PICTURE BY MAIL", activebackground="#00dfdf" )
        self.botMail.grid(row=8, column=34, pady= 1)
        self.botMail.configure(command=self.sendMail,state = "disabled")

        self.botRadera = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="DELETE PICTURE", activebackground="#00dfdf" )
        self.botRadera.grid(row=9, column=34, pady=1)
        self.botRadera.configure(command=self.radera, state = "disabled")

        self.botSave = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SAVE PICTURE", activebackground="#00dfdf" )
        self.botSave.grid(row=10, column=34, pady=1)
        self.botSave.configure(command=self.movepic,state = "disabled")
        
        self.botShoot = tk.Button(self.root,width=24, font=('arial', 14, 'normal'),  text="PICAM-SHOOT", activebackground="#00dfdf" )
        self.botShoot.grid(row=15, column=20)
        self.botShoot.configure(command=self.picam)        

        self.botQuit = tk.Button(self.root,width=6,font=('arial narrow', 14, 'normal'), text="CLOSE", activebackground="#00dfdf")
        self.botQuit.grid(row=15,column=32)
        self.botQuit.configure(command=self.destructor)              
        self.video_loop()
        
    def video_loop(self):
        global test
        global flag
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            cv2image = imutils.rotate_bound(cv2image, 180) # rotate image
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            test = cv2image
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image

        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds
        if flag ==0:
            flag = 1
            self.show_thumb()
            
    def snapshot(self):
        imageName = 'cv-' + time.strftime("%Y-%b-%d_(%H%M%S)")+ '.jpg'
        cv2.imwrite(imageName,test)

    def picam(self):        
        self.disable_buttons()
        self.vs.release() # release the camera to get all resources
        t = threading.Thread(target=do_picam, args=(self,))
        t.start()
        
    def showImg(self):
        image = Image.open(shot)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def show_thumb(self) :
        image = Image.open(bilder)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def sendMail(self):
        t = threading.Thread(target=do_sendMail, args=(self,))
        t.start()
        self.disable_buttons()
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,"SENDING PICTURE BY MAIL")
        self.textBox.update_idletasks()        


    def movepic(self):
        global delete_flag
        pic= shot
        src = path+pic
        dst = moveto+pic
        shutil.move(src,dst)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "   SAVED")
        self.textBox.configure(bg="darkorange")
        self.textBox.update_idletasks()
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        delete_flag = 2
                            
    def radera(self):
        global delete_flag
        pic = shot
        os.remove(pic)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "  DELETED ")
        self.textBox.configure(bg="red")
        self.textBox.update_idletasks()
        delete_flag = 2
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
                            
    def disable_buttons(self):
        self.botShoot.configure(state="disabled")
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        self.botQuit.configure(state="disabled")

    def enable_buttons(self):
        self.botShoot.configure(state="normal")
        self.botMail.configure(state="normal")
        self.botSave.configure(state="normal")
        self.botRadera.configure(state="normal")
        self.botQuit.configure(state="normal")
        
    def destructor(self):
        if delete_flag == 1 : self.radera()
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./Pictures",
help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()


gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 12:14 pm

I got this version working under python3 after installing opencv. NOT FULLY TESTED !!

https://www.deciphertechnic.com/install ... pberry-pi/

(DON'T do sudo rpi-update !!)

and then

sudo apt-get install python3-pil python3-pil.imagetk
sudo pip install imutils

Code: Select all

# -*- coding: utf-8 -*-
# Created i La Selva 18-mars-04 15:15
# Modified i gordon77 19-Januari-15  09:09, beta for python3 & 2

from PIL import Image, ImageTk
import argparse
import time
import datetime
import cv2
import os
import re
import shutil
import smtplib
import subprocess
import urllib
import RPi.GPIO as GPIO
import threading
import picamera
import imutils
import sys

if sys.version_info[0] == 3:
    import tkinter as tk
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.base import MIMEBase
    from email import encoders
    from email.mime.image import MIMEImage


if sys.version_info[0] == 2:
    import Tkinter as tk
    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEText import MIMEText
    from email.MIMEBase import MIMEBase
    from email import encoders
    from email.mime.image import MIMEImage

fromaddr = "xxx.com"
toaddr = "xxx.com"
mailpass = "xxx"

device = "TEST_Kam "

path = "/home/pi/"
moveto = "/home/pi/Pictures/saved/"


bilder = "noimage.jpg"
flag = 0
delete_flag = 0


os.system("sudo modprobe bcm2835-v4l2") # to recognize PiCamera as video0

def do_picam(app):
    global shot
    global texte
    global texmed
    global delete_flag
    global txt_display
    camera = picamera.PiCamera()
    #camera.awb_mode = 'auto'
    camera.brightness = 50
    #camera.rotation= 90
    camera.resolution = (2592, 1944)
    camera.rotation = 180
    data = time.strftime("%y-%b-%d_(%H%M%S)")
    texte = "picture take at: " + data    
    camera.start_preview()
    camera.capture('%s.jpg' % data)
    camera.stop_preview()
    camera.close() # close Picamera to free resources  to restart the video stream
    shot = '%s.jpg' % data
    dagtid = time.strftime("%y-%b-%d (%H:%M)")
    texte = "picture take:" + time.strftime("%y-%b-%d_(%H%M%S)")
    texmed = device + dagtid
    app.showImg()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,shot)
    app.textBox.configure(bg="dodgerblue")
    app.textBox.update_idletasks()
    delete_flag = 1
    app.vs.open(0) # restarting video stream from Pi Camera
    app.enable_buttons()
    txt_display = " " + shot[0:18]

def do_sendMail(app):   
    mail = MIMEMultipart()
    mail['Subject'] =   str(texmed)
    mail['From'] = fromaddr
    mail['To'] = toaddr
    mail.attach(MIMEText(texte, 'plain'))

    attachment = open(shot, 'rb')
    image = MIMEImage(attachment.read())
    attachment.close()
    mail.attach(image)
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login(fromaddr, mailpass)
    text = mail.as_string()
    server.sendmail(fromaddr, toaddr, text)
    server.quit()
    app.textBox.delete("1.0", tk.END)
    app.textBox.insert(tk.END,txt_display + "  MAILED OK")
    app.textBox.update_idletasks() 
    app.enable_buttons()
    
class Application:
    def __init__(self, output_path = "./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0) # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera
        self.root = tk.Tk()  # initialize root window
        defaultbg = self.root.cget('bg') # set de default grey color to use in labels background
        w = 930 # width for the Tk root
        h = 535 # height for the Tk root
        self.root .resizable(0, 0)
        ws = self.root .winfo_screenwidth() # width of the screen
        hs = self.root .winfo_screenheight() # height of the screen
        x = (ws/2) - (w/2)
        y = (hs/2) - (h/2)
        self.root .geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("     LA  SELVA - REMOT VIDEO SNAP SHOT     ")  # set window title
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=15, column=8, columnspan=25, padx=4, pady=6)

        self.labPic= tk.Label(self.root, bg="grey70")  # initialize image panel
        self.labPic.grid(row=5, column=34,  padx=4, pady=6)

        self.textBox = tk.Text(self.root, height=1, width=28, font=('arial narrow', 10, 'bold'), bg="green",fg="white")
        self.textBox.grid(row=6, rowspan=2, column=34, padx=3)
        self.textBox.insert(tk.END, "READY")
        
        self.botMail = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SEND PICTURE BY MAIL", activebackground="#00dfdf" )
        self.botMail.grid(row=8, column=34, pady= 1)
        self.botMail.configure(command=self.sendMail,state = "disabled")

        self.botRadera = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="DELETE PICTURE", activebackground="#00dfdf" )
        self.botRadera.grid(row=9, column=34, pady=1)
        self.botRadera.configure(command=self.radera, state = "disabled")

        self.botSave = tk.Button(self.root,width=25, font=('arial', 12, 'normal'),  text="SAVE PICTURE", activebackground="#00dfdf" )
        self.botSave.grid(row=10, column=34, pady=1)
        self.botSave.configure(command=self.movepic,state = "disabled")
        
        self.botShoot = tk.Button(self.root,width=24, font=('arial', 14, 'normal'),  text="PICAM-SHOOT", activebackground="#00dfdf" )
        self.botShoot.grid(row=15, column=20)
        self.botShoot.configure(command=self.picam)        

        self.botQuit = tk.Button(self.root,width=6,font=('arial narrow', 14, 'normal'), text="CLOSE", activebackground="#00dfdf")
        self.botQuit.grid(row=15,column=32)
        self.botQuit.configure(command=self.destructor)              
        self.video_loop()
        
    def video_loop(self):
        global test
        global flag
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            cv2image = imutils.rotate_bound(cv2image, 180) # rotate image
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            test = cv2image
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image

        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds
        if flag ==0:
            flag = 1
            self.show_thumb()
            
    def snapshot(self):
        imageName = 'cv-' + time.strftime("%Y-%b-%d_(%H%M%S)")+ '.jpg'
        cv2.imwrite(imageName,test)

    def picam(self):        
        self.disable_buttons()
        self.vs.release() # release the camera to get all resources
        t = threading.Thread(target=do_picam, args=(self,))
        t.start()
        
    def showImg(self):
        image = Image.open(shot)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def show_thumb(self) :
        image = Image.open(bilder)          
        image = image.resize((252,195),Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(image)       
        self.labPic.configure(image=photo)
        self.labPic.image=photo
        self.root.update_idletasks()

    def sendMail(self):
        t = threading.Thread(target=do_sendMail, args=(self,))
        t.start()
        self.disable_buttons()
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,"SENDING PICTURE BY MAIL")
        self.textBox.update_idletasks()        


    def movepic(self):
        global delete_flag
        pic= shot
        src = path+pic
        dst = moveto+pic
        shutil.move(src,dst)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "   SAVED")
        self.textBox.configure(bg="darkorange")
        self.textBox.update_idletasks()
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        delete_flag = 2
                            
    def radera(self):
        global delete_flag
        pic = shot
        os.remove(pic)
        self.textBox.delete("1.0", tk.END)
        self.textBox.insert(tk.END,txt_display + "  DELETED ")
        self.textBox.configure(bg="red")
        self.textBox.update_idletasks()
        delete_flag = 2
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
                            
    def disable_buttons(self):
        self.botShoot.configure(state="disabled")
        self.botMail.configure(state="disabled")
        self.botSave.configure(state="disabled")
        self.botRadera.configure(state="disabled")
        self.botQuit.configure(state="disabled")

    def enable_buttons(self):
        self.botShoot.configure(state="normal")
        self.botMail.configure(state="normal")
        self.botSave.configure(state="normal")
        self.botRadera.configure(state="normal")
        self.botQuit.configure(state="normal")
        
    def destructor(self):
        if delete_flag == 1 : self.radera()
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./Pictures",
help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()
Last edited by gordon77 on Tue Jan 15, 2019 10:10 am, edited 3 times in total.

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 12:36 pm

@gordon77
That rotation is brilliant. Thank you very much for your help.

(I have just seen your Python3 version, not yet loaded it)

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 3:58 pm

@gordon
I have failed on Step 8 of the linked document because I do not have permission to change the sawp size in the file dphys-swapfile.

gordon77
Posts: 3599
Joined: Sun Aug 05, 2012 3:12 pm

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 4:00 pm

Did you use sudo nano /etc/dphys-swapfile?

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 4:07 pm

No I didn't - but I have now.
Easy when you know how!

Thank you.

RDS
Posts: 664
Joined: Tue Oct 06, 2015 8:17 am
Location: Lancashire, UK

Re: PiCamera streaming on Tkinter label -Help

Mon Jan 14, 2019 4:16 pm

maybe not that easy!

Failed on step 9 now,

Code: Select all

make -j4
with the following error message:
make: *** No targets specified and no makefile found. Stop.

Return to “Python”