advanced_filters/
advanced-filters.rs

1use pixtra::canvas::{Canvas, Island};
2use pixtra::pixels::{Pixel, PixelBuilder};
3use pixtra::utility::{to_grey_lumiosity, count_colors, counted_colors_to_html};
4use std::path::Path;
5
6fn gaussian_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
7    // NOTE: The kernel needs to sum to 1.0. Below 1.0 your image will appear darker
8    // and above 1.0 it will appear lighter.
9    let kernel = vec![0.06, 0.13, 0.06, 0.13, 0.24, 0.13, 0.06, 0.13, 0.06];
10    let coords = vec![
11        (-1, -1),
12        (0, -1),
13        (1, -1),
14        (-1, 0),
15        (0, 0),
16        (1, 0),
17        (-1, 1),
18        (0, 1),
19        (1, 1),
20    ];
21    // For simplicity, we will leave out the edges of the picture.
22    let canvas_size = canvas.dimensions();
23    if x > 0 && y > 0 && x < canvas_size.width - 1 && y < canvas_size.height - 1 {
24        let pixel = apply_filter(canvas, (x, y), &kernel, &coords);
25        return pixel;
26    }
27    canvas.get_pixel(x, y)
28}
29
30fn lap_of_gaussian_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
31    // NOTE: The kernel needs to sum to 1.0. Below 1.0 your image will appear darker
32    // and above 1.0 it will appear lighter.
33    let kernel = vec![
34        0, 1, 1, 2, 2, 2, 1, 1, 0,
35        1, 2, 4, 5, 5, 5, 4, 2, 1,
36        1, 4, 5, 3, 0, 3, 5, 4, 1,
37        2, 5, 3, -12, -24, -12, 3, 5, 2,
38        2, 5, 0, -24, -40, -24, 0, 5, 2,
39        2, 5, 3, -12, -24, -12, 3, 5, 2,
40        1, 4, 5, 3, 0, 3, 5, 4, 1,
41        1, 2, 4, 5, 5, 5, 4, 2, 1,
42        0, 1, 1, 2, 2, 2, 1, 1, 0];
43    let kernel = kernel.iter().map(|&x| x as f32).collect();
44    let mut coords = vec![];
45    for y in -4..5 {
46        for x in -4..5 {
47            coords.push((x, y));
48        }
49    }
50    // For simplicity, we will leave out the edges of the picture.
51    let canvas_size = canvas.dimensions();
52    if x > 3 && y > 3 && x < canvas_size.width - 4 && y < canvas_size.height - 4 {
53        let pixel = apply_filter(canvas, (x, y), &kernel, &coords);
54        return pixel;
55    }
56    canvas.get_pixel(x, y)
57}
58
59fn grey_scale_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
60    to_grey_lumiosity(&canvas.get_pixel(x, y))
61}
62
63fn black_or_white_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
64    let pixel = canvas.get_pixel(x, y);
65    if pixel.r < 128 {
66        Pixel::new(0, 0, 0, 255)
67    } else {
68        Pixel::new(255, 255, 255, 255)
69    }
70}
71
72
73
74fn apply_filter(
75    canvas: &Canvas,
76    center: (u32, u32),
77    kernel: &Vec<f32>,
78    coords: &Vec<(i32, i32)>,
79) -> Pixel {
80    let scales = kernel
81        .iter()
82        .zip(coords.iter())
83        .fold(Pixel::builder(), |acc, (scale, (x, y))| {
84            let pixel =
85                canvas.get_pixel((center.0 as i32 + *x) as u32, (center.1 as i32 + *y) as u32);
86            acc + PixelBuilder::from(
87                pixel.r as f32 * scale,
88                pixel.g as f32 * scale,
89                pixel.b as f32 * scale,
90                pixel.a as f32,
91            )
92        });
93    scales.build()
94}
95
96fn lap_edge_detection_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
97    // NOTE: The kernel (this vector) needs to sum to 1.0. Below 1.0 your image will appear darker
98    // and above 1.0 it will appear lighter.
99    let kernel = vec![0.5, 1.0, 0.5, 1.0, -6.0, 1.0, 0.5, 1.0, 0.5];
100    let coords = vec![
101        (-1, -1),
102        (0, -1),
103        (1, -1),
104        (-1, 0),
105        (0, 0),
106        (1, 0),
107        (-1, 1),
108        (0, 1),
109        (1, 1),
110    ];
111    // For simplicity, we will leave out the edges of the picture.
112    let canvas_size = canvas.dimensions();
113    if x > 0 && y > 0 && x < canvas_size.width - 1 && y < canvas_size.height - 1 {
114        let pixel = apply_filter(canvas, (x, y), &kernel, &coords);
115        return pixel;
116    }
117    canvas.get_pixel(x, y)
118}
119
120fn prewitt_edge_detection_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
121    let kernel_one: Vec<f32> = vec![1f32, 0f32, -1f32, 1f32, 0f32, -1f32, 1f32, 0f32, -1f32];
122    let kernel_two: Vec<f32> = vec![1f32, 1f32, 1f32, 0f32, 0f32, 0f32, -1f32, -1f32, -1f32];
123    let coords = vec![
124        (-1, -1),
125        (0, -1),
126        (1, -1),
127        (-1, 0),
128        (0, 0),
129        (1, 0),
130        (-1, 1),
131        (0, 1),
132        (1, 1),
133    ];
134
135    let canvas_size = canvas.dimensions();
136    if x > 0 && y > 0 && x < canvas_size.width - 1 && y < canvas_size.height - 1 {
137        let pixel_one = apply_filter(canvas, (x, y), &kernel_one, &coords);
138        let pixel_two = apply_filter(canvas, (x, y), &kernel_two, &coords);
139
140        let pixel = PixelBuilder::from(
141            ((pixel_one.r as u32 * pixel_one.r as u32 + pixel_two.r as u32 * pixel_two.r as u32)
142                as f64)
143                .sqrt() as f32,
144            ((pixel_one.g as u32 * pixel_one.g as u32 + pixel_two.g as u32 * pixel_two.g as u32)
145                as f64)
146                .sqrt() as f32,
147            ((pixel_one.b as u32 * pixel_one.b as u32 + pixel_two.b as u32 * pixel_two.b as u32)
148                as f64)
149                .sqrt() as f32,
150            255f32,
151        );
152        return pixel.build();
153    }
154    canvas.get_pixel(x, y)
155}
156
157fn inverse(pixel: Pixel) -> Pixel {
158    Pixel {
159        r: u8::max_value() - pixel.r,
160        g: u8::max_value() - pixel.g,
161        b: u8::max_value() - pixel.b,
162        a: pixel.a,
163    }
164}
165
166fn inverse_filter(canvas: &Canvas, x: u32, y: u32) -> Pixel {
167    inverse(canvas.get_pixel(x, y))
168}
169
170fn main() {
171    // Gaussian blur
172    let canvas = Canvas::load(Path::new("assets/lena.png")).unwrap();
173    let test_image = Canvas::load(Path::new("assets/IMG_0771.JPG")).unwrap();
174    let gaussian_canvas = canvas.filter(gaussian_filter);
175    let _ = gaussian_canvas
176        .save(Path::new("gaussian_canvas.png"))
177        .unwrap();
178    let gaussian_canvas = gaussian_canvas.filter(gaussian_filter);
179    let gaussian_canvas = gaussian_canvas.filter(gaussian_filter);
180    let gaussian_canvas = gaussian_canvas.filter(gaussian_filter);
181    let gaussian_canvas = gaussian_canvas.filter(gaussian_filter);
182    let _ = gaussian_canvas
183        .save(Path::new("very_gaussian_canvas.png"))
184        .unwrap();
185
186    // Chaining filters
187    let inverse_gaussian_canvas = canvas.filter(inverse_filter).filter(gaussian_filter);
188    let _ = inverse_gaussian_canvas
189        .save(Path::new("inverse_gaussian_canvas.png"))
190        .unwrap();
191
192    let lap_edge_detection_canvas = canvas.filter(lap_edge_detection_filter);
193    let _ = lap_edge_detection_canvas
194        .save(Path::new("lap_edge_detection_filter.png"))
195        .unwrap();
196
197    let prewitt_edge_detection_canvas = canvas.filter(prewitt_edge_detection_filter);
198    let _ = prewitt_edge_detection_canvas
199        .save(Path::new("prewitt_edge_detection_filter.png"))
200        .unwrap();
201
202    let lap_of_gaussian_filter_canvas = test_image.filter(grey_scale_filter).filter(lap_of_gaussian_filter);
203    //let counted_colors = count_colors(&lap_of_gaussian_filter_canvas);
204    //println!("{}", counted_colors_to_html(&counted_colors));
205    let _ = lap_of_gaussian_filter_canvas
206        .save(Path::new("lap_of_gaussian_edge_detection_filter.png"))
207        .unwrap();
208    let filtered_canvas = lap_of_gaussian_filter_canvas.filter(black_or_white_filter);
209    let _ = filtered_canvas
210        .save(Path::new("filtered_canvas.png"))
211        .unwrap();
212
213    let islands = filtered_canvas.find_islands(&Pixel::new(255, 255, 255, 255));
214    let islands_with_size: Vec<Island> = islands.iter().filter(|x| x.points.len() > 40000).map(|x| x.clone()).collect();
215    // TODO: Now we have the outline of the islands. 
216    // Copy the islands a blank canvas.
217    // Fill from the outside
218    // Now you have the entire form of all the islands
219    // Transfer the pixels that were not filled from the outside
220    //
221    //
222    // TODO: NEW IDEA AS WELL: Create a 'fattener'. Paints a 2x2, 3x3, 4x4, ... etc around each
223    // pixel. This way you can expand outwards
224
225
226
227
228
229}