1use crate::helpers;
4use crate::PhotonImage;
5use image::DynamicImage::ImageRgba8;
6use image::{GenericImage, GenericImageView, Pixel};
7use std::cmp::min;
8
9#[cfg(feature = "enable_wasm")]
10use wasm_bindgen::prelude::*;
11
12type Kernel = [f32; 9];
13
14fn conv(photon_image: &mut PhotonImage, kernel: Kernel) {
15 let mut img = helpers::dyn_image_from_raw(photon_image);
16 img = ImageRgba8(img.to_rgba8());
17
18 let mut filtered_img = img.filter3x3(&kernel);
19 filtered_img = ImageRgba8(filtered_img.to_rgba8());
20
21 if filtered_img.pixels().all(|p| p.2[3] == 0) {
22 for x in 0..GenericImageView::width(&img) - 1 {
23 for y in 0..GenericImageView::height(&img) - 1 {
24 let mut pixel = GenericImageView::get_pixel(&filtered_img, x, y);
25 let original_alpha =
26 GenericImageView::get_pixel(&img, x, y).channels()[3];
27 pixel.channels_mut()[3] = original_alpha;
28 filtered_img.put_pixel(x, y, pixel);
29 }
30 }
31 }
32
33 photon_image.raw_pixels = filtered_img.into_bytes();
34}
35
36#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
53pub fn noise_reduction(photon_image: &mut PhotonImage) {
54 conv(
55 photon_image,
56 [0.0_f32, -1.0, 7.0, -1.0, 5.0, 9.0, 0.0, 7.0, 9.0],
57 );
58}
59
60#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
77pub fn sharpen(photon_image: &mut PhotonImage) {
78 conv(
79 photon_image,
80 [0.0_f32, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0],
81 );
82}
83
84#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
100pub fn edge_detection(photon_image: &mut PhotonImage) {
101 conv(
102 photon_image,
103 [-1.0_f32, -1.0, -1.0, -1.0, 8.0, -1.0, -1.0, -1.0, -1.0],
104 );
105}
106
107#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
123pub fn identity(photon_image: &mut PhotonImage) {
124 conv(
125 photon_image,
126 [0.0_f32, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
127 );
128}
129
130#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
146pub fn box_blur(photon_image: &mut PhotonImage) {
147 conv(
148 photon_image,
149 [1.0_f32, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
150 );
151}
152
153#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
170pub fn gaussian_blur(photon_image: &mut PhotonImage, radius: i32) {
171 let img = helpers::dyn_image_from_raw(photon_image);
173 let mut src = img.into_bytes();
174
175 let width = photon_image.get_width();
176 let height = photon_image.get_height();
177 let mut target: Vec<u8> = src.clone();
178
179 let radius = min(width as i32 / 2 - 1, radius);
188 let radius = min(height as i32 / 2 - 1, radius);
189
190 let bxs = boxes_for_gauss(radius as f32, 3);
191 box_blur_inner(&mut src, &mut target, width, height, (bxs[0] - 1) / 2);
192 box_blur_inner(&mut target, &mut src, width, height, (bxs[1] - 1) / 2);
193 box_blur_inner(&mut src, &mut target, width, height, (bxs[2] - 1) / 2);
194
195 photon_image.raw_pixels = target;
197}
198
199fn boxes_for_gauss(sigma: f32, n: usize) -> Vec<i32> {
200 let n_float = n as f32;
201
202 let w_ideal = (12.0 * sigma * sigma / n_float).sqrt() + 1.0;
203 let mut wl: i32 = w_ideal.floor() as i32;
204
205 if wl % 2 == 0 {
206 wl -= 1;
207 };
208
209 let wu = wl + 2;
210
211 let wl_float = wl as f32;
212
213 let m_ideal = (12.0 * sigma * sigma
214 - n_float * wl_float * wl_float
215 - 4.0 * n_float * wl_float
216 - 3.0 * n_float)
217 / (-4.0 * wl_float - 4.0);
218
219 let m: usize = m_ideal.round() as usize;
220
221 let mut sizes = Vec::<i32>::new();
222 for i in 0..n {
223 if i < m {
224 sizes.push(wl);
225 } else {
226 sizes.push(wu);
227 }
228 }
229
230 sizes
231}
232
233fn box_blur_inner(
234 src: &mut [u8],
235 target: &mut [u8],
236 width: u32,
237 height: u32,
238 radius: i32,
239) {
240 let length = (width * height * 4) as usize;
241 target[..length].clone_from_slice(&src[..length]);
242 box_blur_horizontal(target, src, width, height, radius);
243 box_blur_vertical(src, target, width, height, radius);
244}
245
246fn box_blur_horizontal(
247 src: &[u8],
248 target: &mut [u8],
249 width: u32,
250 height: u32,
251 radius: i32,
252) {
253 let iarr = 1.0 / (radius + radius + 1) as f32;
254 for i in 0..height {
255 let mut ti: usize = (i * width) as usize * 4;
256 let mut li: usize = ti;
257 let mut ri: usize = ti + radius as usize * 4;
258
259 let fv_r = src[ti] as i32;
260 let fv_g = src[ti + 1] as i32;
261 let fv_b = src[ti + 2] as i32;
262
263 let lv_r = src[ti + (width - 1) as usize * 4];
264 let lv_g = src[ti + (width - 1) as usize * 4 + 1];
265 let lv_b = src[ti + (width - 1) as usize * 4 + 2];
266
267 let mut val_r = (radius + 1) * fv_r;
268 let mut val_g = (radius + 1) * fv_g;
269 let mut val_b = (radius + 1) * fv_b;
270
271 for j in 0..radius {
272 val_r += src[ti + j as usize * 4] as i32;
273 val_g += src[ti + j as usize * 4 + 1] as i32;
274 val_b += src[ti + j as usize * 4 + 2] as i32;
275 }
276
277 for _ in 0..radius + 1 {
278 val_r += src[ri] as i32 - fv_r;
279 val_g += src[ri + 1] as i32 - fv_g;
280 val_b += src[ri + 2] as i32 - fv_b;
281 ri += 4;
282
283 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
284 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
285 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
286 ti += 4;
287 }
288
289 for _ in (radius + 1)..(width as i32 - radius) {
290 val_r += src[ri] as i32 - src[li] as i32;
291 val_g += src[ri + 1] as i32 - src[li + 1] as i32;
292 val_b += src[ri + 2] as i32 - src[li + 2] as i32;
293 ri += 4;
294 li += 4;
295
296 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
297 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
298 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
299 ti += 4;
300 }
301
302 for _ in (width as i32 - radius)..width as i32 {
303 val_r += lv_r as i32 - src[li] as i32;
304 val_g += lv_g as i32 - src[li + 1] as i32;
305 val_b += lv_b as i32 - src[li + 2] as i32;
306 li += 4;
307
308 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
309 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
310 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
311 ti += 4;
312 }
313 }
314}
315
316fn box_blur_vertical(
317 src: &[u8],
318 target: &mut [u8],
319 width: u32,
320 height: u32,
321 radius: i32,
322) {
323 let iarr = 1.0 / (radius + radius + 1) as f32;
324
325 for i in 0..width {
326 let mut ti: usize = i as usize * 4;
327 let mut li: usize = ti;
328 let mut ri: usize = ti + (radius * width as i32) as usize * 4;
329
330 let fv_r = src[ti] as i32;
331 let fv_g = src[ti + 1] as i32;
332 let fv_b = src[ti + 2] as i32;
333
334 let lv_r = src[ti + ((height - 1) * width) as usize * 4];
335 let lv_g = src[ti + ((height - 1) * width) as usize * 4 + 1];
336 let lv_b = src[ti + ((height - 1) * width) as usize * 4 + 2];
337
338 let mut val_r = (radius + 1) * fv_r;
339 let mut val_g = (radius + 1) * fv_g;
340 let mut val_b = (radius + 1) * fv_b;
341
342 for j in 0..radius {
343 val_r += src[ti + (j * width as i32) as usize * 4] as i32;
344 val_g += src[ti + (j * width as i32) as usize * 4 + 1] as i32;
345 val_b += src[ti + (j * width as i32) as usize * 4 + 2] as i32;
346 }
347
348 for _ in 0..radius + 1 {
349 val_r += src[ri] as i32 - fv_r;
350 val_g += src[ri + 1] as i32 - fv_g;
351 val_b += src[ri + 2] as i32 - fv_b;
352 ri += width as usize * 4;
353
354 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
355 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
356 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
357 ti += width as usize * 4;
358 }
359
360 for _ in (radius + 1)..(height as i32 - radius) {
361 val_r += src[ri] as i32 - src[li] as i32;
362 val_g += src[ri + 1] as i32 - src[li + 1] as i32;
363 val_b += src[ri + 2] as i32 - src[li + 2] as i32;
364 ri += width as usize * 4;
365 li += width as usize * 4;
366
367 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
368 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
369 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
370 ti += width as usize * 4;
371 }
372
373 for _ in (height as i32 - radius)..height as i32 {
374 val_r += lv_r as i32 - src[li] as i32;
375 val_g += lv_g as i32 - src[li + 1] as i32;
376 val_b += lv_b as i32 - src[li + 2] as i32;
377 li += width as usize * 4;
378
379 target[ti] = (val_r as f32 * iarr).clamp(0.0, 255.0) as u8;
380 target[ti + 1] = (val_g as f32 * iarr).clamp(0.0, 255.0) as u8;
381 target[ti + 2] = (val_b as f32 * iarr).clamp(0.0, 255.0) as u8;
382 ti += width as usize * 4;
383 }
384 }
385}
386
387#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
403pub fn detect_horizontal_lines(photon_image: &mut PhotonImage) {
404 conv(
405 photon_image,
406 [-1.0_f32, -1.0, -1.0, 2.0, 2.0, 2.0, -1.0, -1.0, -1.0],
407 );
408}
409
410#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
426pub fn detect_vertical_lines(photon_image: &mut PhotonImage) {
427 conv(
428 photon_image,
429 [-1.0_f32, 2.0, -1.0, -1.0, 2.0, -1.0, -1.0, 2.0, -1.0],
430 );
431}
432
433#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
449pub fn detect_45_deg_lines(photon_image: &mut PhotonImage) {
450 conv(
451 photon_image,
452 [-1.0_f32, -1.0, 2.0, -1.0, 2.0, -1.0, 2.0, -1.0, -1.0],
453 );
454}
455
456#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
472pub fn detect_135_deg_lines(photon_image: &mut PhotonImage) {
473 conv(
474 photon_image,
475 [2.0_f32, -1.0, -1.0, -1.0, 2.0, -1.0, -1.0, -1.0, 2.0],
476 );
477}
478
479#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
495pub fn laplace(photon_image: &mut PhotonImage) {
496 conv(
497 photon_image,
498 [0.0_f32, -1.0, 0.0, -1.0, 4.0, -1.0, 0.0, -1.0, 0.0],
499 );
500}
501
502#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
518pub fn edge_one(photon_image: &mut PhotonImage) {
519 conv(
520 photon_image,
521 [0.0_f32, -2.2, -0.6, -0.4, 2.8, -0.3, -0.8, -1.0, 2.7],
522 );
523}
524
525#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
541pub fn emboss(photon_image: &mut PhotonImage) {
542 conv(
543 photon_image,
544 [-2.0_f32, -1.0, 0.0, -1.0, 1.0, 1.0, 0.0, 1.0, 2.0],
545 );
546}
547
548#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
564pub fn sobel_horizontal(photon_image: &mut PhotonImage) {
565 conv(
566 photon_image,
567 [-1.0_f32, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0],
568 );
569}
570
571#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
587pub fn prewitt_horizontal(photon_image: &mut PhotonImage) {
588 conv(
589 photon_image,
590 [5.0_f32, -3.0, -3.0, 5.0, 0.0, -3.0, 5.0, -3.0, -3.0],
591 );
592}
593
594#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
610pub fn sobel_vertical(photon_image: &mut PhotonImage) {
611 conv(
612 photon_image,
613 [-1.0_f32, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0],
614 );
615}
616
617#[cfg_attr(feature = "enable_wasm", wasm_bindgen)]
636pub fn sobel_global(photon_image: &mut PhotonImage) {
637 let mut sobel_x = photon_image.clone();
638 let sobel_y = photon_image;
639
640 sobel_horizontal(&mut sobel_x);
641 sobel_vertical(sobel_y);
642
643 let sob_x_values = sobel_x.get_raw_pixels();
644 let sob_y_values = sobel_y.get_raw_pixels();
645
646 let mut sob_xy_values = vec![];
647
648 for i in 0..(sob_x_values.len()) {
649 let kx = (sob_x_values[i]) as u32;
650 let ky = (sob_y_values[i]) as u32; let kxy_2 = kx * kx + ky * ky; sob_xy_values.push((kxy_2 as f64).sqrt() as u8); }
654
655 sobel_y.raw_pixels = sob_xy_values;
656}