Hough transform: Difference between revisions
Content added Content deleted
m (→{{header|Phix}}: IupCloseOnEscape no longer needed, bugfix, speedup) |
(Add Rust implementation) |
||
Line 935: | Line 935: | ||
out |
out |
||
end</lang> |
end</lang> |
||
=={{header|Rust}}== |
|||
<lang rust> |
|||
//! Contributed by Gavin Baker <gavinb@antonym.org> |
|||
//! Adapted from the Go version |
|||
use std::fs::File; |
|||
use std::io::{self, BufRead, BufReader, BufWriter, Read, Write}; |
|||
use std::iter::repeat; |
|||
/// Simple 8-bit grayscale image |
|||
struct ImageGray8 { |
|||
width: usize, |
|||
height: usize, |
|||
data: Vec<u8>, |
|||
} |
|||
fn load_pgm(filename: &str) -> io::Result<ImageGray8> { |
|||
// Open file |
|||
let mut file = BufReader::new(File::open(filename)?); |
|||
// Read header |
|||
let mut magic_in = String::new(); |
|||
let _ = file.read_line(&mut magic_in)?; |
|||
let mut width_in = String::new(); |
|||
let _ = file.read_line(&mut width_in)?; |
|||
let mut height_in = String::new(); |
|||
let _ = file.read_line(&mut height_in)?; |
|||
let mut maxval_in = String::new(); |
|||
let _ = file.read_line(&mut maxval_in)?; |
|||
assert_eq!(magic_in, "P5\n"); |
|||
assert_eq!(maxval_in, "255\n"); |
|||
// Parse header |
|||
let width = width_in |
|||
.trim() |
|||
.parse::<usize>() |
|||
.map_err(|_| io::ErrorKind::InvalidData)?; |
|||
let height: usize = height_in |
|||
.trim() |
|||
.parse::<usize>() |
|||
.map_err(|_| io::ErrorKind::InvalidData)?; |
|||
println!("Reading pgm file {}: {} x {}", filename, width, height); |
|||
// Create image and allocate buffer |
|||
let mut img = ImageGray8 { |
|||
width, |
|||
height, |
|||
data: vec![], |
|||
}; |
|||
// Read image data |
|||
let expected_bytes = width * height; |
|||
let bytes_read = file.read_to_end(&mut img.data)?; |
|||
if bytes_read != expected_bytes { |
|||
let kind = if bytes_read < expected_bytes { |
|||
io::ErrorKind::UnexpectedEof |
|||
} else { |
|||
io::ErrorKind::InvalidData |
|||
}; |
|||
let msg = format!("expected {} bytes", expected_bytes); |
|||
return Err(io::Error::new(kind, msg)); |
|||
} |
|||
Ok(img) |
|||
} |
|||
fn save_pgm(img: &ImageGray8, filename: &str) { |
|||
// Open file |
|||
let mut file = BufWriter::new(File::create(filename).unwrap()); |
|||
// Write header |
|||
if let Err(e) = writeln!(&mut file, "P5\n{}\n{}\n255", img.width, img.height) { |
|||
println!("Failed to write header: {}", e); |
|||
} |
|||
println!( |
|||
"Writing pgm file {}: {} x {}", |
|||
filename, img.width, img.height |
|||
); |
|||
// Write binary image data |
|||
if let Err(e) = file.write_all(&(img.data[..])) { |
|||
println!("Failed to image data: {}", e); |
|||
} |
|||
} |
|||
#[allow(clippy::cast_precision_loss)] |
|||
#[allow(clippy::clippy::cast_possible_truncation)] |
|||
fn hough(image: &ImageGray8, out_width: usize, out_height: usize) -> ImageGray8 { |
|||
let in_width = image.width; |
|||
let in_height = image.height; |
|||
// Allocate accumulation buffer |
|||
let out_height = ((out_height / 2) * 2) as usize; |
|||
let mut accum = ImageGray8 { |
|||
width: out_width, |
|||
height: out_height, |
|||
data: repeat(255).take(out_width * out_height).collect(), |
|||
}; |
|||
// Transform extents |
|||
let rmax = (in_width as f64).hypot(in_height as f64); |
|||
let dr = rmax / (out_height / 2) as f64; |
|||
let dth = std::f64::consts::PI / out_width as f64; |
|||
// Process input image in raster order |
|||
for y in 0..in_height { |
|||
for x in 0..in_width { |
|||
let in_idx = y * in_width + x; |
|||
let col = image.data[in_idx]; |
|||
if col == 255 { |
|||
continue; |
|||
} |
|||
// Project into rho,theta space |
|||
for jtx in 0..out_width { |
|||
let th = dth * (jtx as f64); |
|||
let r = (x as f64) * (th.cos()) + (y as f64) * (th.sin()); |
|||
let iry = out_height as i64 / 2 - (r / (dr as f64) + 0.5).floor() as i64; |
|||
#[allow(clippy::clippy::cast_sign_loss)] |
|||
let out_idx = (jtx as i64 + iry * out_width as i64) as usize; |
|||
let col = accum.data[out_idx]; |
|||
if col > 0 { |
|||
accum.data[out_idx] = col - 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
accum |
|||
} |
|||
fn main() -> io::Result<()> { |
|||
let image = load_pgm("resources/Pentagon.pgm")?; |
|||
let accum = hough(&image, 460, 360); |
|||
save_pgm(&accum, "hough.pgm"); |
|||
Ok(()) |
|||
} |
|||
</lang> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |