Code Monkey home page Code Monkey logo

etchify's Introduction

Etchify

Description

Etchify is a single page website that creates etchings from user uploaded photos. This is achieved through line recognition and shading that matches the perceived luminosity of the original image. Users are able to customize the produced etching through a variety of settings. The user experience is streamlined so that users need only make essential choices, and can explore more advanced settings if they wish.

Live

Create an etching here!

Table of Contents

Languages & Technologies

  • Node.js
  • Vanilla DOM manipulation
  • HTML5
  • SCSS
  • Canvas API
  • Webpack for bundling javascript
  • Npm for managing dependencies

On Features & Functions

For the etching, users are directed to upload an image, select a color, and select one of four styles: Shade, Outline, Shade & Outline, and Silhouette. Of additional note, among the advanced settings, users can decide the shading and outline colors separately and can opt for Refine Outline.

Screenshot of Settings

As an example image to be processed, we will use The Great Wave off Kanagawa by Hokusai.1 The woodblock print's contrast in brightness and solid colors are well-suited for the software, though great results can be achieved with a wide variety of images.

The Great Wave off Kanagawa by Hokusai

  • Shade will analyze the original image as a grid of n by n cells, and will match the perceived luminance of each cell with an appropriate amount of shading. Each pixel is either shaded or not, meaning there are no intermediate tones. Given this, the luminance of a cell is purely a product of what percentage of its pixels are shaded. The perceived luminance of each pixel in the cell is calculated from its RGB values and summed to determine the cell's overall luminance. This calculation is performed using an industry standard formula:

    Luminance = .299(R) + .587(G) + .114(B)2

    The size of n is referred to as the Dimension of the Cell and can be adjusted as an advanced setting. Increasing the Dimension of the Cell has a twofold impact: it simultaneously increases the pixellation of the resulting image, while also increasing the number of tones.

Shading of The Great Wave off Kanagawa

  • Outline employs line recognition to trace the original image. A candidate pixel is designated as a line if it differs enough from its neighbors. This calculation is performed with a five by five kernel centered on the candidate pixel. The neighboring pixels each "vote" on if the candidate pixel is a line. With each neighboring pixel, their vote depends on if their color difference exceeds the Color Difference Threshold, an advanced setting, and is weighted according to the inverse square of their distance from the candidate pixel. If the overall vote exceeds the Minimum Percent of Neighbors, an additional advanced setting, the candidate pixel is considered part of the outline.

Outline of The Great Wave off Kanagawa

  • Refine Outline is an advanced option that allows the user to reduce the "noise" produced by the Outline algorithm. It achieves this by removing any line pixels that are not part of a sufficiently large contiguous line complex. This threshold can be adjusted as the Minimum Contiguous Line Size. Currently this features employs a 2d array of line pixels, which while functional, has poor time and space complexity, and is in need of refactoring with a linked list.

Refined Outline of The Great Wave off Kanagawa

  • Shade & Outline merely employs both the Shading and the Outline algorithms at once, with the outline resting atop the shading.

Combined shading and outline of The Great Wave off Kanagawa

  • Silhouette is actually a particular application of the Shading algorithm, produced by setting the Dimension of the Cell to one. The result is that each pixel is considered its own cell, which means that there is essentially no pixellation, but each pixel can only be one of two tones: the background color or the shading color.

Silhouette of The Great Wave off Kanagawa

Code Example

processImage() {
  this.pixels = {};

  for (let row = 0; row < this.height; row++) {
    for (let column = 0; column < this.width; column++) {
      let currentIndex = this.pixelIndex(row, column);
      let currentRGB = this.retrievePixelRGB(currentIndex);
      let currentDarkness = this.calculateDarkness(currentRGB);

      this.pixels[currentIndex] = new Pixel(currentRGB, currentDarkness);

      let currentPixel = this.pixels[currentIndex];

      if (this.line) {
        let precedingNeighbors = this.precedingNeighbors(row, column);

        precedingNeighbors.forEach((neighborValues) => {
          let neighborIndex = neighborValues[0];
          let weight = neighborValues[1];
          let neighbor = this.pixels[neighborIndex];
          let neighborRGB = neighbor.colors;
          let colorDifference = this.calculateColorDifference(currentRGB, neighborRGB);
          Pixel.addColorDifference(currentPixel, neighbor, colorDifference, weight);
        });
      }
    }
  }
}

The above function is the initial image processing. It proceeds by rows and columns, instead of the raw pixel index, in order to make its operation more intuitive. It creates a Pixel instance for every pixel in the source image, which is saved in an object called Pixels. Each Pixel has its colors and luminance saved, and if outline is selected, the color differences and inverse-squared-distances (referred to as weight) of its preceding neighbors are saved as well. As a minor optimization, since the neighbor relationship is mutual, the static Pixel methodjavascript addColorDifference() adds the combination of color differences and weights to both Pixel instances.

The creation of the the Pixels object, as well as its composite Pixel instances, was to allow for more rapid reprocessing. In essence, this information is stable, even when the majority of the settings are adjusted. Currently, redrawing the image causes complete reprocessing, which is one of the issues discussed below in Future Considerations.

Future Considerations

Solved Problems Awaiting Implementation

  • Refactor the line refining algorithm using a linked list structure
  • As previous iterations had, provide multiple shading style options: horizontal, vertical, cross-hatching, diagonal, etc.
  • Allow users to select the background color
  • Allow users to use either a 3-by-3 or 5-by-5 kernel for the outline recognition
  • Refactor HTML and CSS to be more DRY
  • Refactor silhouette as a separate process, and allows for users to specify multiple darkness thresholds and corresponding colors
  • Add additional vanilla DOM manipulation to have steps appear only after the previous steps have been completed

Problems being Researched

  • Allow users to download the produced image, and additionally allow users to directly share the images on Instagram
  • Identify the best way to temporarily save the etching data (potentially directly on the window or localStorage) to allow for rapid reprocessing
  • Identify and implement a better way to have background Javascript processes with an updating document, so that the user can be more precisely apprised of the stage of the process
  • Add an additional style using the Fourier series to mimic the outline data

Footnotes

  1. The source for this image can be found here.

  2. This formula is typically used to calculate the Y component in RGB to YIQ conversion. Documentation can be found here.

etchify's People

Contributors

n-angleton avatar

Stargazers

 avatar

Watchers

 avatar

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.