Skip to content
Snippets Groups Projects
Commit 6bce8641 authored by rarbore2's avatar rarbore2
Browse files

Merge branch 'edge_refactor' into 'main'

Edge refactor

See merge request !191
parents b935a755 0b175b52
No related branches found
No related tags found
1 merge request!191Edge refactor
Pipeline #201775 passed
...@@ -1338,6 +1338,7 @@ version = "0.1.0" ...@@ -1338,6 +1338,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-std", "async-std",
"clap", "clap",
"criterion",
"hercules_rt", "hercules_rt",
"juno_build", "juno_build",
"opencv", "opencv",
......
...@@ -13,6 +13,10 @@ name = "juno_edge_detection" ...@@ -13,6 +13,10 @@ name = "juno_edge_detection"
path = "src/main.rs" path = "src/main.rs"
required-features = ["opencv"] required-features = ["opencv"]
[lib]
path = "src/lib.rs"
required-features = ["opencv"]
[build-dependencies] [build-dependencies]
juno_build = { path = "../../juno_build" } juno_build = { path = "../../juno_build" }
...@@ -23,3 +27,10 @@ async-std = "*" ...@@ -23,3 +27,10 @@ async-std = "*"
clap = { version = "*", features = ["derive"] } clap = { version = "*", features = ["derive"] }
with_builtin_macros = "0.1.0" with_builtin_macros = "0.1.0"
opencv = { version = "*", features = ["clang-runtime"], optional = true } opencv = { version = "*", features = ["clang-runtime"], optional = true }
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
[[bench]]
name = "edge_detection_bench"
harness = false
#![feature(concat_idents)]
use std::slice::from_raw_parts;
use criterion::{criterion_group, criterion_main, Criterion};
use opencv::core::{Mat, Size, CV_32F, CV_8U};
use opencv::imgproc::{cvt_color_def, ColorConversionCodes};
use opencv::prelude::{MatTraitConst, VideoCaptureTrait, VideoCaptureTraitConst};
use opencv::videoio::{VideoCapture, VideoCaptureProperties, VideoWriter, VideoWriterTrait};
use hercules_rt::{runner, HerculesImmBox, HerculesImmBoxTo};
use juno_edge_detection::*;
juno_build::juno!("edge_detection");
fn edge_detection_bench(c: &mut Criterion) {
let mut group = c.benchmark_group("edge detection bench");
group.sample_size(10);
let input = "examples/formula1_scaled.mp4";
let gs: usize = 7;
let gaussian_filter: Vec<f32> = vec![
0.000036, 0.000363, 0.001446, 0.002291, 0.001446, 0.000363, 0.000036, 0.000363, 0.003676,
0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.001446, 0.014662, 0.058488, 0.092651,
0.058488, 0.014662, 0.001446, 0.002291, 0.023226, 0.092651, 0.146768, 0.092651, 0.023226,
0.002291, 0.001446, 0.014662, 0.058488, 0.092651, 0.058488, 0.014662, 0.001446, 0.000363,
0.003676, 0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.000036, 0.000363, 0.001446,
0.002291, 0.001446, 0.000363, 0.000036,
];
let gaussian_filter_h = HerculesImmBox::from(gaussian_filter.as_slice());
let sz: usize = 3;
let structure: Vec<f32> = vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let structure_h = HerculesImmBox::from(structure.as_slice());
let sb: usize = 3;
let sx: Vec<f32> = vec![-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0];
let sx_h = HerculesImmBox::from(sx.as_slice());
let sy: Vec<f32> = vec![-1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0];
let sy_h = HerculesImmBox::from(sy.as_slice());
let theta: f32 = 0.1;
let mut video = VideoCapture::from_file_def(&input).expect("Error loading video");
assert!(video.is_opened().unwrap());
let fps = video
.get(VideoCaptureProperties::CAP_PROP_FPS.into())
.expect("Error getting fps");
let _num_frames = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_COUNT.into())
.expect("Error getting number of frames") as usize;
let width = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_WIDTH.into())
.expect("Error getting width") as usize;
let height = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT.into())
.expect("Error getting height") as usize;
let num_frames = 5;
let mut r = runner!(edge_detection);
let frames: Vec<_> = (0..num_frames).map(|_| load_frame(&mut video)).collect();
group.bench_function("edge detection bench", |b| {
b.iter(|| {
for i in 0..num_frames {
let frame = &frames[i];
let ptr = frame.ptr_def().unwrap() as *const f32;
assert!(frame.rows() as usize == height);
assert!(frame.cols() as usize == width);
let input = unsafe { from_raw_parts(ptr, height * width) };
let input_h = HerculesImmBox::from(input);
let result = async_std::task::block_on(async {
r.run(
height as u64,
width as u64,
gs as u64,
sz as u64,
sb as u64,
input_h.to(),
gaussian_filter_h.to(),
structure_h.to(),
sx_h.to(),
sy_h.to(),
theta,
)
.await
});
}
})
});
}
criterion_group!(benches, edge_detection_bench);
criterion_main!(benches);
#![feature(concat_idents)]
mod edge_detection_rust;
use hercules_rt::{runner, HerculesImmBox, HerculesImmBoxTo, HerculesMutBox};
use std::slice::from_raw_parts;
use clap::Parser;
use opencv::core::{Mat, Size, CV_32F, CV_8U};
use opencv::highgui::{imshow, wait_key};
use opencv::imgproc::{cvt_color_def, ColorConversionCodes};
use opencv::prelude::{MatTraitConst, VideoCaptureTrait, VideoCaptureTraitConst};
use opencv::videoio::{VideoCapture, VideoCaptureProperties, VideoWriter, VideoWriterTrait};
juno_build::juno!("edge_detection");
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
pub struct EdgeDetectionInputs {
pub input: String,
#[clap(short, long)]
pub display: bool,
#[clap(short, long, value_name = "PATH")]
pub output: Option<String>,
#[clap(short, long)]
pub verify: bool,
#[clap(long = "display-verify")]
pub display_verify: bool,
#[clap(long = "output-verify", value_name = "PATH")]
pub output_verify: Option<String>,
#[clap(short, long, value_name = "COUNT")]
pub frames: Option<usize>,
}
pub fn load_frame(video: &mut VideoCapture) -> Mat {
let mut frame = Mat::default();
let Ok(true) = video.read(&mut frame) else {
panic!("Failed to load frame");
};
let result = if frame.channels() == 3 {
let mut converted = Mat::default();
let () = cvt_color_def(
&frame,
&mut converted,
ColorConversionCodes::COLOR_BGR2GRAY.into(),
)
.expect("Failure in conversion to grayscale");
let mut result = Mat::default();
let () = converted
.convert_to(&mut result, CV_32F, 1.0 / 255.0, 0.0)
.expect("Failure in conversion to f32");
result
} else if frame.channels() == 1 {
let mut result = Mat::default();
let () = frame
.convert_to(&mut result, CV_32F, 1.0 / 255.0, 0.0)
.expect("Failure in conversion to f32");
result
} else {
panic!("Expected either RGB or grayscale image");
};
assert!(result.is_continuous());
result
}
pub fn frame_from_slice(frame: &[f32], height: usize, width: usize) -> Mat {
let result = Mat::from_slice(frame)
.expect("Failed to create matrix from result")
.reshape(1, height as i32)
.expect("Failed to reshape result matrix")
.clone_pointee();
assert!(result.cols() == width as i32);
// Convert to u8 since the VideoWriter seems to require that
let mut converted = Mat::default();
let () = result
.convert_to(&mut converted, CV_8U, 255.0, 0.0)
.expect("Failure in conversion to u8");
converted
}
pub fn edge_detection_harness(args: EdgeDetectionInputs) {
let EdgeDetectionInputs {
input,
display,
output,
verify,
display_verify,
output_verify,
frames,
} = args;
let gs: usize = 7;
let gaussian_filter: Vec<f32> = vec![
0.000036, 0.000363, 0.001446, 0.002291, 0.001446, 0.000363, 0.000036, 0.000363, 0.003676,
0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.001446, 0.014662, 0.058488, 0.092651,
0.058488, 0.014662, 0.001446, 0.002291, 0.023226, 0.092651, 0.146768, 0.092651, 0.023226,
0.002291, 0.001446, 0.014662, 0.058488, 0.092651, 0.058488, 0.014662, 0.001446, 0.000363,
0.003676, 0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.000036, 0.000363, 0.001446,
0.002291, 0.001446, 0.000363, 0.000036,
];
let gaussian_filter_h = HerculesImmBox::from(gaussian_filter.as_slice());
let sz: usize = 3;
let structure: Vec<f32> = vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let structure_h = HerculesImmBox::from(structure.as_slice());
let sb: usize = 3;
let sx: Vec<f32> = vec![-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0];
let sx_h = HerculesImmBox::from(sx.as_slice());
let sy: Vec<f32> = vec![-1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0];
let sy_h = HerculesImmBox::from(sy.as_slice());
let theta: f32 = 0.1;
let mut video = VideoCapture::from_file_def(&input).expect("Error loading video");
assert!(video.is_opened().unwrap());
let fps = video
.get(VideoCaptureProperties::CAP_PROP_FPS.into())
.expect("Error getting fps");
let num_frames = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_COUNT.into())
.expect("Error getting number of frames") as usize;
let width = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_WIDTH.into())
.expect("Error getting width") as usize;
let height = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT.into())
.expect("Error getting height") as usize;
let num_frames = if let Some(frames) = frames {
usize::min(frames, num_frames)
} else {
num_frames
};
let mut r = runner!(edge_detection);
let mut output = output.map(|filename| {
VideoWriter::new(
&filename,
VideoWriter::fourcc('m', 'p', '4', 'v').unwrap(),
fps,
Size {
width: width as i32,
height: height as i32,
},
false,
)
.expect("Error opening output video")
});
let mut output_verify = output_verify.map(|filename| {
VideoWriter::new(
&filename,
VideoWriter::fourcc('m', 'p', '4', 'v').unwrap(),
fps,
Size {
width: width as i32,
height: height as i32,
},
false,
)
.expect("Error opening output video")
});
for i in 0..num_frames {
let frame = load_frame(&mut video);
let ptr = frame.ptr_def().unwrap() as *const f32;
assert!(frame.rows() as usize == height);
assert!(frame.cols() as usize == width);
let input = unsafe { from_raw_parts(ptr, height * width) };
let input_h = HerculesImmBox::from(input);
let result = async_std::task::block_on(async {
HerculesMutBox::from(
r.run(
height as u64,
width as u64,
gs as u64,
sz as u64,
sb as u64,
input_h.to(),
gaussian_filter_h.to(),
structure_h.to(),
sx_h.to(),
sy_h.to(),
theta,
)
.await,
)
})
.as_slice()
.to_vec();
if display {
let result = frame_from_slice(&result, height, width);
let () = imshow("Juno", &result).expect("Failure in displaying image");
}
if let Some(ref mut output) = output {
let result = frame_from_slice(&result, height, width);
let () = output.write(&result).expect("Failure in writing frame");
}
if verify {
let rust_result = edge_detection_rust::edge_detection(
height,
width,
gs,
sz,
sb,
input,
&gaussian_filter,
&structure,
&sx,
&sy,
theta,
);
assert_eq!(result, rust_result);
println!("Frames {} match", i);
if display_verify {
let rust_result = frame_from_slice(&rust_result, height, width);
let () = imshow("Rust", &rust_result).expect("Failure in displaying image");
}
if let Some(ref mut output) = output_verify {
let result = frame_from_slice(&rust_result, height, width);
let () = output.write(&result).expect("Failure in writing frame");
}
}
if display || (verify && display_verify) {
let _ = wait_key(0);
}
}
if let Some(mut output) = output {
let () = output.release().expect("Failure releasing output video");
}
if let Some(mut output) = output_verify {
let () = output.release().expect("Failure releasing output video");
}
}
#![feature(concat_idents)]
mod edge_detection_rust;
use hercules_rt::{runner, HerculesImmBox, HerculesImmBoxTo, HerculesMutBox};
use std::slice::from_raw_parts;
use clap::Parser; use clap::Parser;
use opencv::core::{Mat, Size, CV_32F, CV_8U}; use juno_edge_detection::{edge_detection_harness, EdgeDetectionInputs};
use opencv::highgui::{imshow, wait_key};
use opencv::imgproc::{cvt_color_def, ColorConversionCodes};
use opencv::prelude::{MatTraitConst, VideoCaptureTrait, VideoCaptureTraitConst};
use opencv::videoio::{VideoCapture, VideoCaptureProperties, VideoWriter, VideoWriterTrait};
juno_build::juno!("edge_detection");
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct EdgeDetectionInputs {
input: String,
#[clap(short, long)]
display: bool,
#[clap(short, long, value_name = "PATH")]
output: Option<String>,
#[clap(short, long)]
verify: bool,
#[clap(long = "display-verify")]
display_verify: bool,
#[clap(long = "output-verify", value_name = "PATH")]
output_verify: Option<String>,
#[clap(short, long, value_name = "COUNT")]
frames: Option<usize>,
}
fn load_frame(video: &mut VideoCapture) -> Mat {
let mut frame = Mat::default();
let Ok(true) = video.read(&mut frame) else {
panic!("Failed to load frame");
};
let result = if frame.channels() == 3 {
let mut converted = Mat::default();
let () = cvt_color_def(
&frame,
&mut converted,
ColorConversionCodes::COLOR_BGR2GRAY.into(),
)
.expect("Failure in conversion to grayscale");
let mut result = Mat::default();
let () = converted
.convert_to(&mut result, CV_32F, 1.0 / 255.0, 0.0)
.expect("Failure in conversion to f32");
result
} else if frame.channels() == 1 {
let mut result = Mat::default();
let () = frame
.convert_to(&mut result, CV_32F, 1.0 / 255.0, 0.0)
.expect("Failure in conversion to f32");
result
} else {
panic!("Expected either RGB or grayscale image");
};
assert!(result.is_continuous());
result
}
fn frame_from_slice(frame: &[f32], height: usize, width: usize) -> Mat {
let result = Mat::from_slice(frame)
.expect("Failed to create matrix from result")
.reshape(1, height as i32)
.expect("Failed to reshape result matrix")
.clone_pointee();
assert!(result.cols() == width as i32);
// Convert to u8 since the VideoWriter seems to require that
let mut converted = Mat::default();
let () = result
.convert_to(&mut converted, CV_8U, 255.0, 0.0)
.expect("Failure in conversion to u8");
converted
}
async fn safe_run<'a, 'b, 'c, 'd, 'e, 'f>(
runner: &'a mut HerculesRunner_edge_detection,
n: u64,
m: u64,
gs: u64,
sz: u64,
sb: u64,
input: &'b HerculesImmBox<'b, f32>,
gaussian_filter: &'c HerculesImmBox<'c, f32>,
structure: &'d HerculesImmBox<'d, f32>,
sx: &'e HerculesImmBox<'e, f32>,
sy: &'f HerculesImmBox<'f, f32>,
theta: f32,
) -> HerculesMutBox<'a, f32> {
HerculesMutBox::from(
runner
.run(
n,
m,
gs,
sz,
sb,
input.to(),
gaussian_filter.to(),
structure.to(),
sx.to(),
sy.to(),
theta,
)
.await,
)
}
fn edge_detection_harness(args: EdgeDetectionInputs) {
let EdgeDetectionInputs {
input,
display,
output,
verify,
display_verify,
output_verify,
frames,
} = args;
let gs: usize = 7;
let gaussian_filter: Vec<f32> = vec![
0.000036, 0.000363, 0.001446, 0.002291, 0.001446, 0.000363, 0.000036, 0.000363, 0.003676,
0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.001446, 0.014662, 0.058488, 0.092651,
0.058488, 0.014662, 0.001446, 0.002291, 0.023226, 0.092651, 0.146768, 0.092651, 0.023226,
0.002291, 0.001446, 0.014662, 0.058488, 0.092651, 0.058488, 0.014662, 0.001446, 0.000363,
0.003676, 0.014662, 0.023226, 0.014662, 0.003676, 0.000363, 0.000036, 0.000363, 0.001446,
0.002291, 0.001446, 0.000363, 0.000036,
];
let gaussian_filter_h = HerculesImmBox::from(gaussian_filter.as_slice());
let sz: usize = 3;
let structure: Vec<f32> = vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let structure_h = HerculesImmBox::from(structure.as_slice());
let sb: usize = 3;
let sx: Vec<f32> = vec![-1.0, 0.0, 1.0, -2.0, 0.0, 2.0, -1.0, 0.0, 1.0];
let sx_h = HerculesImmBox::from(sx.as_slice());
let sy: Vec<f32> = vec![-1.0, -2.0, -1.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0];
let sy_h = HerculesImmBox::from(sy.as_slice());
let theta: f32 = 0.1;
let mut video = VideoCapture::from_file_def(&input).expect("Error loading video");
assert!(video.is_opened().unwrap());
let fps = video
.get(VideoCaptureProperties::CAP_PROP_FPS.into())
.expect("Error getting fps");
let num_frames = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_COUNT.into())
.expect("Error getting number of frames") as usize;
let width = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_WIDTH.into())
.expect("Error getting width") as usize;
let height = video
.get(VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT.into())
.expect("Error getting height") as usize;
let num_frames = if let Some(frames) = frames {
usize::min(frames, num_frames)
} else {
num_frames
};
let mut r = runner!(edge_detection);
let mut output = output.map(|filename| {
VideoWriter::new(
&filename,
VideoWriter::fourcc('m', 'p', '4', 'v').unwrap(),
fps,
Size {
width: width as i32,
height: height as i32,
},
false,
)
.expect("Error opening output video")
});
let mut output_verify = output_verify.map(|filename| {
VideoWriter::new(
&filename,
VideoWriter::fourcc('m', 'p', '4', 'v').unwrap(),
fps,
Size {
width: width as i32,
height: height as i32,
},
false,
)
.expect("Error opening output video")
});
for i in 0..num_frames {
let frame = load_frame(&mut video);
let ptr = frame.ptr_def().unwrap() as *const f32;
assert!(frame.rows() as usize == height);
assert!(frame.cols() as usize == width);
let input = unsafe { from_raw_parts(ptr, height * width) };
let input_h = HerculesImmBox::from(input);
let result = async_std::task::block_on(async {
safe_run(
&mut r,
height as u64,
width as u64,
gs as u64,
sz as u64,
sb as u64,
&input_h,
&gaussian_filter_h,
&structure_h,
&sx_h,
&sy_h,
theta,
)
.await
})
.as_slice()
.to_vec();
if display {
let result = frame_from_slice(&result, height, width);
let () = imshow("Juno", &result).expect("Failure in displaying image");
}
if let Some(ref mut output) = output {
let result = frame_from_slice(&result, height, width);
let () = output.write(&result).expect("Failure in writing frame");
}
if verify {
let rust_result = edge_detection_rust::edge_detection(
height,
width,
gs,
sz,
sb,
input,
&gaussian_filter,
&structure,
&sx,
&sy,
theta,
);
assert_eq!(result, rust_result);
println!("Frames {} match", i);
if display_verify {
let rust_result = frame_from_slice(&rust_result, height, width);
let () = imshow("Rust", &rust_result).expect("Failure in displaying image");
}
if let Some(ref mut output) = output_verify {
let result = frame_from_slice(&rust_result, height, width);
let () = output.write(&result).expect("Failure in writing frame");
}
}
if display || (verify && display_verify) {
let _ = wait_key(0);
}
}
if let Some(mut output) = output {
let () = output.release().expect("Failure releasing output video");
}
if let Some(mut output) = output_verify {
let () = output.release().expect("Failure releasing output video");
}
}
fn main() { fn main() {
let args = EdgeDetectionInputs::parse(); let args = EdgeDetectionInputs::parse();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment