Skip to content

Commit

Permalink
Feature detection - trying it out
Browse files Browse the repository at this point in the history
  • Loading branch information
madsravn committed Aug 17, 2023
1 parent eeac664 commit d7d45e5
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ test = true
[[example]]
name = "rotations"
test = true

[[example]]
name = "feature-extraction"
test = true
Binary file added assets/IMG_0773.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/IMG_0775.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/IMG_0776.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/IMG_0777.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
234 changes: 234 additions & 0 deletions examples/feature-extraction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use pixtra::canvas::{Canvas, Island};
use pixtra::pixels::{Pixel, PixelBuilder};
use pixtra::utility::{to_grey_lumiosity, count_colors, counted_colors_to_html};
use std::path::Path;

fn lap_of_gaussian_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
// NOTE: The kernel needs to sum to 1.0. Below 1.0 your image will appear darker
// and above 1.0 it will appear lighter.
let kernel = vec![
0, 1, 1, 2, 2, 2, 1, 1, 0,
1, 2, 4, 5, 5, 5, 4, 2, 1,
1, 4, 5, 3, 0, 3, 5, 4, 1,
2, 5, 3, -12, -24, -12, 3, 5, 2,
2, 5, 0, -24, -40, -24, 0, 5, 2,
2, 5, 3, -12, -24, -12, 3, 5, 2,
1, 4, 5, 3, 0, 3, 5, 4, 1,
1, 2, 4, 5, 5, 5, 4, 2, 1,
0, 1, 1, 2, 2, 2, 1, 1, 0];
let kernel = kernel.iter().map(|&x| x as f32).collect();
let mut coords = vec![];
for y in -4..5 {
for x in -4..5 {
coords.push((x, y));
}
}
// For simplicity, we will leave out the edges of the picture.
let canvas_size = canvas.dimensions();
if x > 3 && y > 3 && x < canvas_size.width - 4 && y < canvas_size.height - 4 {
let pixel = apply_filter(canvas, (x, y), &kernel, &coords);
return pixel;
}
canvas.get_pixel(x, y)
}

fn grey_scale_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
to_grey_lumiosity(&canvas.get_pixel(x, y))
}

fn black_or_white_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
let pixel = canvas.get_pixel(x, y);
if pixel.r < 128 {
Pixel::new(0, 0, 0, 255)
} else {
Pixel::new(255, 255, 255, 255)
}
}

fn apply_filter(
canvas: &Canvas,
center: (u32, u32),
kernel: &Vec<f32>,
coords: &Vec<(i32, i32)>,
) -> Pixel {
let scales = kernel
.iter()
.zip(coords.iter())
.fold(Pixel::builder(), |acc, (scale, (x, y))| {
let pixel =
canvas.get_pixel((center.0 as i32 + *x) as u32, (center.1 as i32 + *y) as u32);
acc + PixelBuilder::from(
pixel.r as f32 * scale,
pixel.g as f32 * scale,
pixel.b as f32 * scale,
pixel.a as f32,
)
});
scales.build()
}

// TODO: Parametiser så meget som muligt. Gem alle parametre i filnavnet sådan det kan genskabes
// eller justeres.
fn main() {
//let images = vec!["IMG_0773", "IMG_0775", "IMG_0776", "IMG_0777"];
//for image in images.iter() {
//fix_image(image);
//}
let images = vec!["IMG_0776", "IMG_0777"];
let grapes = "IMG_0775";
let donut = "IMG_0773";
for image in images.iter() {
fix_image_with_map(image);
}
fix_image_with_map_grapes(grapes);
fix_image_with_map_donut(donut);


}
fn fix_image_with_map(image_name: &str) {

let test_image = Canvas::load(Path::new(&format!("assets/{}.JPG", image_name))).unwrap();
let canvas = Canvas::load(&Path::new(&format!("{}-draw-islands.png", image_name))).unwrap();


let empty_pixel = Pixel::new(255, 0, 0, 254);
let canvas = canvas.fill(100, 100, &empty_pixel);

let size = test_image.dimensions();
let mut output_image = Canvas::new_with_background(size.width, size.height, Pixel::new(0, 0, 0, 0));
for x in 0..size.width {
for y in 0..size.height {
let test_pixel = canvas.get_pixel(x, y);
if test_pixel != empty_pixel {
output_image.set_pixel_mut(x, y, &test_image.get_pixel(x, y));
}
}
}
output_image.save(&Path::new(&format!("{}-final-result.png", image_name))).unwrap();

}



fn fix_image_with_map_grapes(image_name: &str) {

let test_image = Canvas::load(Path::new(&format!("assets/{}.JPG", image_name))).unwrap();
let canvas = Canvas::load(&Path::new(&format!("{}-draw-islands.png", image_name))).unwrap();


let empty_pixel = Pixel::new(255, 0, 0, 254);
let canvas = canvas.fill(100, 100, &empty_pixel); // TODO: Find a smarter way to
// indicate that a pixel is
// "empty"
// TODO: This is for the image with the grapes
let canvas = canvas.fill(836, 3500, &empty_pixel);
let canvas = canvas.fill(963, 3500, &empty_pixel);
let canvas = canvas.fill(628, 3811, &empty_pixel);
let canvas = canvas.fill(800, 3971, &empty_pixel);
let canvas = canvas.fill(1000, 3900, &empty_pixel);

let size = test_image.dimensions();
let mut output_image = Canvas::new_with_background(size.width, size.height, Pixel::new(0, 0, 0, 0));
for x in 0..size.width {
for y in 0..size.height {
let test_pixel = canvas.get_pixel(x, y);
if test_pixel != empty_pixel {
output_image.set_pixel_mut(x, y, &test_image.get_pixel(x, y));
}
}
}
output_image.save(&Path::new(&format!("{}-final-result.png", image_name))).unwrap();

}

fn fix_image_with_map_donut(image_name: &str) {

let test_image = Canvas::load(Path::new(&format!("assets/{}.JPG", image_name))).unwrap();
let canvas = Canvas::load(&Path::new(&format!("{}-draw-islands.png", image_name))).unwrap();


let empty_pixel = Pixel::new(255, 0, 0, 254);
let canvas = canvas.fill(100, 100, &empty_pixel); // TODO: Find a smarter way to
// indicate that a pixel is
// "empty"
let canvas = canvas.fill(1830, 3260, &empty_pixel);

let size = test_image.dimensions();
let mut output_image = Canvas::new_with_background(size.width, size.height, Pixel::new(0, 0, 0, 0));
for x in 0..size.width {
for y in 0..size.height {
let test_pixel = canvas.get_pixel(x, y);
if test_pixel != empty_pixel {
output_image.set_pixel_mut(x, y, &test_image.get_pixel(x, y));
}
}
}
output_image.save(&Path::new(&format!("{}-final-result.png", image_name))).unwrap();

}

fn fix_image(image_name: &str) {
// Gaussian blur
let test_image = Canvas::load(Path::new(&format!("assets/{}.JPG", image_name))).unwrap();

let lap_of_gaussian_filter_canvas = test_image.filter(grey_scale_filter).filter(lap_of_gaussian_filter);
let filtered_canvas = lap_of_gaussian_filter_canvas.filter(black_or_white_filter);

let islands = filtered_canvas.find_islands(&Pixel::new(255, 255, 255, 255));
let islands_with_size: Vec<Island> = islands.iter().filter(|x| x.points.len() > 30000).map(|x| x.clone()).collect();

let mut canvas = Canvas::new_with_background(test_image.dimensions().width, test_image.dimensions().height, Pixel::new(0,0,0,255));
for island in islands_with_size.iter() {
canvas.draw_island_mut(&island, &Pixel::new(255, 255, 255, 255));
}
canvas.save(&Path::new(&format!("{}-draw-islands.png", image_name))).unwrap();

let empty_pixel = Pixel::new(255, 0, 0, 254);
let canvas = canvas.fill(100, 100, &empty_pixel); // TODO: Find a smarter way to
// indicate that a pixel is
// "empty"
// TODO: This is for the image with the grapes
//let canvas = canvas.fill(836, 3500, &empty_pixel);
//let canvas = canvas.fill(963, 3500, &empty_pixel);
//let canvas = canvas.fill(628, 3811, &empty_pixel);
//let canvas = canvas.fill(800, 3971, &empty_pixel);
//let canvas = canvas.fill(1000, 3900, &empty_pixel);
//canvas.save(&Path::new("draw-islands-green")).unwrap();

let size = test_image.dimensions();
let mut output_image = Canvas::new_with_background(size.width, size.height, Pixel::new(0, 0, 0, 0));
for x in 0..size.width {
for y in 0..size.height {
let test_pixel = canvas.get_pixel(x, y);
if test_pixel != empty_pixel {
output_image.set_pixel_mut(x, y, &test_image.get_pixel(x, y));
}
}
}
output_image.save(&Path::new(&format!("{}-final-result.png", image_name))).unwrap();

// TODO: Put timers in measuring what part of this is soooo slow.
// Could be the copying?


// TODO: Now we have the outline of the islands.
// Copy the islands a blank canvas.
// Fill from the outside
// Now you have the entire form of all the islands
// Transfer the pixels that were not filled from the outside


//TODO: Ideas to make the algorithm more roburt to "holes":
// Draw the lines very roughly yourself in a copy of the source image. Use this "rough-image"
// to generate the edges for the edge detection and feature extraction. Then only use source
// image when actually copying the pixels.
//
// OR
//
// Find "Orphan" white pixels in the black lines. This can be done by using a filter. If a
// 15x15 contains more black than white, it is probably a white pixel that should be black




}
6 changes: 2 additions & 4 deletions src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,12 +569,11 @@ impl Canvas {
}

// TODO: Four parameters? Ugly
pub fn trace(mut self, island: &Island, left: u32, right: u32, up: u32, down: u32) -> Canvas {
pub fn trace(mut self, island: &Island, color: &Pixel, left: u32, right: u32, up: u32, down: u32) -> Canvas {
let left: i64 = left.into();
let right: i64 = right.into();
let up: i64 = up.into();
let down: i64 = down.into();
let color = self.get_pixel(island.points[0].x, island.points[0].y);

for point in island.points.iter() {

Expand All @@ -588,12 +587,11 @@ impl Canvas {
self
}

pub fn trace_mut(&mut self, island: &Island, left: u32, right: u32, up: u32, down: u32) {
pub fn trace_mut(&mut self, island: &Island, color: &Pixel, left: u32, right: u32, up: u32, down: u32) {
let left: i64 = left.into();
let right: i64 = right.into();
let up: i64 = up.into();
let down: i64 = down.into();
let color = self.get_pixel(island.points[0].x, island.points[0].y);

for point in island.points.iter() {

Expand Down

0 comments on commit d7d45e5

Please sign in to comment.