Using PIL to analyze a video frame by frame - python

I'm working on using PIL to average the pixel intensities over a subarea of a video. What I want to do is:
-Use ffmpeg to turn the video into several frames
-Use PIL to choose a window in each frame (this is the step I'd like help with)
-Do some sort of analysis on that window in each frame, and aggregate the data (like, say, average color vs. time)
I'm at quite a loss as to how to do the middle step -- does anyone have suggestions?

Found a solution using Tkinter:
import Tkinter
import Image, ImageDraw, ImageTk
window = Tkinter.Tk()
window.title('Calcium Imaging Software')
mouse_X = 0
mouse_Y = 0
ellipseBox = []
listOfCircles = []
#stuff to set up the image
image = Image.open("test.jpg")
draw = ImageDraw.Draw(image)
canvas = Tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
#define a function to be run on the mouseclick
def drawEllipse(event):
global ellipseBox
print "clicked at: ", event.x, event.y
mouse_X = event.x
mouse_Y = event.y
ellipseBox.append((mouse_X,mouse_Y))
print "box corners: ",ellipseBox
#When two corners are selected, draw the ellipse
if len(ellipseBox) == 2:
draw.ellipse(ellipseBox,outline=(255,255,255))
listOfCircles.append(tuple(ellipseBox))
ellipseBox = []
window.update()
#bind mouse click to drawing an ellipse
canvas.bind("<Button-1>", drawEllipse)
Tkinter.mainloop()
And this does almost everything I want! However, I can't get the ellipses to show up on the image -- any suggestions?

Related

Get color at point of mouse click in Tkinter

I'm working on a program (in Linux) where I need to know the color at the point where of mouse click. I can't install external modules (PIL is fine though). I did try and see if there are solutions posted in the net (Return RGB Color of Image Pixel under Mouse Tkinter) but all of them seem to use modules which I'll have to install. Is there a way I can do it with these restrictions in mind?
Here is a short script that enables colors to be found and displayed in tkinter.
It uses PIL ImageGrab.grab() to grab the entire screen as pic then you simply press space-bar to find the color under the mouse pointer. It uses pic.getpixel(x, y)
import tkinter as tk
from PIL import ImageGrab
master = tk.Tk()
master.columnconfigure(0, weight = 1)
master.title("Colors")
button = tk.Button(master, text = "Press Spacebar")
button.grid(sticky = tk.NSEW)
canvas = tk.Canvas(master, width = 200, height = 200)
canvas.grid(sticky = tk.NSEW)
pic = ImageGrab.grab()
def color():
x, y = master.winfo_pointerx(), master.winfo_pointery()
r, g, b = pic.getpixel((x, y))
hue = f"#{r:02x}{g:02x}{b:02x}"
button.config( text = f"{x}, {y} = {hue}")
canvas["background"] = hue
button["command"] = color
button.focus_force()
master.mainloop()

Roll up image implementation using canvas - Python

I want an image to scroll up inside a canvas. But I am unable to do it.
import tkinter
def scroll_image(event):
global roll
roll = roll - 10
canvas1.itemconfig(canvas1.create_image(20, roll, image=i))
roll = 10
windows = tkinter.Tk()
windows.title("My Application")
# Adding canvas to show image there
canvas1 = tkinter.Canvas(windows, width=200, height=100)
i = tkinter.PhotoImage(file="Capture.gif")
canvas1.create_image(20, 20, image=i)
# trying to implement roll-up image
canvas1.itemconfig(canvas1.create_image(20, 20, image=i))
canvas1.bind("<Configure>", scroll_image)
canvas1.grid(column=0, row=2)
windows.mainloop()
I tried using loops but I noticed that loops are running as expected but unfortunately the canvas update is taking place only once. So I removed the loop. But I need to find a way out to implement a roll up image.
Image is moving up and down.
This example use
module PIL/pillow to load jpg/png instead of gif
after(time_in_millisecond, function_name) to repeat function which moves image
img_id to use only one image (instead of creating many images with create_image)
canvas.move(ID, offset_x, offset_y) to move image (or other object)
canvas.coords(ID) to get current positon of image (or other object)
canvas.pack(fill='both', expand=True) to use full window. Canvas will use full window even when you resize window.
Code:
import tkinter as tk
from PIL import Image, ImageTk
def scroll_image():
global offset_y # inform function that you want to assign value to external variable instead of local one.
# move image
canvas.move(img_id, offset_x, offset_y)
# get current position
x, y = canvas.coords(img_id)
print(x, y)
# set position (if you don't use canvas.move)
#canvas.coords(img_id, x+offset_x, y+offset_y)
# x += offset_x
# y += offset_y
# change direction
if y <= -100 or y >= 0:
offset_y = -offset_y
# repeat after 20ms
root.after(20, scroll_image)
offset_x = 0
offset_y = -3
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.pack(fill='both', expand=True) # use full window
#photo = tk.PhotoImage(file="Capture.gif")
image = Image.open("image.jpg")
photo = ImageTk.PhotoImage(image)
img_id = canvas.create_image(0, 0, image=photo)
# start scrolling
scroll_image()
root.mainloop()
You can use also canvas.coords(ID, x, y) to set new position.
More examples on GitHub for: Tkinter and other Python's modules

Rotate a label with image in tkinter

I am attempting to rotate an label with an image which is on a canvas with tkinter.
I have three images that need rotating (pitch, roll and yaw) and they will eventually rotate according to IMU sensor outputs.
import tkinter as tk
from tkinter import PhotoImage
from PIL import Image
root = tk.Tk()
root.title('Pitch/Roll/Yaw Simulator')
# pick image file
fname = "PRY_Diag_Dials.png"
bg_image = tk.PhotoImage(file=fname)
# get the width and height of the image
w = bg_image.width()
h = bg_image.height()
# size the window so the image will fill it
root.geometry("%dx%d+50+30" % (w, h))
cv = tk.Canvas(root, width=w, height=h)
cv.pack(side='top', fill='both', expand='yes')
cv.create_image(0, 0, image=bg_image, anchor='nw')
#add images
pitch = tk.PhotoImage(file="Pitch2.gif")
pitch_lbl = tk.Label(cv, image=pitch,bg="white")
pitch_lbl.image = pitch
pitch_lbl.place(x=60, y=180)
roll = tk.PhotoImage(file="Roll2.gif")
roll_lbl = tk.Label(cv, image=roll,bg="white")
roll_lbl.image = roll
roll_lbl.place(x=325, y=180)
yaw = tk.PhotoImage(file="Yaw2.gif")
yaw_lbl = tk.Label(cv, image=yaw,bg="white")
yaw_lbl.image = yaw
yaw_lbl.place(x=590, y=180)
root.mainloop()
How can I rotate the image labels through tkinter?
tkinter doesn't have any support for rotating canvas items.
From the official tcl/tk documentation (tkinter is a wrapper around a tcl interpreter):
Individual items may be moved or scaled using widget commands described below, but they may not be rotated.
If you are looking to simple rotate 90, 180 or flip then you can use PIL to transpose.
See this link for the details: Transpose.
Here is the important bits just in case link brakes in the future:
Image.transpose(method)
Transpose image (flip or rotate in 90 degree steps)
Parameters: method – One of:
PIL.Image.FLIP_LEFT_RIGHT
PIL.Image.FLIP_TOP_BOTTOM
PIL.Image.ROTATE_90
PIL.Image.ROTATE_180
PIL.Image.ROTATE_270
PIL.Image.TRANSPOSE
Returns: Returns a flipped or rotated copy of this image.

PIL: Jump to next photo after one click event

I am preparing data for deep running. So I have to get certain pixel coordinates for each picture. Only one coordinate per photo is required.
So when I use PIL to input one click, I try to implement coordinates so that I can go to the next picture.
However, when I write the code as below, the coordinates are output in duplicate to only one picture, and the next picture does not appear on the screen.
How can I make sure that only one coordinate is recorded on a single picture?
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
print(imgList)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
def callback(event):
print("clicked at: ", event.x, event.y)
window = tkinter.Tk(className='pla')
for file in sorted(imgList):
a=True
image = Image.open(os.path.join(URL, file))
print(image)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
tkinter.mainloop()
I am not 100% sure I understand what you need but it looks like to me you are trying to get one set of cord's for each image in a list of images.
I would do this by creating a function and a tracking variable to loop through each image on at a time and on click update a new list with the image and cord's then loop to next image.
Let me know if you have any questions.
Example:
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
tracker = 0
list_images_with_cords = [] # added list for final results
def callback(event):
# Added global's.
global tracker,list_images_with_cords
# Used to append final results to list.
list_images_with_cords.append([imgList[tracker], event.x, event.y])
# This tracker lets us go through each item on the list.
tracker += 1
# After appending list go to next image.
open_next()
window = tkinter.Tk(className='pla')
# Creates just one canvas that we can update later.
canvas = tkinter.Canvas(window)
canvas.pack()
def open_next():
# Adding global's.
global image, canvas, image_tk, tracker
# Clearing canvas before drawing new image.
canvas.delete("all")
# Checking for valid index in list.
if tracker < len(imgList):
image = Image.open(os.path.join(URL, imgList[tracker]))
# use config() to update canvas.
canvas.config(width=image.size[0], height=image.size[1])
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
else:
# This else statement is just for when we run out of images.
# It will display all the results in a textbox.
canvas.destroy()
txt = tkinter.Text(window, width=25)
txt.pack()
for item in list_images_with_cords:
txt.insert("end", "{}\n\n".format(item))
open_next()
tkinter.mainloop()

Cropping and displaying images using PIL and Tkinter

I am beginning to learn Python after being trapped using VB6 forever. To help myself learn some gui with Tkinter, I'm making a simple slot machine. To animate the wheels, I have a ~300x2000 image that has all of the pictures I intend to use displayed as a strip. About 6 pictures right now. I just show part of the image (wheel) at a time to make it spin. Anyway, I'm having trouble with the part of the code where I come to the end of the image and need to transition from the end of the image back to the beginning (wheel spins down, so the start is the bottom and end is the top).
For some reason, I can't get the images to crop and display correctly, even though I think my script makes sense logically. My code is posted below. You can make an image around the resolution of 300x2000 or so to use with this to see the problem I am having. My code starts printing outside of the area that I want it to show (outside the showsize variable) and I can't figure out why.
Any help with this problem would be much appreciated. It seems that the crops don't cut the images short enough, but all the information that I found about it make me think my script should be working just fine. I've tried to annotate my code to explain what's going on.
from Tkinter import *
from PIL import Image, ImageTk
def main():
root = Tk()
root.title = ("Slot Machine")
canvas = Canvas(root, width=1500, height=800)
canvas.pack()
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 #amount of source image to show at a time - part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 0 #amount of pause between each frame
for spintimes in range(1,numberofspins):
for y in range(wheelh,showsize,-speed): #spin to end of image, from bottom to top
cropped = im.crop((0, y-showsize, wheelw, y)) #crop which part of wheel is seen
tk_im = ImageTk.PhotoImage(cropped)
canvas.create_image(bx1, by, image=tk_im) #display image
canvas.update() # This refreshes the drawing on the canvas.
canvas.after(cycle_period) # This makes execution pause
for y in range (speed,showsize,speed): #add 2nd image to make spin loop
cropped1 = im.crop((0, 0, wheelw, showsize-y)) #img crop 1
cropped2 = im.crop((0, wheelh - y, wheelw, wheelh)) #img crop 2
tk_im1 = tk_im2 = None
tk_im1 = ImageTk.PhotoImage(cropped1)
tk_im2 = ImageTk.PhotoImage(cropped2)
#canvas.delete(ALL)
canvas.create_image(bx1, by, image=tk_im2) ###THIS IS WHERE THE PROBLEM IS..
canvas.create_image(bx1, by + y, image=tk_im1) ###PROBLEM
#For some reason these 2 lines are overdrawing where they should be. as y increases, the cropped img size should decrease, but doesn't
canvas.update() # This refreshes the drawing on the canvas
canvas.after(cycle_period) # This makes execution pause
root.mainloop()
if __name__ == '__main__':
main()
It's simpler, and much faster and smoother if you don't recreate your images on every cycle. Here's my solution using canvas.move(). Notice that I moved the canvas.create_image calls outside of the for loop. I also put the code in a class and added a 'spin' button, and added something to make it exit without errors.
from Tkinter import *
from PIL import Image, ImageTk
import sys
class SlotMachine():
def __init__(self):
root = Tk()
root.title = ("Slot Machine")
self.canvas = Canvas(root, width=1200, height=800)
self.canvas.grid(column=0,row=0)
button = Button(root, text="Spin!", width=20, command = self.spin)
button.grid(column=0,row=1)
self.alive = True
root.protocol("WM_DELETE_WINDOW", self.die)
root.mainloop()
def spin(self):
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 # amount of source image to show at a time -
# part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 3 #amount of pause between each frame
tk_im1 = ImageTk.PhotoImage(im)
tk_im2 = ImageTk.PhotoImage(im)
im1id = self.canvas.create_image(bx1, by + showsize, image=tk_im1)
im2id = self.canvas.create_image(bx1, by + showsize + wheelh,
image=tk_im2)
for spintimes in range(1,numberofspins):
for y in range(wheelh,0,-speed):
if self.alive:
self.canvas.move(im1id, 0, -speed)
self.canvas.move(im2id, 0, -speed)
self.canvas.update()
self.canvas.after(cycle_period)
else:
sys.exit()
self.canvas.move(im1id, 0, wheelh)
self.canvas.move(im2id, 0, wheelh)
def die(self):
self.alive = False
if __name__ == '__main__':
mySlotMachine = SlotMachine()
This will place parts of the wheel outside of its box, but you can just put your slot machine texture on top of that.

Categories

Resources