uneven growth rate of progression in animation - python

Look at flowing code:
from kivy.app import App;
from kivy.uix.widget import Widget;
from kivy.animation import Animation;
from kivy.uix.button import Button;
from time import time;
import json;
but = Button();
anim = Animation(size_hint = (.75 , .75), duration = 1);
anim += Animation(size_hint = (.5 , .5), duration = 1);
anim += Animation(size_hint = (.25 , .25), duration = 1);
anim += Animation(size_hint = (.0 , .0), duration = 1);
progress_array = [];
time_array = [];
start_time = time();
def progr_fun(*args):
global time_array, progress_array;
time_array.append((time() - start_time));
print((time() - start_time));
progress_array.append(args[2]);
print(args[2]);
anim.bind(on_progress = progr_fun);
anim.start(but);
class testApp(App):
def build(self):
return but;
if __name__ == '__main__':
testApp().run();
f_obj = open('hello', 'w');
json.dump([progress_array, time_array], f_obj);
f_obj.close();
Its program, that makes easy animation for button. Animation consists of several parts (its important). There is callback on_progress that collects time data and progression. This data saved at the end of program.
I'm using another script to build plot by time and progression and get something like that:
enter image description here
As you can see in different parts of animation progression growing unevenly.
Why is this happening? How to fix it?

It is odd behavior, but I think the cause is a different time interval used to step through the different parts of the animation. I believe those intervals, while changing for the different phases, are within the resolution of the kivy clock.
I'm not sure why those intervals change. I believe that is done in the C-code underneath the kivy Clock.
The + operator for Animation objects produces a Sequence subclass of Animation, and that seems to trigger the difference. A workaround is to construct your own sequence, Something like this:
self.anim1 = Animation(size_hint=(.75, .75))
self.anim2 = Animation(size_hint=(.5, .5))
self.anim3 = Animation(size_hint=(.25, .25))
self.anim4 = Animation(size_hint=(.0, .0))
self.anim1.bind(on_progress=self.progr_fun, on_complete=self.start2)
self.anim2.bind(on_progress=self.progr_fun, on_complete=self.start3)
self.anim3.bind(on_progress=self.progr_fun, on_complete=self.start4)
self.anim4.bind(on_progress=self.progr_fun)
self.anim1.start(self.but)
def start2(self, *args):
self.anim2.start(self.but)
def start3(self, *args):
self.anim3.start(self.but)
def start4(self, *args):
self.anim4.start(self.but)

One more possible solution - make your own progress rate using time.time(). Its can looks like that:
but = Button();
anim = Animation(size_hint = (.75 , .75), duration = 1);
anim += Animation(size_hint = (.5 , .5), duration = 1);
anim += Animation(size_hint = (.25 , .25), duration = 1);
anim += Animation(size_hint = (.0 , .0), duration = 1);
def start_fun(*args):
global start_time;
start_time = time();
def progr_fun(*args):
global time_array, progress_array, start_time , anim;
my_progress = (time() - start_time) / anim.duration;
print(my_progress);
anim.bind( on_progress = progr_fun,
on_start = start_fun);
anim.start(but);
But this variant not so accurate. You can get something like that at the end of animation:
1.0635101795196533
1.0677716732025146
1.0720353722572327

Related

How to make this task improve cpu usage?

I try to hash many file, but it not use full of cpu power. it only consume 25%. i test to move the heavy process into thread. but still no different. im from nodejs use sharp library. with same task. it consume all cpu usage. How python to make it full power?
import cv2
import math
import datetime
import hashlib
import threading
def thread_function(image, yPos, xPos, wSizeBlock, hSizeBlock):
block = image[yPos:yPos+wSizeBlock, xPos:xPos+hSizeBlock]
hash = hashlib.sha256()
hash.update(block.tobytes())
print(hash.hexdigest())
image = cv2.imread('frame323.jpg', cv2.IMREAD_COLOR)
dimension = {
'width': image.shape[1],
'height': image.shape[0]
}
wSizeBlock = int(16)
hSizeBlock = int(16)
wBlockLength = math.floor(dimension['width'] / wSizeBlock)
hBlockLength = math.floor(dimension['height'] / hSizeBlock)
count = 0
start_time = datetime.datetime.now()
print(start_time)
for k in range(0, 500):
for i in range(0, wBlockLength):
for j in range(0, hBlockLength):
xPos = int(i*wSizeBlock)
yPos = int(j*hSizeBlock)
x = threading.Thread(target=thread_function, args=(image, xPos, yPos, wSizeBlock, hSizeBlock))
x.start()
count += 1
count = 0
end_time = datetime.datetime.now()
print(end_time)
For CPU intensive operations that can be split up into smaller tasks, you would want to use the multiprocessing module. It is similar to the threading module in that it allows multiple functions to be ran at once. Syntax looks something like this:
import multiprocessing as mp
def add(a, b):
return a + b
p = mp.Process(target=add, args=(1, 2))
p.start()

How to synchronize ranges only when the user stops panning and zooming?

I have many plots and many samples per plot. I need to zoom and pan in all plots. Also, all ranges must be synchronized in real time. If I share range works well with a few plots, but with many plots it becomes laggy. Then, to solve this I would like to trigger the synchronization just when the pan or zoom action finishes.
There is a PanEnd event which is triggered when the the user stops panning. But I cannot do the same with the wheel zoom because there is no a MouseWheelEnd event, just a MouseWheel event, so I cannot detect when the user stops. Finally I added a periodic callback to update the ranges from time to time. But I do not like this solution.
I have also tried LODStart and LODEnd events (related with downsampling) and I had to force lod_threshold=1. But sometimes LODEnd is not triggered, only LODStart is always triggered.
from bokeh.plotting import figure
from bokeh.models.sources import ColumnDataSource, CDSView
from bokeh.models.filters import IndexFilter
from bokeh.models.markers import Scatter, Circle
from bokeh.models.tools import LassoSelectTool
from bokeh.models.ranges import DataRange1d
from bokeh.plotting import curdoc, gridplot
from bokeh.events import MouseWheel, PanEnd
import numpy as np
N = 3500
x = np.random.random(size=N) * 200
y = np.random.random(size=N) * 200
source = ColumnDataSource(data=dict(x=x, y=y))
plots = []
x_ranges = []
y_ranges = []
p_last_modified = -1
def render_plot(i, p_last_modified):
range_padding = 0.25
x_range = DataRange1d(
range_padding=range_padding,
renderers=[]
)
y_range = DataRange1d(
range_padding=range_padding,
renderers=[]
)
plot = figure(
width=500,
height=500,
x_range=x_range,
y_range=y_range,
toolbar_location='left',
tools='pan,wheel_zoom,tap,lasso_select',
output_backend='webgl',
)
c = plot.scatter(
x='x',
y='y',
size=3,
fill_color='blue',
line_color=None,
line_alpha=1.0,
source=source,
nonselection_fill_color='blue',
nonselection_line_color=None,
nonselection_fill_alpha=1.0,
)
c.selection_glyph = Scatter(
fill_color='yellow',
line_color='red',
line_alpha=1.0,
)
def mouse_wheel_event(event):
print('>> MOUSE WHEEL EVENT: PLOT NUMBER: {}'.format(i))
global p_last_modified
p_last_modified = i
plot.on_event(MouseWheel, mouse_wheel_event)
def pan_end_event(event):
print('>> PAN END: {}'.format(i))
for p in range(len(plots)):
if p != i:
plots[p].x_range.end = plots[i].x_range.end
plots[p].x_range.start = plots[i].x_range.start
plots[p].y_range.end = plots[i].y_range.end
plots[p].y_range.start = plots[i].y_range.start
plot.on_event(PanEnd, pan_end_event)
plots.append(plot)
x_ranges.append(x_range)
y_ranges.append(y_range)
for i in range(12):
render_plot(i, p_last_modified)
gp = gridplot(
children=plots,
ncols=4,
plot_width=300,
plot_height=300,
toolbar_location='left',
)
def callback():
global p_last_modified
print('-- CALLBACK: last_modified: {}'.format(p_last_modified))
if p_last_modified != -1:
for p in range(len(plots)):
if p != p_last_modified:
plots[p].x_range.end = plots[p_last_modified].x_range.end
plots[p].x_range.start = plots[p_last_modified].x_range.start
plots[p].y_range.end = plots[p_last_modified].y_range.end
plots[p].y_range.start = plots[p_last_modified].y_range.start
p_last_modified = -1
curdoc().add_periodic_callback(callback, 3000)
curdoc().add_root(gp)
Any other suggestion?
I got it working, although I don't like it so much.
It involves some JS and 3 'dummy' widgets, I'd expect there to be a more simple way, but anyhow that is one way.
dum_txt_timer is a textinput that will be used as a timer, its value is in seconds and will be updated with a desired timestep. When the value reaches a desired threshold the update on the ranges will be triggered. When the value is below the threshold it does nothing
dum_button is a button which does two things, a first click will start the timer in dum_txt_timer, a second click will stop the timer.
dum_txt_trigger is another textinput that is used to click dum_button and start/stop the timer.
The mouse_wheel_event function triggers on every single iteration of the mouse wheel. The value of the plot in which the mouse is is stored in mod_source, a data source that is passed to the dum_txt_timer callback.
It checks if the dum_txt_timer value is 0, if it is it updates the value in dum_txt_trigger, which clicks the button and starts the timer, and it updates dum_txt_timer so that other wheel events do nothing until the update. If it is different from 0 it does nothing.
The callback of the dum_txt_timer needs the dum_txt_trigger, the mod_source datasource that stores the plot ID and all the plot ranges.
The callback does nothing until the dum_txt_timer value is updated at the end of the timeout function. Otherwise it first updates the value of dum_txt_trigger which clicks dum_button a second time and stops the timer (resets it to 0. Then it updates the range of all the plots.
I this example the time before the update is set by the timeout function in the button callback.
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, Scatter, Circle, LassoSelectTool, DataRange1d, CustomJS, TextInput, Button
from bokeh.events import MouseWheel, PanEnd
from bokeh.layouts import widgetbox, gridplot
import numpy as np
N = 3500
x = np.random.random(size=N) * 200
y = np.random.random(size=N) * 200
source = ColumnDataSource(data=dict(x=x, y=y))
dum_txt_timer = TextInput(value='0',visible=False)
# javascript code for a dummy (invisible) button, it starts and stops a timer that will be written in dum_txt_timer
dum_button_code = """
if (cb_obj.button_type.includes('success')){
// start a timer in dum_txt by updating its value with a fixed timestep
var start = new Date();
var intervalID = setInterval(function(){var current = new Date(); var diff=((current-start)/1000.0).toFixed(4); dum_txt_timer.value=diff.toString(); }, 500)
cb_obj.button_type = 'warning';
} else {
// stop the timer and set the dum_txt_timer value back to 0
var noIntervals = setInterval(function(){});
for (var i = 0; i<noIntervals; i++) { window.clearInterval(i);}
dum_txt_timer.value='0';
cb_obj.button_type = 'success';
}
"""
dum_button = Button(label='dummy_button',button_type='success',visible=False) # the dummy button itself
dum_button.callback = CustomJS(args={'dum_txt_timer':dum_txt_timer},code=dum_button_code) # the callback of the button
# dummy textinput to click the dummy button
dum_txt_trigger = TextInput(value='0',visible=False)
dum_txt_trigger_code = """
// click the dummy button
var button_list = document.getElementsByTagName('button');
for(var i=0;i<button_list.length;i++){
if(button_list[i].textContent==="dummy_button"){button_list[i].click()}
}
"""
dum_txt_trigger.js_on_change('value',CustomJS(code=dum_txt_trigger_code))
dum_box = widgetbox(dum_txt_timer,dum_txt_trigger,dum_button,visible=False)
plots = []
x_ranges = []
y_ranges = []
mod_source = ColumnDataSource(data={'x':[]})
reference = None
def render_plot(i):
range_padding = 0.25
x_range = DataRange1d(range_padding=range_padding,renderers=[])
y_range = DataRange1d(range_padding=range_padding,renderers=[])
plot = figure(width=500,height=500,x_range=x_range,y_range=y_range,toolbar_location='left',tools='pan,wheel_zoom,tap,lasso_select',output_backend='webgl',)
c = plot.scatter(x='x',y='y',size=3,fill_color='blue',line_color=None,line_alpha=1.0,source=source,nonselection_fill_color='blue',nonselection_line_color=None,nonselection_fill_alpha=1.0,)
c.selection_glyph = Scatter(fill_color='yellow',line_color='red',line_alpha=1.0,)
def mouse_wheel_event(event):
if dum_txt_timer.value != '0':
return
# if the timer value is 0, start the timer
dum_txt_trigger.value = str(int(dum_txt_trigger.value)+1)
dum_txt_timer.value = '0.0001' # immediatly update the timer value for the check on 0 in the python callback to work immediatly
mod_source.data.update({'x':[i]})
plot.on_event(MouseWheel, mouse_wheel_event)
def pan_end_event(event):
print('>> PAN END: {}'.format(i))
for p in range(len(plots)):
if p != i:
plots[p].x_range.end = plots[i].x_range.end
plots[p].x_range.start = plots[i].x_range.start
plots[p].y_range.end = plots[i].y_range.end
plots[p].y_range.start = plots[i].y_range.start
plot.on_event(PanEnd, pan_end_event)
plots.append(plot)
x_ranges.append(x_range)
y_ranges.append(y_range)
for i in range(12):
render_plot(i)
dum_txt_timer_args = {'dum_txt_trigger':dum_txt_trigger,'mod_source':mod_source}
dum_txt_timer_args.update( {'xrange{}'.format(i):plot.x_range for i,plot in enumerate(plots)} )
dum_txt_timer_args.update( {'yrange{}'.format(i):plot.y_range for i,plot in enumerate(plots)} )
set_arg_list = "var xrange_list = [{}];".format(','.join(['xrange{}'.format(i) for i in range(len(plots))]))
set_arg_list += "var yrange_list = [{}];".format(','.join(['yrange{}'.format(i) for i in range(len(plots))]))
# code that triggers when the dum_txt_timer value is changed, so every 100 ms, but only clicks dum_button when the value is greater than 2 (seconds)
dum_txt_timer_code = set_arg_list + """
var timer = Number(cb_obj.value);
var trigger_val = Number(dum_txt_trigger.value);
// only do something when the value is greater than 2 (seconds)
if (timer>0.0001) {
trigger_val = trigger_val + 1;
dum_txt_trigger.value = trigger_val.toString(); // click button again to stop the timer
// update the plot ranges
var p_last_modified = mod_source.data['x'][0];
var nplots = xrange_list.length;
for (var i=0; i<nplots; i++){
if (i!=p_last_modified){
xrange_list[i].start = xrange_list[p_last_modified].start;
xrange_list[i].end = xrange_list[p_last_modified].end;
yrange_list[i].start = yrange_list[p_last_modified].start;
yrange_list[i].end = yrange_list[p_last_modified].end;
}
}
}
"""
dum_txt_timer.js_on_change('value',CustomJS(args=dum_txt_timer_args,code=dum_txt_timer_code))
gp = gridplot(children=plots,ncols=4,plot_width=300,plot_height=300,toolbar_location='left',)
grid = gridplot([[gp],[dum_box]],toolbar_location=None)
curdoc().add_root(grid)
One nice thing is that the same dummy widgets can be used to set a delay on range updates from different events, the event callback just needs to update dum_txt_trigger like in mouse_wheel_event

How to use vispy to generate a rolling image?

Some context:
I was looking into the vispy module to plot in realtime (or as close as possible to) data coming from an instrument. My attempt follow.
from vispy.plot import Fig
from vispy import app,scene
from vispy.visuals import TextVisual
import numpy as np
import Queue
FONT_SIZE = 14
MIN = 0
MAX = 1.1
w_size = 100
N = 5000
M = 2500
color_map = 'cubehelix'
q_size = 1000
Nb = 5
#generate (empty) initial data to fill the plot
data = np.zeros(N*M)
data = np.reshape(data, (N,M))
#setup the plot
fig = Fig(show = False,size = (16*w_size,9*w_size),bgcolor='black')
fig.title = 'my plot'
main_plot = fig[0,0].image(data = data,fg_color='w',cmap=color_map,clim=(MIN,MAX))
fig[0,0].view.camera.aspect = N/float(M) * 16./9.
title = scene.Label("someoutput", font_size=FONT_SIZE, color = 'w')
fig[0,0].grid.add_widget(title, row=0, col=4)
fig[0,0].grid[2,4].border_color = 'black'
fig[0,0].grid[2,4].bgcolor = 'black'
xlabel_title = scene.Label("x_axis [unit]", font_size=FONT_SIZE, color = 'w')
fig[0,0].grid.add_widget(xlabel_title, row=4, col=4)
ylabel_title = scene.Label("y_axis [unit]", font_size=FONT_SIZE,rotation=-90, color='w')
fig[0,0].grid.add_widget(ylabel_title, row=2, col=2)
scale = scene.ColorBarWidget(orientation='left',
cmap=color_map,
label='Some value',
clim=(MIN,MAX),
border_color = 'w',
border_width = 1,
label_color = 'w'
)
fig[0,0].grid.add_widget(scale, row=2, col=6)
fig[0,0].cbar_right.width_max = \
fig[0,0].cbar_right.width_min = 50
#fill a queue so to excude the generation time from the plotting time
q = Queue.Queue()
for i in range(q_size):
new_data = (np.abs(0.5*np.random.randn(Nb*M)[:])).astype('float32')
new_data = np.reshape(new_data, (Nb,M))
q.put(new_data[:])
#update function
def update(ev):
global main_plot, q, data, Nb,M,fig,index
#acquire
new_data = q.get()
#roll the plot data
data[Nb:, :] = data[:-Nb, :]
data[:Nb,:] = new_data
#recycle the new data
q.put(new_data)
#update the plot
main_plot.set_data(data)
main_plot.update()
# setup timer
interv = 0.01
timer = app.Timer(interval = interv)
timer.connect(update)
timer.start(interval = interv)
if __name__ == '__main__':
fig.show(run=True)
app.run()
This code currently works but it's much slower than the data rate. In the vispy gallery, as well as in some examples, I saw much more points being plotted and updated. I think that the main problem is that I completely set each time all the data of the plot instead of shifting them and inserting new points.
I also had a look at this example:
https://github.com/vispy/vispy/blob/master/examples/demo/scene/oscilloscope.py
However I don't know how to generalize the update function that rolls the data (I have no knowledge of OpenGL) and I cannot use the example as is because I need a quantitative color scale (that seems well implemented in vispy.plot).
The question:
Is there a way to write a function that rolls the data of a plot generated with the vispy.plot class?
Thanks.

Implement multithreading in Python Zelle graphics

I am creating a program which opens a world map in a window using Zelle's graphics.py. It has one function which draws dots on the map, and another function which undraws those dots after they are on the screen for 1 second (which are stored in a list after being drawn). I want these functions to work concurrently, but when the addDots() function is called in a thread it won't draw the dot in the window, it just stalls. Here is the module which I run:
import thread
import threading
import time
import random
import sys
sys.path.append('..')
from Display import map
import tester
import datetime
dots = list(())
def deleteDots():
while 1==1:
tF = datetime.datetime.now()
a = 0
for i in range(len(dots)):
tD = tF - dots[i-a][2]
tD = int(str(tD)[5:7])
if tD >= 1:
map.deletePoint(dots[i-a][0],dots[i-a][1])
dots.pop(i-a)
a = a+1
def addDots():
oldResponseCount = tester.getResponseCount()
oldResponseCount = int(str(oldResponseCount))
while 1==1:
print(oldResponseCount)
newResponseCount = tester.getResponseCount()
newResponseCount = int(str(newResponseCount))
print(newResponseCount)
if(newResponseCount != oldResponseCount):
difference = newResponseCount - oldResponseCount
for i in range(difference):
lat = random.randint(-90,90)
long = random.randint(-180,180)
map.drawPoint(lat,long)
tI = datetime.datetime.now()
dots.append([lat,long,tI])
oldResponseCount = newResponseCount
if __name__ == '__main__':
threading.Thread(target=addDots).start()
threading.Thread(target=deleteDots).start()
And here is the map module which draws the map on a graphics window and contains the functions to plot and delete a point:
from graphics import *
import math
import images
size = 0.6
Circles = list(())
win = GraphWin("My Window", 1920*size, 1080*size)
win.setBackground('blue')
images.test(size)
myImage = Image(Point(960*size,540*size), "../Display/temp.gif")
myImage.draw(win)
import time
def drawPoint(lat,long):
x = int(long*5.3+960)*size
y = int(lat*(-5.92)+540)*size
pt = Point(x,y)
cir = Circle(pt,5)
cir.setFill(color_rgb(255,0,0))
Circles.append([cir,x,y])
cir.draw(win)
def deletePoint(lat,long):
x = int(long*5.3+960)*size
y = int(lat*(-5.92)+540)*size
for c in Circles:
if c[1]==x and c[2]==y:
c[0].undraw()
How should I go about doing this?
There are a couple of issues that have to be addressed. First, any graphics.py commands that invoke tkinter (i.e. commands that cause something to be drawn/undrawn) must be issued by the primary (main) thread. So we need the secondary threads to communicate drawing requests to the primary thread.
Second, you have both your secondary threads modifying the Circles and dots lists -- you need to syncronize (lock) access to these lists so that only one thread at a time can modify or iterate them.
Below is my rework of your code as an example. I've eliminated map and tester routines as I'm just putting dots up on a window with one thread and deleting them after they are a second old from another thread:
from threading import Thread, Lock
from queue import Queue # use for thread-safe communications
from random import randint
import time
from graphics import *
def drawPoint(lat, long):
x = int(long * 5.3 + 960)
y = int(lat * -5.92 + 540)
point = Point(x, y)
circle = Circle(point, 5)
circle.setFill(color_rgb(255, 0, 0))
circles_lock.acquire()
circles.append(circle)
circles_lock.release()
actions.put((circle.draw, win))
def deletePoint(lat, long):
global circles
x = int(long * 5.3 + 960)
y = int(lat * -5.92 + 540)
keep_circles = []
circles_lock.acquire()
for circle in circles:
center = circle.getCenter()
if center.getX() == x and center.getY() == y:
actions.put((circle.undraw,))
else:
keep_circles.append(circle)
circles = keep_circles
circles_lock.release()
def deleteDots():
global dots
while True:
keep_dots = []
dots_lock.acquire()
now = time.time()
for dot in dots:
lat, long, then = dot
if now - then >= 1.0:
deletePoint(lat, long)
else:
keep_dots.append(dot)
dots = keep_dots
dots_lock.release()
time.sleep(0.5)
def addDots():
while True:
lat = randint(-90, 90)
long = randint(-180, 180)
drawPoint(lat, long)
dots_lock.acquire()
dots.append((lat, long, time.time()))
dots_lock.release()
time.sleep(0.25)
win = GraphWin("My Window", 1920, 1080)
circles = []
circles_lock = Lock()
dots = []
dots_lock = Lock()
actions = Queue()
Thread(target=addDots, daemon=True).start()
Thread(target=deleteDots, daemon=True).start()
while True:
if not actions.empty():
action, *arguments = actions.get()
action(*arguments)
time.sleep(0.125)

Python Speed Optimization

I am creating a program (to test a theory), and to get the data I need, I need a program to run as fast as possible.
Here's the problem - I have made it as fast as I could manage and it is still to slow. It is using a very small amount of my computer's RAM and CPU capacity. I am running the program with PyCharm 2017 Community Edition.
The code is below; How would I further optimize or change this to make it run faster?
Main:
from functions import *
from graphics import *
import time
Alpha = True
x = timestamp()
while Alpha:
master = GraphWin(title="Image", width=512, height=512)
build_image(master)
getter(master, x)
x = timestamp()
time.sleep(3)
master.close()
Module "Functions":
from graphics import *
import random
from PIL import ImageGrab
def build_image(window):
for i in range(513):
for j in range(513):
fig = Rectangle(Point(j, i), Point(j + 1, i + 1))
color = random.randrange(256)
fig.setFill(color_rgb(color, color, color))
fig.setOutline(color_rgb(color, color, color))
fig.draw(window)
def getter(widget, counter):
x = widget.winfo_rootx()+widget.winfo_x()
y = widget.winfo_rooty()+widget.winfo_y()
x1 = x+widget.winfo_width()
y1 = y+widget.winfo_height()
ImageGrab.grab().crop((x, y, x1, y1)).save("{}.png".format(str(counter)))
def timestamp():
timelist = time.gmtime()
filename = ("Image" + "_" + str(timelist[0]) + "_" + str(timelist[1]) + "_" + str(timelist[2]) + "_" +
str(timelist[3]) + "_" + str(timelist[4]) + "_" + str(timelist[5]) + "_UTC")
return filename
Note: Module "Graphics" is a module that allows for easy manipulation of Tkinter.
Your slowness is probably from treating the pixels as rectangles in your window.
If all you want to do is generate random images, you can skip the window part. I found this code laying about, after not too much ducking:
from PIL import Image
import random
def drawImage():
testImage = Image.new("RGB", (600,600), (255,255,255))
pixel = testImage.load()
for x in range(600):
for y in range(600):
red = random.randrange(0,255)
blue = random.randrange(0,255)
green = random.randrange(0,255)
pixel[x,y]=(red,blue,green)
return testImage
def main():
finalImage = drawImage()
finalImage.save("finalImage.jpg")
Use a profiler to see where your program is fast/slow. Here is a profile wrapper you can use on your functions to see what is taking too long in your program.
def line_profiler(view=None, extra_view=None):
import line_profiler
def wrapper(view):
def wrapped(*args, **kwargs):
prof = line_profiler.LineProfiler()
prof.add_function(view)
if extra_view:
[prof.add_function(v) for v in extra_view]
with prof:
resp = view(*args, **kwargs)
prof.print_stats()
return resp
return wrapped
if view:
return wrapper(view)
return wrapper
Now how to use it
#line_profiler
def simple():
print("Hello")
print("World")
Now when you run your function, you will get a printout of how long everything takes.
You might need to do pip install line_profiler
this may be a bit faster if you use numpy. loops inside loops will kill your speed.
from PIL import Image
import numpy as np
def drawImage():
return Image.fromarray(np.random.randint(255, size=(600, 600, 3)).astype(np.uint8))
Since you do a lot of independent tasks, you could benefit from parallelism. Something like:
from concurrent.futures import ThreadPoolExecutor
def build_image(window, start, end, step):
for i in range(start, end, step):
for j in range(end):
fig = Rectangle(Point(j, i), Point(j + 1, i + 1))
color = random.randrange(256)
fig.setFill(color_rgb(color, color, color))
fig.setOutline(color_rgb(color, color, color))
fig.draw(window)
max_workers = 8
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for id in range(max_workers):
executor.submit(build_image, window, id, 513, max_workers)

Categories