1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-23 10:50:18 +02:00

camera.md

This commit is contained in:
sakex 2023-08-03 03:42:40 +02:00
parent afbd29e814
commit 449ac4d302
7 changed files with 205 additions and 1 deletions

View File

@ -309,7 +309,8 @@
- [Borrow Checker](webassembly/limitations/borrow-checker.md)
- [Closures](webassembly/limitations/closures.md)
- [Async](webassembly/async.md)
- [Exercises](exercises/webassembly/webassembly.md)
- [Camera](exercises/webassembly/camera.md)
# Final Words

View File

@ -0,0 +1,48 @@
# Camera
In this exercise we will play with the camera in real time.
Serve the web server and navigate to [http://localhost:8000/exercises/camera/](http://localhost:8000/exercises/camera/).
You will edit [lib.rs](../../rust-wasm-template/lib.rs) as usual.
Here is the basic code you will need:
```rust
extern crate console_error_panic_hook;
use js_sys::Uint8ClampedArray;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
}
#[wasm_bindgen]
pub fn set_panic_hook() {
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
}
```
We want to edit the function `edit_bitmap`. `image_data` is a reference to the [Uint8ClampedArray](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Uint8ClampedArray.html) being rendered on your screen. `Uint8ClampedArray` is a flat array of unsigned int of `8` bits.
It represents `srgb` image, which means that each pixel is represented as a vector of 4 elements [red, green, blue, illumination].
The image can be thought of as a matrix of dimension `(width, height, 4)`, it is row-major.
We will reuse our different implementations. So do not erase them when going from one exercise to the next.
1. The first exercise is to turn the top half the image to black.
2. Turn the left half of the image to white.
3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance `0<->255`, `100<->155`. Beware of illumination.
4. You can play with the function, for instance you can implement greyscale, or add random noise.
5. In the `setup` function create a dropdown (`<select>`) that will change which transformation to apply to the image.
6. Blur only the background. This can be done by figuring out only the pixels that didn't change between multiple frames.
While this can be achieved without touching Javascript, I recommend editing it.

View File

@ -0,0 +1,85 @@
# Camera exercise solution
1. The first exercise is to turn the top half the image to black.
```rust
#[wasm_bindgen]
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
for i in 0..image_data.length() / 2 {
image_data.set_index(i, 0);
}
}
```
2. The second exercise is to turn the left half of the image to white.
```rust
#[wasm_bindgen]
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
for i in 0..height {
for j in 0..(width * 4) / 2 {
image_data.set_index(i * 4 * width + j, 255);
}
}
}
```
3. Create an X-ray effect. This is done by mapping colors to their opposite, for instance 0<->255, 100<->155. Beware of illumination.
```rust
#[wasm_bindgen]
pub fn edit_bitmap(image_data: &Uint8ClampedArray, width: u32, height: u32) {
for i in 0..image_data.length() {
if i % 4 == 3 {
continue;
}
let pixel = image_data.get_index(i);
image_data.set_index(i, 255 - pixel);
}
}
```
4. You can play with the function, for instance you can implement greyscale, or add random noise.
5. In the `setup` function create a dropdown (`<select>`) that will change which transformation to apply to the image.
```rust
static METHODS: [&str; 3] = ["xray", "top-black", "left-white"];
#[wasm_bindgen]
pub fn setup() -> Result<(), JsValue> {
console_error_panic_hook::set_once();
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let body = document.body().expect("document should have a body");
let select = document.create_element("select")?;
for option_value in METHODS {
let option = document.create_element("option")?;
option.set_inner_html(option_value);
option.set_attribute("value", option_value)?;
select.append_child(&option)?;
}
body.append_child(&select)?;
Ok(())
}
#[wasm_bindgen]
pub fn edit_bitmap(
image_data: &Uint8ClampedArray,
width: u32,
height: u32,
) -> Result<(), JsValue> {
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let select = document
.query_selector("select")?
.expect("No select")
.dyn_into::<web_sys::HtmlSelectElement>()
.expect("Failed to cast <select>");
let selected_option = select.value();
match selected_option.as_str() {
"xray" => xray(image_data, width, height),
"top-black" => top_black(image_data, width, height),
"left-white" => left_white(image_data, width, height),
_ => (),
};
Ok(())
}
```

View File

@ -0,0 +1 @@
# Exercises

View File

@ -25,6 +25,7 @@ features = [
'CssStyleDeclaration',
'Document',
'Element',
'HtmlSelectElement',
'HtmlElement',
'Node',
'Window',

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>comprehensive-rust camera exercise</title>
<link rel="stylesheet" href="/index.css">
<style>
video, canvas {
width: 400px;
height: 400px;
display: block;
background-color: black;
}
select {
position: fixed;
left: 50%;
bottom: 50%;
}
</style>
</head>
<body>
<h1>
comprehensive-rust camera exercise
</h1>
<video src=""></video>
<canvas></canvas>
<script type="module" src="/exercises/camera/index.mjs"></script>
</body>
</html>

View File

@ -0,0 +1,38 @@
import init, {setup, edit_bitmap} from '/wasm/project.js';
const renderCanvas = (ctx, video, bitmapConversionCanvas) => {
const bitmapconversionContext = bitmapConversionCanvas.getContext("2d");
requestAnimationFrame(async () => {
try {
bitmapconversionContext.drawImage(video, 0, 0);
const bitmap = bitmapconversionContext.getImageData(0, 0, 400, 400);
edit_bitmap(bitmap.data, 400, 400);
ctx.putImageData(bitmap, 0, 0);
} catch(e){
console.error(e);
} finally {
renderCanvas(ctx, video, bitmapConversionCanvas, bitmapconversionContext);
}
});
};
const initCamera = async (video, canvas, bitmapConversionCanvas) => {
const stream = await navigator.mediaDevices.getUserMedia({video: {width: 400, height: 400}});
video.srcObject = stream;
video.onloadedmetadata = () => {
video.play();
};
canvas.width = 400;
canvas.height = 400;
renderCanvas(canvas.getContext("2d"), video, bitmapConversionCanvas);
}
(async () => {
// Run the init method to initiate the WebAssembly module.
await init();
setup();
const video = document.querySelector("video");
const canvas = document.querySelector("canvas");
const bitmapConversionCanvas = new OffscreenCanvas(400, 400);
initCamera(video, canvas, bitmapConversionCanvas);
})();