///|
pub fn distance_transform(mask : BinaryMask) -> GrayImage {
  let result = GrayImage(width=mask.width, height=mask.height, fill=1.0e6)
  for y in 0..= 0 {
    let mut x = mask.width - 1
    while x >= 0 {
      if mask.at(x, y) {
        let right = if x + 1 >= mask.width {
          1.0e6
        } else {
          result.at(x + 1, y) + 1.0
        }
        let bottom = if y + 1 >= mask.height {
          1.0e6
        } else {
          result.at(x, y + 1) + 1.0
        }
        result.set(x, y, result.at(x, y).min(right).min(bottom))
      }
      x -= 1
    }
    y -= 1
  }
  result
}

///|
pub fn medial_axis(mask : BinaryMask, minimum_distance : Double) -> BinaryMask {
  let distance = distance_transform(mask)
  let result = BinaryMask(width=mask.width, height=mask.height)
  for y in 0..= minimum_distance &&
        value >= distance.at(x - 1, y) &&
        value >= distance.at(x + 1, y) &&
        value >= distance.at(x, y - 1) &&
        value >= distance.at(x, y + 1) {
        result.set(x, y, true)
      }
    }
  }
  result
}

///|
pub fn remove_small_objects(
  mask : BinaryMask,
  minimum_area : Int,
) -> BinaryMask {
  filter_components_by_area(mask, minimum_area, 1_000_000_000)
}

///|
pub fn remove_large_objects(
  mask : BinaryMask,
  maximum_area : Int,
) -> BinaryMask {
  filter_components_by_area(mask, 0, maximum_area)
}

///|
pub fn majority_filter(mask : BinaryMask, radius : Int) -> BinaryMask {
  let result = BinaryMask(width=mask.width, height=mask.height)
  let safe = radius.max(0)
  let total = (safe * 2 + 1) * (safe * 2 + 1)
  for y in 0..= total)
    }
  }
  result
}

///|
pub fn border_mask(width : Int, height : Int, thickness : Int) -> BinaryMask {
  let result = BinaryMask(width~, height~)
  let safe = thickness.max(0)
  for y in 0..= width - safe || y >= height - safe {
        result.set(x, y, true)
      }
    }
  }
  result
}

///|
pub fn interior_mask(width : Int, height : Int, thickness : Int) -> BinaryMask {
  border_mask(width, height, thickness).invert()
}

///|
pub fn mask_perimeter(mask : BinaryMask) -> Double {
  let boundary = mask.boundary()
  Double::from_int(boundary.count())
}

///|
pub fn mask_euler_estimate(mask : BinaryMask) -> Int {
  let components = label_components(mask).length()
  let holes = label_components(fill_mask_holes(mask).difference(mask)).length()
  components - holes
}

///|
pub fn mask_symmetry_score(mask : BinaryMask, horizontal : Bool) -> Double {
  if mask.width * mask.height == 0 {
    0.0
  } else {
    let mut equal = 0
    for y in 0.. Array[Int] {
  let projection = if horizontal {
    mask.column_projection()
  } else {
    mask.row_projection()
  }
  let result : Array[Int] = []
  for i in 0..= threshold {
      result.push(i)
    }
  }
  result
}

///|
pub fn mask_fill_ratio(mask : BinaryMask) -> Double {
  if mask.width * mask.height == 0 {
    0.0
  } else {
    Double::from_int(mask.count()) / Double::from_int(mask.width * mask.height)
  }
}