Skip to content

Commit

Permalink
examples: add perlin noise
Browse files Browse the repository at this point in the history
  • Loading branch information
arrufat committed Oct 22, 2024
1 parent 18ba479 commit a02f17c
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 13 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Zignal

<img src="https://github.com/bfactory-ai/zignal/blob/master/assets/liza.jpg" width=400>
<img src="https://github.com/bfactory-ai/zignal/blob/master/assets/liza.jpg" width=400>

Zignal is an image processing library heavily inspired by the amazing [dlib](http://dlib.net).

Expand Down Expand Up @@ -48,6 +48,7 @@ They can be accessed from [here](https://bfactory-ai.github.io/zignal/examples/)
Currently, there are examples for:
- [Color space conversions](https://bfactory-ai.github.io/zignal/examples/colorspace.html)
- [Face alignment](https://bfactory-ai.github.io/zignal/examples/face-alignment.html)
- [Perlin noise generation](https://bfactory-ai.github.io/zignal/examples/perlin.html)

## Acknowledgements

Expand Down
3 changes: 2 additions & 1 deletion examples/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
_ = buildModule(b, "face_alignment", target, optimize);
_ = buildModule(b, "colorspace", target, optimize);
_ = buildModule(b, "face_alignment", target, optimize);
_ = buildModule(b, "perlin", target, optimize);

const fmt_step = b.step("fmt", "Run zig fmt");
const fmt = b.addFmt(.{
Expand Down
1 change: 1 addition & 0 deletions examples/lib/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ <h2>Examples</h2>
<ul>
<li><a href="colorspace.html">Colorspace</a></li>
<li><a href="face-alignment.html">Face aligment</a></li>
<li><a href="perlin.html">Perlin noise</a></li>
</ul>
</main>
<script src="main.js"></script>
Expand Down
2 changes: 0 additions & 2 deletions examples/lib/main.js

This file was deleted.

44 changes: 44 additions & 0 deletions examples/lib/perlin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Perlin Noise</title>
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="data:image/png;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////////8AAD//AAAf/wAAD/8AAA4xB/4OMQf8DgEAAA8DAAAfAQAAGAAAABgAB/wMCAf8D4cAAB+HAAAfzwAAP/8AAH////////////////////////////////////////////8=">
</head>
<body>
<h1>Perlin Noise</h1>
<div id="image-container">
<fieldset class="settings-container">
<legend style="color:black;font-weight:bold;font-family:sans-serif;">Settings</legend>
<div class="amplitude-slider">
<label for="amplitude-range"><code>amplitude:&nbsp;&nbsp;</code></label>
<input type="range" min="0" max="10" value="1" step="0.1" class="slider" id="amplitude-range">
<label for="amplitude-range"><code><span id="amplitude"></span></code></label>
</div>
<div class="frequency-slider">
<label for="frequency-range"><code>frequency:&nbsp;&nbsp;</code></label>
<input type="range" min="0" max="100" value="1" step="0.1" class="slider" id="frequency-range">
<label for="frequency-range"><code><span id="frequency"></span></code></label>
</div>
<div class="octaves-slider">
<label for="octaves-range"><code>octaves:&nbsp;&nbsp;&nbsp;&nbsp;</code></label>
<input type="range" min="0" max="16" value="1" step="1" class="slider" id="octaves-range">
<label for="octaves-range"><code><span id="octaves"></span></code></label>
</div>
<div class="persistence-slider">
<label for="persistence-range"><code>persistence:</code></label>
<input type="range" min="0" max="1" value="0.5" step="0.1" class="slider" id="persistence-range">
<label for="persistence-range"><code><span id="persistence"></span></code></label>
</div>
<div class="lacunarity-slider">
<label for="lacunarity-range"><code>lacunarity:&nbsp;</code></label>
<input type="range" min="0" max="10" value="2" step="0.1" class="slider" id="lacunarity-range">
<label for="lacunarity-range"><code><span id="lacunarity"></span></code></label>
</div>
</fieldset>
<canvas id="canvas" width="512" height="512"></canvas>
<script src="perlin.js"></script>
</div>
</body>
</html>
79 changes: 79 additions & 0 deletions examples/lib/perlin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
(function() {
let wasm_promise = fetch("perlin.wasm");
var wasm_exports = null;
const text_decoder = new TextDecoder();

function decodeString(ptr, len) {
if (len === 0) return "";
return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
}

WebAssembly.instantiateStreaming(wasm_promise, {
js: {
log: function(ptr, len) {
const msg = decodeString(ptr, len);
console.log(msg);
},
now: function() {
return performance.now();
},
},
}).then(function(obj) {
wasm_exports = obj.instance.exports;
window.wasm = obj;
console.log("wasm loaded");
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rows = 512;
const cols = 512;
const rgbaSize = rows * cols * 4;

const amplitudeRange = document.getElementById("amplitude-range");
amplitudeRange.oninput = function() {
document.getElementById("amplitude").innerHTML = amplitudeRange.value;
generateNoise();
}
const frequencyRange = document.getElementById("frequency-range");
frequencyRange.oninput = function() {
document.getElementById("frequency").innerHTML = frequencyRange.value;
generateNoise();
}
const octavesRange = document.getElementById("octaves-range");
octavesRange.oninput = function() {
document.getElementById("octaves").innerHTML = octavesRange.value;
generateNoise();
}
const persistenceRange = document.getElementById("persistence-range");
persistenceRange.oninput = function() {
document.getElementById("persistence").innerHTML = persistenceRange.value;
generateNoise();
}
const lacunarityRange = document.getElementById("lacunarity-range");
lacunarityRange.oninput = function() {
document.getElementById("lacunarity").innerHTML = lacunarityRange.value;
generateNoise();
}

function generateNoise() {
document.getElementById("amplitude").innerHTML = amplitudeRange.value;
wasm_exports.set_amplitude(amplitudeRange.value);
document.getElementById("frequency").innerHTML = frequencyRange.value;
wasm_exports.set_frequency(frequencyRange.value);
document.getElementById("octaves").innerHTML = octavesRange.value;
wasm_exports.set_octaves(octavesRange.value);
document.getElementById("persistence").innerHTML = persistenceRange.value;
wasm_exports.set_persistence(persistenceRange.value);
document.getElementById("lacunarity").innerHTML = lacunarityRange.value;
wasm_exports.set_lacunarity(lacunarityRange.value);

const rgbaPtr = wasm_exports.alloc(rgbaSize);
const rgba = new Uint8ClampedArray(wasm_exports.memory.buffer, rgbaPtr, rgbaSize);
wasm_exports.generate(rgbaPtr, rows, cols);
let image = ctx.getImageData(0, 0, cols, rows);
image.data.set(rgba);
ctx.putImageData(image, 0, 0);
wasm_exports.free(rgbaPtr, rgbaSize);
}
generateNoise();
});
})();
10 changes: 4 additions & 6 deletions examples/lib/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@
position: relative;
}

.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 20px;
}

#form {
width: 300px;
margin: 10px;
Expand All @@ -30,6 +24,10 @@
top: 0px;
}

#canvas {
border: 1px solid black;
}

#video {
position: absolute;
}
Expand Down
5 changes: 2 additions & 3 deletions examples/src/js.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const gpa = std.heap.wasm_allocator;

pub const js = struct {
extern "js" fn log(ptr: [*]const u8, len: usize) void;
Expand Down Expand Up @@ -29,10 +28,10 @@ pub fn logFn(
}

export fn alloc(len: usize) [*]u8 {
const slice = gpa.alloc(u8, len) catch @panic("OOM");
const slice = std.heap.wasm_allocator.alloc(u8, len) catch @panic("OOM");
return slice.ptr;
}

export fn free(ptr: [*]const u8, len: usize) void {
gpa.free(ptr[0..len]);
std.heap.wasm_allocator.free(ptr[0..len]);
}
60 changes: 60 additions & 0 deletions examples/src/perlin.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const std = @import("std");
const builtin = @import("builtin");
const perlin = @import("zignal").perlin;
const Rgba = @import("zignal").Rgba;
const Image = @import("zignal").Image;

pub const std_options: std.Options = .{
.logFn = if (builtin.cpu.arch.isWasm()) @import("js.zig").logFn else std.log.defaultLog,
.log_level = .info,
};

var opts = perlin.Options(f32){
.amplitude = 1,
.frequency = 1,
.octaves = 1,
.persistence = 0.5,
.lacunarity = 2,
};

pub export fn set_amplitude(val: f32) void {
std.log.debug("setting amplitude to {d}", .{val});
opts.amplitude = val;
}
pub export fn set_frequency(val: f32) void {
std.log.debug("setting frequency to {d}", .{val});
opts.frequency = val;
}

pub export fn set_octaves(val: usize) void {
std.log.debug("setting octaves to {d}", .{val});
opts.octaves = val;
}

pub export fn set_persistence(val: f32) void {
std.log.debug("setting persistence to {d}", .{val});
opts.persistence = val;
}

pub export fn set_lacunarity(val: f32) void {
std.log.debug("setting lacunarity to {d}", .{val});
opts.lacunarity = val;
}

pub export fn generate(rgba_ptr: [*]Rgba, rows: usize, cols: usize) void {
const size = rows * cols;
const image = Image(Rgba).init(rows, cols, rgba_ptr[0..size]);
for (0..image.rows) |r| {
const y: f32 = @as(f32, @floatFromInt(r)) / @as(f32, @floatFromInt(image.rows));
for (0..image.cols) |c| {
const pos = r * image.cols + c;
const x: f32 = @as(f32, @floatFromInt(c)) / @as(f32, @floatFromInt(image.cols));
const val: u8 = @intFromFloat(
@max(0, @min(255, @round(
255 * (opts.amplitude / 2 * (perlin.generate(f32, x, y, 0, opts) + opts.amplitude)),
))),
);
image.data[pos] = Rgba.fromGray(val, 255);
}
}
}

0 comments on commit a02f17c

Please sign in to comment.