use crate::core::{Box, Pix, PixelDepth};
use crate::region::conncomp::{ConnectivityType, label_connected_components};
use crate::region::error::{RegionError, RegionResult};
pub fn pix_label_connected_components(
pix: &Pix,
connectivity: ConnectivityType,
) -> RegionResult<Pix> {
if pix.depth() != PixelDepth::Bit1 {
return Err(RegionError::UnsupportedDepth {
expected: "1-bit",
actual: pix.depth().bits(),
});
}
label_connected_components(pix, connectivity)
}
pub fn pix_count_components(pix: &Pix, connectivity: ConnectivityType) -> RegionResult<u32> {
let labeled = pix_label_connected_components(pix, connectivity)?;
let width = labeled.width();
let height = labeled.height();
let mut max_label = 0u32;
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y) {
max_label = max_label.max(label);
}
}
}
Ok(max_label)
}
pub fn pix_get_component_bounds(
pix: &Pix,
connectivity: ConnectivityType,
) -> RegionResult<Vec<Box>> {
let labeled = pix_label_connected_components(pix, connectivity)?;
get_component_bounds_from_labels(&labeled)
}
pub fn get_component_bounds_from_labels(labeled: &Pix) -> RegionResult<Vec<Box>> {
if labeled.depth() != PixelDepth::Bit32 {
return Err(RegionError::UnsupportedDepth {
expected: "32-bit (labeled image)",
actual: labeled.depth().bits(),
});
}
let width = labeled.width();
let height = labeled.height();
let mut bounds_map: std::collections::HashMap<u32, (i32, i32, i32, i32)> =
std::collections::HashMap::new();
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y)
&& label > 0
{
let entry = bounds_map
.entry(label)
.or_insert((x as i32, y as i32, x as i32, y as i32));
entry.0 = entry.0.min(x as i32); entry.1 = entry.1.min(y as i32); entry.2 = entry.2.max(x as i32); entry.3 = entry.3.max(y as i32); }
}
}
let mut label_bounds: Vec<(u32, Box)> = bounds_map
.into_iter()
.map(|(label, (min_x, min_y, max_x, max_y))| {
(
label,
Box::new_unchecked(min_x, min_y, max_x - min_x + 1, max_y - min_y + 1),
)
})
.collect();
label_bounds.sort_by_key(|(label, _)| *label);
Ok(label_bounds.into_iter().map(|(_, b)| b).collect())
}
pub fn get_component_sizes(labeled: &Pix) -> RegionResult<Vec<u32>> {
if labeled.depth() != PixelDepth::Bit32 {
return Err(RegionError::UnsupportedDepth {
expected: "32-bit (labeled image)",
actual: labeled.depth().bits(),
});
}
let width = labeled.width();
let height = labeled.height();
let mut counts: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y)
&& label > 0
{
*counts.entry(label).or_insert(0) += 1;
}
}
}
let mut label_counts: Vec<(u32, u32)> = counts.into_iter().collect();
label_counts.sort_by_key(|(label, _)| *label);
Ok(label_counts.into_iter().map(|(_, count)| count).collect())
}
#[derive(Debug, Clone)]
pub struct ComponentStats {
pub label: u32,
pub bounds: Box,
pub pixel_count: u32,
pub centroid_x: f64,
pub centroid_y: f64,
}
pub fn get_component_stats(labeled: &Pix) -> RegionResult<Vec<ComponentStats>> {
if labeled.depth() != PixelDepth::Bit32 {
return Err(RegionError::UnsupportedDepth {
expected: "32-bit (labeled image)",
actual: labeled.depth().bits(),
});
}
let width = labeled.width();
let height = labeled.height();
#[derive(Default)]
struct Accum {
count: u32,
sum_x: u64,
sum_y: u64,
min_x: i32,
min_y: i32,
max_x: i32,
max_y: i32,
}
let mut stats: std::collections::HashMap<u32, Accum> = std::collections::HashMap::new();
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y)
&& label > 0
{
let acc = stats.entry(label).or_insert_with(|| Accum {
count: 0,
sum_x: 0,
sum_y: 0,
min_x: x as i32,
min_y: y as i32,
max_x: x as i32,
max_y: y as i32,
});
acc.count += 1;
acc.sum_x += x as u64;
acc.sum_y += y as u64;
acc.min_x = acc.min_x.min(x as i32);
acc.min_y = acc.min_y.min(y as i32);
acc.max_x = acc.max_x.max(x as i32);
acc.max_y = acc.max_y.max(y as i32);
}
}
}
let mut result: Vec<ComponentStats> = stats
.into_iter()
.map(|(label, acc)| {
let centroid_x = acc.sum_x as f64 / acc.count as f64;
let centroid_y = acc.sum_y as f64 / acc.count as f64;
let bounds = Box::new_unchecked(
acc.min_x,
acc.min_y,
acc.max_x - acc.min_x + 1,
acc.max_y - acc.min_y + 1,
);
ComponentStats {
label,
bounds,
pixel_count: acc.count,
centroid_x,
centroid_y,
}
})
.collect();
result.sort_by_key(|s| s.label);
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_image(width: u32, height: u32, pixels: &[(u32, u32)]) -> Pix {
let pix = Pix::new(width, height, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for &(x, y) in pixels {
let _ = pix_mut.set_pixel(x, y, 1);
}
pix_mut.into()
}
#[test]
fn test_count_components() {
let pix = create_test_image(
10,
10,
&[
(0, 0),
(1, 0), (5, 5),
(6, 5), (8, 8), ],
);
let count = pix_count_components(&pix, ConnectivityType::FourWay).unwrap();
assert_eq!(count, 3);
}
#[test]
fn test_get_component_bounds() {
let pix = create_test_image(10, 10, &[(0, 0), (1, 0), (2, 0), (1, 1)]);
let bounds = pix_get_component_bounds(&pix, ConnectivityType::FourWay).unwrap();
assert_eq!(bounds.len(), 1);
assert_eq!(bounds[0].x, 0);
assert_eq!(bounds[0].y, 0);
assert_eq!(bounds[0].w, 3);
assert_eq!(bounds[0].h, 2);
}
#[test]
fn test_get_component_sizes() {
let pix = create_test_image(
10,
10,
&[
(0, 0),
(1, 0), (5, 5), ],
);
let labeled = pix_label_connected_components(&pix, ConnectivityType::FourWay).unwrap();
let sizes = get_component_sizes(&labeled).unwrap();
assert_eq!(sizes.len(), 2);
assert!(sizes.contains(&2));
assert!(sizes.contains(&1));
}
#[test]
fn test_get_component_stats() {
let pix = create_test_image(10, 10, &[(0, 0), (2, 0), (1, 1)]);
let labeled = pix_label_connected_components(&pix, ConnectivityType::EightWay).unwrap();
let stats = get_component_stats(&labeled).unwrap();
assert_eq!(stats.len(), 1);
assert_eq!(stats[0].pixel_count, 3);
assert!((stats[0].centroid_x - 1.0).abs() < 0.01);
assert!((stats[0].centroid_y - 1.0 / 3.0).abs() < 0.01);
}
#[test]
fn test_empty_image() {
let pix = create_test_image(10, 10, &[]);
let count = pix_count_components(&pix, ConnectivityType::FourWay).unwrap();
assert_eq!(count, 0);
let bounds = pix_get_component_bounds(&pix, ConnectivityType::FourWay).unwrap();
assert!(bounds.is_empty());
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnCompTransform {
Area,
Label,
}
pub fn conn_comp_transform(
pix: &Pix,
connectivity: ConnectivityType,
transform_type: ConnCompTransform,
) -> RegionResult<Pix> {
let labeled = label_connected_components(pix, connectivity)?;
let sizes = get_component_sizes(&labeled)?;
let width = labeled.width();
let height = labeled.height();
let output = Pix::new(width, height, PixelDepth::Bit32).map_err(RegionError::Core)?;
let mut result = output.try_into_mut().unwrap_or_else(|p| p.to_mut());
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y) {
let value = if label > 0 {
match transform_type {
ConnCompTransform::Area => {
sizes.get(label as usize - 1).copied().unwrap_or(0)
}
ConnCompTransform::Label => label,
}
} else {
0
};
let _ = result.set_pixel(x, y, value);
}
}
}
Ok(result.into())
}
fn uf_find(parent: &mut [u32], mut x: u32) -> u32 {
while parent[x as usize] != x {
parent[x as usize] = parent[parent[x as usize] as usize];
x = parent[x as usize];
}
x
}
fn uf_union(parent: &mut [u32], rank: &mut [u8], x: u32, y: u32) {
let rx = uf_find(parent, x);
let ry = uf_find(parent, y);
if rx == ry {
return;
}
if rank[rx as usize] < rank[ry as usize] {
parent[rx as usize] = ry;
} else if rank[rx as usize] > rank[ry as usize] {
parent[ry as usize] = rx;
} else {
parent[ry as usize] = rx;
rank[rx as usize] += 1;
}
}
pub struct IncrementalLabeler {
width: u32,
height: u32,
connectivity: ConnectivityType,
labels: Vec<u32>,
next_label: u32,
parent: Vec<u32>,
rank: Vec<u8>,
}
impl std::fmt::Debug for IncrementalLabeler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IncrementalLabeler")
.field("width", &self.width)
.field("height", &self.height)
.field("connectivity", &self.connectivity)
.field("next_label", &self.next_label)
.finish()
}
}
impl IncrementalLabeler {
pub fn new(width: u32, height: u32, connectivity: ConnectivityType) -> Self {
let size = (width as usize)
.checked_mul(height as usize)
.expect("image dimensions too large to allocate labels");
Self {
width,
height,
connectivity,
labels: vec![0; size],
next_label: 1,
parent: vec![0],
rank: vec![0],
}
}
pub fn add_component(&mut self, pix: &Pix, x: u32, y: u32) -> RegionResult<u32> {
if x >= self.width || y >= self.height {
return Err(RegionError::InvalidSeed { x, y });
}
if pix.depth() != PixelDepth::Bit1 {
return Err(RegionError::UnsupportedDepth {
expected: "1-bit",
actual: pix.depth().bits(),
});
}
if pix.width() != self.width || pix.height() != self.height {
return Err(RegionError::InvalidSeed { x, y });
}
let idx = (y * self.width + x) as usize;
if self.labels[idx] != 0 {
return Ok(self.labels[idx]); }
match pix.get_pixel(x, y) {
Some(1) => {} _ => return Err(RegionError::InvalidSeed { x, y }),
}
let label = self.next_label;
self.parent.push(label);
self.rank.push(0);
let mut queue = std::collections::VecDeque::new();
queue.push_back((x, y));
self.labels[idx] = label;
const FOUR_WAY: &[(i32, i32)] = &[
(0, -1), (0, 1), (-1, 0), (1, 0), ];
const EIGHT_WAY: &[(i32, i32)] = &[
(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (1, -1), (-1, 1), (1, 1), ];
let offsets: &[(i32, i32)] = match self.connectivity {
ConnectivityType::FourWay => FOUR_WAY,
ConnectivityType::EightWay => EIGHT_WAY,
};
while let Some((cx, cy)) = queue.pop_front() {
for &(dx, dy) in offsets {
let nx = cx as i32 + dx;
let ny = cy as i32 + dy;
if nx < 0 || nx >= self.width as i32 || ny < 0 || ny >= self.height as i32 {
continue;
}
let nx = nx as u32;
let ny = ny as u32;
let n_idx = (ny * self.width + nx) as usize;
if self.labels[n_idx] != 0 {
continue;
}
if pix.get_pixel(nx, ny) != Some(1) {
continue;
}
self.labels[n_idx] = label;
queue.push_back((nx, ny));
}
}
self.next_label += 1;
Ok(label)
}
pub fn finish(mut self) -> Pix {
let pix =
Pix::new(self.width, self.height, PixelDepth::Bit32).expect("Failed to allocate image");
let mut pix_mut = pix.try_into_mut().unwrap_or_else(|p| p.to_mut());
for idx in 0..self.labels.len() {
let label = self.labels[idx];
let resolved = if label > 0 {
uf_find(&mut self.parent, label)
} else {
0
};
let y = (idx as u32) / self.width;
let x = (idx as u32) % self.width;
let _ = pix_mut.set_pixel(x, y, resolved);
}
pix_mut.into()
}
}
pub fn label_to_color(labeled: &Pix) -> RegionResult<Pix> {
if labeled.depth() != PixelDepth::Bit32 {
return Err(RegionError::UnsupportedDepth {
expected: "32-bit (labeled image)",
actual: labeled.depth().bits(),
});
}
let width = labeled.width();
let height = labeled.height();
let pix = Pix::new(width, height, PixelDepth::Bit32).map_err(RegionError::Core)?;
let mut pix_mut = pix.try_into_mut().unwrap_or_else(|p| p.to_mut());
let mut color_map: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
let palette = vec![
0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0xFF00FFFF, 0x00FFFFFF, 0xFF8000FF, 0x8000FFFF, 0x00FF80FF, 0xFF0080FF, 0x80FF00FF, 0x0080FFFF, ];
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y) {
let color = if label == 0 {
0x000000FF } else {
if let Some(&c) = color_map.get(&label) {
c
} else {
let idx = (label as usize).wrapping_mul(2654435761) % palette.len();
let c = palette[idx];
color_map.insert(label, c);
c
}
};
let _ = pix_mut.set_pixel(x, y, color);
}
}
}
Ok(pix_mut.into())
}
pub fn pix_conn_comp_incr_init(
pix: &Pix,
connectivity: ConnectivityType,
) -> RegionResult<(IncrementalLabeler, u32)> {
if pix.depth() != PixelDepth::Bit1 {
return Err(RegionError::UnsupportedDepth {
expected: "1-bit",
actual: pix.depth().bits(),
});
}
let width = pix.width();
let height = pix.height();
let mut has_fg = false;
'outer: for y in 0..height {
for x in 0..width {
if pix.get_pixel(x, y) == Some(1) {
has_fg = true;
break 'outer;
}
}
}
if !has_fg {
return Ok((IncrementalLabeler::new(width, height, connectivity), 0));
}
let labeled = label_connected_components(pix, connectivity)?;
let mut max_label = 0u32;
let mut labeler = IncrementalLabeler::new(width, height, connectivity);
for y in 0..height {
for x in 0..width {
if let Some(label) = labeled.get_pixel(x, y)
&& label > 0
{
let idx = (y * width + x) as usize;
labeler.labels[idx] = label;
if label > max_label {
max_label = label;
}
}
}
}
labeler.next_label = max_label + 1;
labeler.parent = (0..=max_label).collect();
labeler.rank = vec![0; (max_label + 1) as usize];
Ok((labeler, max_label))
}
pub fn pix_conn_comp_incr_add(
labeler: &mut IncrementalLabeler,
ncc: &mut u32,
x: u32,
y: u32,
) -> RegionResult<()> {
if x >= labeler.width || y >= labeler.height {
return Err(RegionError::InvalidSeed { x, y });
}
let idx = (y * labeler.width + x) as usize;
if labeler.labels[idx] != 0 {
return Ok(());
}
let offsets: &[(i32, i32)] = match labeler.connectivity {
ConnectivityType::FourWay => &[(0, -1), (0, 1), (-1, 0), (1, 0)],
ConnectivityType::EightWay => &[
(0, -1),
(0, 1),
(-1, 0),
(1, 0),
(-1, -1),
(1, -1),
(-1, 1),
(1, 1),
],
};
let mut neighbor_labels: Vec<u32> = Vec::with_capacity(8);
for &(dx, dy) in offsets {
let nx = x as i32 + dx;
let ny = y as i32 + dy;
if nx >= 0 && nx < labeler.width as i32 && ny >= 0 && ny < labeler.height as i32 {
let n_idx = (ny as u32 * labeler.width + nx as u32) as usize;
let val = labeler.labels[n_idx];
if val > 0 {
let root = uf_find(&mut labeler.parent, val);
if !neighbor_labels.contains(&root) {
neighbor_labels.push(root);
}
}
}
}
neighbor_labels.sort_unstable();
if neighbor_labels.is_empty() {
let label = labeler.next_label;
labeler.labels[idx] = label;
labeler.parent.push(label);
labeler.rank.push(0);
labeler.next_label += 1;
*ncc += 1;
} else {
let first_label = neighbor_labels[0];
labeler.labels[idx] = first_label;
if neighbor_labels.len() > 1 {
for &other_label in &neighbor_labels[1..] {
uf_union(
&mut labeler.parent,
&mut labeler.rank,
first_label,
other_label,
);
*ncc -= 1;
}
}
}
Ok(())
}
pub fn pix_loc_to_color_transform(pix: &Pix) -> RegionResult<Pix> {
use crate::core::pixel::compose_rgba;
if pix.depth() != PixelDepth::Bit1 {
return Err(RegionError::UnsupportedDepth {
expected: "1-bit",
actual: pix.depth().bits(),
});
}
let w = pix.width();
let h = pix.height();
let w2 = w / 2;
let h2 = h / 2;
let inv_w2 = if w2 > 0 { 255.0 / w2 as f32 } else { 0.0 };
let inv_h2 = if h2 > 0 { 255.0 / h2 as f32 } else { 0.0 };
let area_pix = crate::region::conncomp::component_area_transform(&label_connected_components(
pix,
ConnectivityType::EightWay,
)?)?;
let out = Pix::new(w, h, PixelDepth::Bit32).map_err(RegionError::Core)?;
let mut out_mut = out.try_into_mut().unwrap_or_else(|p| p.to_mut());
for y in 0..h {
for x in 0..w {
if pix.get_pixel(x, y).unwrap_or(0) == 0 {
continue;
}
let (rval, gval) = if w < h {
let r = (inv_h2 * (y as f32 - h2 as f32).abs()) as u8;
let g = (inv_w2 * (x as f32 - w2 as f32).abs()) as u8;
(r, g)
} else {
let r = (inv_w2 * (x as f32 - w2 as f32).abs()) as u8;
let g = (inv_h2 * (y as f32 - h2 as f32).abs()) as u8;
(r, g)
};
let area = area_pix.get_pixel(x, y).unwrap_or(0);
let bval = area.min(255) as u8;
let color = compose_rgba(rval, gval, bval, 255);
let _ = out_mut.set_pixel(x, y, color);
}
}
Ok(out_mut.into())
}
pub fn pix_get_sorted_neighbor_values(
pix: &Pix,
x: u32,
y: u32,
connectivity: ConnectivityType,
) -> RegionResult<Vec<u32>> {
let depth = pix.depth().bits();
if depth < 8 {
return Err(RegionError::UnsupportedDepth {
expected: "8, 16, or 32 bpp",
actual: depth,
});
}
let w = pix.width();
let h = pix.height();
let neighbors: Vec<(i32, i32)> = match connectivity {
ConnectivityType::FourWay => vec![(-1, 0), (1, 0), (0, -1), (0, 1)],
ConnectivityType::EightWay => vec![
(-1, -1),
(0, -1),
(1, -1),
(-1, 0),
(1, 0),
(-1, 1),
(0, 1),
(1, 1),
],
};
let mut values = std::collections::BTreeSet::new();
for (dx, dy) in &neighbors {
let nx = x as i32 + dx;
let ny = y as i32 + dy;
if nx >= 0
&& ny >= 0
&& (nx as u32) < w
&& (ny as u32) < h
&& let Some(val) = pix.get_pixel(nx as u32, ny as u32)
&& val != 0
{
values.insert(val);
}
}
Ok(values.into_iter().collect())
}
#[cfg(test)]
mod tests_phase4 {
use super::*;
fn create_test_image_with_components(width: u32, height: u32) -> Pix {
let pix = Pix::new(width, height, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for x in 1..3 {
for y in 1..3 {
let _ = pix_mut.set_pixel(x, y, 1);
}
}
for x in 6..9 {
let _ = pix_mut.set_pixel(x, 5, 1);
}
let _ = pix_mut.set_pixel(2, 8, 1);
pix_mut.into()
}
#[test]
fn test_conn_comp_area_transform() {
let pix = create_test_image_with_components(10, 10);
let transformed =
conn_comp_transform(&pix, ConnectivityType::FourWay, ConnCompTransform::Area).unwrap();
assert_eq!(transformed.get_pixel(1, 1), Some(4));
assert_eq!(transformed.get_pixel(2, 2), Some(4));
assert_eq!(transformed.get_pixel(6, 5), Some(3));
assert_eq!(transformed.get_pixel(8, 5), Some(3));
assert_eq!(transformed.get_pixel(2, 8), Some(1));
assert_eq!(transformed.get_pixel(0, 0), Some(0));
}
#[test]
fn test_conn_comp_label_transform() {
let pix = create_test_image_with_components(10, 10);
let transformed =
conn_comp_transform(&pix, ConnectivityType::FourWay, ConnCompTransform::Label).unwrap();
let label1 = transformed.get_pixel(1, 1).unwrap();
assert_eq!(transformed.get_pixel(2, 2), Some(label1));
let label2 = transformed.get_pixel(6, 5).unwrap();
assert_ne!(label2, label1);
assert_eq!(transformed.get_pixel(8, 5), Some(label2));
let label3 = transformed.get_pixel(2, 8).unwrap();
assert_ne!(label3, label1);
assert_ne!(label3, label2);
assert_eq!(transformed.get_pixel(0, 0), Some(0));
}
#[test]
fn test_incremental_labeler_basic() {
let pix = create_test_image_with_components(10, 10);
let mut labeler = IncrementalLabeler::new(10, 10, ConnectivityType::FourWay);
let label1 = labeler.add_component(&pix, 1, 1).unwrap();
assert_eq!(label1, 1);
let label2 = labeler.add_component(&pix, 6, 5).unwrap();
assert_eq!(label2, 2);
let label3 = labeler.add_component(&pix, 2, 8).unwrap();
assert_eq!(label3, 3);
let result = labeler.finish();
assert_eq!(result.get_pixel(1, 1), Some(label1));
assert_eq!(result.get_pixel(2, 2), Some(label1));
assert_eq!(result.get_pixel(6, 5), Some(label2));
assert_eq!(result.get_pixel(2, 8), Some(label3));
}
#[test]
fn test_incremental_vs_batch_labeling() {
let pix = create_test_image_with_components(10, 10);
let mut labeler = IncrementalLabeler::new(10, 10, ConnectivityType::FourWay);
let _ = labeler.add_component(&pix, 1, 1);
let _ = labeler.add_component(&pix, 6, 5);
let _ = labeler.add_component(&pix, 2, 8);
let incremental_result = labeler.finish();
let batch_count = pix_count_components(&pix, ConnectivityType::FourWay).unwrap();
let incremental_count = get_component_sizes(&incremental_result).unwrap().len() as u32;
assert_eq!(batch_count, incremental_count);
}
#[test]
fn test_label_to_color_basic() {
let pix = create_test_image_with_components(10, 10);
let labeled = pix_label_connected_components(&pix, ConnectivityType::FourWay).unwrap();
let colored = label_to_color(&labeled).unwrap();
assert_eq!(colored.depth(), PixelDepth::Bit32);
assert_eq!(colored.width(), labeled.width());
assert_eq!(colored.height(), labeled.height());
let bg_color = colored.get_pixel(0, 0).unwrap();
assert_eq!(bg_color, 0x000000FF); }
#[test]
fn test_label_to_color_adjacent_different() {
let pix = create_test_image_with_components(10, 10);
let labeled = pix_label_connected_components(&pix, ConnectivityType::FourWay).unwrap();
let colored = label_to_color(&labeled).unwrap();
let color1 = colored.get_pixel(1, 1).unwrap();
assert_eq!(colored.get_pixel(1, 2), Some(color1));
let _color2 = colored.get_pixel(6, 5).unwrap();
let _color3 = colored.get_pixel(2, 8).unwrap();
}
}