Image Processing Pipeline
Relevant source files
The following files were used as context for generating this wiki page:The hdrify image processing pipeline is a client-server system designed to convert Standard Dynamic Range (SDR) images into High Dynamic Range (HDR) images. The process is initiated by a user on a web interface, which communicates with a Rust backend. The backend orchestrates the core image transformation by executing a series of operations using the ImageMagick command-line tool.
The pipeline begins with the user uploading an image through the frontend. The frontend performs initial validation and sends the image data along with a user-defined HDR intensity value to the server. The server then performs its own validation, decodes the image, and uses a temporary file to run the ImageMagick process. The resulting HDR image is then encoded and sent back to the client for display and download.
Sources: README.md:1-4, src/main.rs:18-24, static/script.js:213-223
System Architecture
The application follows a simple client-server architecture. The frontend is built with vanilla HTML, CSS, and JavaScript, while the backend is a Rust application using the warp web framework. The core image processing is delegated to an external dependency, ImageMagick.
This diagram illustrates the high-level interaction between the user, the frontend, the backend, and ImageMagick.
Frontend Processing
The client-side logic is encapsulated within the HDRify class in script.js. It manages the UI state, user interactions, client-side validation, and communication with the backend.
Sources: static/script.js:1-7
User Interaction and State Management
The user initiates the process by either clicking the upload area or dragging and dropping a file onto it. The UI provides visual feedback for these actions, such as the dragover class being applied. Once an image is selected, the UI transitions from the upload prompt to an image display view with controls for HDR intensity, download, and reset.
- Upload: Handled by
handleFile, triggered by a file input change or drop event. - HDR Adjustment: The
hdrSliderinput triggers theprocessImagefunction after a 250ms debounce period. - Reset: The
resetButtonreverts the UI to its initial state. - Keyboard Support: Enter/Space on the upload area triggers file input, and Escape resets the view.
Sources: static/index.html:24-49, static/script.js:31-110, static/style.css:66-72
Client-Side Validation
Before sending data to the server, the frontend performs two key validation checks:
- File Type: Ensures the file is one of the supported formats:
image/jpeg,image/png, orimage/webp. - File Size: Checks if the file size is under the 10MB limit.
If validation fails, an error message is displayed to the user.
Sources: static/script.js:146-167, static/index.html:27
API Communication
The processImage function is responsible for communicating with the backend. It constructs a URLSearchParams payload containing the base64-encoded image data and the current HDR value. This payload is sent via a POST request to the /upload endpoint.
// static/script.js:214-223
const params = new URLSearchParams();
params.append('image', this.currentImageData);
params.append('hdr_value', hdrValue.toString());
const response = await fetch('/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params,
});
The frontend also implements a caching mechanism (this.imageCache) to avoid re-processing an image with the same HDR value.
Sources: static/script.js:192-251
Backend Processing
The backend is a warp server written in Rust. It exposes a single API endpoint, /upload, to handle all image processing requests.
Sources: src/main.rs:189-192, Cargo.toml:6-7
API Endpoint: POST /upload
This endpoint accepts application/x-www-form-urlencoded data and is handled by the handle_upload async function.
| Parameter | Type | Description |
|---|---|---|
image | String | Base64-encoded string of the source image. |
hdr_value | String | A floating-point number representing the HDR intensity. |
The function returns a JSON object of type ProcessResponse.
// src/main.rs:8-13
#[derive(Debug, Serialize)]
struct ProcessResponse {
success: bool,
message: String,
image_data: Option<String>, // base64 encoded HDR image
}
Sources: src/main.rs:8-13, 15-21, 191
Request Handling and Validation Flow
The handle_upload function executes a series of validation and processing steps.
This flowchart shows the logic within the handle_upload function.
Sources: src/main.rs:15-141
Image Format Validation
To ensure only supported image types are processed, the backend validates the file's "magic bytes" rather than trusting the file extension or MIME type.
The is_supported_image_format function checks for the following byte patterns at the beginning of the file:
- JPEG:
FF D8 FF - PNG:
89 50 4E 47 0D 0A 1A 0A - WebP:
RIFFat the start andWEBPat offset 8.
Sources: src/main.rs:143-172
ImageMagick Execution
The core of the HDR conversion is a call to the magick command-line tool. The backend constructs and executes this command using temporary files for input and output.
// src/main.rs:92-111
let output = Command::new("magick")
.arg(input_file.path())
.arg("-define")
.arg("quantum:format=floating-point")
.arg("-colorspace")
.arg("RGB")
.arg("-auto-gamma")
.arg("-evaluate")
.arg("Multiply")
.arg(hdr_value.to_string())
.arg("-evaluate")
.arg("Pow")
.arg("0.9")
.arg("-colorspace")
.arg("sRGB")
.arg("-depth")
.arg("16")
.arg("-profile")
.arg("2020_profile.icc")
.arg(output_file.path())
.output();
The arguments passed to ImageMagick perform the following steps, as documented in the README.md:
| Argument(s) | Purpose |
|---|---|
-define quantum:format=floating-point | Enables high-precision floating-point processing. |
-colorspace RGB | Converts the image to a linear RGB colorspace. |
-auto-gamma | Applies automatic gamma correction. |
-evaluate Multiply <hdr_value> | Multiplies pixel brightness by the user-defined value. |
-evaluate Pow 0.9 | Applies a power curve adjustment. |
-colorspace sRGB | Converts the image back to the sRGB colorspace. |
-depth 16 | Sets the color depth to 16 bits per channel. |
-profile 2020_profile.icc | Applies the Rec. 2020 HDR color profile. |
Sources: src/main.rs:92-111, README.md:31-37
Summary
The hdrify image processing pipeline is a well-defined, two-part system. The JavaScript frontend provides a responsive user interface, handles user input, and performs initial validation before sending a request to the server. The Rust backend acts as a secure and robust controller, performing its own validation and orchestrating the core image transformation by delegating to the powerful ImageMagick command-line tool. This separation of concerns creates an efficient workflow, leveraging the strengths of each technology: a dynamic frontend for user experience and a compiled, system-level backend for heavy processing.
Sources: README.md:40-42, src/main.rs, static/script.js