Skip to content

Commit

Permalink
Adds canny edge detection (#87)
Browse files Browse the repository at this point in the history
* Adds canny edge detection

* Addresses PR feedback

- Adds an EmptyResult type and corresponding CEmptyResult
- Adds error case for edge detection test

* Fixes clang-format lint
  • Loading branch information
joelgallant authored and Pzixel committed Feb 8, 2019
1 parent e88ae6e commit b602103
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 0 deletions.
20 changes: 20 additions & 0 deletions native/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,26 @@ struct Result {
}
};

// Caller is responsible for disposing `error` field
struct EmptyResult {
CDisposableString error;

static EmptyResult FromFunction(std::function<void()> function) {
char* error = nullptr;

try {
function();
} catch (cv::Exception& e) {
const char* err_msg = e.what();
auto len = std::strlen(err_msg);
error = new char[len + 1];
std::strcpy(error, err_msg);
}

return EmptyResult{CDisposableString{error}};
}
};

template <typename T>
struct CVec {
T* array;
Expand Down
7 changes: 7 additions & 0 deletions native/imgproc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,11 @@ void cv_compare_hist(cv::Mat* first_image, cv::Mat* second_image, int method, Re
*result = Result<double>::FromFunction(
[first_image, second_image, method]() { return cv::compareHist(*first_image, *second_image, method); });
}

EmptyResult
cv_canny(cv::Mat* image, cv::Mat* edges, double threshold1, double threshold2, int aperture_size, bool l2_gradient) {
return EmptyResult::FromFunction([image, edges, threshold1, threshold2, aperture_size, l2_gradient]() {
cv::Canny(*image, *edges, threshold1, threshold2, aperture_size, l2_gradient);
});
}
}
2 changes: 2 additions & 0 deletions native/imgproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ void cv_calc_back_project(const cv::Mat* images,
cv::Mat* back_project,
const float** ranges);
void cv_compare_hist(cv::Mat* first_image, cv::Mat* second_image, int method, Result<double>* result);
EmptyResult
cv_canny(cv::Mat* image, cv::Mat* edges, double threshold1, double threshold2, int aperture_size, bool l2_gradient);
}

#endif // CV_RS_IMGPROC_H
37 changes: 37 additions & 0 deletions src/imgproc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ extern "C" {
method: HistogramComparisionMethod,
result: *mut CResult<c_double>,
);

fn cv_canny(
image: *const CMat,
edges: *mut CMat,
threshold1: c_double,
threshold2: c_double,
aperture_size: c_int,
l2_gradient: c_int,
) -> CEmptyResult;
}

/// Possible methods for histogram comparision method
Expand Down Expand Up @@ -548,6 +557,34 @@ impl Mat {
result.into()
}

/// Performs canny edge detection
pub fn canny(
&self,
threshold1: f64,
threshold2: f64,
aperture_size: i32,
l2_gradient: bool,
) -> Result<Mat, String> {
let edges = Mat::new();
let result = unsafe {
cv_canny(
self.inner,
edges.inner,
threshold1,
threshold2,
aperture_size,
match l2_gradient {
true => 1,
false => 0,
},
)
};

let result: Result<(), String> = result.into();

result.map(|_| edges)
}

fn matrix_to_vec<T, MElem: AsRef<[T]>, M: AsRef<[MElem]>>(value: M) -> Vec<*const T> {
value.as_ref().iter().map(|x| x.as_ref().as_ptr()).collect::<Vec<_>>()
}
Expand Down
19 changes: 19 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ struct CResult<T: Copy> {
error: CDisposableString,
}

#[repr(C)]
struct CEmptyResult {
error: CDisposableString,
}

impl<T: Copy> Into<Result<T, String>> for CResult<T> {
fn into(self) -> Result<T, String> {
if self.error.value.is_null() {
Expand All @@ -80,6 +85,20 @@ impl<T: Copy> CResult<T> {
}
}

impl Into<Result<(), String>> for CEmptyResult {
fn into(self) -> Result<(), String> {
if self.error.value.is_null() {
Ok(())
} else {
unsafe {
let c_str = std::ffi::CStr::from_ptr(self.error.value);
let err = c_str.to_string_lossy().into_owned();
Err(err)
}
}
}
}

#[repr(C)]
struct CDisposableString {
value: *mut c_char,
Expand Down
11 changes: 11 additions & 0 deletions tests/test_imgproc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,14 @@ fn get_image_histogram(path: &'static str) -> Mat {
let image = image.normalize(0.0, 1.0, NormType::MinMax);
image
}

#[test]
fn canny_edge_detection() {
let lenna = utils::load_lenna();
let edges = lenna.canny(70.0, 140.0, 3, false);
assert!(edges.is_ok());

let bad_edges = lenna.canny(70.0, 140.0, 1, false);
// error: (-206:Bad flag (parameter or structure field)) Aperture size should be odd between 3 and 7 in function \'Canny\'
assert!(bad_edges.is_err());
}

0 comments on commit b602103

Please sign in to comment.