diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index df33d85..c7a7aee 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest permissions: write-all steps: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index dfec8af..e125366 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -8,7 +8,7 @@ on: jobs: dependencies: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Install prerequisites run: | diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 9e3f7c2..d205536 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -7,15 +7,12 @@ on: jobs: dependencies: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - name: Install prerequisites run: | sudo apt update - sudo apt install -y build-essential curl \ - cmake clang libclang-dev llvm llvm-dev \ - qt5-default libopencv-dev \ - python3-dev python3-numpy python3-opencv + sudo apt install -y libopencv-dev - uses: actions/checkout@v3 - name: Build project @@ -23,6 +20,3 @@ jobs: - name: Run tests run: cargo test --lib --bins --tests --jobs $(nproc) - -# - name: Build docs -# run: cargo doc --lib --verbose --jobs $(nproc) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 76a7176..7ab55ca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: create-release: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest permissions: write-all outputs: @@ -37,7 +37,7 @@ jobs: build-linux: name: Build Linux version - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: create-release permissions: contents: write diff --git a/Cargo.toml b/Cargo.toml index 0857da5..251d601 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ndarray = "^0.15" -opencv = "^0.85" -thiserror = "^1.0" +ndarray = "0.16.1" +opencv = "0.93.4" +thiserror = "2.0.3" [build-dependencies] -cbindgen = "^0.24" +cbindgen = "0.27.0" [[example]] name = "file_example" diff --git a/Dockerfile b/Dockerfile index a928b09..786694d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,6 @@ RUN apt-get update && apt-get install -y build-essential git curl cmake clang li libcanberra-gtk3-module libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev libopencv-dev RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ - sh -s -- --default-toolchain 1.71.0 -y && \ ln -s $HOME/.cargo/bin/* /usr/bin/ RUN rustup default nightly-unknown-linux-gnu diff --git a/VERSION b/VERSION index 9084fa2..781dcb0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.1.3 diff --git a/examples/compute_statistic.rs b/examples/compute_statistic.rs index b208920..1f2f6f5 100644 --- a/examples/compute_statistic.rs +++ b/examples/compute_statistic.rs @@ -1,4 +1,5 @@ extern crate cvlcore; + use cvlcore::api::capture::*; use cvlcore::api::chain::*; use cvlcore::errors::CaptureResult; @@ -24,7 +25,7 @@ fn main() -> CaptureResult { fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { let mut own_chain = ChainProcessing::default(); while let Ok(frame) = vcap.read_frame() { - let precessing_result = own_chain + let processing = own_chain .run_chain(frame) .grayscale() .canny() @@ -33,18 +34,18 @@ fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { .vibrating() .statistic(); - let dispersion = &precessing_result.get_dispersion(); - if dispersion.is_some() { - println!("{:?}", dispersion.unwrap()); - } + if let Some(dispersion) = processing.get_dispersion() { + println!("dispersion: {dispersion:?}"); + }; - let chain_result = precessing_result.get_result(); - if chain_result.is_err() { - println!("{}", chain_result.err().unwrap()); - continue; - } + let cvl_mat = match processing.get_result() { + Ok(mat) => mat, + Err(err) => { + println!("failed while processing frame: {err:#?}"); + continue; + } + }; - let cvl_mat = chain_result.unwrap(); window.show_frame(&cvl_mat); match window.wait_event() { WindowSignals::KeepProcessing => {} diff --git a/examples/file_example.rs b/examples/file_example.rs index 33d5fee..6ab9769 100644 --- a/examples/file_example.rs +++ b/examples/file_example.rs @@ -1,4 +1,5 @@ extern crate cvlcore; + use cvlcore::api::capture::*; use cvlcore::api::chain::*; use cvlcore::errors::*; @@ -24,7 +25,7 @@ fn main() -> CaptureResult { fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { let mut own_chain = ChainProcessing::default(); while let Ok(frame) = vcap.read_frame() { - let precessing_result = own_chain + let processing = own_chain .run_chain(frame) .grayscale() .canny() @@ -32,13 +33,14 @@ fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { .reduce_abs() .vibrating(); - let chain_result = precessing_result.get_result(); - if chain_result.is_err() { - println!("{}", chain_result.err().unwrap()); - continue; - } + let cvl_mat = match processing.get_result() { + Ok(mat) => mat, + Err(err) => { + println!("failed while processing frame: {err:#?}"); + continue; + } + }; - let cvl_mat = chain_result.unwrap(); window.show_frame(&cvl_mat); match window.wait_event() { WindowSignals::KeepProcessing => {} diff --git a/examples/rtsp_example.rs b/examples/rtsp_example.rs index 5db9f17..b05187a 100644 --- a/examples/rtsp_example.rs +++ b/examples/rtsp_example.rs @@ -1,4 +1,5 @@ extern crate cvlcore; + use cvlcore::api::capture::*; use cvlcore::api::chain::*; use cvlcore::errors::*; @@ -24,7 +25,7 @@ fn main() -> CaptureResult { fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { let mut own_chain = ChainProcessing::default(); while let Ok(frame) = vcap.read_frame() { - let precessing_result = own_chain + let processing = own_chain .run_chain(frame) .grayscale() .canny() @@ -32,13 +33,14 @@ fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { .reduce_abs() .vibrating(); - let chain_result = precessing_result.get_result(); - if chain_result.is_err() { - println!("{}", chain_result.err().unwrap()); - continue; - } + let cvl_mat = match processing.get_result() { + Ok(mat) => mat, + Err(err) => { + println!("failed while processing frame: {err:#?}"); + continue; + } + }; - let cvl_mat = chain_result.unwrap(); window.show_frame(&cvl_mat); match window.wait_event() { WindowSignals::KeepProcessing => {} diff --git a/examples/web_cam_example.rs b/examples/web_cam_example.rs index 4598862..67e3e7f 100644 --- a/examples/web_cam_example.rs +++ b/examples/web_cam_example.rs @@ -1,4 +1,5 @@ extern crate cvlcore; + use cvlcore::api::capture::*; use cvlcore::api::chain::*; use cvlcore::errors::*; @@ -20,7 +21,7 @@ fn main() -> CaptureResult { fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { let mut own_chain = ChainProcessing::default(); while let Ok(frame) = vcap.read_frame() { - let precessing_result = own_chain + let processing = own_chain .run_chain(frame) .grayscale() .canny() @@ -28,13 +29,14 @@ fn processing_stream(vcap: &mut CvlCapture, window: &MainWindow) { .reduce_abs() .vibrating(); - let chain_result = precessing_result.get_result(); - if chain_result.is_err() { - println!("{}", chain_result.err().unwrap()); - continue; - } + let cvl_mat = match processing.get_result() { + Ok(mat) => mat, + Err(err) => { + println!("failed while processing frame: {err:#?}"); + continue; + } + }; - let cvl_mat = chain_result.unwrap(); window.show_frame(&cvl_mat); match window.wait_event() { WindowSignals::KeepProcessing => {} diff --git a/src/api/chain.rs b/src/api/chain.rs index ccde3a8..e096a83 100644 --- a/src/api/chain.rs +++ b/src/api/chain.rs @@ -18,7 +18,7 @@ pub struct ProcessingSettings { impl Default for ProcessingSettings { fn default() -> Self { ProcessingSettings { - frames_count: 5, + frames_count: 15, neighbours: 8, window_size: 2, is_reduced_abs: true, diff --git a/src/lib.rs b/src/lib.rs index d5f7b71..c641f75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ use crate::errors::{ProcessingError, ProcessingResult}; use ndarray::{Array, Array1}; +use opencv::boxed_ref::BoxedRef; use opencv::core::{absdiff, cart_to_polar, count_non_zero, find_non_zero}; use opencv::core::{Mat, MatExprTraitConst, MatTrait, MatTraitConst, MatTraitConstManual}; use opencv::core::{Point, Rect, Scalar, Vector}; @@ -44,13 +45,11 @@ pub const ANY_2_DIM_IMAGE: i32 = 0; #[inline(always)] pub fn gen_grayscale_frame(frame: &CvlMat) -> ProcessingResult { let mut gray_frame = Mat::default(); - match cvt_color(frame.frame(), &mut gray_frame, COLOR_BGR2GRAY, 0) { - Ok(_) => Ok(CvlMat::from(gray_frame)), - Err(_) => { - let msg = "Failed while trying to transform frame to grayscale."; - Err(ProcessingError::GenGrayScale(msg.to_string())) - } + if let Err(err) = cvt_color(frame.frame(), &mut gray_frame, COLOR_BGR2GRAY, 0) { + return Err(ProcessingError::GenGrayScale(err.message)); } + + Ok(CvlMat::from(gray_frame)) } /// This method returns threshold image from passed bgr-image by passed black/white bounds @@ -73,13 +72,11 @@ pub fn gen_grayscale_frame(frame: &CvlMat) -> ProcessingResult { #[inline(always)] pub fn gen_threshold_frame(frame: &CvlMat, thresh: f64, maxval: f64) -> ProcessingResult { let mut gray: Mat = Mat::default(); - match threshold(frame.frame(), &mut gray, thresh, maxval, THRESH_BINARY) { - Ok(_) => Ok(CvlMat::from(gray)), - Err(_) => { - let msg = "Failed while trying to transform frame to threshold."; - Err(ProcessingError::GenThreshold(msg.to_string())) - } + if let Err(err) = threshold(frame.frame(), &mut gray, thresh, maxval, THRESH_BINARY) { + return Err(ProcessingError::GenThreshold(err.message)); } + + Ok(CvlMat::from(gray)) } /// This method returns canny image from passed grayscale image by passed parameters. @@ -112,13 +109,11 @@ pub fn gen_canny_frame( is_l2: bool, ) -> ProcessingResult { let mut canny_frame = Mat::default(); - match canny(frame.frame(), &mut canny_frame, low, high, size, is_l2) { - Ok(_) => Ok(CvlMat::from(canny_frame)), - Err(_) => { - let msg = "Failed while trying to transform frame to canny."; - Err(ProcessingError::GenCanny(msg.to_string())) - } + if let Err(err) = canny(frame.frame(), &mut canny_frame, low, high, size, is_l2) { + return Err(ProcessingError::GenCanny(err.message)) } + + Ok(CvlMat::from(canny_frame)) } /// This method returns canny image from passed grayscale image by passed parameters. @@ -152,13 +147,11 @@ pub fn gen_canny_frame_by_sigma( let (low, high) = (1f64 - sigma + median, 1f64 + &sigma + median); let mut canny_frame = Mat::default(); - match canny(frame.deref(), &mut canny_frame, low, high, size, is_l2) { - Ok(_) => Ok(CvlMat::from(canny_frame)), - Err(_) => { - let msg = "Failed while trying to transform frame to canny."; - Err(ProcessingError::GenCanny(msg.to_string())) - } + if let Err(err) = canny(frame.deref(), &mut canny_frame, low, high, size, is_l2) { + return Err(ProcessingError::GenCanny(err.message)) } + + Ok(CvlMat::from(canny_frame)) } /// This method returns new Mat object with zeros by passed rows, columns and type parameters. @@ -189,7 +182,7 @@ fn create_zeros_mat(rows: i32, cols: i32, cv_type: i32) -> Option { /// ## Returns: /// Returns `Option` of executing [`Mat::roi`] method from opencv library. #[inline(always)] -fn create_roi_mat(frame: &Mat, row: i32, col: i32, window: i32) -> Option { +fn create_roi_mat(frame: &Mat, row: i32, col: i32, window: i32) -> Option> { let l_corn = Point::new(col - window, row - window); let r_corn = Point::new(col + window, row + window); let rect = Rect::from_points(l_corn, r_corn); @@ -293,13 +286,11 @@ pub fn gen_distribution_frame(image: &CvlMat, thresh: f64, maxval: f64) -> Proce #[inline(always)] fn gen_sobel_frame(frame: &Mat) -> ProcessingResult { let mut g_x = Mat::default(); - match sobel(frame, &mut g_x, CV_32F, 1, 0, 3, 1.0, 0f64, BORDER_DEFAULT) { - Ok(_) => Ok(CvlMat::new(g_x.to_owned())), - Err(_) => { - let msg = "Failed while trying to transform frame to sobel."; - Err(ProcessingError::GenSobel(msg.to_string())) - } + if let Err(err) = sobel(frame, &mut g_x, CV_32F, 1, 0, 3, 1.0, 0f64, BORDER_DEFAULT) { + return Err(ProcessingError::GenSobel(err.message)); } + + Ok(CvlMat::new(g_x.to_owned())) } /// There is wrapper method to invoke opencv::absdiff() method. @@ -317,13 +308,11 @@ fn gen_sobel_frame(frame: &Mat) -> ProcessingResult { #[inline] fn gen_diff_frame(img1: &Mat, img2: &Mat) -> ProcessingResult { let mut tmp = Mat::default(); - match absdiff(img1, img2, &mut tmp) { - Ok(_) => Ok(CvlMat::from(tmp)), - Err(_) => { - let msg = "Failed while trying to execute absdiff function."; - Err(ProcessingError::GenDifferences(msg.to_string())) - } + if let Err(err) = absdiff(img1, img2, &mut tmp) { + return Err(ProcessingError::GenDifferences(err.message)) } + + Ok(CvlMat::from(tmp)) } /// This recursive method returns result-image of opencv::absdiff() method by passed @@ -393,12 +382,13 @@ pub fn gen_abs_frame_reduce(frame_images: &[Rc]) -> ProcessingResult { let result = frame_images .iter() .cloned() - .reduce(|img1, img2| Rc::new(gen_diff_frame(img1.frame(), img2.frame()).unwrap())); + .reduce(|img1, img2| { + Rc::new(gen_diff_frame(img1.frame(), img2.frame()).unwrap()) + }); - match result { - None => Err(ProcessingError::GenAbs), - Some(frame) => Ok(frame.as_ref().to_owned()), - } + result + .map(|it| it.as_ref().to_owned()) + .ok_or(ProcessingError::GenAbs) } /// This method returns image with vibrating pixels (colored by bounds values) by passed image. @@ -429,27 +419,27 @@ pub fn compute_vibration( ) -> ProcessingResult { let frame_mat = image.frame(); let mut statistic = Statistic::default(); - let mut result_frame = create_zeros_mat(frame_mat.rows(), frame_mat.cols(), CV_64FC4).unwrap(); + let Some(mut result_frame) = create_zeros_mat(frame_mat.rows(), frame_mat.cols(), CV_64FC4) else { + let msg = "returned empty zeros mat".to_string(); + return Err(ProcessingError::ComputeVibration(msg)); + }; let mut non_zero_pixels = Vector::::new(); find_non_zero(frame_mat, &mut non_zero_pixels).unwrap(); - for non_zero_point in non_zero_pixels.to_vec() { + for non_zero_point in non_zero_pixels.into_iter() { let (row, col) = (non_zero_point.y, non_zero_point.x); if row == 0 || col == 0 { continue; } - let roi_mat = create_roi_mat(frame_mat, row, col, window_size); - if roi_mat.is_none() { + let Some(roi_mat) = create_roi_mat(frame_mat, row, col, window_size) else { continue; - } + }; - let roi_matrix = &roi_mat.unwrap(); - let non_zero_count = count_non_zero(roi_matrix).unwrap(); - if non_zero_count < neighbours { + let Ok(non_zero_count) = count_non_zero(&roi_mat) else { continue; - } + }; let colored_scalar = match non_zero_count { val if val >= color_bounds.get(4) => { @@ -471,10 +461,11 @@ pub fn compute_vibration( _ => Scalar::from(BLACK_COLOR), }; - result_frame - .at_2d_mut::(row, col) - .unwrap() - .copy_from_slice(colored_scalar.as_slice()); + let Ok(scalar) = result_frame.at_2d_mut::(row, col) else { + continue; + }; + + scalar.copy_from_slice(colored_scalar.as_slice()); } let mut cvlmat = CvlMat::from(result_frame);