mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-23 19:00:13 +02:00
camera.md
This commit is contained in:
parent
afbd29e814
commit
449ac4d302
@ -309,7 +309,8 @@
|
|||||||
- [Borrow Checker](webassembly/limitations/borrow-checker.md)
|
- [Borrow Checker](webassembly/limitations/borrow-checker.md)
|
||||||
- [Closures](webassembly/limitations/closures.md)
|
- [Closures](webassembly/limitations/closures.md)
|
||||||
- [Async](webassembly/async.md)
|
- [Async](webassembly/async.md)
|
||||||
|
- [Exercises](exercises/webassembly/webassembly.md)
|
||||||
|
- [Camera](exercises/webassembly/camera.md)
|
||||||
|
|
||||||
# Final Words
|
# Final Words
|
||||||
|
|
||||||
|
48
src/exercises/webassembly/camera.md
Normal file
48
src/exercises/webassembly/camera.md
Normal 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.
|
85
src/exercises/webassembly/solutions-camera.md
Normal file
85
src/exercises/webassembly/solutions-camera.md
Normal 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(())
|
||||||
|
}
|
||||||
|
```
|
1
src/exercises/webassembly/webassembly.md
Normal file
1
src/exercises/webassembly/webassembly.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Exercises
|
@ -25,6 +25,7 @@ features = [
|
|||||||
'CssStyleDeclaration',
|
'CssStyleDeclaration',
|
||||||
'Document',
|
'Document',
|
||||||
'Element',
|
'Element',
|
||||||
|
'HtmlSelectElement',
|
||||||
'HtmlElement',
|
'HtmlElement',
|
||||||
'Node',
|
'Node',
|
||||||
'Window',
|
'Window',
|
||||||
|
30
src/rust-wasm-template/static/exercises/camera/index.html
Normal file
30
src/rust-wasm-template/static/exercises/camera/index.html
Normal 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>
|
38
src/rust-wasm-template/static/exercises/camera/index.mjs
Normal file
38
src/rust-wasm-template/static/exercises/camera/index.mjs
Normal 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);
|
||||||
|
})();
|
Loading…
x
Reference in New Issue
Block a user