Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 46 additions & 24 deletions src/img_to_line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,28 @@ fn first_white_from(img: &DynamicImage, start: (u32, u32)) -> Option<(u32, u32)>
first_col_from(img, WHITE, start)
}

fn oob(x: i32, y: i32, img: &DynamicImage) -> bool {
x < 0 || y < 0 || x as u32 >= img.width() || y as u32 >= img.height()
}

fn dfs(
x: i32,
y: i32,
visited: &mut [bool],
img: &DynamicImage,
// visited: &mut Vec<Vec<bool>>,
img: &mut Vec<Vec<bool>>,
path: &mut Vec<(i32, i32)>,
col: image::Rgba<u8>,
) {
if oob(x, y, img)
|| img.get_pixel(x as u32, y as u32) != col
|| visited[(y * img.width() as i32 + x) as usize]
if x < 0
|| y < 0
|| x >= img[0].len() as i32
|| y >= img.len() as i32
// || visited[y as usize][x as usize]
|| !img[y as usize][x as usize]
{
// if out of bounds or already visited or not white
// if out of bounds or already visited or not color match
return;
}
// TODO: add path of (x, y) -> path[-1] to path, otherwise there are disconnected lines which means weird equations
path.push((x, y));
// path.push((x, y)); // add to path
visited[(y * img.width() as i32 + x) as usize] = true; // set visited
// visited[y as usize][x as usize] = true; // set visited
img[y as usize][x as usize] = false; // set visited
for (i, j) in [
(-1, -1),
(0, -1),
Expand All @@ -88,7 +87,7 @@ fn dfs(
(1, 1),
] {
// loop through surrounding 3x3
dfs(x + i, y + j, visited, img, path, col);
dfs(x + i, y + j, img, path);
}
}

Expand All @@ -104,6 +103,7 @@ fn is_palindrome(s: usize, e: usize, path: &[(i32, i32)]) -> bool {
true
}

// some attempts at path shortening by removing end/beginning palindromic paths
fn remove_end_palindrome(path: &mut Vec<(i32, i32)>) {
let longest_palindrome = (0..path.len()).rev().find(|&i| is_palindrome(0, i, path));
if let Some(i) = longest_palindrome {
Expand All @@ -119,21 +119,37 @@ fn remove_start_palindrome(path: &mut Vec<(i32, i32)>) {
}
}

// takes in an image and returns a 2D vec of bools, true if pixel is color
// purpose is to reduce memory usage, since img contains 4 u8 per pixel
// compiler might end up optimizing this away anyway, but I think it's worth a shot
fn img_to_bool(img: &DynamicImage, col: image::Rgba<u8>) -> Vec<Vec<bool>> {
let mut bool_img = vec![];
for y in 0..img.height() {
let mut row = vec![];
for x in 0..img.width() {
row.push(img.get_pixel(x, y) == col);
}
bool_img.push(row);
}
bool_img
}

// takes in an image and returns a 2D vec of points, each inner vec represents a line/path
pub fn edges_to_lines(img: &mut DynamicImage, col: image::Rgba<u8>) -> Vec<Vec<(i32, i32)>> {
let mut lines = vec![];
let dims = img.dimensions();
let mut visited = vec![false; (dims.0 * dims.1) as usize];
// let mut visited = vec![vec![false; dims.0 as usize]; dims.1 as usize];
let mut img = img_to_bool(img, col);

for x in 0..dims.0 {
for y in 0..dims.1 {
if !visited[(y * dims.0 + x) as usize] && img.get_pixel(x, y) == col {
if img[y as usize][x as usize] {
let mut path = vec![];
dfs(x as i32, y as i32, &mut visited, img, &mut path, col);
// somehow neither of these seem to do anything but they work in tests
// the idea is that if dfs backtracks to a point that is already in the path
// from the end of the path, then it can be partially truncated
remove_start_palindrome(&mut path);
remove_end_palindrome(&mut path);
dfs(x as i32, y as i32, &mut img, &mut path);
// the idea with these is that if dfs backtracks to a point that is already in the
// path from the end/beginning of the path, then it can be partially truncated
// remove_start_palindrome(&mut path);
// remove_end_palindrome(&mut path);
if path.len() > 16 {
lines.push(path);
}
Expand All @@ -153,21 +169,25 @@ pub fn edges_to_lines_b(img: &mut DynamicImage) -> Vec<Vec<(i32, i32)>> {
edges_to_lines(img, BLACK)
}

// generates a random color, used in lines_to_img to visualize generated lines
fn random_col() -> image::Rgba<u8> {
let col1 = rand::thread_rng().gen_range(100..255);
let col2 = rand::thread_rng().gen_range(100..255);
let col3 = rand::thread_rng().gen_range(100..255);
image::Rgba([col1, col2, col3, 255])
}

pub fn line_to_img(img: &mut DynamicImage, line: &[(i32, i32)], col: image::Rgba<u8>) {
// takes in a mutable image and a line, and colors the pixels in the line
// used in lines_to_img
fn line_to_img(img: &mut DynamicImage, line: &[(i32, i32)], col: image::Rgba<u8>) {
for point in line.iter() {
img.put_pixel(point.0 as u32, point.1 as u32, col);
}
} // fn line_to_img()
}

// takes in a 2D vec of points where the inner vectors represent lines
pub fn lines_to_img(lines: &[Vec<(i32, i32)>]) {
let (mut max_x, mut max_y) = (0, 0);
let (mut max_x, mut max_y) = (0, 0); // find max x and y to set image dimensions
for line in lines.iter() {
for point in line.iter() {
if point.0 > max_x {
Expand All @@ -178,6 +198,8 @@ pub fn lines_to_img(lines: &[Vec<(i32, i32)>]) {
}
}
}
// + 50 for a bit of padding, might need a better system for this
// TODO?
let mut img = DynamicImage::new_rgb8(max_x as u32 + 50, max_y as u32 + 50);
for line in lines.iter() {
let col = random_col();
Expand Down
15 changes: 4 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,18 @@ fn main() -> std::io::Result<()> {

let handler = builder
.spawn(|| {
let mut img = img_to_line::get_image("images/big_apple.jpg");
let img = img_to_line::get_image("images/shapes1.png");

let now = Instant::now();
let blurred = edge_detection::gaussian_blur_5x5(&img);
blurred.save("generated/blurred.png").unwrap();
println!("Gaussian blur new: {:?}", now.elapsed());

let now = Instant::now();
let mut edges = edge_detection::sobel_default(&img);
let mut edges = edge_detection::canny(&img, 50.0, 100.0);
println!("Sobel: {:?}", now.elapsed());
edges.save("generated/edges.png").unwrap();

let blurred_edges = edge_detection::sobel(&blurred);
blurred_edges.save("generated/blurred_edges.png").unwrap();

let now = Instant::now();
let mut lines = img_to_line::edges_to_lines_w(&mut edges);
let mut lines = img_to_line::edges_to_lines_b(&mut edges);
lines.sort_by_key(|b| std::cmp::Reverse(b.len())); // sort by length
lines.truncate(32); // only take n longest lines
img_to_line::lines_to_img(&lines);
println!("Edges to lines: {:?}", now.elapsed());

let mut file = File::create("generated/equations.txt").unwrap();
Expand Down