Giter Site home page Giter Site logo

silvia-odwyer / photon Goto Github PK

View Code? Open in Web Editor NEW
2.5K 37.0 137.0 67.3 MB

⚡ Rust/WebAssembly image processing library

Home Page: https://silvia-odwyer.github.io/photon

License: Apache License 2.0

Rust 65.19% HTML 13.55% CSS 0.65% JavaScript 18.17% SCSS 2.43%
rust image-processing image-manipulation rust-library wasm webassembly hacktoberfest

photon's Introduction

photon

Photon banner, showing the Photon logo on a dark background

Photon

Status GitHub Issues Gitter Chat NPM Monthly Downloads GitHub Workflow Status

Crates.io License GitHub Pull Requests


High-performance, cross-platform Rust/WebAssembly image processing library

📝 Table of Contents

Photon is a high-performance Rust image processing library, which compiles to WebAssembly, allowing for safe, blazing-fast image processing both natively and on the web.

You can run Photon:

  • natively
  • in the browser with WebAssembly
  • on Node with WebAssembly

Features

  • Fast: On the web, Photon's high-performance allows it to run at near-native speed. Benchmarks can be found here.
  • Call with JS: Want to use Photon on the web or with Node? Using a simple npm package, you're good to go. Get all the benefits of WebAssembly with zero-cost abstraction.
  • Use Natively: For command-line apps, native photo editing apps, and so forth, Photon's core codebase is in Rust, allowing for cross-platform development.
  • Pure Rust - Unlike other libraries, 100% of the library's codebase is written in Rust, so security and safety is guaranteed.

Live Demo

View the official demo of WASM in action.

Photon In Action

Imgur

Version 0.3.2 Release [2023 Update]

Version 0.3.2 has been released on Crates.io and NPM, with new features including:

  • A duotone filter and preset duotone effects
  • Image rotation
  • Dithering filter
  • Image cropping update (fixed in PR #100)

Supported Image Formats

The following image formats are supported:

  • PNG
  • JPEG
  • BMP
  • ICO
  • TIFF
  • WEBP

Get Started

Getting Started Guide

Check out Photon's getting started guide, complete with tutorials, installation instructions, and more

📚 Documentation

View the official documentation.

Functions

96 customisable functions are available, for varying image effects.

Functions include:

  • Image correction: Hue rotation, sharpening, brightness adjustment, adjusting saturation, lightening/darkening all within various colour spaces.
  • Resizing: Resize images both natively and on the web.
  • Convolutions: Sobel filters, blurs, Laplace effects, edge detection, etc.,
  • Channel manipulation: Increasing/decreasing RGB channel values, swapping channels, removing channels, etc.
  • Transform: Resize, crop, rotate and flip images.
  • Monochrome effects: Monochrome tints, greyscaling of various forms, thresholding, sepia, averaging RGB values
  • Colour manipulation: Work with the image in various colour spaces such as HSL, LCh, and sRGB, and adjust the colours accordingly.
  • Filters: Over 30 pre-set filters available, incorporating various effects and transformations.
  • Watermarking: Watermark images in multiple formats.
  • Blending: Blend images together using 10 different techniques, change image backgrounds.

Get Started Natively

Install

Add the following line to the dependencies section of your Rust project's Cargo.toml:

Cargo.toml
[dependencies]
photon-rs = "0.3.2"

Using Photon Natively

The following code opens an image from the filesystem, applies an effect, and outputs it as a PNG.

Here is a code sample to get you started:

extern crate photon_rs;
use photon_rs::native::{open_image, save_image};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Open the image (a PhotonImage is returned)
    let mut img = open_image("test_image.PNG")?;

    // Increment the red channel by 40
    photon_rs::channels::alter_red_channel(&mut img, 40);

    // Write file to filesystem.
    save_image(img, "raw_image.JPG")?;

    Ok(())
}
See More Examples

For more examples, check out the guide on how to get started with Photon natively.

Building WebAssembly package

In order to build the WebAssembly package you will need wasm-pack. Check https://rustwasm.github.io/wasm-pack/installer/ on how to install it. Then you can run the command:

wasm-pack build ./crate

Get Started With WebAssembly

Using a Bundler?

Installing Photon

If you're using Webpack or a bundler to build your project, install Photon via npm:

npm install @silvia-odwyer/photon

You can run Photon directly in any web browser that supports WebAssembly, which includes Chrome, Firefox, Safari, and Edge.

Get Started with Photon on The Web

To get started, check out the guide.

Using NodeJS?

If you're intending to use Photon with NodeJS, you can install the NodeJS version of the library:

npm install @silvia-odwyer/photon-node

Using Cloudflare Workers?

If you're using Cloudflare Workers, you can install the following library to use Photon with Cloudflare Workers:

npm install @cf-wasm/photon

Modules

Photon contains a series of modules, which include:

  • effects: Various image effects, including adding offsets, thresholding, duotoning, solarization, etc.,
  • channels: Functions related to increasing/decreasing the red, green, and blue channels of the image data.
  • transform: Resize, crop, flip, and rotate images.
  • filters: Preset filters, which alter the rgb channels of the image. Contains over 20.
  • conv: Laplace, Sobel, emboss; image proc functions which require image convolution.
  • noise: Noise generation of varying tints and hues.
  • multiple: A module for dealing with multiple images, such as watermarking images, etc.,
  • correction: Hue rotation, adjusting saturation, lightening/darkening: all techniques available in multiple colour spaces, which lead to varying effects.

Quick Start Example

Clone this repo:

git clone https://github.com/silvia-odwyer/photon

Run the binary, which will perform an image processing function on an image:

cargo run --release

Compare the original image with the outputted image, and you'll see the desired effect has been applied.

Got Questions?

If you have further questions about this library, you can ask them on Gitter or Spectrum, and I'll get back to you!

If there are any issues involving running/using the library, make sure to open an issue, it would be greatly appreciated, and will help improve the library.

Additional Notes

Functions have been designed with flexibility in mind, so that full customization of effects and filters can be utilised; for every function, hundreds of differing image effects/tints/hues can be created, just by changing parameters slightly, so with every function comes the ability to fully experiment.

For developers who would like to work with high-level constructs can do so, such as applying effects to imagery (eg: Laplace or Sobel) or filters; this library provides a complete suite of functions to do so, as well as in-built filters and presets.

photon can be thought of as a high-level wrapper to the Rust image crate, but conversely also includes functions which provide low-level access to pixel and channel manipulation, perfect for developers who wish to work with this data directly.

Contributing

Photon is always accepting new filters and functions. In that vein if you'd like to contribute to Photon please submit a Pull Request or add new issues for investigation. Our community is our lifeblood, and we appreciate all the support we get from individuals like you.

To Do

  • Blend images using browser-specific functions for WASM version of library.
  • Vintage images with light leaks, grains, etc.,
  • Tests in a headless web browser for WebAssembly version of library

Contributors

This repository continually receives new filters and updates from fellow contributors, for which I'm very grateful for! Thanks to the generous contributions of others, there are even more cool effects available in this library.

I'd like to extend my gratitude to all of you!

Contributors include (be sure to add yourself to the list if you submitted a PR):

License

This project is licensed under the Apache 2.0 License - see the LICENSE.md file for details.

photon's People

Contributors

a1trl9 avatar advancedtw avatar axetroy avatar benliao avatar chepachilo avatar chriamue avatar clivern avatar daggy1234 avatar elad-yosifon avatar elpiel avatar emmaboecker avatar estebanborai avatar fineshop avatar gp-97 avatar horki avatar maexn avatar mawie81 avatar mickdekkers avatar nathanbabcock avatar norbertgarfield avatar osman-turan avatar rail-ka avatar rsk700 avatar samuelvanderwaal avatar scarboroughcoral avatar sellig6792 avatar silvia-odwyer avatar styfle avatar sunny4381 avatar sylk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

photon's Issues

SyntaxError: import not found

Chrome88: SyntaxError: The requested module '/node_modules/@silvia-odwyer/photon/photon_bg.wasm?import' does not provide an export named 'hsl'

Firefox85: SyntaxError: import not found: monochrome

I was trying the resize function.

cargo run --release got Err

thread 'main' panicked at 'called Result::unwrap() on an Err value: IoError(Os { code: 2, kind: NotFound, message: "No such file or directory" })', src/libcore/result.rs:1165:5

Updating Dependencies

I want to use photon with the rust image crate. However image v0.23.10 is not compatible with photon which is using image v0.21.1. I would love to see support for the latest image crate with photon as it offers great gif support!

When I tried manually, there were 188 issues present. A lot of the library has changed, so someone well versed with the library would have to do it.

[RFC] Adopt photon in AI framework to preprocess vision workload

Introduction

Hello everyone, I'm working for a new open-source AI framework (MindSpore) and our team recently created a high-level machine learning development kit called TinyMS to reduce the learning complexity for machine learning beginners.

Recently I found photon on Twitter and I was so impressed by its support for WASM. I think it would be awesome for this project to be seamlessly integrated to AI framework pipeline. And I'm also willing to bring it into TinyMS to make this idea happen.

Proposed Solution

Currently most of machine learning framework or development kit are written in Python, and so do TinyMS. So we can firstly develop some image processing functions using Photon natively and compile them into WASM bytecode, then we can add a new data transform backend (such as WASMTransform) in TinyMS data process pipeline, and by leveraging wasmtime-py it's easy to load the wasm file into the system.

This proposal is still in design phase, so if you have any question please be free to ask here. And if anyone is interested into it, you can reach out to me with [email protected], thanks!

blend() shifts colors

I was experimenting with blend function and seems like it has some issue. I was using "over" method which, as I understand, should put one image above other without mixing colors, with original photon code colors become lighter on blend here is example result with two source images, photon result, and expected result:
https://imgur.com/a/CfT2m6o

problems looks like related to this line:
https://github.com/silvia-odwyer/photon/blob/master/crate/src/multiple.rs#L97

as I was exploring code and with this fast hack, I get expected blend result:

img.put_pixel(x, y, image::Rgba {
                // data: Srgba::from_linear(blended.into()).into_format().into_raw()
                data:
                [
                    (255f32 * blended.color.red) as u8,
                    (255f32 * blended.color.green) as u8,
                    (255f32 * blended.color.blue) as u8,
                    (255f32 * blended.alpha) as u8
                ]
            });

my code for tests

use std::env;
use photon_rs::native::{open_image, save_image};
use photon_rs::multiple;


fn main() {
    let mut args = env::args();
    let _ = args.next();
    let image_path = args.next().expect("need path to image");
    let bg_path = args.next().expect("need path to background");
    let effect_name = args.next().unwrap_or(String::from("over"));
    let image = open_image(&image_path);
    let mut bg = open_image(&bg_path);
    multiple::blend(&mut bg, &image, &effect_name);
    save_image(bg, "out.png");
}

"Performance" section on web site

Hi there!

Props for the nice library. I'm curious about the performance numbers on the web site. It says

Photon outperforms even the fastest of libraries, both natively and on the web. Photon has greatly outperformed ImageMagick and the Python Imaging Library, as shown in the graph to the right

but there's no note on what exactly was benchmarked in each case, or how. Could that information be added to the site? Right now it just says Photon does something in 2 seconds, ImageMagick does something in 22, and PIL (btw, did you use PIL or Pillow?) does something in 40. 😅

Docs: Need a detailed list of filters

I got following words from the readme:
image
image
One paragraph said there're 30+ filters, and another paragraph said there're 20+ filters.

But on the document:

image

There're only 10+ filters... that's a little bit weired...

bench: fix errors; increase measurement time

Bugs:

  • paths to images are bad
  • missing to include
    use photon_rs::transform::{resize, SamplingFilter}

Updates:

  • increase measurement time to 10 seconds
  • added png img(converted underground.jpg, via gm tool

Feature request: panic-free functions

I think it would be better to reimplement open_image and save_image as panic-free functions that return Result<T, E>. for example:

impl PhotonImage {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, PhotonError> {
        // open image ...
    }

    pub fn save_as<P: AsRef<Path>>(&self, path: P) -> Result<(), PhotonError> {
        // save image ...
    }
}

bin.rs :

fn main() -> Result<(), PhotonError>  {
    // Open the image (a PhotonImage is returned)
    let mut img = PhotonImage::open("examples/input_images/daisies_fuji.jpg")?;

    // Increment the red channel by 40
    photon_rs::channels::alter_red_channel(&mut img, 40);

    let output_img_path = "raw_image.jpg";

    // Write file to filesystem.
    img.save_as(output_img_path)?;    

    println!("Saved image: {}. Please check this directory for the image.", output_img_path);

    Ok(())
}

Runtime Error for image with less than 4 channels

Hi Silvia,

For now, at first step of many functions in this project, we would try to create a DynamicImage instance with function helper::dyn_image_from_raw and it works perfectly in most circumstance. However, when I tried to pass a grayscale image as a base64 string, for example:

let base64_str = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAcCAAAAAAG8rMrAAAEt2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjUwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjgiCiAgIGV4aWY6Q29sb3JTcGFjZT0iNjU1MzUiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iNTAiCiAgIHRpZmY6SW1hZ2VMZW5ndGg9IjI4IgogICB0aWZmOlJlc29sdXRpb25Vbml0PSIyIgogICB0aWZmOlhSZXNvbHV0aW9uPSI3Mi4wIgogICB0aWZmOllSZXNvbHV0aW9uPSI3Mi4wIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIxIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0iR3JleXNjYWxlIEQ1MCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDEtMTNUMjM6MTk6MTIrMDg6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjAtMDEtMTNUMjM6MTk6MTIrMDg6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gKFNlcCAyNiAyMDE5KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMC0wMS0xM1QyMzoxOToxMiswODowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+SMh6XQAAAN1pQ0NQR3JleXNjYWxlIEQ1MAAAGJVjYGCMyUnOLWYxYGDIzSspcg9yjIyIjFJgf8LAyMDLwM0gxaCVmFxc4BgQ4MOAE3y7BlQNBJd1QWbhVocVsKSkFicD6S1AbJZcUFQCpN8AsU95SQGQzWgBZItkhwQ5A9kgNwjk5pQmQ/WCbOVJzQsNBtJSQCzD4M5QxJDKUMlQzJDMkMiQA2QrMLgwmDKA/IddnwFYnx9DPlBlMpAsAOouYshkSGfIYChh0AGKlgJNA5mTBjY7FWhqJSiM0P1ekFiUCPcVk7ExAJd2Mpkpzru2AAAACXBIWXMAAAsTAAALEwEAmpwYAAACf0lEQVQ4jVVUyXLdRgzsBsDtyYqtyFXJNf//WTlaTqLlkY/LoHOYoSSTLBJTgwaaAHr4F43tIUHQCJCQkMpMpfn0dfz+gP6+246cn6MwSRhhJEGCAEEIkpQCrOvz7rINk23ifjtCAokk3hEVJUASRKfpy90+XWxPrvP8IxLU6dhuJYKAAAFG5m9fhcmP5La8/XyNpCgKjRDBsogxGgAITrPHB2Hq1vR92Z/e1oAACEQjw1wKuHEAIbiZPf5uHMcZWK96XveIBhAFgKJSgFBEAWYe999NY1xhZcHd1OMpUC8CbBbbuiH+sBztpuR+PDz+KIyou++1JRgFsI6AuXeXb1ZGvykzNf/585/X4xJsKRoEq0Y/2DtgHt14QQ5xhYr8ens6XvfIOAGsjGhi3wOgedcPw+Dub5Dkr7fj78tq/l80Z559n0ABpHnX931H51USbF6PXNdO/97CKsROYiAE0j2iC6djSYm2bCWL4bqlhYFAm0kCFCGYhXsEZVwk0ec9M6VtR+xhgBE0WssiwMzczUjEVRCGZU8lsgBeMsgKoJFnj2hmZlTqBRCHedfyXKYvpkhZeGVlJ+KkSaoOpsV8IF8OzMPokiEqgHb2BwRJEqrT574elS4QKoSdkI/2nwWXAMC7rVCwuxcNk5JUhqGGJFr0SlAt6bgmAOD+UgYUELAQATX14/RuFmxaCygC8l4FAKgAWn3eR7kZBLxfC1Wba0pUgYSbmfNs/PkCAURsWVMQnnlORgSd9inDhxmxZ1uJmVXuVUVm/CSrZpI27OUMYtB58gAR/KDCjw8Qa77Xrp4LFCUhPif4Rc+HqmNDEPU0hE5V/prkPUzFqP44JED5P4cLQ8uMjoS3AAAAAElFTkSuQmCC";
let mut photon_image = base64_to_image(base64_str);
helpers::dyn_image_from_raw(&photon_image);

A runtime error raises as ImageBuffer::from_vec(xx) in dyn_image_from_raw returns None and unwrap fails.

When I recursively looked into base64_to_image and printed out raw_pixels.len(), I found for the image data above (50 x 28), raw_pixels.len() == 1440 instead of 1440 x 4 -- looks like load_from_memory only returns data for one channel. Thus, ImageBuffer::from_vec returns None as expected since existing buffer isn't long enough for RGBA (See: https://docs.rs/image/0.19.0/image/struct.ImageBuffer.html#method.from_vec).

A similar error occurs when I tried to call get_image_data from the PhotonImage instance created above. As documented here, array's length should be a multiple of (4 * width) or (4 * width * height), otherwise, an IndexSizeError will be thrown.

For double-check, I modified code like following as a workaround:

pub fn transform_grayscale_to_rgba(pixels: &Vec<u8>) -> Vec<u8> {
    pixels.iter()
        .flat_map(|&v| vec!(v, v, v, 255))
        .collect()
}

pub fn base64_to_image(base64: &str) -> PhotonImage {

    let base64_to_vec: Vec<u8> = base64_to_vec(base64);

    let slice = base64_to_vec.as_slice();

    let img = image::load_from_memory(slice).unwrap();
    
    let mut raw_pixels = img.raw_pixels();

    if raw_pixels.len() as u32 == img.width() * img.height() {
         raw_pixels = transform_grayscale_to_rgba(&raw_pixels);
    } 
    return PhotonImage { raw_pixels: raw_pixels, width: img.width(), height: img.height()};

}

pub fn get_image_data(&mut self) -> ImageData {
    if self.raw_pixels.len() as u32 == self.width * self.height {
        let mut pixels = helpers::transform_grayscale_to_rgba(&self.raw_pixels);
        let new_img_data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut pixels), self.width, self.height).unwrap();
        return new_img_data;
        }
    let new_img_data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&mut self.raw_pixels), self.width, self.height).unwrap();
    new_img_data
}

Both errors resolved.

I also tried some other examples (as base64 string), like:

test_rgba_trans

It is not a grayscale image, but with only 3 channels. A same error raises.

But for a PNG type base64 string with transparent background, like:

test_real_trans

Everything works perfectly.

Would you mind checking my steps? I would appreciate for any feedback. Also I am happy to make a PR (for at least fixing RGB and grayscale) if it is confirmed an issue.

PS: I think it wont be a problem for image data from HTML canvas as it by default contains 4 channels.

Sudden Change in the `apply_gradient` functionality

How it used to function::

Input Image:

Old Output when apply_gradient was used once

Current output:

Based on preliminary browsing and testing, this seems to be caused by the new library changes for blend

photon usage on WebWorker

Hello,
I want to use this library on Cloudflare workers. Cloudflare workers run on WebWorker environment.

I imported photon-rs on rust project and build with wasm-pack.

I get "RuntimeError: unreachable" error on some images converted into base64.

In docs, I saw an example utilizing canvas but are there other ways to store it on memory?

[Code]

const base64Encode = buf => {
  let string = '';
  (new Uint8Array(buf)).forEach(
    (byte) => {
      string += String.fromCharCode(byte)
    }
  );
  return btoa(string)
};

async function handleRequest(request) {
  const { PhotonImage } = wasm_bindgen;
  await wasm_bindgen(wasm);
  
  const url = `IMAGE_URL`;
  const headers = {};
  const originRequest = new Request(url, { headers: headers });
  const upstreamResponse = await fetch(originRequest);
  const base64 = base64Encode(await upstreamResponse.arrayBuffer());
  let data = base64.replace(/^data:image\/(png|jpg);base64,/, "");
  const image = PhotonImage.new_from_base64(data);
  return new Response('test', {status: 200})
}

[Error]

Uncaught (in promise)
RuntimeError: unreachable
    at <anonymous>:wasm-function[1829]:0x9b26a
    at <anonymous>:wasm-function[769]:0x7fc50
    at <anonymous>:wasm-function[1661]:0x99339
    at <anonymous>:wasm-function[1763]:0x9a7c9
    at <anonymous>:wasm-function[1120]:0x8dcfb
    at <anonymous>:wasm-function[499]:0x6e5a1
    at photonimage_new_from_base64 (<anonymous>:wasm-function[966]:0x88d97)
    at Function.new_from_base64 (worker.js:2618:24)
    at handleRequest (worker.js:3000:29)
Uncaught (in response)
Error: internal error

Create image using Blob

At the moment it seems photon relies on loading an image into a canvas in order to then pass onto photon.

Isn't this inefficent and potentially causes loss of quality?

open_image | Convert a HTML5 Canvas Element to a PhotonImage.

Is there any way to pass just a raw Blob to photon and have it pass that onto the WASM for decoding + transforming, etc? This would be useful for image uploads where you can receive a blob file.

Loading onto a canvas seems like an unnecessary transformation/step...

The required JS dependency "@silvia-odwyer/photon" is not available -- ClojureScript

I am doing example project https://github.com/minimal-xyz/minimal-shadow-cljs-importing-npm and after following the steps I get this warning.

Before execute 'npx shadow-cljs watch client' or 'yarn shadow-cljs watch client' I tried with:
npm install --save @silvia-odwyer/photon
npm install @silvia-odwyer/photon
yarn add @silvia-odwyer/photon
and the files are in node_modules folder indeed, but it doesn't work

The others dependencies on the project works fine...

Here is my packege.json after installing:
{
"scripts": {
"html": "mkdir -p target/; cp entry/index.html target/"
},
"dependencies": {
"@silvia-odwyer/photon": "^0.2.0",
"dayjs": "^1.7.7",
"lodash": "^4.17.19",
"shortid": "^2.2.13"
},
"devDependencies": {
"shadow-cljs": "^2.6.23"
},
"name": "minimal-shadow-cljs-importing-npm",
"description": "",
"version": "0.1.0",
"main": "index.js",
"author": "",
"license": "MIT"
}

Here is my shadow-cljs.edn:
{:source-paths ["src"]
:builds {:client {:output-dir "target/"
:asset-path "./"
:target :browser
:modules {:client {:init-fn app.main/main!}}
:devtools {:after-load app.main/reload!
:http-root "target"
:http-port 8080}}}
:jvm-opts ["-Xmx1024m"]}

And heres is the main.cljs file requiring/importing the dependency the same as the others:
(ns app.main
(:require ["dayjs" :as dayjs]
["shortid" :as shortid]
["lodash" :as lodash]
["lodash" :refer [isString]]
["@silvia-odwyer/photon" :as sop]))

I'm missing something?

Watermark functionality in the example is broken

When trying out the example https://silvia-odwyer.github.io/photon/demo.html

Seems like the watermake and the watermark-js are broken
Error in Watermark
Uncaught TypeError: e.watermark_img_browser is not a function

Error in Watermark JS
Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'

compilation to WASM not working..

cargo build --release does not create WASM bindings... only libphoton.so file .

ERROR in ./js/index.js
Module not found: Error: Can't resolve '../../crate/pkg' in ???
 @ ./js/index.js 12:0-25

after cargo build, the target directory does not have a pkg dir, there is a release directory.

FYI, trying to compile from scratch using current master branch, cargo version cargo 1.38.0 (23ef9a4ef 2019-08-20)

Make wasm-bindgen an optional feature

Currently, all methods are marked with #[wasm_bindgen], which makes them exported even when crate is used as a transitive dependency in a Wasm project.

This is not ideal, because one might want to save code size when using only one or two methods from a crate. If methods weren't exported, they wouldn't be compiled in, and would be eliminated as a dead code from the final bundle, but right now they will be still included.

As one way to fix this, wasm-bindgen could be made an optional dependency, its attribute would be used only when it's enabled.

Enhancement: choosing FilterType in resize functions

@silvia-odwyer Many thanks for implementing resize, crop etc... I'm gonna test them out in the next couple of days!

I have noticed you are using NN ("nearest- neighbor") as the resize algorithm.
There are several use cases for using different algorithms (e.g. images with crisp curvy lines, lanczos3 is a much better choice for that...) - why should we limit it to NN exclusively?
Luckily, there are several other algorithms that are already implemented - Triangle, CatmullRom, Gaussian, Lanczos3. (https://docs.rs/image/0.19.0/image/enum.FilterType.html).

It would be great if we can expose the image::FilterType enum as a function parameter, or provide an overloaded function for better control over the expected outcome.

I might be able to help out and provide a PR for this change in the next couple of days.. any concerns before I jump into it?

npm run build failing while trying to resolve ./js/worker.js

ERROR in Entry module not found: Error: Can't resolve './js/worker.js' in '~/photon/webpack_demo'
    
ERROR in multi (webpack)-dev-server/client?http://localhost:8080 ./js/worker.js
Module not found: Error: Can't resolve './js/worker.js' in '~/photon/webpack_demo'
     @ multi (webpack)-dev-server/client?http://localhost:8080 ./js/worker.js main[1]

Security and safety guarantee

Under "Features", the Readme says:

Unlike other libraries, this 100% of the library's codebase is written in Rust, so security and safety is guaranteed.

This is great as marketing, but do you actively do anything to check this? Does CI run clippy? Do you deny/verify unsafe code? I know the image crate has some fuzzing targets which found a bunch of issues. Do you do anything like that?

Webpack import problem

I am trying to import photon in a existing web project.
Here is my webpack loader.

{
        test: /\.wasm$/,
        use: [
            {
                loader: 'file-loader',
            },
        ],
        type: 'javascript/auto',
    }

I get this error :

./node_modules/@silvia-odwyer/photon/photon.js
Attempted import error: '__wbg_photonimage_free' is not exported from './photon_bg.wasm' (imported as 'wasm').

Have you an example to import photon ?

photon-node usage on backend

Hi, congratulations about the huge work, I can't wait to try it with Rust, but currently I'd need to use the lib in node.
I don't exactly get how it is intended to be used on backend, it seems it requires anyway to work with a canvas, while with node the most common usage is probably reading directly from fs or to use a readStream from fs. I think an example would be really convenient.

RejectionError: unreachable on Call to photon.PhotonImage.new_from_base64 (WASM-Node)

Hey there! First off, really cool project, it appears to be exactly what I'm looking for. Bad news however is that I'm having difficulty getting it to work. On attempting to create a new PhotonImage in Node, I'm encountering a bunch of wasm errors that I cannot decipher. Here's the console output from my test:

 FAIL   isomorphic-vibrant  src/__TEST__/vibrant.spec.ts
  vibrant
    ✕ generates a palette (1308 ms)

  ● vibrant › generates a palette

    RuntimeError: unreachable
        at wasm-function[1200]:26
        at wasm-function[624]:286
        at wasm-function[888]:116
        at wasm-function[1153]:45
        at wasm-function[871]:126
        at wasm-function[670]:212
        at wasm-function[930]:47
        at wasm-function[863]:38
        at wasm-function[147]:1104
        at wasm-function[36]:4639
        at wasm-function[543]:23
        at wasm-function[39]:2496
        at wasm-function[477]:68
        at wasm-function[797]:18

      25 |     const b64 = await convertBlobToBase64(blob)
      26 |     const str = b64.replace(/^data:.*\/.*;base64,/, ``)
    > 27 |     this.#image = photon.PhotonImage.new_from_base64(str)
         |                                      ^
      28 |     return this
      29 |   }
      30 |

      at Function.new_from_base64 (node_modules/@silvia-odwyer/photon-node/photon.js:2561:24)
      at NodeImage [as load] (src/image.ts:27:38)

My goal here is to use Photon to aid in extracting the prominent colors from an image, and I would like it to work universally across node, the browser, and web workers. The reason for hitting those targets is that I'm mostly intending to use this in serverless environments, which have restrictions on the total size of compiled code and do not always have native node features (example, you can't use fs or http in Cloudflare Workers). Execution speed is also really important, which was what made using the project I forked this from (node-vibrant) prohibitive to use. WASM definitely seems the way to go.

You can check out the implementation in the CodeSandbox linked below. To reproduce the error, click the + button in the Terminal tab to open a new terminal, where you can then run yarn test the same as you would from your local machine. Here's a screenshot for reference since it can be a little hard to find:

https://codesandbox.io/s/github/Saeris/isomorphic-vibrant?file=/src/image.ts

image

I understand that currently image resizing is not available in the node package. For now I'm just trying to debug why I can't create a new PhotonImage from a valid base64 string. I'm clueless when it comes to Rust development, so I don't know if there's a way you could also make these errors more helpful.

Update repo to work with async web assembly

I recently started using this package and was trying to use it with webpack 5 by setting their experiments.asyncWebAssembly flag to true but there was a missing function from the compiled JS that prevented it from working.

Webpack 5 notes related to implementing this from the JS side is here https://webpack.js.org/blog/2020-10-10-webpack-5-release/#experiments

The related documentation from the wasm_bindgen docs are here https://rustwasm.github.io/docs/wasm-bindgen/print.html#long-lived-js-objects

It looks to be working with webpack's synchronous web assembly option but it would be nice to be able to use an async implementation too so that we code that at the moment runs sequentially (e.g. resizing the image and then extracting exif data from the image) could, instead, run concurrently.

Gaussian blur panics on small images

The function pub fn gaussian_blur(photon_image: &mut PhotonImage, radius: i32) seems to cause a panic whenever the input image is small e.g a 10px by 10px image, especially when the width or height of the image is smaller than the radius.

The trace seems to point to either box_blur_vertical or box_blur_horizontal in conv.rs

too many warnings + some errors

I saw many other places where I can help you, but for start, it's really annoying to soo many warning messages.

for example:

  • too many return statements
  • "wrong" swaps
  • using loop, instead of clone
  • reference to Vec, instead of slice
  • not using *= oper
  • unused mut var
  • condition for u8 with 255

in channels.rs

// before, with return
#[wasm_bindgen]
pub fn remove_blue_channel(img: &mut PhotonImage, min_filter: u8) {
    return remove_channel(img, 2, min_filter);
}
// after
#[wasm_bindgen]
pub fn remove_blue_channel(img: &mut PhotonImage, min_filter: u8) {
    remove_channel(img, 2, min_filter)
}

// before, "wrong" swap
    if channel1 > channel2 {
        let temp = channel1;
        channel1 = channel2;
        channel2 = temp;
    }

// after, corrected swap
    if channel1 > channel2 {
        std::mem::swap(&mut channel1, &mut channel2);
    }

// before, another "wrong" swap
    for i in (channel1..end).step_by(4) {
        let difference = channel2 - channel1;

        let temp_channel1 = img.raw_pixels[i];
        img.raw_pixels[i] = img.raw_pixels[i + difference];
        img.raw_pixels[i + difference] = temp_channel1;
    }
// after, corrected swap
    for i in (channel1..end).step_by(4) {
        let difference = channel2 - channel1;
        img.raw_pixels.swap(i, i + difference);
    }

in conv.rs

// before: using loop
    let length = (width * height * 4) as usize;
    for i in 0..length {
        target[i] = src[i];
    }
// after: using clone
    let length = (width * height * 4) as usize;
    target[..length].clone_from_slice(&src[..length]);

// before: Vec reference
fn box_blur_vertical(
    src: &Vec<u8>,
    // ...
)
// after: using ref to slice
fn box_blur_vertical(
    src: &[u8],
    // ...
}

in effects.rs

// before
            if square_distance < i32::pow(threshold, 2) {
                r = r * 0.5;
                g = g * 1.25;
                b = b * 0.5;
            }
// after
            if square_distance < i32::pow(threshold, 2) {
                r *= 0.5;
                g *= 1.25;
                b *= 0.5;
            }

in monochrome.rs

// before
        // not used 'mut' val
        let mut avg: u8 = (r_val * 0.3 + g_val * 0.59 + b_val * 0.11) as u8;

        // avg is defined as u8, (255 is max val for u8 in rust)
        if avg >= 255 {
            avg = 255
        }

        img.raw_pixels[i] = avg as u8;
        img.raw_pixels[i + 1] = avg as u8;
        img.raw_pixels[i + 2] = avg as u8;
// after
        let avg: u8 = (r_val * 0.3 + g_val * 0.59 + b_val * 0.11) as u8;

        img.raw_pixels[i] = avg;
        img.raw_pixels[i + 1] = avg;
        img.raw_pixels[i + 2] = avg;

do wee need features cfg ?

Hi!
i'm new in rust and also wasm

this is interesting project,
i want to use photon on my private module, but i don't need all the crates.

for example i only want use the transform crate in my project.
so i don't have to download and compile all crates

Dependencies need updating

Currently some of the dependencies are out of date, which breaks the usage with conjunction of other image creation/manipulation crates.
Simple example error of an outdated dependency:

error[E0034]: multiple applicable items in scope
   --> /home/nitsuga/.cargo/registry/src/github.com-1ecc6299db9ec823/imageproc-0.18.0/src/hog.rs:440:33
    |
440 |             let intensity = u8::clamp(hist[bucket]);
    |                                 ^^^^^ multiple `clamp` found
    |
    = note: candidate #1 is defined in an impl of the trait `Ord` for the type `u8`
note: candidate #2 is defined in the trait `Clamp`
   --> /home/nitsuga/.cargo/registry/src/github.com-1ecc6299db9ec823/imageproc-0.18.0/src/definitions.rs:68:5
    |
68  |     fn clamp(x: T) -> Self;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
    |
440 |             let intensity = Ord::clamp(hist[bucket]);
    |                             ^^^^^^^^^^
help: disambiguate the associated function for candidate #2
    |
440 |             let intensity = Clamp::clamp(hist[bucket]);
    |                             ^^^^^^^^^^^^

error[E0034]: multiple applicable items in scope
   --> /home/nitsuga/.cargo/registry/src/github.com-1ecc6299db9ec823/imageproc-0.18.0/src/hog.rs:444:33
    |
444 |             let intensity = u8::clamp(hist[bucket]);
    |                                 ^^^^^ multiple `clamp` found
    |
    = note: candidate #1 is defined in an impl of the trait `Ord` for the type `u8`
note: candidate #2 is defined in the trait `Clamp`
   --> /home/nitsuga/.cargo/registry/src/github.com-1ecc6299db9ec823/imageproc-0.18.0/src/definitions.rs:68:5
    |
68  |     fn clamp(x: T) -> Self;
    |     ^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #1
    |
444 |             let intensity = Ord::clamp(hist[bucket]);
    |                             ^^^^^^^^^^
help: disambiguate the associated function for candidate #2
    |
444 |             let intensity = Clamp::clamp(hist[bucket]);
    |                             ^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0034`.
error: could not compile `imageproc`

Node example in the guide doesn't work

Example from https://silvia-odwyer.github.io/photon/guide/using-photon-node/#sample-code throws the following error.

➜  node example.js                   
wasm://wasm/0040e156:1


RuntimeError: unreachable
    at <anonymous>:wasm-function[886]:0xd72cf
    at <anonymous>:wasm-function[1016]:0xdcfa3
    at <anonymous>:wasm-function[1378]:0xe4a01
    at <anonymous>:wasm-function[1289]:0xe37c9
    at <anonymous>:wasm-function[1380]:0xe4a65
    at <anonymous>:wasm-function[1052]:0xde1b4
    at <anonymous>:wasm-function[850]:0xd51a3
    at <anonymous>:wasm-function[1131]:0xe047b
    at <anonymous>:wasm-function[526]:0xbbaed
    at <anonymous>:wasm-function[1199]:0xe1df2

blend() failing on non transparent images

blenc() panics on this line
https://github.com/silvia-odwyer/photon/blob/master/crate/src/helpers.rs#L47
when a non transparent image is given.

It panics with all jpg/jpeg images, but it doesn't panic on png images that are transparent

async fn pride_image(image_vec: &[u8], name: String) -> Result<Vec<u8>, Box<dyn std::error::Error>>{
    let mut og_image = PhotonImage::new_from_byteslice(image_vec.to_vec());
    let pride_path = format!("pride/{}.png", name);
    let mut pride_image = open_image(Box::leak(pride_path.into_boxed_str()));

    let og_image_arc = Arc::new(RwLock::new(&mut og_image));
    let og_image_clone = Arc::clone(&og_image_arc);
    let og_image_clone_clone = Arc::clone(&og_image_arc);

    block_in_place(move || {
        pride_image = {
            let og_x = og_image_clone_clone.read().unwrap().get_width();
            let og_y = og_image_clone_clone.read().unwrap().get_height();

            resize(&pride_image, og_x, og_y, SamplingFilter::Nearest)
        };

        let mut og_img_mut = og_image_clone.write().unwrap();

        blend(&mut og_img_mut, &pride_image, "overlay");
    });

    let mut result = Vec::new();

    let raw_pixels = og_image.get_raw_pixels();
    let width = og_image.get_width();
    let height = og_image.get_height();

    let img_buffer = image::ImageBuffer::from_vec(width, height, raw_pixels).unwrap();
    image::DynamicImage::ImageRgba8(img_buffer)
        .write_to(&mut result, image::ImageOutputFormat::Jpeg(255))?;

    Ok(result)
}

A fix for this or a possible workaround would be appreciated, as i can't figure out exactly the cause, and im unable to find a way to easily make the image transparent.

What is the name of the npm package?

Firstly, this is a fantastic library, thank you for sharing it with the community.

I'm trying to use it in a node project, but I can't seem to find the documentation for usage with JS in node. Is there a npm package I can install, or do I need to write my own bindings to the lib?

Seam Carver

@silvia-odwyer Hi, I have included seam carver from imageproc library, which is still experimental, but in some near iteration it will get better, also I have added some small bug fixes #42

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.