Objective my code is:
Displaying the cam output that is read through OpenCV at web browser <img>
tag.
Threads in process:
- Server backed by rocker.rs
- WebView backed by web-view.rs
- OpenCV backed by opencv-rust
I think each once of the above required to be run in a separate thread to get things done smoothly, I wrote the below code:
use web_view::*;
use std::thread;
use opencv::{core, highgui, prelude::*, videoio, core::Size2i, imgcodecs};
use rocket_contrib::serve::StaticFiles;
fn main() {
thread::spawn(move || {
rocket::ignite()
.mount("/static",
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static")))
.launch();
});
let webview:WebView<()> = web_view::builder()
.title("My Project")
.content(Content::Url("http://localhost:8000/static/"))
.size(800, 600)
.resizable(false)
.debug(true)
.user_data(())
.invoke_handler(invoke_handler)
.build()
.unwrap();
webview.run().unwrap();
}
fn invoke_handler(wv: &mut WebView<()>, arg: &str) -> WVResult {
match arg {
"Run openCV" => openCV(wv),
_ => (),
}
Ok(())
}
fn openCV(wv: &mut WebView<()>){
run(wv).unwrap();
}
fn refresh(wv: &mut WebView<()>){
wv.eval(&format!(r#"
var dt = new Date();
img = document.getElementById('img');
img.src="c:/photos/savedImage.jpg"+ "?" + dt.getTime();
"#)).unwrap();
}
fn run(wv: &mut WebView<()>) -> opencv::Result<()> {
let window = "video capture";
let filename = "static/savedImage.jpg";
highgui::named_window(window, 1)?;
highgui::set_window_property(window, highgui::WND_PROP_FULLSCREEN, highgui::WINDOW_NORMAL as f64)?;
#[cfg(feature = "opencv-32")]
let mut cam = videoio::VideoCapture::new_default(0)?; // 0 is the default camera
#[cfg(not(feature = "opencv-32"))]
let mut cam = videoio::VideoCapture::new(0, videoio::CAP_ANY)?; // 0 is the default camera
let opened = videoio::VideoCapture::is_opened(&cam)?;
if !opened {
panic!("Unable to open default camera!");
}
loop {
let mut frame = core::Mat::default()?;
cam.read(&mut frame)?;
if frame.size()?.width > 0 {
highgui::imshow(window, &mut frame)?;
imgcodecs::imwrite(filename, &frame, &opencv::types::VectorOfi32::new());
// refresh(wv); // This one is not working
thread::spawn(move || {
refresh(wv);
});
}
let key = highgui::wait_key(10)?;
if key > 0 && key != 255 {
break;
}
}
highgui::destroy_all_windows();
Ok(())
}
With the below Cargo.toml
[package]
name = "wv"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"
[dependencies]
opencv = {version = "0.34", features = ["buildtime-bindgen"]}
web-view = "0.6.2"
rocket = "0.4.4"
rocket_contrib = "0.4.4"
And below static/index.html
:
<!doctype html>
<html>
<body>
<button onclick="external.invoke('Run openCV')">OpenCV</button><br><br><br>
<img id='img' src='/static/savedImage.jpg' alt='Lamp' width='320' height='320'/>
</body>
</html>
With the above, server, WebView and OpenCV are working fine, but the refresh
command is not working, and once I tried to get it in a separate thread as below, I got error:
Note: The refresh function is a rust
function calling the WebView
to execute JavaScript
function that refresh the saved image.
thread::spawn(move || {
refresh(wv);
})
The error I got is:
error[E0277]: `*mut webview_sys::CWebView` cannot be sent between threads safely
--> src\main.rs:71:14
|
71 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `*mut webview_sys::CWebView` cannot be sent between threads safely
|
::: C:\Users\Yara Yousef\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libstd\thread\mod.rs:616:8
|
616 | F: Send + 'static,
| ---- required by this bound in `std::thread::spawn`
|
= help: within `web_view::WebView<'_, ()>`, the trait `std::marker::Send` is not implemented for `*mut webview_sys::CWebView`
= note: required because it appears within the type `web_view::WebView<'_, ()>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut web_view::WebView<'_, ()>`
= note: required because it appears within the type `[closure@src\main.rs:71:28: 73:15 wv:&mut web_view::WebView<'_, ()>]`