Further optimisations and rustification of interpolation
Co-authored-by: Patrickfen <Patrickfen@users.noreply.github.com>
This commit is contained in:
parent
8447002de6
commit
9c008eb58d
10 changed files with 352 additions and 96 deletions
|
@ -1,4 +1,5 @@
|
|||
import hashlib
|
||||
from itertools import permutations
|
||||
import json
|
||||
import os.path
|
||||
import math
|
||||
|
@ -46,6 +47,7 @@ class Point2D:
|
|||
|
||||
|
||||
def divide_points_into_groups(points):
|
||||
print("divide_points_into_groups: ", len(points))
|
||||
result = fastproot.divide_into_groups(points)
|
||||
res = [[Point2D(p[0], p[1], p[2]) for p in arr] for arr in result]
|
||||
return res
|
||||
|
@ -90,7 +92,27 @@ def calculate_distance(points1: list[Point2D], points2: list[Point2D]) -> float:
|
|||
center2_y = (points2[0].y + points2[-1].y) / 2
|
||||
return ((center1_x - center2_x) ** 2 + (center1_y - center2_y) ** 2) ** 0.5
|
||||
|
||||
|
||||
def pair_groups(set_a: list[list[Point2D]], set_b: list[list[Point2D]]) -> list[tuple[list[Point2D], list[Point2D]]]:
|
||||
# print("set_a", [[(b.x, b.y) for b in i] for i in set_a])
|
||||
# print("set_b", [[(b.x, b.y) for b in i] for i in set_b])
|
||||
result = []
|
||||
if len(set_a) <= len(set_b):
|
||||
result = fastproot.pair_groups(set_a, set_b)
|
||||
else:
|
||||
for (b, a) in fastproot.pair_groups(set_b, set_a):
|
||||
result.append((a,b))
|
||||
res = [
|
||||
(
|
||||
[Point2D(a1[0], a1[1], a1[2]) for a1 in a],
|
||||
[Point2D(b1[0], b1[1], b1[2]) for b1 in b]
|
||||
) for (a, b) in result
|
||||
]
|
||||
#res = [[Point2D(p[0], p[1], p[2]) for p in arr] for arr in result]
|
||||
return res
|
||||
|
||||
|
||||
def _pair_groups(set_a: list[list[Point2D]], set_b: list[list[Point2D]]) -> list[tuple[list[Point2D], list[Point2D]]]:
|
||||
pairs = []
|
||||
|
||||
# Create dictionaries to store bounding boxes for each group
|
||||
|
@ -131,7 +153,7 @@ def pair_groups(set_a: list[list[Point2D]], set_b: list[list[Point2D]]) -> list[
|
|||
def mirror_points(points: list[Point2D]) -> list[Point2D]:
|
||||
mirrored_points = []
|
||||
for point in points:
|
||||
mirrored_x = 128 - point.x # Calculate the mirrored x-coordinate
|
||||
mirrored_x = 127 - point.x # Calculate the mirrored x-coordinate
|
||||
mirrored_point = Point2D(mirrored_x, point.y, point.color)
|
||||
mirrored_points.append(mirrored_point)
|
||||
return mirrored_points
|
||||
|
@ -174,7 +196,7 @@ def generate_point_array_from_image(image):
|
|||
width, height = image.size
|
||||
pixel_array = []
|
||||
|
||||
image.save(LOADED_IMAGE_PATH)
|
||||
#image.save(LOADED_IMAGE_PATH)
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
|
@ -204,13 +226,18 @@ def generate_image_from_point_array(points: list[Point2D], width: int, height: i
|
|||
|
||||
return image
|
||||
|
||||
|
||||
def interpolate_point_pairs(pairs: list[tuple[Point2D, Point2D]], percentage: float) -> list[Point2D]:
|
||||
result = fastproot.interpolate_point_pairs(pairs, percentage)
|
||||
res = [Point2D(p[0], p[1], p[2]) for p in result]
|
||||
return res
|
||||
|
||||
def _interpolate_point_pairs(pairs: list[tuple[Point2D, Point2D]], percentage: float) -> list[Point2D]:
|
||||
interpolated_points:list[Point2D] = []
|
||||
for pair in pairs:
|
||||
point1, point2 = pair
|
||||
interpolated_point = point1.interpolate(point2, percentage)
|
||||
interpolated_points.append(interpolated_point)
|
||||
if not interpolated_point in interpolated_points:
|
||||
interpolated_points.append(interpolated_point)
|
||||
return interpolated_points
|
||||
|
||||
|
||||
|
@ -229,7 +256,7 @@ def pair_points(points1: list[Point2D], points2: list[Point2D]) -> list[tuple[Po
|
|||
duplicated_points = np.random.choice(points1, size=num_duplicates).tolist()
|
||||
points1 += duplicated_points
|
||||
|
||||
row_ind, col_ind = fastproot.solve(points1, points2)
|
||||
row_ind, col_ind = (fastproot.solve(points1, points2))
|
||||
|
||||
# Create pairs of points based on the optimal assignment
|
||||
pairs = []
|
||||
|
|
|
@ -2,4 +2,8 @@ Crosscompile instructions:
|
|||
* Build docker image with python armv7 installed:
|
||||
`docker build -t my-image .`
|
||||
* Use cross to crosscompile:
|
||||
`cross.exe build --target=armv7-unknown-linux-gnueabihf --release`
|
||||
`cross.exe build --target=armv7-unknown-linux-gnueabihf --release`
|
||||
* Move built library to the correct folder:
|
||||
`cp fastproot/target/armv7-unknown-linux-gnueabihf/release/libfastproot.so ./`
|
||||
* Move windows binary
|
||||
`cp fastproot/target/release/fastproot.dll ./fastproot.pyd`
|
BIN
yaml parse test/angryFace.png
Normal file
BIN
yaml parse test/angryFace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 302 B |
Binary file not shown.
Before Width: | Height: | Size: 1,023 B After Width: | Height: | Size: 1,020 B |
BIN
yaml parse test/fastproot.so
Normal file
BIN
yaml parse test/fastproot.so
Normal file
Binary file not shown.
|
@ -1,16 +1,35 @@
|
|||
use pyo3::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, FromPyObject)]
|
||||
#[derive(Copy, Clone, FromPyObject, Default, Debug)]
|
||||
pub struct Point {
|
||||
x: u8,
|
||||
y: u8,
|
||||
|
||||
color: (u8, u8, u8),
|
||||
color: [u8; 3],
|
||||
}
|
||||
|
||||
impl PartialEq for Point {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.x == other.x && self.y == other.y
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Point {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
type TuplePoint = (u8,u8, (u8,u8,u8));
|
||||
|
||||
impl Point {
|
||||
pub fn new(x: u8, y: u8) -> Self {
|
||||
Self {
|
||||
x, y,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn distance(self, other: Point) -> f64 {
|
||||
let dx = self.x as f64 - other.x as f64;
|
||||
let dy = self.y as f64 - other.y as f64;
|
||||
|
@ -18,7 +37,20 @@ impl Point {
|
|||
}
|
||||
|
||||
fn into_tuple(&self) -> TuplePoint {
|
||||
(self.x, self.y, self.color)
|
||||
(self.x, self.y, (self.color[0], self.color[1], self.color[2]))
|
||||
}
|
||||
|
||||
fn interpolate(self, other: Point, percent: f32) -> Point{
|
||||
let x = self.x as f32 + ((other.x as f32 - self.x as f32) as f32 * percent);
|
||||
let y = self.y as f32 + ((other.y as f32 - self.y as f32) as f32 * percent);
|
||||
|
||||
let color = (0..3).map(|i| ((1.0 - percent) * self.color[i] as f32 + percent * other.color[i] as f32) as u8).collect::<Vec<_>>();
|
||||
let color = [
|
||||
color[0],
|
||||
color[1],
|
||||
color[2],
|
||||
];
|
||||
Point { x: x as u8, y: y as u8, color }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,17 +67,26 @@ pub fn solve(points1: Vec<Point>, points2: Vec<Point>) -> (Vec<usize>, Vec<usize
|
|||
lsap::solve(points1.len(), points2.len(), &cost_matrix, false).unwrap()
|
||||
}
|
||||
|
||||
pub fn flood_fill(point: Point, mut group: Vec<Point>, points: Vec<Point>) -> Vec<Point> {
|
||||
pub fn flood_fill(point: Point, mut group: Vec<Point>, points: &[Point]) -> Vec<Point> {
|
||||
if !group.contains(&point) {
|
||||
group.push(point);
|
||||
|
||||
let neighbors = [(point.x + 1, point.y), (point.x - 1, point.y), (point.x, point.y + 1), (point.x, point.y - 1)];
|
||||
let neighbors = [
|
||||
(point.x + 1, point.y),
|
||||
(point.x - 1, point.y),
|
||||
(point.x, point.y + 1),
|
||||
(point.x, point.y - 1),
|
||||
(point.x + 1, point.y + 1),
|
||||
(point.x + 1, point.y - 1),
|
||||
(point.x - 1, point.y + 1),
|
||||
(point.x - 1, point.y - 1),
|
||||
];
|
||||
for neighbor_coords in neighbors {
|
||||
let neighbor = points.iter().find(|p| p.x == neighbor_coords.0 && p.y == neighbor_coords.1);
|
||||
|
||||
if let Some(neighbor) = neighbor {
|
||||
if !group.contains(&neighbor) {
|
||||
group = flood_fill(*neighbor, group, points.clone())
|
||||
group = flood_fill(*neighbor, group, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,78 +95,181 @@ pub fn flood_fill(point: Point, mut group: Vec<Point>, points: Vec<Point>) -> Ve
|
|||
group
|
||||
}
|
||||
|
||||
// def divide_points_into_groups(points):
|
||||
// def flood_fill(point, group):
|
||||
// if point not in group:
|
||||
// group.append(point)
|
||||
// neighbors = [(point.x + 1, point.y), (point.x - 1, point.y), (point.x, point.y + 1), (point.x, point.y - 1)]
|
||||
// for neighbor_coords in neighbors:
|
||||
// neighbor = next((p for p in points if p.x == neighbor_coords[0] and p.y == neighbor_coords[1]), None)
|
||||
// if neighbor and neighbor not in group:
|
||||
// flood_fill(neighbor, group)
|
||||
//
|
||||
// groups = []
|
||||
// remaining_points = [point for point in points if point.x < 64] # Filter points with x < 64
|
||||
// while remaining_points:
|
||||
// group = []
|
||||
// flood_fill(remaining_points[0], group)
|
||||
// groups.append(group)
|
||||
// # Remove points in the group from the remaining points
|
||||
// remaining_points = [point for point in remaining_points if point not in group]
|
||||
// return groups
|
||||
|
||||
|
||||
#[pyfunction]
|
||||
pub fn divide_into_groups(points: Vec<Point>) -> Vec<Vec<TuplePoint>> {
|
||||
let mut groups = vec![];
|
||||
|
||||
let mut remaining = points.iter().filter(|p| p.x < 64).collect::<Vec<_>>();
|
||||
while let Some(&point) = remaining.get(0) {
|
||||
let group = flood_fill(*point, vec![], points.clone());
|
||||
groups.push(group.iter().map(|v| v.into_tuple()).collect());
|
||||
let mut remaining = points.clone().into_iter().filter(|p| p.x < 64).collect::<std::collections::VecDeque<_>>();
|
||||
while let Some(point) = remaining.pop_front() {
|
||||
let group = flood_fill(point, vec![], &points);
|
||||
groups.push(group.clone());
|
||||
|
||||
remaining = remaining.into_iter().filter(|p| !group.contains(p)).collect();
|
||||
}
|
||||
|
||||
groups
|
||||
groups.into_iter().map(|r| r.into_iter().map(|c| c.into_tuple()).collect()).collect()
|
||||
}
|
||||
|
||||
// #[pyfunction]
|
||||
// fn pair_groups(set_a: Vec<Vec<Point>>, set_b: Vec<Vec<Point>>) -> Vec<(TuplePoint, TuplePoint)> {
|
||||
// let mut pairs = vec![];
|
||||
|
||||
// // Create dictionaries to store bounding boxes for each group
|
||||
// let mut bounding_boxes_a: std::collections::HashMap::new(); // dict[int, tuple[float, float, float, float]] = {}
|
||||
// let mut bounding_boxes_a: std::collections::HashMap::new();
|
||||
pub fn distance(this: (u8, u8), other: (u8, u8)) -> f64 {
|
||||
let dx = this.0 as f64 - other.0 as f64;
|
||||
let dy = this.1 as f64 - other.1 as f64;
|
||||
(dx.powf(2.0) + dy.powf(2.0)).sqrt()
|
||||
}
|
||||
|
||||
// // Calculate bounding boxes for all groups in both sets
|
||||
// for (i, group) in set_a.iter().chain(set_b).iter().enumerate() {
|
||||
// let bounding_box = compute_bounding_box(group);
|
||||
// if i < set_a.len() {
|
||||
// bounding_boxes_a[i] = bounding_box;
|
||||
// } else {
|
||||
// bounding_boxes_b[i - set_a.len()] = bounding_box;
|
||||
// }
|
||||
// }
|
||||
pub fn centroid(points: &[Point]) -> (u8, u8) {
|
||||
let x = points.iter().fold(0usize, |acc, p| acc + p.x as usize) / points.len();
|
||||
let y = points.iter().fold(0usize, |acc, p| acc + p.y as usize) / points.len();
|
||||
(x as u8, y as u8)
|
||||
}
|
||||
|
||||
// // Check for overlaps and determine pairs
|
||||
// for (i, group_a) in set_a.iter().enumerate() {
|
||||
// let overlap_detected = false;
|
||||
// for (j, group_b) in set_b.iter().enenumerate() {
|
||||
// let bounding_box_a = bounding_boxes_a[i];
|
||||
// let bounding_box_b = bounding_boxes_b[j];
|
||||
// if (
|
||||
// bounding_box_a[0] <= bounding_box_b[1] &&
|
||||
// bounding_box_a[1] >= bounding_box_b[0] &&
|
||||
// bounding_box_a[2] <= bounding_box_b[3] &&
|
||||
// bounding_box_a[3] >= bounding_box_b[2]
|
||||
// ) {
|
||||
// pairs.push((group_a, group_b));
|
||||
// overlap_detected = true;
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// if !overlap_detected {
|
||||
// // Find the nearest neighbor in set B
|
||||
// let mut nearest_group = set_b[0];
|
||||
// let mut nearest_value = 0.0f64;
|
||||
// for val in set_b.iter() {
|
||||
// if calculate_distance(group_a, val) < nearest_value {
|
||||
// nearest_group = val;
|
||||
// }
|
||||
// }
|
||||
// pairs.append((group_a, nearest_group));
|
||||
// }
|
||||
// }
|
||||
#[pyfunction]
|
||||
pub fn pair_groups(smaller: Vec<Vec<Point>>, larger: Vec<Vec<Point>>) -> Vec<(Vec<TuplePoint>, Vec<TuplePoint>)> {
|
||||
let smaller_center = smaller.iter().map(|v| centroid(v)).collect::<Vec<_>>();
|
||||
let larger_center = larger.iter().map(|v| centroid(v)).collect::<Vec<_>>();
|
||||
|
||||
let mut map = std::collections::HashMap::<usize, Vec<usize>>::new();
|
||||
|
||||
// Map each item from centroids2 to an item in centroids1.
|
||||
for (idx2, coords2) in larger_center.iter().enumerate() {
|
||||
// Get nearest item in centroids1
|
||||
let mut nearest_idx = 0;
|
||||
let mut nearest_dist = f64::MAX;
|
||||
for (idx1, coords1) in smaller_center.iter().enumerate() {
|
||||
if distance(*coords1, *coords2) < nearest_dist {
|
||||
nearest_dist = distance(*coords1, *coords2);
|
||||
nearest_idx = idx1;
|
||||
}
|
||||
}
|
||||
// now, nearest_idx is the index in centroid1 of the nearest point.
|
||||
map.entry(nearest_idx).and_modify(|v| v.push(idx2)).or_insert_with(||vec![idx2]);
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
for (k, indexes) in map {
|
||||
let mut combined = vec![];
|
||||
for i in indexes {
|
||||
combined.extend(larger[i].iter())
|
||||
}
|
||||
|
||||
let key = smaller[k].iter().map(|v| v.into_tuple()).collect();
|
||||
let combined = combined.into_iter().map(|v: Point| v.into_tuple()).collect();
|
||||
result.push((key, combined))
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
pub fn interpolate_point_pairs(pairs: Vec<(Point, Point)>, percent: f32) -> Vec<TuplePoint> {
|
||||
let mut interpolated_points = vec![];
|
||||
for (point1, point2) in pairs {
|
||||
let interpolated_point = point1.interpolate(point2, percent);
|
||||
if !interpolated_points.contains(&interpolated_point){
|
||||
interpolated_points.push(interpolated_point);
|
||||
}
|
||||
}
|
||||
interpolated_points.into_iter().map(|v| v.into_tuple()).collect()
|
||||
}
|
||||
|
||||
// pairs
|
||||
// }
|
||||
|
||||
#[pymodule]
|
||||
fn fastproot(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(solve, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(divide_into_groups, m)?)?;
|
||||
// m.add_function(wrap_pyfunction!(pair_groups, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(pair_groups, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(interpolate_point_pairs, m)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::{Point, flood_fill, divide_into_groups, centroid, pair_groups, interpolate_point_pairs};
|
||||
#[test]
|
||||
fn should_do_flood_fill() {
|
||||
let groups = flood_fill(Point::new(1,1), vec![], &vec![
|
||||
Point::new(1,1),
|
||||
Point::new(1,2),
|
||||
Point::new(2,1),
|
||||
|
||||
Point::new(3,3),
|
||||
]);
|
||||
println!("{groups:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_divide_into_groups() {
|
||||
let groups = divide_into_groups(vec![
|
||||
Point::new(1,1),
|
||||
Point::new(1,2),
|
||||
Point::new(2,1),
|
||||
|
||||
Point::new(3,3),
|
||||
Point::new(3,4),
|
||||
Point::new(4,4)
|
||||
]);
|
||||
for (i, g) in groups.iter().enumerate() {
|
||||
println!("{i}: {g:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_centroid() {
|
||||
let centroid = centroid(&vec![
|
||||
Point::new(1,1),
|
||||
Point::new(1,2),
|
||||
Point::new(2,1),
|
||||
Point::new(20,10),
|
||||
]);
|
||||
|
||||
println!("{centroid:?}");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_pair_groups() {
|
||||
|
||||
let a = vec![vec![(59, 2), (60, 2), (61, 2), (62, 2), (62, 3)], vec![(15, 8), (16, 8), (17, 8), (18, 8), (19, 8), (20, 8), (21, 8), (22, 8), (23, 8), (24, 8), (25, 8), (26, 8), (27, 8), (27, 9), (28, 9), (29, 9), (30, 9), (30, 10), (31, 10), (31, 11), (29, 10), (26, 9), (25, 9), (24, 9), (23, 9), (22, 9), (21, 9), (20, 9), (19, 9), (18, 9), (17, 9), (16, 9), (15, 9)], vec![(26, 22), (27, 22), (28, 22), (28, 23), (29, 23), (30, 23), (31, 23), (31, 24), (32, 24), (33, 24), (33, 25), (34, 25), (35, 25), (36, 25), (36, 26), (37, 26), (38, 26), (39, 26), (40, 26), (41, 26), (42, 26), (42, 27), (43, 27), (44, 27), (45, 27), (46, 27), (47, 27), (47, 26), (48, 26), (49, 26), (50, 26), (50, 25), (51, 25), (52, 25), (52, 26), (53, 26), (53, 27), (53, 28), (54, 28), (54, 29), (55, 29), (55, 30), (56, 30), (57, 30), (57, 29), (58, 29), (58, 28), (59, 28), (59, 27), (60, 27), (60, 26), (61, 26), (61, 25), (62, 25), (62, 24), (51, 24)]];
|
||||
let b = vec![vec![(21, 1), (22, 1), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), (28, 2), (29, 2), (30, 2), (31, 2), (31, 3), (32, 3), (32, 4), (33, 4), (33, 5), (34, 5), (34, 6), (34, 7), (35, 7), (35, 8), (35, 9), (35, 10), (35, 11), (34, 11), (34, 12), (33, 12), (33, 13), (32, 13), (32, 14), (31, 14), (30, 14), (29, 14), (29, 15), (28, 15), (27, 15), (26, 15), (25, 15), (24, 15), (23, 15), (22, 15), (21, 15), (20, 15), (19, 15), (19, 14), (18, 14), (17, 14), (16, 14), (16, 13), (15, 13), (15, 12), (14, 12), (13, 12), (13, 11), (12, 11), (12, 10), (12, 9), (12, 8), (21, 2), (20, 2), (19, 2), (18, 2), (18, 3), (17, 3), (17, 4), (17, 5), (17, 6), (16, 6), (16, 7), (16, 8), (16, 9), (17, 9), (17, 10), (18, 10), (18, 11), (19, 11), (19, 12), (20, 12), (21, 12), (21, 13), (22, 13), (23, 13), (24, 13), (25, 13), (26, 13), (27, 13), (27, 12), (28, 12), (29, 12), (29, 11), (30, 11), (30, 10), (31, 10), (31, 9), (31, 8), (31, 7), (30, 7), (30, 6), (30, 5), (29, 5), (28, 5), (28, 4), (27, 4), (26, 4), (25, 4), (24, 4), (23, 4), (22, 4), (22, 5), (21, 5), (21, 6), (20, 6), (20, 7), (20, 8), (20, 9), (21, 9), (21, 10), (22, 10), (23, 10), (24, 10), (25, 10), (26, 10), (27, 10), (27, 9), (27, 8), (28, 8), (27, 7), (26, 7), (25, 7), (24, 7)], vec![(59, 2), (60, 2), (61, 2), (62, 2), (62, 3)], vec![(26, 22), (27, 22), (28, 22), (28, 23), (29, 23), (30, 23), (31, 23), (31, 24), (32, 24), (33, 24), (33, 25), (34, 25), (35, 25), (36, 25), (36, 26), (37, 26), (38, 26), (39, 26), (40, 26), (41, 26), (42, 26), (42, 27), (43, 27), (44, 27), (45, 27), (46, 27), (47, 27), (47, 26), (48, 26), (49, 26), (50, 26), (50, 25), (51, 25), (52, 25), (52, 26), (53, 26), (53, 27), (53, 28), (54, 28), (54, 29), (55, 29), (55, 30), (56, 30), (57, 30), (57, 29), (58, 29), (58, 28), (59, 28), (59, 27), (60, 27), (60, 26), (61, 26), (61, 25), (62, 25), (62, 24), (51, 24)]];
|
||||
|
||||
let a = a.into_iter().map(|r| r.into_iter().map(|(x,y)| Point::new(x,y)).collect()).collect();
|
||||
let b = b.into_iter().map(|r| r.into_iter().map(|(x,y)| Point::new(x,y)).collect()).collect();
|
||||
|
||||
let res = pair_groups(a, b
|
||||
// vec![
|
||||
// vec![Point::new(1,1)],
|
||||
// vec![Point::new(3,3),Point::new(3,4)],
|
||||
// vec![Point::new(10,10), Point::new(11,11)]
|
||||
// ],
|
||||
// vec![
|
||||
// vec![Point::new(2,2)],
|
||||
// vec![Point::new(2,5)],
|
||||
// vec![Point::new(12,12)],
|
||||
// ]
|
||||
);
|
||||
for (i, s) in res {
|
||||
println!("====> {i:?} => {s:?}");
|
||||
}
|
||||
}
|
||||
}
|
BIN
yaml parse test/loveFace.png
Normal file
BIN
yaml parse test/loveFace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 575 B |
|
@ -179,6 +179,9 @@ class RenderingLayer:
|
|||
self.frame_rate = frame_rate # Set the desired frame rate
|
||||
self.frame_duration = 1.0 / frame_rate # Calculate the frame duration
|
||||
self.animation_queue = [] # Initialize the animation queue
|
||||
|
||||
self.previous_to = []
|
||||
self.previous_point_pairs_groups = []
|
||||
|
||||
def play_animation_by_name(self, animation_name):
|
||||
for animation in self.animations:
|
||||
|
@ -209,15 +212,16 @@ class RenderingLayer:
|
|||
animation_queue.append({'type': graphic.get('type'), 'point_array': point_array})
|
||||
|
||||
if graphic.get('type') == 'transition':
|
||||
to_file_name = graphic.get('to_file')
|
||||
to_point_array = graphic.get('to_point_array')
|
||||
duration = graphic.get('duration', 1) # Default duration is 1 frame if not specified
|
||||
|
||||
# Add frames to the queue based on the specified duration
|
||||
for i in range(int(duration)):
|
||||
animation_queue.append({'type': graphic.get('type'), 'to_point_array': to_point_array, 'stepPercentage' : (1/(int(duration)-i))})
|
||||
animation_queue.append({'type': graphic.get('type'), 'to_point_array': to_point_array, 'to_file_name': to_file_name, 'additionalStepPercentage' : (1/(int(duration)-i)), 'stepPercentage' : ((i+1)/duration)})
|
||||
|
||||
return animation_queue
|
||||
|
||||
|
||||
def start_rendering(self):
|
||||
frameCount = 0
|
||||
new_image = Image.new("RGB", (128, 32), "black")
|
||||
|
@ -226,8 +230,10 @@ class RenderingLayer:
|
|||
pairgrouptime = 0
|
||||
pairpointtime = 0
|
||||
imagingtime = 0
|
||||
interpolatetime = 0
|
||||
transitionFrameCount = 1
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
start_time = time.time() # Get the current time before rendering
|
||||
|
@ -245,38 +251,87 @@ class RenderingLayer:
|
|||
print("image generated")
|
||||
|
||||
elif current_animation_action.get('type') == "transition":
|
||||
transitionFrameCount += 1
|
||||
divtime_start = time.time()
|
||||
groupsa = divide_points_into_groups(self.current_point_array)
|
||||
groupsb = divide_points_into_groups(current_animation_action.get('to_point_array'))
|
||||
devisiontime += time.time() - divtime_start
|
||||
|
||||
pairgrouptime_start = time.time()
|
||||
paired_groups = pair_groups(groupsa, groupsb)
|
||||
pairgrouptime += time.time() - pairgrouptime_start
|
||||
|
||||
new_point_array = []
|
||||
for pair in paired_groups:
|
||||
pairpointtime_start = time.time()
|
||||
point_pairs = pair_points(pair[0], pair[1])
|
||||
pairpointtime += time.time() - pairpointtime_start
|
||||
print(str(current_animation_action.get('stepPercentage')))
|
||||
new_point_array += interpolate_point_pairs(point_pairs, current_animation_action.get('stepPercentage'))
|
||||
frame_time_start = time.time()
|
||||
|
||||
|
||||
|
||||
if self.previous_to == str(current_animation_action.get('to_file_name')):
|
||||
|
||||
|
||||
point_pairs_groups = self.previous_point_pairs_groups
|
||||
|
||||
new_point_array = []
|
||||
for point_pairs in point_pairs_groups:
|
||||
interpolatetime_start = time.time()
|
||||
new_point_array += interpolate_point_pairs(point_pairs, current_animation_action.get('stepPercentage'))
|
||||
print("interpolationg took: " + str(time.time() - interpolatetime_start) + " sec. (cached)")
|
||||
print("step percentate for this transition step is: " + str(current_animation_action.get('stepPercentage')))
|
||||
|
||||
else:
|
||||
print("starting transition generation")
|
||||
transitionFrameCount += 1
|
||||
divtime_start = time.time()
|
||||
|
||||
groupsa = divide_points_into_groups(self.current_point_array)
|
||||
groupsb = divide_points_into_groups(current_animation_action.get('to_point_array'))
|
||||
devisiontime += time.time() - divtime_start
|
||||
|
||||
print("groups divided, len(groupsa), len(groups(b)=", len(groupsa), len(groupsb))
|
||||
|
||||
pairgrouptime_start = time.time()
|
||||
paired_groups = pair_groups(groupsa, groupsb)
|
||||
pairgrouptime += time.time() - pairgrouptime_start
|
||||
|
||||
|
||||
print("paired_groups generated, len(paired_groups)=", len(paired_groups))
|
||||
|
||||
self.previous_point_pairs_groups = []
|
||||
new_point_array = []
|
||||
for pair in paired_groups:
|
||||
pairpointtime_start = time.time()
|
||||
point_pairs = pair_points(pair[0], pair[1])
|
||||
self.previous_point_pairs_groups.append(point_pairs)
|
||||
pairpointtime += time.time() - pairpointtime_start
|
||||
print("step percentate for this transition stepp is: " + str(current_animation_action.get('additionalStepPercentage')))
|
||||
|
||||
interpolatetime_start = time.time()
|
||||
new_point_array += interpolate_point_pairs(point_pairs, current_animation_action.get('additionalStepPercentage'))
|
||||
print("interpolationg took: " + str(time.time() - interpolatetime_start) + " sec.")
|
||||
|
||||
print("face feature interpolated len(new_point_array) =", len(new_point_array))
|
||||
|
||||
# set previous "to" screen. This is used
|
||||
self.previous_to = str(current_animation_action.get('to_file_name'))
|
||||
|
||||
imagingtime_start = time.time()
|
||||
|
||||
new_image = generate_image_from_point_array(new_point_array + mirror_points(new_point_array), 128, 32)
|
||||
#new_image.save("output/frameNumber"+str(frameCount)+".png")
|
||||
|
||||
imagingtime += time.time() - imagingtime_start
|
||||
|
||||
self.current_point_array = new_point_array
|
||||
print("transition generated, len(new_point_array) =", len(new_point_array))
|
||||
|
||||
print("creating transition frame took: " + str(time.time() - frame_time_start) + " sec.")
|
||||
print("================== end frame ==================")
|
||||
|
||||
else:
|
||||
print("unknown action: ", current_animation_action)
|
||||
|
||||
print("setting image to canvas")
|
||||
offscreen_canvas = matrix.CreateFrameCanvas()
|
||||
offscreen_canvas.SetImage(new_image, unsafe=False)
|
||||
print("pushing image to matrix")
|
||||
matrix.SwapOnVSync(offscreen_canvas)
|
||||
print("pushing image done")
|
||||
|
||||
# Save the image to a file with the desired format and file name
|
||||
# new_image.save("output/frameNumber"+str(frameCount)+".png")
|
||||
frameCount += 1
|
||||
frameCount += 1
|
||||
# new_image.save("output/frameNumber"+str(frameCount)+".png")
|
||||
|
||||
elapsed_time = time.time() - start_time # Calculate time elapsed during rendering
|
||||
|
||||
|
@ -285,11 +340,12 @@ class RenderingLayer:
|
|||
print("remaining time in frame: " + str(sleep_time))
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
print("average time cost per part for transition frames:")
|
||||
print("devisiontime :" + str(devisiontime /transitionFrameCount ))
|
||||
print("pairgrouptime :" + str(pairgrouptime /transitionFrameCount))
|
||||
print("pairpointtime :" + str(pairpointtime /transitionFrameCount))
|
||||
print("imagingtime :" + str(imagingtime /transitionFrameCount))
|
||||
# print("average time cost per part for transition frames:")
|
||||
# print("devisiontime :" + str(devisiontime /transitionFrameCount ))
|
||||
# print("pairgrouptime :" + str(pairgrouptime /transitionFrameCount))
|
||||
# print("pairpointtime :" + str(pairpointtime /transitionFrameCount))
|
||||
# print("interpolatetime :" + str(interpolatetime /transitionFrameCount))
|
||||
# print("imagingtime :" + str(imagingtime /transitionFrameCount))
|
||||
|
||||
|
||||
devisiontime = 0
|
||||
|
@ -345,7 +401,7 @@ def main():
|
|||
mqtt_client.loop_start()
|
||||
|
||||
# Initialize the rendering layer
|
||||
rendering_layer = RenderingLayer(animations, frame_rate=10)
|
||||
rendering_layer = RenderingLayer(animations, frame_rate=40)
|
||||
rendering_thread = threading.Thread(target=rendering_layer.start_rendering)
|
||||
rendering_thread.start()
|
||||
|
||||
|
|
|
@ -41,16 +41,16 @@ animations:
|
|||
|
||||
graphics:
|
||||
- type: transition # close the eye from whatever the current state
|
||||
to_file: dizzyFace.png
|
||||
duration: 5
|
||||
to_file: eyesClosed_neutral.png
|
||||
duration: 6
|
||||
|
||||
- type: image # hold eye closed
|
||||
source_file: dizzyFace.png
|
||||
source_file: eyesClosed_neutral.png
|
||||
duration: 10
|
||||
|
||||
- type: transition # open the eye again from being closed
|
||||
to_file: neutral.png
|
||||
duration: 5
|
||||
duration: 6
|
||||
|
||||
- name: openEye
|
||||
description: Animation for blinking
|
||||
|
@ -65,9 +65,21 @@ animations:
|
|||
source_file: eyesClosed_neutral.png
|
||||
duration: 1
|
||||
|
||||
- type: transition # open the eye again from being closed
|
||||
- type: transition
|
||||
to_file: dizzyFace.png
|
||||
duration: 5
|
||||
duration: 10
|
||||
|
||||
- type: transition
|
||||
to_file: angryFace.png
|
||||
duration: 10
|
||||
|
||||
- type: transition
|
||||
to_file: loveFace.png
|
||||
duration: 10
|
||||
|
||||
- type: transition
|
||||
to_file: neutral.png
|
||||
duration: 10
|
||||
|
||||
- name: make dizzy
|
||||
description: Animation for making dizzy
|
||||
|
|
13
yaml parse test/util.py
Normal file
13
yaml parse test/util.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from time import time
|
||||
|
||||
def timer(func):
|
||||
# This function shows the execution time of
|
||||
# the function object passed
|
||||
def wrap_func(*args, **kwargs):
|
||||
t1 = time()
|
||||
result = func(*args, **kwargs)
|
||||
t2 = time()
|
||||
print(f'Function {func.__name__!r} execution: {(t2-t1):.4f}s')
|
||||
return result
|
||||
return wrap_func
|
||||
|
Loading…
Reference in a new issue