diff --git a/Cargo.lock b/Cargo.lock index ae0b043..6b12e8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "async-channel" version = "1.8.0" @@ -234,6 +243,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clang" version = "2.0.0" @@ -283,6 +307,12 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + [[package]] name = "cpufeatures" version = "0.2.7" @@ -527,7 +557,7 @@ checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -563,6 +593,29 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "instant" version = "0.1.12" @@ -731,6 +784,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -961,6 +1024,7 @@ name = "rust_ocr" version = "0.1.0" dependencies = [ "async-std", + "chrono", "config", "derivative", "fern", @@ -1136,6 +1200,17 @@ dependencies = [ "syn 2.0.16", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "toml" version = "0.5.11" @@ -1191,6 +1266,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1306,6 +1387,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 97451e6..53318c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] async-std = "1.12.0" +chrono = "0.4.24" config = "0.13.3" derivative = "2.2.0" fern = "0.6.2" diff --git a/src/image_facade.rs b/src/image_facade.rs index f381518..9192559 100644 --- a/src/image_facade.rs +++ b/src/image_facade.rs @@ -1,47 +1,53 @@ +use std::sync::Arc; use std::{sync::Mutex, thread, collections::VecDeque}; use config::*; -#[allow(unused_imports)] +use opencv::imgcodecs::{IMWRITE_PNG_BILEVEL, IMREAD_UNCHANGED}; use opencv::{videoio::{VideoCapture, VideoCaptureTrait, CAP_PROP_FOURCC, CAP_ANY, VideoWriter, CAP_PROP_FRAME_WIDTH, CAP_PROP_FRAME_HEIGHT}, imgproc::{THRESH_BINARY, COLOR_BGR2GRAY,threshold,cvt_color}, - core::{bitwise_and,Mat}, + core::{bitwise_and,Rect_,Mat,MatTraitConst,no_array,Vector}, highgui::{select_roi,imshow}, - imgcodecs::{imread,imwrite,imdecode}}; -#[allow(unused_imports)] -use leptess::{leptonica::{pix_read,Pix}, - tesseract::TessApi}; + imgcodecs::{imwrite,imread}}; +use leptess::LepTess; const FRAME_WIDTH:i32 = 800; const FRAME_HEIGHT:i32 = 600; -const MJPG_FOURCC_CODE:i32 = VideoWriter::fourcc('m','j','p','g').unwrap(); +const CROP_X:&str = "crop x"; +const CROP_Y:&str = "crop y"; +const CROP_WIDTH:&str = "crop width"; +const CROP_HEIGHT:&str = "crop height"; +const THRESHOLD:&str = "threshold value"; +const COMPOSITE_FRAMES:u16 = 5; pub struct Camera{ settings: Config, camera: FrameGrabber, name: String, - ocr: TessApi, + ocr: LepTess, + active: bool } struct FrameGrabber{ - camera: VideoCapture, - image_queue: Mutex>, + image_queue: Arc>>, } impl FrameGrabber{ - fn new(camera:VideoCapture, composite_frames:i64) -> Self{ - let mut output = Self{ - camera, - image_queue: Mutex::new(VecDeque::with_capacity(composite_frames as usize)) + fn new(mut camera:VideoCapture, composite_frames:u16) -> Self{ + let output = Self{ + image_queue: Arc::new(Mutex::new(VecDeque::with_capacity(composite_frames as usize))) }; + let image_queue = output.image_queue.clone(); thread::spawn(move||{ loop { let mut image = Mat::default(); - let result = output.camera.read(&mut image); + let result = &camera.read(&mut image); match result{ Ok(true) => { - let mut image_queue = *output.image_queue.lock().unwrap(); + let mut converted_image = Mat::default(); + _ = cvt_color(&mut image, &mut converted_image, COLOR_BGR2GRAY, 0); + let mut image_queue = image_queue.lock().unwrap(); if image_queue.len() == image_queue.capacity(){ image_queue.pop_back(); } @@ -54,60 +60,130 @@ impl FrameGrabber{ return output; } - fn grab(&self, mut image:&Mat){ - let mut image_queue = *self.image_queue.lock().unwrap(); - image = &mut image_queue.pop_front().unwrap(); + fn grab(&self) -> Mat{ + let mut image_queue = self.image_queue.lock().unwrap(); + image_queue.pop_front().unwrap() } fn burst(&self) -> Vec{ - let mut image_queue = *self.image_queue.lock().unwrap(); + let image_queue = self.image_queue.lock().unwrap(); return image_queue.clone().into(); } + + fn change_composite_frames(&self, composite_frames:u16){ + let mut image_queue = self.image_queue.lock().unwrap(); + image_queue.resize(composite_frames as usize, Default::default()); + } } impl Camera{ pub fn new(camera_name:String) -> Option{ - let defaults = Config::builder(); - defaults.set_default("crop x", 275); - defaults.set_default("crop y", 200); - defaults.set_default("crop width", 80); - defaults.set_default("crop height", 50); - defaults.set_default("threshold value", 50); - defaults.set_default("composite frames", 5); - defaults.set_default("active", true); + let mut defaults = Config::builder(); + defaults = defaults.set_default(CROP_X, 275).unwrap(); + defaults = defaults.set_default(CROP_Y, 200).unwrap(); + defaults = defaults.set_default(CROP_WIDTH, 80).unwrap(); + defaults = defaults.set_default(CROP_HEIGHT, 50).unwrap(); + defaults = defaults.set_default(THRESHOLD, 50).unwrap(); let default = defaults.build().unwrap(); let settings = Config::builder().add_source(default).build().unwrap(); let mut camera = VideoCapture::from_file(&camera_name, CAP_ANY).unwrap(); - camera.set(CAP_PROP_FOURCC, MJPG_FOURCC_CODE as f64); - camera.set(CAP_PROP_FRAME_WIDTH, FRAME_WIDTH as f64); - camera.set(CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT as f64); - if let result = camera.open_file(&camera_name, CAP_ANY){ - match result { - Ok(_) => {}, - Err(error) => return None - } + _ = camera.set(CAP_PROP_FOURCC, VideoWriter::fourcc('m','j','p','g').unwrap() as f64); + _ = camera.set(CAP_PROP_FRAME_WIDTH, FRAME_WIDTH as f64); + _ = camera.set(CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT as f64); + match camera.open_file(&camera_name, CAP_ANY) { + Ok(_) => {}, + Err(_) => return None }; - let frame_grabber = FrameGrabber::new(camera, settings.get_int("composite frames").unwrap_or(1)); + let frame_grabber = FrameGrabber::new(camera, COMPOSITE_FRAMES); let name = camera_name.split('-').last().unwrap().to_string(); - let ocr = TessApi::new(Some("tessdata"), "Pro6_temp_test").unwrap(); + let ocr = LepTess::new(Some("tessdata"), "Pro6_temp_test").unwrap(); Some(Self{ settings, camera: frame_grabber, name, ocr, + active: true, }) } - fn take_picture(&mut self) -> Mat{ - let mut image = Mat::default(); - self.camera.grab(&mut image); - let mut output = Mat::default(); - cvt_color(&mut image, &mut output, COLOR_BGR2GRAY, 0); - return output; + pub fn show_image(&self){ + _ = imshow("Test image", &imread(&self.complete_process(),IMREAD_UNCHANGED).unwrap()); + } + + fn complete_process(&self) -> String{ + let images = self.camera.burst(); + let mut final_image = images[0].clone(); + for mut image in images{ + image = self.crop(image); + image = self.threshold(image); + _ = bitwise_and(&final_image.clone(), &image, &mut final_image, &no_array()); + } + return self.save(final_image).unwrap_or("".to_string()); + } + + fn crop(&self,image:Mat) -> Mat{ + let x = self.settings.get_int(&CROP_X).unwrap(); + let y = self.settings.get_int(&CROP_Y).unwrap(); + let width = self.settings.get_int(&CROP_WIDTH).unwrap(); + let height = self.settings.get_int(&CROP_HEIGHT).unwrap(); + let roi = Rect_::new(x as i32, y as i32, width as i32, height as i32); + return image.apply_1(roi).unwrap(); + } + + fn threshold(&self,image:Mat) -> Mat{ + let mut output = image.clone(); + let threshold_value = self.settings.get_int(&THRESHOLD).unwrap(); + _ = threshold(&image,&mut output, threshold_value as f64, 255 as f64 ,THRESH_BINARY); + return Mat::default(); + } + + fn save(&self,image:Mat) -> Result{ + let mut write_parameters:Vector = Vector::new(); + write_parameters.push(IMWRITE_PNG_BILEVEL); + let mut filename:String = String::new(); + filename.push_str(&chrono::Local::now().to_rfc3339().to_string()); + filename.push_str(&self.name); + match imwrite(&filename, &image, &write_parameters){ + Ok(_) => Ok(filename), + Err(error) => Err(error) + } + } + + pub fn set_crop(&mut self) -> Result<(),opencv::Error>{ + match select_roi(&self.camera.grab(), false, false){ + Err(error) => { return Err(error); } + Ok(rect) => { + let mut new_settings = Config::builder().add_source(self.settings.clone()); + new_settings = new_settings.set_override(CROP_X, rect.x).unwrap(); + new_settings = new_settings.set_override(CROP_Y, rect.y).unwrap(); + new_settings = new_settings.set_override(CROP_WIDTH, rect.width).unwrap(); + new_settings = new_settings.set_override(CROP_HEIGHT, rect.height).unwrap(); + self.settings = new_settings.build().unwrap(); + Ok(()) + } + } + } + + pub fn set_threshold(&mut self, thresh:u16){ + let new_settings = Config::builder().add_source(self.settings.clone()).set_override(THRESHOLD, thresh); + self.settings = new_settings.expect("Bad config settings!").build().unwrap(); + } + + pub fn set_composite_frames(&self, composite_frames:u16){ + self.camera.change_composite_frames(composite_frames); + } + + pub fn deactivate(&mut self){ self.active = false; } + pub fn activate(&mut self) { self.active = true; } + pub fn is_active(&self) -> bool { self.active } + + pub fn parse_image(&mut self, file_location:String) -> f64{ + _ = self.ocr.set_image(file_location); + return str::parse(&self.ocr.get_utf8_text().unwrap()).unwrap() } } diff --git a/src/lib.rs b/src/lib.rs index 62a192f..f82c2de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -pub mod config_facade; pub mod gpio_facade; pub mod image_facade; pub mod output_facade;