Commit d63e9150 authored by Richard Torenvliet's avatar Richard Torenvliet

Rewritten GUI to kivy to display the results

parent 8e5a7e83
......@@ -10,11 +10,31 @@ data/imm_face_db: data/imm_face_db.tar.gz
data/imm_face_db.tar.gz:
(cd data; wget http://www.imm.dtu.dk/~aam/datasets/imm_face_db.tar.gz)
generate_test_model:
train_model: data/pca_train_model.npy
python src/main.py \
--store_pca \
--save_pca \
--asf data/imm_face_db/*.asf \
--file data/pca_test_model
--model_file data/pca_train_model
show_pca:
python src/main.py \
--show_pca \
--asf data/imm_face_db/*.asf \
--model_file data/pca_model.npy
test_model:
python src/main.py \
--reconstruct \
--asf `./scripts/imm_test_set.sh` \
--model_file data/pca_train_model.npy \
--n_components 6
show_reconstruction:
python src/main.py \
--reconstruct \
--asf data/imm_face_db/*.asf \
--model_file data/pca_train_model.npy \
--n_components 6
test:
py.test -f src/*_test.py
......
files=`ls data/imm_face_db/ | grep -E "^[0-2][0-9].*.asf"`
files=`ls data/imm_face_db/ | grep -E "^[4][0-9].*.asf"`
for f in $files; do
echo "data/imm_face_db/$f"
......
files=`ls data/imm_face_db/ | grep -E "^[3-4][0-9].*.asf"`
files=`ls data/imm_face_db/ | grep -E "^[0-3][0-9].*.asf"`
for f in $files; do
echo "data/imm_face_db/$f"
......
......@@ -5,27 +5,31 @@ def get_mean(imm_points):
""" construct a mean from a matrix of x,y values
Args:
imm_points(numpy array) that follows the following structure:
observations:
0. [[x_0_0, y_0_0], ... , [x_0_m, y_0_m]],
1. [[x_1_0, y_1_0], ... , [x_1_m, y_1_m]],
2. [[x_2_0, y_2_0], ... , [x_2_m, y_2_m]],
3. [[x_3_0, y_3_0], ... , [x_3_m, y_3_m]]
.... .... .m...
Returns:
mean_values (numpy array)
n. [[x_4_0, y_4_0], ... , [x_n_m, y_n_m]]
Examples:
Input observations:
0. [[x_0_0, y_0_0], ... , [x_0_m, y_0_m]],
1. [[x_1_0, y_1_0], ... , [x_1_m, y_1_m]],
2. [[x_2_0, y_2_0], ... , [x_2_m, y_2_m]],
3. [[x_3_0, y_3_0], ... , [x_3_m, y_3_m]]
.... .... .....
n. [[x_4_0, y_4_0], ... , [x_n_m, y_n_m]]
Returns mean_values (numpy array)
This vector containts the mean values of the corresponding column, like so:
0. [[x_0_0, y_0_0], ... , [x_0_k, y_0_k]],
1. [[x_1_0, y_1_0], ... , [x_1_k, y_1_k]],
2. [[x_2_0, y_2_0], ... , [x_2_k, y_2_k]],
3. [[x_3_0, y_3_0], ... , [x_3_k, y_3_k]]
0. [[x_0_0, y_0_0], ... , [x_0_k, y_0_k]],
1. [[x_1_0, y_1_0], ... , [x_1_k, y_1_k]],
2. [[x_2_0, y_2_0], ... , [x_2_k, y_2_k]],
3. [[x_3_0, y_3_0], ... , [x_3_k, y_3_k]]
.... .... .....
.... .... .....
n. [[x_4_0, y_4_0], ... , [x_n_k, y_n_k]]
n. [[x_4_0, y_4_0], ... , [x_n_k, y_n_k]]
mean. [[x_mean_0, y_mean_0], ... [x_mean_n, y_mean_n]]
mean. [[x_mean_0, y_mean_0], ... [x_mean_n, y_mean_n]]
"""
return np.mean(imm_points, axis=0)
......@@ -57,7 +57,6 @@ class IMMPoints():
def show_on_img(self, img, window_name='image'):
self.draw_triangles(img, self.points)
cv2.imshow(window_name, img)
def show(self, window_name='image'):
"""show the image and datapoints on the image"""
......@@ -67,7 +66,6 @@ class IMMPoints():
img = cv2.imread('data/imm_face_db/' + self.filename)
self.draw_triangles(img, self.points)
cv2.imshow(window_name, img)
def flatten_feature_vectors(data):
......
import copy
import argparse
import logging
import sys
import cv2
import numpy as np
# local imports
import pca
from aam import get_mean
from imm_points import IMMPoints, build_feature_vectors, \
flatten_feature_vectors
......@@ -15,63 +18,56 @@ logging.basicConfig(level=logging.INFO,
logger = logging.getLogger(__name__)
def add_parser_options():
parser = argparse.ArgumentParser(description='IMMPoints tool')
pca_group = parser.add_argument_group('show_pca')
pca_group.add_argument(
'--store_pca', action='store_true',
help='Store the pca model'
'--reconstruct', action='store_true',
help='Reconstruct one face with a given pca model'
)
pca_group.add_argument(
'--save_pca', action='store_true',
help='save the pca model'
)
pca_group.add_argument(
'--show_pca', action='store_true',
help='Show and manipulate the stored PCA model'
help='Show and manipulate the saved PCA model'
)
# asf files
# asf model_files
pca_group.add_argument(
'--asf', nargs='+', help='asf files to process'
)
pca_group.add_argument(
'--n_components', default=5, type=int,
'--n_components', default=10, type=int,
help='number of principle components to keep and are able to manipulate'
)
pca_group.add_argument(
'--file', type=str,
'--model_file', type=str,
help='pca model file that contains or is going to contain the pca model'
)
return parser
def nothing(_):
pass
def init_eigenvalue_trackbars(n_components, s):
cv2.namedWindow('eigenvalues')
for i in range(n_components):
cv2.createTrackbar('{}'.format(i), 'eigenvalues', 500, 1000, nothing)
def store_pca_model(args):
def save_pca_model(args):
"""
Store the U, s, Vt and mean of all the asf datafiles given by the asf
save the U, s, Vt and mean of all the asf datafiles given by the asf
files.
It is stored in the following way:
It is saved in the following way:
np.load(filename, np.assary([U, s, Vt, mean_values])
And accessed by:
UsVtm = np.load(args.file)
UsVtm = np.load(args.model_file)
U = UsVtm[0]
s = UsVtm[1]
......@@ -80,71 +76,88 @@ def store_pca_model(args):
"""
assert args.asf, '--asf files should be given'
assert args.file, '--file needs to be provided to store the pca model'
assert args.model_file, '--model_file needs to be provided to save the pca model'
imm_points = build_feature_vectors(args.asf, flattened=True)
mean_values = get_mean(imm_points)
U, s, Vt = pca.pca(imm_points, mean_values)
np.save(args.file, np.asarray([U, s, Vt, mean_values]))
logger.info('Stored pca model in %s', args.file)
pca.save(U, s, Vt, mean_values, args.model_file)
logger.info('saved pca model in %s', args.model_file)
def show_pca_model(args):
assert args.asf, '--asf files should be given to allow the image to be shown'
assert args.file, '--file needs to be provided to get the pca model'
assert args.model_file, '--model_file needs to be provided to get the pca model'
# load the stored model file
UsVtm = np.load(args.file)
U = UsVtm[0]
s = UsVtm[1]
Vt = UsVtm[2]
mean_values = UsVtm[3]
U, s, Vt, mean_values = pca.load(args.model_file)
# init trackbars
index = 0
cv2.namedWindow('index')
cv2.createTrackbar('index', 'index', index, len(args.asf) - 1, nothing)
# index = 0
# cv2.namedWindow('index')
# cv2.createTrackbar('index', 'index', index, len(args.asf) - 1, trackbarUpdate)
n_components = args.n_components
init_eigenvalue_trackbars(n_components, s)
view.init_eigenvalue_trackbars(n_components, s, window='eigenvalues')
# use a copy of s to manipulate so we never touch the original
s_copy = copy.copy(s)
reconstruction = np.dot(Vt[:n_components], x - mean_values)
while True:
projection = pca.reconstruct(U, s_copy, Vt, n_components)
X_reconstructed = (projection[index] + mean_values).reshape((58, 2))
imm = IMMPoints(filename=args.asf[index])
reconstruction = np.dot(V[:n_components], x - mean_values)
# reconstruction = pca.reconstruct(U[index], s_copy, Vt, n_components, mean_values)
# reshape to x, y values
reconstructed = reconstruction.reshape((58, 2))
imm = IMMPoints(points=X_reconstructed)
imm = IMMPoints(points=reconstructed)
img = np.full((480, 640, 3), 255, np.uint8)
imm.show_on_img(img)
for i in range(n_components):
s_copy[i] = s[i] * (
(cv2.getTrackbarPos(str(i), 'eigenvalues') - 500) / 10.0)
s_copy[i] = s[i] + (
(cv2.getTrackbarPos(str(i), 'eigenvalues') - 50) / 10.0)
index = cv2.getTrackbarPos('index', 'index')
imm = IMMPoints(filename=args.asf[index])
imm.show(window_name='original')
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
def reconstruct_with_model(args):
assert args.asf, '--asf files should be given to allow the image to be shown'
assert args.model_file, '--model_file needs to be provided to get the pca model'
# clear args. arguments are conflicting with parseargs
# kivy will parse args upon import and will crash if it finds our
# 'unsuported by kivy' arguments.
sys.argv[1:] = []
from view.reconstruct import ReconstructApp
U, s, Vt, mean_values = pca.load(args.model_file)
ReconstructApp(
args=args, eigen_vectors=Vt, mean_values=mean_values,
).run()
def main():
parser = add_parser_options()
args = parser.parse_args()
if args.show_pca:
show_pca_model(args)
elif args.store_pca:
store_pca_model(args)
elif args.save_pca:
save_pca_model(args)
elif args.reconstruct:
reconstruct_with_model(args)
......
......@@ -4,12 +4,77 @@ import numpy as np
def pca(data, mean_values):
# subtract mean
zero_mean = data - mean_values
U, s, Vt = np.linalg.svd(zero_mean, full_matrices=False)
U, s, V = np.linalg.svd(zero_mean, full_matrices=False)
return U, s, Vt
return U, s, V
def reconstruct(feature_vector, Vt, mean_values, n_components=10):
"""
Reconstruct with U, s, Vt
def reconstruct(U, s, Vt, n_components):
return np.dot(U[:, :n_components],
np.dot(np.diag(s[:n_components]), Vt[:n_components]))
Args:
U (numpy ndarray): One feature vector from the reduced SVD.
U should have shape (n_features,), (i.e., one dimensional)
s (numpy ndarray): The singular values as a one dimensional array
Vt (numpy ndarray): Two dimensional array with dimensions
(n_features, n_features)
mean_values (numpy ndarray): mean values of the features of the model,
this should have dimensions (n_featurs, )
"""
zm = feature_vector - mean_values
yk = np.dot(Vt[:n_components], zm.T)
return np.dot(Vt[:n_components].T, yk) + mean_values
def save(U, s, Vt, mean_values, filename):
"""
Store the U, s, Vt and mean of all the asf datafiles given by the asf
files.
It is stored in the following way:
np.load(filename, np.assary([U, s, Vt, mean_values])
And accessed by:
UsVtm = np.load(args.model_file)
U = UsVtm[0]
s = UsVtm[1]
Vt = UsVtm[2]
mean_values = UsVtm[3]
"""
np.save(filename, np.asarray([U, s, Vt, mean_values]))
def load(filename):
"""
The model stored by pca.store (see ``pca.store`` method above) is loaded as:
UsVtm = np.load(args.model_file)
U = UsVtm[0]
s = UsVtm[1]
Vt = UsVtm[2]
mean_values = UsVtm[3]
Returns:
(tuple): U, s, Vt, mean_values
U (numpy ndarray): One feature vector from the reduced SVD.
U should have shape (n_features,), (i.e., one dimensional)
s (numpy ndarray): The singular values as a one dimensional array
Vt (numpy ndarray): Two dimensional array with dimensions
(n_features, n_features)
mean_values (numpy ndarray): mean values of the features of the model,
this should have dimensions (n_featurs, )
"""
# load the stored model file
UsVtm = np.load(filename)
U = UsVtm[0]
s = UsVtm[1]
Vt = UsVtm[2]
mean_values = UsVtm[3]
return U, s, Vt, mean_values
def eigenCallback(x):
global reconstructed
imm_orig = IMMPoints(filename=args.asf[index])
img = cv2.imread('data/imm_face_db/' + imm_orig.filename)
yk = np.dot(Vt[:n_components], X[index].T)
reconstructed = (np.dot(Vt[:n_components].T, yk) + mean_values).reshape((58, 2))
imm_orig.show_on_img(img)
cv2.imshow('image', img)
import cv2
from imm_points import IMMPoints
state = {}
def cb(index):
state['index'] = index
imm_orig = IMMPoints(filename=index)
state['imm_original'] = imm_orig
img = cv2.imread('data/imm_face_db/' + imm_orig.filename)
imm_orig.show_on_img(img)
cv2.imshow('image', img)
import kivy
kivy.require('1.0.7')
import numpy as np
from matplotlib.tri import Triangulation
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.uix.button import Button
from kivy.uix.slider import Slider
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.graphics import Rectangle, Mesh, Line, Triangle
from kivy.graphics.instructions import InstructionGroup
from kivy.graphics.context_instructions import Color
from functools import partial
from math import cos, sin, pi
from imm_points import IMMPoints, build_feature_vectors, \
flatten_feature_vectors
import pca
class ImageCanvas(Widget):
def __init__(self, **kwargs):
super(ImageCanvas, self).__init__(**kwargs)
self.filename_image = 'data/imm_face_db/40-1m.jpg'
self.canvas.clear()
with self.canvas:
self.image = Image(pos=self.pos, size=self.size, source=self.filename_image)
self.mesh = Mesh(mode='triangle_fan')
self.triangles = InstructionGroup()
self.middle_group = InstructionGroup()
self.bind(pos=self.update_rect, size=self.update_rect)
def get_rendered_size(self):
"""get the rendered size of the image
Returns:
(tuple) width, height in pixels
"""
return self.image.get_norm_image_size()
def get_image_left(self, image_width):
"""
return the location of the image, calculated from the center of the
canvas, using the image width
"""
return self.center[0] - image_width / 2.0
def get_image_bottom(self, image_height):
"""
return the location of the image, calculated from the center of the
canvas, using the image width
"""
return self.center[1] - image_height / 2.0
def update_rect(self, *args):
self.image.pos = self.pos
self.image.size = self.size
self.image.source = self.filename_image
def update_image(self, filename):
self.filename_image = filename
self.image.source = self.filename_image
self.canvas.ask_update()
def build_line_grid(self, reconstructed):
self.triangles.clear()
image_width, image_height = self.get_rendered_size()
triangles = Triangulation(reconstructed[:, 0], reconstructed[:, 1]).triangles
for tri in triangles:
self.triangles.add(Color(0, 0, 1, 1))
points = reconstructed[tri]
x = points[:, 0] * image_width + self.get_image_left(image_width)
y = (1.0 - points[:, 1]) * image_height + self.get_image_bottom(image_height)
# draw lines between three points
self.triangles.add(Line(points=[
x[0], y[0], x[1], y[1], x[2], y[2], x[0], y[0]])
)
self.triangles.add(Color(0, 1, 0, 0.5))
self.triangles.add(Line(circle=(x[0], y[0], 3)))
self.triangles.add(Line(circle=(x[1], y[1], 3)))
self.triangles.add(Line(circle=(x[2], y[2], 3)))
self.canvas.add(self.triangles)
self.canvas.add(self.middle_group)
self.canvas.ask_update()
def build_mesh(self, reconstructed):
vertices = []
xy_vertices = []
for i in range(58):
x = reconstructed[i][0] * (self.center[0] + self.image.size[0] / 2.)
y = (1.0 - reconstructed[i][1]) * self.center[1] + self.center[1] / 2.
vertices.extend([x, y, 0, 0])
xy_vertices.append([x, y])
xy_vertices = np.array(xy_vertices)
indices = []
indices = Triangulation(xy_vertices[:, 0], xy_vertices[:, 1]).triangles
indices = np.ndarray.flatten(indices)[:30]
self.mesh.vertices = vertices
self.mesh.indices = indices
class RootWidget(BoxLayout):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
self.images = kwargs['args'].asf
self.mean_values = kwargs['mean_values']
self.Vt = kwargs['eigen_vectors']
self.n_components = kwargs['args'].n_components
self.multipliers = np.ones(self.Vt.shape[1])
self.index = 0
self.filename = ''
image_slider = self.ids['image_slider']
image_slider.max = len(self.images) - 1
image_slider.bind(value=self.update_image)
n_components_slider = self.ids['n_components']
n_components_slider.value = self.n_components
n_components_slider.bind(value=self.update_n_components)
self.landmark_list = build_feature_vectors(self.images, flattened=True)
self.ids['image_viewer'].bind(size=self.on_resize)
box_layout = self.ids['eigenvalues']
for c in range(self.n_components):
slider = Slider(min=-10, max=10, value=0, id=str(c))
box_layout.add_widget(slider)
slider.bind(value=self.update_eigenvalues)
def reset_sliders(self):
self.multipliers = np.ones(self.Vt.shape[1])
box_layout = self.ids['eigenvalues']
for c in box_layout.children:
c.value = 0
def update_image_viewer(self):
self.filename = self.images[self.index].split('.')[0] + '.jpg'
Vt = np.dot(np.diag(self.multipliers), self.Vt)
reconstruction = pca.reconstruct(
self.landmark_list[self.index], Vt, self.mean_values,
n_components=self.n_components
)
reconstruction = reconstruction.reshape((58, 2))
self.ids['image_viewer'].update_rect()
self.ids['image_viewer'].update_image(self.filename)
self.ids['image_viewer'].build_line_grid(reconstruction)
def on_resize(self, *args):
self.update_image_viewer()
def update_n_components(self, slider, index):
self.n_components = int(index)
self.update_image_viewer()
def update_image(self, slider, index):
self.index = int(index)
self.reset_sliders()
self.update_image_viewer()
def update_eigenvalues(self, slider, value):
multiplier_index = int(slider.id)
self.multipliers[multiplier_index] = value
self.update_image_viewer()
class ReconstructApp(App):
kv_directory = 'src/view/templates'
def __init__(self, **kwargs):
self.eigen_vectors = kwargs['eigen_vectors']
self.mean_values = kwargs['mean_values']
self.args = kwargs['args']
super(ReconstructApp, self).__init__(**kwargs)
def build(self):
return RootWidget(args=self.args, eigen_vectors=self.eigen_vectors,
mean_values=self.mean_values
)
if __name__ == '__main__':
ReconstructApp().run()
'''
Mesh test
=========
This demonstrates the use of a mesh mode to distort an image. You should see
a line of buttons across the bottom of a canvas. Pressing them displays
the mesh, a small circle of points, with different mesh.mode settings.
'''
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.graphics import Mesh
from functools import partial
from math import cos, sin, pi
class MeshTestApp(App):
def change_mode(self, mode, *largs):
self.mesh.mode = mode
def build_mesh(self):
""" returns a Mesh of a rough circle. """
vertices = []
indices = []
step = 10
istep = (pi * 2) / float(step)
x = [200, 200, 300, 340]
y = [400, 200, 300, 340]
for i in range(4):
vertices.extend([x[i], y[i], 0, 0])
indices.append(i)
indices = [0, 1, 2, 3]
#for i in range(step):
# x = 300 + cos(istep * i) * 100
# y = 300 + sin(istep * i) * 100
# vertices.extend([x, y, 0, 0])
# indices.append(i)
return Mesh(vertices=vertices, indices=indices)
def build(self):
wid = Widget()
with wid.canvas:
self.mesh = self.build_mesh()
layout = BoxLayout(size_hint=(1, None), height=50)
for mode in ('points', 'line_strip', 'line_loop', 'lines',
'triangle_strip', 'triangle_fan', 'triangles'):
button = Button(text=mode)
button.bind(on_release=partial(self.change_mode, mode))
layout.add_widget(button)
root = BoxLayout(orientation='vertical')
root.add_widget(wid)
root.add_widget(layout)
return root
if __name__ == '__main__':
MeshTestApp().run()
'''
Line (SmoothLine) Experiment
============================
This demonstrates the experimental and unfinished SmoothLine feature
for fast line drawing. You should see a multi-segment
path at the top of the screen, and sliders and buttons along the bottom.
You can click to add new points to the segment, change the transparency
and width of the line, or hit 'Animate' to see a set of sine and cosine
animations. The Cap and Joint buttons don't work: SmoothLine has not
implemented these features yet.
'''
from kivy.app import App
from kivy.properties import OptionProperty, NumericProperty, ListProperty, \
BooleanProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.clock import Clock
from math import cos, sin
Builder.load_string('''
<LinePlayground>:
canvas:
Color:
rgba: .4, .4, 1, root.alpha
SmoothLine:
points: self.points
joint: self.joint
cap: self.cap
width: self.linewidth
close: self.close
Color:
rgba: .8, .8, .8, root.alpha_controlline
SmoothLine:
points: self.points
close: self.close
Color:
rgba: 1, .4, .4, root.alpha
SmoothLine:
points: self.points2
joint: self.joint
cap: self.cap
width: self.linewidth
close: self.close
GridLayout:
cols: 2
size_hint: 1, None
height: 44 * 5
GridLayout:
cols: 2
Label:
text: 'Alpha'
Slider:
value: root.alpha
on_value: root.alpha = float(args[1])
min: 0.
max: 1.
Label:
text: 'Alpha Control Line'
Slider:
value: root.alpha_controlline
on_value: root.alpha_controlline = float(args[1])
min: 0.
max: 1.
Label:
text: 'Width'
Slider:
value: root.linewidth
on_value: root.linewidth = args[1]
min: 1
max: 40
Label:
text: 'Cap'
GridLayout:
rows: 1
ToggleButton:
group: 'cap'
text: 'none'
on_press: root.cap = self.text
ToggleButton:
group: 'cap'
text: 'round'
on_press: root.cap = self.text
ToggleButton:
group: 'cap'
text: 'square'
on_press: root.cap = self.text
Label:
text: 'Joint'
GridLayout:
rows: 1
ToggleButton:
group: 'joint'
text: 'none'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'round'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'miter'
on_press: root.joint = self.text
ToggleButton:
group: 'joint'
text: 'bevel'
on_press: root.joint = self.text
Label:
text: 'Close'
ToggleButton:
text: 'Close line'
on_press: root.close = self.state == 'down'
AnchorLayout:
GridLayout:
cols: 1
size_hint: None, None
size: self.minimum_size
ToggleButton:
size_hint: None, None
size: 100, 44
text: 'Animate'
on_state: root.animate(self.state == 'down')
Button:
size_hint: None, None
size: 100, 44
text: 'Clear'
on_press: root.points = root.points2 = []
''')
class LinePlayground(FloatLayout):
alpha_controlline = NumericProperty(1.0)
alpha = NumericProperty(0.5)
close = BooleanProperty(False)
points = ListProperty([(500, 500),
[300, 300, 500, 300],
[500, 400, 600, 400]])
points2 = ListProperty([])
joint = OptionProperty('none', options=('round', 'miter', 'bevel', 'none'))
cap = OptionProperty('none', options=('round', 'square', 'none'))
linewidth = NumericProperty(10.0)
dt = NumericProperty(0)
def on_touch_down(self, touch):
if super(LinePlayground, self).on_touch_down(touch):
return True
touch.grab(self)
self.points.append(touch.pos)
return True
def on_touch_move(self, touch):
if touch.grab_current is self:
self.points[-1] = touch.pos
return True
return super(LinePlayground, self).on_touch_move(touch)
def on_touch_up(self, touch):
if touch.grab_current is self:
touch.ungrab(self)
return True
return super(LinePlayground, self).on_touch_up(touch)
def animate(self, do_animation):
if do_animation:
Clock.schedule_interval(self.update_points_animation, 0)
else:
Clock.unschedule(self.update_points_animation)
def update_points_animation(self, dt):
cy = self.height * 0.6
cx = self.width * 0.1
w = self.width * 0.8
step = 20
points = []
points2 = []
self.dt += dt
for i in range(int(w / step)):
x = i * step
points.append(cx + x)
points.append(cy + cos(x / w * 8. + self.dt) * self.height * 0.2)
points2.append(cx + x)
points2.append(cy + sin(x / w * 8. + self.dt) * self.height * 0.2)
self.points = points
self.points2 = points2
class TestLineApp(App):
def build(self):
return LinePlayground()
if __name__ == '__main__':
TestLineApp().run()
<Widget>
canvas.after:
Line:
rectangle: self.x+1,self.y+1,self.width-1,self.height-1
dash_offset: 5
dash_length: 3
<ImageCanvas>
Widget
<RootWidget>
BoxLayout:
orientation: 'horizontal'
BoxLayout:
ImageCanvas
id: image_viewer
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
size_hint_y: 0.5
Label:
text: "Image {}".format(int(image_slider.value))
Slider:
id: image_slider
min: 0
max: 100
value: 0
Label:
text: "Using {} components".format(int(n_components.value))
Slider:
id: n_components
min: 0
max: 58
value: 0
BoxLayout:
Label:
text: "Eigenvalues"
BoxLayout:
orientation: 'vertical'
id: eigenvalues
# size_hint_y: None
# height: sp(100)
# BoxLayout:
# orientation: 'vertical'
# Slider:
# id: e1
# min: -360.
# max: 360.
# Label:
# text: 'angle_start = {}'.format(e1.value)
# BoxLayout:
# orientation: 'vertical'
# Slider:
# id: e2
# min: -360.
# max: 360.
# value: 360
# Label:
# text: 'angle_end = {}'.format(e2.value)
# BoxLayout:
# size_hint_y: None
# height: sp(100)
# BoxLayout:
# orientation: 'vertical'
# Slider:
# id: wm
# min: 0
# max: 2
# value: 1
# Label:
# text: 'Width mult. = {}'.format(wm.value)
# BoxLayout:
# orientation: 'vertical'
# Slider:
# id: hm
# min: 0
# max: 2
# value: 1
# Label:
# text: 'Height mult. = {}'.format(hm.value)
# Button:
# text: 'Reset ratios'
# on_press: wm.value = 1; hm.value = 1
# FloatLayout:
# canvas:
# Color:
# rgb: 1, 1, 1
# Rectangle:
# pos: 100, 100
# size: 200 * wm.value, 201 * hm.value
# source: 'data/imm_face_db/16-2m.jpg'
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment