Algorithm 在二维坐标系下实现Hough变换直线检测
我想在一个简单的坐标系中实现线检测。我大致遵循了一篇关于如何实现的文章,但我得到的结果与我想要的相差甚远 给定这样的3 x 3矩阵:Algorithm 在二维坐标系下实现Hough变换直线检测,algorithm,math,rust,ascii-art,Algorithm,Math,Rust,Ascii Art,我想在一个简单的坐标系中实现线检测。我大致遵循了一篇关于如何实现的文章,但我得到的结果与我想要的相差甚远 给定这样的3 x 3矩阵: X X X X - - - 我想检测从0,0开始到2,0的行。我将坐标系表示为一个简单的元组数组,元组中的第一项是x,第二项是y,第三项是点的类型(画布或线) 我认为使用Hough来检测直线比较容易,因为边缘检测基本上只是一个二进制决策:要么元组是line类型,要么不是 我在Rust中实施了以下程序: use std::f32; extern crate na
X
X X X
- - -
我想检测从0,0
开始到2,0
的行。我将坐标系表示为一个简单的元组数组,元组中的第一项是x,第二项是y,第三项是点的类型(画布或线)
我认为使用Hough来检测直线比较容易,因为边缘检测基本上只是一个二进制决策:要么元组是line类型,要么不是
我在Rust中实施了以下程序:
use std::f32;
extern crate nalgebra as na;
use na::DMatrix;
#[derive(Debug, PartialEq, Clone)]
enum Representation {
Canvas,
Line,
}
fn main () {
let image_width = 3;
let image_height = 3;
let grid = vec![
(0, 0, Representation::Line), (1, 0, Representation::Line), (2, 0, Representation::Line),
(0, 1, Representation::Canvas), (1, 1, Representation::Canvas), (2, 1, Representation::Canvas),
(0, 2, Representation::Canvas), (1, 2, Representation::Canvas), (2, 2, Representation::Canvas),
];
//let tmp:f32 = (image_width as f32 * image_width as f32) + (image_height as f32 * image_height as f32);
let max_line_length = 3;
let mut accumulator = DMatrix::from_element(180, max_line_length as usize, 0);
for y in 0..image_height {
for x in 0..image_width {
let coords_index = (y * image_width) + x;
let coords = grid.get(coords_index as usize).unwrap();
// check if coords is an edge
if coords.2 == Representation::Line {
for angle in 0..180 {
let r = (x as f32) * (angle as f32).cos() + (y as f32) * (angle as f32).sin();
let r_scaled = scale_between(r, 0.0, 2.0, -2.0, 2.0).round() as u32;
accumulator[(angle as usize, r_scaled as usize)] += 1;
}
}
}
}
let threshold = 3;
// z = angle
for z in 0..180 {
for r in 0..3 {
let val = accumulator[(z as usize, r as usize)];
if val < threshold {
continue;
}
let px = (r as f32) * (z as f32).cos();
let py = (r as f32) * (z as f32).sin();
let p1_px = px + (max_line_length as f32) * (z as f32).cos();
let p1_py = py + (max_line_length as f32) * (z as f32).sin();
let p2_px = px - (max_line_length as f32) * (z as f32).cos();
let p2_py = px - (max_line_length as f32) * (z as f32).cos();
println!("Found lines from {}/{} to {}/{}", p1_px.ceil(), p1_py.ceil(), p2_px.ceil(), p2_py.ceil());
}
}
}
fn scale_between(unscaled_num: f32, min_allowed: f32, max_allowed: f32, min: f32, max: f32) -> f32 {
(max_allowed - min_allowed) * (unscaled_num - min) / (max - min) + min_allowed
}
这实际上是相当多的,因为我只想检测一条线。我的实现显然是错误的,但我不知道该往哪里看,我的数学fu不够高,无法进一步调试
我认为第一部分,实际的Hough变换,似乎有点正确,因为链接文章说:
我一直专注于映射和过滤,根据文章:
use std::f32;
extern crate nalgebra as na;
use na::DMatrix;
fn main () {
let image_width = 3;
let image_height = 3;
let mut grid = DMatrix::from_element(image_width as usize, image_height as usize, 0);
grid[(0, 0)] = 1;
grid[(1, 0)] = 1;
grid[(2, 0)] = 1;
let accu_width = 7;
let accu_height = 3;
let max_line_length = 3;
let mut accumulator = DMatrix::from_element(accu_width as usize, accu_height as usize, 0);
for y in 0..image_height {
for x in 0..image_width {
let coords = (x, y);
let is_edge = grid[coords] == 1;
if !is_edge {
continue;
}
for i in 0..7 {
let angle = i * 30;
let r = (x as f32) * (angle as f32).cos() + (y as f32) * (angle as f32).sin();
let r_scaled = scale_between(r, 0.0, 2.0, -2.0, 2.0).round() as u32;
accumulator[(i as usize, r_scaled as usize)] += 1;
println!("angle: {}, r: {}, r_scaled: {}", angle, r, r_scaled);
}
}
}
let threshold = 3;
// z = angle index
for z in 0..7 {
for r in 0..3 {
let val = accumulator[(z as usize, r as usize)];
if val < threshold {
continue;
}
let px = (r as f32) * (z as f32).cos();
let py = (r as f32) * (z as f32).sin();
let p1_px = px + (max_line_length as f32) * (z as f32).cos();
let p1_py = py + (max_line_length as f32) * (z as f32).sin();
let p2_px = px - (max_line_length as f32) * (z as f32).cos();
let p2_py = px - (max_line_length as f32) * (z as f32).cos();
println!("Found lines from {}/{} to {}/{} - val: {}", p1_px.ceil(), p1_py.ceil(), p2_px.ceil(), p2_py.ceil(), val);
}
}
}
fn scale_between(unscaled_num: f32, min_allowed: f32, max_allowed: f32, min: f32, max: f32) -> f32 {
(max_allowed - min_allowed) * (unscaled_num - min) / (max - min) + min_allowed
}
我在坐标系上绘制了这些线,这些线与我预期的线相差很远。我想知道转换回积分是否仍然关闭 Hough变换原理是搜索通过每个考虑点的所有直线,并通过累加器计算这些直线的出现次数 然而,我们不能确定所有这些线,因为它们的数量是无限的。此外,图像是离散化的,因此计算所有线没有意义 问题来自于这种离散化。角度离散化需要与图像大小相关。在这里,计算180个角度的半径是过度计算的,因为图像只有9个像素,并且该图像中任何线的可能角度都限制为12个值 这里,对于第一个点(0,0),对于每个角度,相关半径为r=0 对于第二个(1,0),相关半径为r=cos(角度) 对于第三个(2,0),相关半径为r=2 cos(角度) 使用舍入,对于相同的角度,许多值的关联半径为0,这会导致过度检测。离散化会导致Hough累加器的扩展
因此,需要根据图像大小计算半径和角度离散化。这里,30°的步长,因此7*3累加器足以检测一条线。Hough变换原理是搜索通过每个考虑点的所有线,并通过累加器计算这些线的出现次数 然而,我们不能确定所有这些线,因为它们的数量是无限的。此外,图像是离散化的,因此计算所有线没有意义 问题来自于这种离散化。角度离散化需要与图像大小相关。在这里,计算180个角度的半径是过度计算的,因为图像只有9个像素,并且该图像中任何线的可能角度都限制为12个值 这里,对于第一个点(0,0),对于每个角度,相关半径为r=0 对于第二个(1,0),相关半径为r=cos(角度) 对于第三个(2,0),相关半径为r=2 cos(角度) 使用舍入,对于相同的角度,许多值的关联半径为0,这会导致过度检测。离散化会导致Hough累加器的扩展
因此,需要根据图像大小计算半径和角度离散化。这里是30°的步长,因此7*3的累加器足以检测直线。您的角度以度而不是弧度为单位 Rust和所有其他编程语言一样,使用弧度作为其三角函数。运行
let ang_d = 30.0;
let ang_r = ang_d * 3.1415926 / 180.0;
println!("sin(30) {} sin(30*pi/180) {}", (ang_d as f32).sin(), (ang_r as f32).sin());
给出了结果
sin(30)-0.9880316 sin(30*pi/180)0.5
在调用cos
和sin
之前,需要将所有角度转换为弧度
在我的第一个循环中
let angle = (i as f32) * 30.0 * 3.1415926 / 180.0;
let r = (x as f32) * (angle as f32).cos() + (y as f32) * (angle as f32).sin();
在第二个例子中,计算直线上的点
let ang = (z as f32) * 30.0 * 3.1415926 / 180.0;
let px = (r as f32) * (ang as f32).cos();
let py = (r as f32) * (ang as f32).sin();
let p1_px = px + (max_line_length as f32) * (ang as f32).cos();
let p1_py = py + (max_line_length as f32) * (ang as f32).sin();
let p2_px = px - (max_line_length as f32) * (ang as f32).cos();
let p2_py = px - (max_line_length as f32) * (ang as f32).cos();
我的铁锈生锈了(实际上不存在),所以有更好的转换方法,而且应该有一个常数,精确值为π。你的角度是度而不是弧度 Rust和所有其他编程语言一样,使用弧度作为其三角函数。运行
let ang_d = 30.0;
let ang_r = ang_d * 3.1415926 / 180.0;
println!("sin(30) {} sin(30*pi/180) {}", (ang_d as f32).sin(), (ang_r as f32).sin());
给出了结果
sin(30)-0.9880316 sin(30*pi/180)0.5
在调用cos
和sin
之前,需要将所有角度转换为弧度
在我的第一个循环中
let angle = (i as f32) * 30.0 * 3.1415926 / 180.0;
let r = (x as f32) * (angle as f32).cos() + (y as f32) * (angle as f32).sin();
在第二个例子中,计算直线上的点
let ang = (z as f32) * 30.0 * 3.1415926 / 180.0;
let px = (r as f32) * (ang as f32).cos();
let py = (r as f32) * (ang as f32).sin();
let p1_px = px + (max_line_length as f32) * (ang as f32).cos();
let p1_py = py + (max_line_length as f32) * (ang as f32).sin();
let p2_px = px - (max_line_length as f32) * (ang as f32).cos();
let p2_py = px - (max_line_length as f32) * (ang as f32).cos();
<