簡単な座標系で線検出を実装したいと思います。私はおおよそthe Hough Transformの実装方法に関する記事をたどったが、私が得た結果は私が望むものからかなり離れている。私は2,0
に行く0,0
で始まる行を検出したい二次元座標系でのハフ変換線検出の実装
X X X
X X X
- - -
:
は、このような3×3行列を考えます。私はタプルの単純な配列として座標系を表し、タプルの最初の項目はx、2番目はy、3番目はポイント(キャンバスまたはライン)のタイプです。
私は、エッジ検出が基本的に単なるバイナリの決定であるため、Houghを使ってラインを検出するのは比較的簡単だと考えました。タプルはタイプラインであるかどうかです。
私はルーストに次のプログラムを実装:
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
}
を結果のようなものです:私は、単一の行を検出したいことを考えると、実際には非常にたくさんある
Found lines from -1/4 to 1/1
Found lines from 2/4 to 0/0
Found lines from 2/-3 to 0/0
Found lines from -1/4 to 1/1
Found lines from 1/-3 to 0/0
Found lines from 0/4 to 1/1
...
。私の実装ははっきりと間違っていますが、見た目がわからないので、私のmaths-fuはそれ以上デバッグするのに十分ではありません。
私は最初の部分を考えて、実際のハフ変換、リンク先の記事は言うので、一種の正しいようだ:、私はマッピングおよびフィルタリングにこだわっている
for each image point p { if (p is part of an edge) { for each possible angle { r = x * cos(angle) + y * sin(angle); houghMatrix[angle][r]++; } } }
によるとされています記事:
は、ハフ空間内の各点は、角度αと距離rで与えられます。これらの値を用いて、直線の1つの点p(x、y)は、 px = r * cos(angle) py = r * sin(angle)によって計算することができる。
行の最大長は、sqrt(imagewidth2 + imageheight2)によって制限されます。
点p、線の角度aおよび最大線長 'maxLength'を使用して、線の他の2つの点を計算することができます。ここで最大の長さは、両方の点が実際の画像の外側にあることを保証します。その結果、これらの2点間に線が引かれると、線は画像境界から画像境界に移動し、決して切り取られません画像のどこかにある。
これらの2つの点p1およびp2は、次のように計算されます。 p1_x = px + maxLength * cos(angle);p1_y = py + maxLength * sin(angle);p2_x = px - maxLength * cos(angle);p2_y = py - maxLength * sin(angle);
...
EDIT
@RaymoAislause 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
}
により示唆されるように報告された出力は今、考慮に画像サイズを取る
更新版:
angle: 0, r: 0, r_scaled: 1
angle: 30, r: 0, r_scaled: 1
angle: 60, r: 0, r_scaled: 1
angle: 90, r: 0, r_scaled: 1
angle: 120, r: 0, r_scaled: 1
angle: 150, r: 0, r_scaled: 1
angle: 180, r: 0, r_scaled: 1
...
Found lines from 3/4 to -1/-1
Found lines from -3/1 to 2/2
I座標系上の線をプロットすると、線は私が期待する線から非常に遠く離れています。私は、ポイントへの変換がまだオフであるのだろうかと思います。
ハフ変換では、実際の画像とエッジがどれほど鮮明であるかに大きく依存します。イメージが混雑しているほど、変換の結果はより複雑になります。私がやることは、出力のイメージを生成して、それが何が生成されているかを調べることです。質問に入力イメージと出力イメージを追加すると役立ちます。 –
疑問に思う人は、r値を4レベルで非常に強く丸めているという事実が考えられます。これは、基本的に非常に広いラインでチェックしていることを意味します、多くのポイントが同じラインに貢献します。 –
実際に出力に線をプロットすると、それらはかなり類似した線であることがわかります。そのうちのいくつかは、ピクセルによって異なるパラレルです。小さなイメージを考えれば、自分の人生をもっと難しくしています。 100×100のイメージのようなものを試してみると、結果はより明確になります。何が起こるかを見るために、rとangle stepsのgranuallityを変更してみてください。 –