Image Transforms

This Lambda converts a jpeg image to grayscale, using Rust code that is compiled to WASI-compliant WASM. It demonstrates the type of image transforms you may want to do at the source of the signal; for example, if your application involves a camera, you might want to convert to grayscale and downscale at the source of the signal, before passing the image along to downstream logic that performs object detection, collision detection, or some other kind of inference.

The source code can be found in the On Prem CLI repository at examples/lambda_grayscale.

How it works

In the On Prem platform, lambda events are JSON objects with a special data field reserved for binary content. Every layer of the platform transfers the data field separately from the rest of the event object's other fields, ensuring efficient transfer encoding that does not involve JSON parsing of any large content. Additionally, event objects are conveyed as gRPC structs, further ensuring binary transfer encoding.

When an On Prem edge lambda is written in WASI+WASM, the input event is pre-processed by the On Prem edge agent, and the data field is extracted and passed into the WASM module via stdin, while the other input event fields are passed in as program arguments. Then, the transformed grayscale image is emitted via stdout, and the On Prem agent conveys it to downstream chained lambdas, or a remote desktop in the case where the lambda was manually triggered via the On Prem CLI, which is how one-shot testing of high-frequency lambdas is typically done.

Assets

deployment.yaml
Cargo.toml
Makefile
src/
  main.rs

main.rs:

use image::ImageFormat;
use std::io::{self, Cursor, Read, Write};

fn main() -> anyhow::Result<()> {
    // Read
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf)?;

    // Convert
    let img = image::load_from_memory(&buf)?;
    let transformed = img.grayscale();

    // Serialize to a buffer
    let mut buf: Vec<u8> = Vec::new();
    transformed.write_to(&mut Cursor::new(&mut buf), ImageFormat::Jpeg)?;

    // Write
    io::stdout().write_all(&buf)?;
    io::stdout().flush()?;
    Ok(())
}

Cargo.toml:

[package]
name = "on-prem-example-lambda-grayscale"
description = "An On Prem edge lambda that converts a jpeg to grayscale using a WASI-compliant WASM module"
version = "0.1.0"
edition = "2024"

[[bin]]
name = "grayscale"
path = "src/main.rs"

[dependencies]
anyhow = "1.0"
image = { version = "0.25.9", default-features = false, features = ["jpeg"] }

Makefile:

.PHONY: build
build:
	rustup target add wasm32-wasip1
	cargo build --target wasm32-wasip1 --release


.PHONY: clean
clean:
	cargo clean

deployment.yaml:

id: d6hl1qhkun569e9bu6v0
kind: Lambda
name: lambda_grayscale
description: Convert a jpeg image to grayscale with a WASI-compliant WASM module.
runAt:
  device:
    deviceId: d5vmhgk3ofhmc59g2870
fileIds:
  - "@target/wasm32-wasip1/release/grayscale.wasm"
scriptContentType: None

Building and Testing

First build the WASM module:

$ make

Use a local jpeg file as a test input. Examine the input file:

$ open in.jpg

Input file

Now test the image transform locally, before moving it to your edge environment:

$ wasmer run target/wasm32-wasip1/release/grayscale.wasm < in.jpg > out.jpg

Examine the output file:

$ open out.jpg

Output file

Deploying

Customize the runAt field in your deployment to point to one of your device ids. These can be taken from the URL when using the cloud console.

$ vi deployment.yaml
runAt:
  device:
    deviceId: d5vmhgk3ofhmc59g2870

Next, upload it, along with the lambda definition, to the control plane:

$ onprem apply deployment.yaml
Uploaded grayscale.wasm (465.7K bytes)
File assets found: 1
Uploading import request...
Import complete
Operations performed:
┌────────────────────────┬─────────┬──────┬─────────┬─────────┬────────┐
│ Object                 ┆ Records ┆ Adds ┆ Updates ┆ Deletes ┆ Errors │
│ ---                    ┆ ---     ┆ ---  ┆ ---     ┆ ---     ┆ ---    │
│ str                    ┆ u32     ┆ u32  ┆ u32     ┆ u32     ┆ u32    │
╞════════════════════════╪═════════╪══════╪═════════╪═════════╪════════╡
│ ApiKeys                ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Controllers            ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ ControllerTemplates    ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Devices                ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Facilities             ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Lambdas                ┆ 1       ┆ 1    ┆ 0       ┆ 0       ┆ 0      │
│ LambdaTemplates        ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Roles                  ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Tags                   ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Teams                  ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
└────────────────────────┴─────────┴──────┴─────────┴─────────┴────────┘

If the agent is connected to the control plane, it will have downloaded its new config bundle containing the new lambda and associated WASM module file within a few seconds.

Run the Lambda

Now ask the control plane to invoke the lambda on the remote edge device.

$ onprem run lambda d6hl1qhkun569e9bu6v0 --event-data-from-file in.jpg --event-data-to-file out.jpg
Wrote event[data] to out.jpg (28.5K)
{}

©2026 Megalithic LLC | Website | GitLab | GitLab (Megalithic)