Skip to content
Snippets Groups Projects
edge_detection.jn 5.19 KiB
const gs : usize = 7;
const sz : usize = 3;
const sb : usize = 3;

fn gaussian_smoothing<n, m : usize>(
  input: f32[n, m],
  filter: f32[7, 7],
) -> f32[n, m] {
  @res let result : f32[n, m];

  // Define the gaussian radius as half the gaussian size
  const gr = gs / 2;

  @image_loop for row = 0 to n {
    for col = 0 to m {
      let smoothed = 0.0;

      @filter_loop for i = 0 to gs {
        for j = 0 to gs {
          let br = min!(max!(row + i, gr) - gr, n - 1);
          let bc = min!(max!(col + j, gr) - gr, m - 1);
          let val = input[br, bc];
          smoothed += val * filter[i, j];
        }
      }

      result[row, col] = smoothed;
    }
  }

  return result;
}

const MIN_BR : f32 = 0;
const MAX_BR : f32 = 1;

fn laplacian_estimate<n, m : usize>(
  input: f32[n, m],
  structure: f32[3, 3],
) -> f32[n, m] {
  const r = sz / 2;

  @res let result : f32[n, m];

  @image_loop for row = 0 to n {
    for col = 0 to m {
      // Compute pixel of dilated image
      let dilated = MIN_BR;
      @filter_loop for i = 0 to sz {
        for j = 0 to sz {
	  let filter = if row + i < r
                         || row + i - r > n - 1
                         || col + j < r
                         || col + j - r > m - 1 then MIN_BR
                      else input[row + i - r, col + j - r];
          dilated = max!(dilated, filter * structure[i, j]);
        }
      }

      // Compute pixel of eroded image
      let eroded = MAX_BR;
      @filter_loop for i = 0 to sz {
        for j = 0 to sz {
	  let filter = if row + i < r
                         || row + i - r > n - 1
                         || col + j < r
                         || col + j - r > m - 1 then MAX_BR
                      else input[row + i - r, col + j - r];
          eroded = min!(eroded, filter * structure[i, j]);
        }
      }

      let laplacian = dilated + eroded - 2 * input[row, col];
      result[row, col] = laplacian;
    }
  }

  return result;
}

fn zero_crossings<n, m : usize>(
  input: f32[n, m],
  structure: f32[3, 3],
) -> f32[n, m] {
  const r = sz / 2;

  @res let result : f32[n, m];

  @image_loop for row = 0 to n {
    for col = 0 to m {
      // Compute the pixel of dilated image
      let dilated = MIN_BR;
      @filter_loop for i = 0 to sz {
        for j = 0 to sz {
	  let filter = if row + i < r
                          || row + i - r > n - 1
                          || col + j < r
                          || col + j - r > m - 1 then MIN_BR
                       else if input[row + i - r, col + j - r] > MIN_BR then MAX_BR
                       else MIN_BR;
          dilated = max!(dilated, filter * structure[i, j]);
        }
      }

      // Compute the pixel of eroded image
      let eroded = MAX_BR;
      @filter_loop for i = 0 to sz {
        for j = 0 to sz {
	  let filter = if row + i < r
                          || row + i - r > n - 1
                          || col + j < r
                          || col + j - r > m - 1 then MAX_BR
                       else if input[row + i - r, col + j - r] > MIN_BR then MAX_BR
                       else MIN_BR;
          eroded = min!(eroded, filter * structure[i, j]);
        }
      }

      let sign = dilated - eroded;
      result[row, col] = sign;
    }
  }

  return result;
}

fn gradient<n, m : usize>(
  input: f32[n, m],
  sx: f32[3, 3],
  sy: f32[3, 3],
) -> f32[n, m] {
  const sbr = sb / 2;

  @res let result : f32[n, m];

  for row = 0 to n {
    for col = 0 to m {

      let gx = 0;
      let gy = 0;

      @filter_loop for i = 0 to sb {
        for j = 0 to sb {
          let val = input[if row + i < sbr              then 0
                          else if row + i - sbr > n - 1 then n - 1
                                                        else row + i - sbr,
                          if col + j < sbr              then 0
                          else if col + j - sbr > m - 1 then m - 1
                                                        else col + j - sbr];
          gx += val * sx[i, j];
          gy += val * sy[i, j];
        }
      }

      result[row, col] = sqrt!(gx * gx + gy * gy);
    }
  }

  return result;
}

fn max_gradient<n, m: usize>(gradient: f32[n, m]) -> f32 {
  let max = -1.0;

  for i = 0 to n {
    for j = 0 to m {
      max = max!(max, gradient[i, j]);
    }
  }

  return max;
}

fn reject_zero_crossings<n, m: usize>(
  crossings: f32[n, m],
  gradient: f32[n, m],
  max_gradient: f32,
  theta: f32,
) -> f32[n, m] {
  @res let result : f32[n, m];

  for row = 0 to n {
    for col = 0 to m {
      result[row, col] =
        if crossings[row, col] > 0 && gradient[row, col] > theta * max_gradient
        then 1.0
        else 0.0;
    }
  }

  return result;
}

#[entry]
fn edge_detection<n, m : usize>(
  input: f32[n, m],
  gaussian_filter: f32[gs, gs],
  structure: f32[3, 3],
  sx: f32[3, 3],
  sy: f32[3, 3],
  theta: f32,
) -> f32[n, m] {
  let smoothed = gaussian_smoothing::<n, m>(input, gaussian_filter);
  @le let laplacian = laplacian_estimate::<n, m>(smoothed, structure);
  @zc let zcs = zero_crossings::<n, m>(laplacian, structure);
  let gradient = gradient::<n, m>(smoothed, sx, sy);
  let maxgrad = max_gradient::<n, m>(gradient);
  return reject_zero_crossings::<n, m>(zcs, gradient, maxgrad, theta);
}