From 926b31d76930f1bf591421967c7ba90b2609c793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Fri, 6 Dec 2024 09:45:49 +0900 Subject: [PATCH] examples: add seam carving --- examples/build.zig | 1 + examples/lib/index.html | 1 + examples/lib/perlin-noise.html | 2 +- examples/lib/perlin-noise.js | 2 +- examples/lib/seam-carving.html | 23 ++++++ examples/lib/seam-carving.js | 128 +++++++++++++++++++++++++++++++++ examples/lib/styles.css | 8 ++- 7 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 examples/lib/seam-carving.html create mode 100644 examples/lib/seam-carving.js diff --git a/examples/build.zig b/examples/build.zig index a82d68f..99b8391 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -6,6 +6,7 @@ pub fn build(b: *std.Build) void { _ = buildModule(b, "colorspace", target, optimize); _ = buildModule(b, "face_alignment", target, optimize); _ = buildModule(b, "perlin_noise", target, optimize); + _ = buildModule(b, "seam_carving", target, optimize); const fmt_step = b.step("fmt", "Run zig fmt"); const fmt = b.addFmt(.{ diff --git a/examples/lib/index.html b/examples/lib/index.html index 38963b4..b4e1c73 100644 --- a/examples/lib/index.html +++ b/examples/lib/index.html @@ -16,6 +16,7 @@

Examples

  • Colorspace
  • Face aligment
  • Perlin noise
  • +
  • Seam carving
  • diff --git a/examples/lib/perlin-noise.html b/examples/lib/perlin-noise.html index 7987e0c..b62bd30 100644 --- a/examples/lib/perlin-noise.html +++ b/examples/lib/perlin-noise.html @@ -37,7 +37,7 @@

    Perlin Noise

    - + diff --git a/examples/lib/perlin-noise.js b/examples/lib/perlin-noise.js index 2fb2677..dc39af2 100644 --- a/examples/lib/perlin-noise.js +++ b/examples/lib/perlin-noise.js @@ -22,7 +22,7 @@ wasm_exports = obj.instance.exports; window.wasm = obj; console.log("wasm loaded"); - const canvas = document.getElementById("canvas"); + const canvas = document.getElementById("canvas-perlin"); const ctx = canvas.getContext("2d"); const rows = 512; const cols = 512; diff --git a/examples/lib/seam-carving.html b/examples/lib/seam-carving.html new file mode 100644 index 0000000..89da4b3 --- /dev/null +++ b/examples/lib/seam-carving.html @@ -0,0 +1,23 @@ + + + + + Seam Carving + + + + +

    Seam Carving

    +
    +
    + Settings +
    + +
    +

    +
    + + +
    + + diff --git a/examples/lib/seam-carving.js b/examples/lib/seam-carving.js new file mode 100644 index 0000000..16972b2 --- /dev/null +++ b/examples/lib/seam-carving.js @@ -0,0 +1,128 @@ +(function () { + let wasmPromise = fetch("seam_carving.wasm"); + var wasmExports = null; + const text_decoder = new TextDecoder(); + let image = null; + + const removeButton = document.getElementById("remove-button"); + const canvas = document.getElementById("canvas-seam"); + const ctx = canvas.getContext("2d"); + canvas.ondragover = function (event) { + event.preventDefault(); + }; + + function displayImageSize() { + let sizeElement = document.getElementById("size"); + sizeElement.textContent = "size: " + canvas.width + "×" + canvas.height + " px."; + } + + canvas.ondrop = function (event) { + event.preventDefault(); + let img = new Image(); + img.onload = function () { + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + image = ctx.getImageData(0, 0, canvas.width, canvas.height); + original = new ImageData(image.width, image.height); + original.data.set(image.data); + displayImageSize(); + }; + img.src = URL.createObjectURL(event.dataTransfer.files[0]); + }; + + function displayImage(file) { + const reader = new FileReader(); + reader.onload = function (e) { + const imageData = e.target.result; + const img = document.createElement("img"); + img.src = imageData; + img.onload = function () { + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + image = ctx.getImageData(0, 0, canvas.width, canvas.height); + original = new ImageData(image.width, image.height); + original.data.set(image.data); + displayImageSize(); + }; + }; + reader.readAsDataURL(file); + } + + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.style.display = "none"; + fileInput.addEventListener("change", function (e) { + const file = e.target.files[0]; + displayImage(file); + }); + + canvas.addEventListener("click", function () { + fileInput.click(); + }); + + function decodeString(ptr, len) { + if (len === 0) return ""; + return text_decoder.decode(new Uint8Array(wasmExports.memory.buffer, ptr, len)); + } + + WebAssembly.instantiateStreaming(wasmPromise, { + js: { + log: function (ptr, len) { + const msg = decodeString(ptr, len); + console.log(msg); + }, + now: function () { + return performance.now(); + }, + }, + }).then(function (obj) { + wasmExports = obj.instance.exports; + window.wasm = obj; + console.log("wasm loaded"); + + let copy = null; + function seamCarve() { + if (copy) { + ctx.putImageData(copy, 0, 0); + } + const rows = image.height; + const cols = image.width; + const rgbaSize = rows * cols * 4; + const rgbaPtr = wasmExports.alloc(rgbaSize); + const seamSize = rows * 4; + const seamPtr = wasmExports.alloc(seamSize); + const extraSize = rows * cols * 10; + const extraPtr = wasmExports.alloc(extraSize); + var rgba = new Uint8ClampedArray(wasmExports.memory.buffer, rgbaPtr, rgbaSize); + var seam = new Uint32Array(wasmExports.memory.buffer, seamPtr, image.height); + image = ctx.getImageData(0, 0, canvas.width, canvas.height); + rgba.set(image.data); + wasmExports.seam_carve(rgbaPtr, rows, cols, extraPtr, extraSize, seamPtr, rows); + canvas.width = cols - 1; + image = ctx.getImageData(0, 0, canvas.width, canvas.height); + rgba = new Uint8ClampedArray(wasmExports.memory.buffer, rgbaPtr, rgbaSize - 4 * rows); + + image.data.set(rgba); + ctx.putImageData(image, 0, 0); + copy = ctx.getImageData(0, 0, canvas.width, canvas.height); + displayImageSize(); + + for (let i = 1; i < rows; ++i) { + ctx.beginPath(); + ctx.moveTo(seam[i - 1], [i - 1]); + ctx.lineTo(seam[i], [i]); + ctx.strokeStyle = "red"; + ctx.lineWidth = 1; + ctx.stroke(); + } + + wasmExports.free(rgbaPtr, rgbaSize); + wasmExports.free(extraPtr, extraSize); + } + removeButton.addEventListener("click", () => { + seamCarve(); + }); + }); +})(); diff --git a/examples/lib/styles.css b/examples/lib/styles.css index edfc415..93d9d67 100644 --- a/examples/lib/styles.css +++ b/examples/lib/styles.css @@ -15,6 +15,10 @@ margin: 10px; } +#video { + position: absolute; +} + #canvas1 { border: 1px dashed black; vertical-align: center; @@ -30,8 +34,8 @@ #canvas-seam{ border: 1px solid black; - width: 640px; - height: auto; + width: auto; + height: 640px; } #toggle-button {