Skip to content
This repository has been archived by the owner on Dec 7, 2023. It is now read-only.

Node and Browsers

Oscar edited this page Dec 27, 2019 · 2 revisions

If you are using an old version of SSIM.js it may include image loading. It has been removed from recent versions to keep this library focused on the SSIM algorithm. The implementations below show how to load images on both Node and the web and provide the results to ssim.js.

Node

Loading images on node can be tricky, requiring node-gyp and a couple libraries to help loading. The following implementation is similar to what's used in the node tests and produces equivalent results to the browser version below:

import fs from "fs";
import http from "https";
import Canvas from "canvas";
import imageType from "image-type";
import bmp from "bmp-js";

/**
 * If `limit` is set, it will return proportional dimensions to `width` and `height` with the
 * smallest dimesion limited to `limit`.
 */
export function getLimitDimensions(
  width: number,
  height: number,
  limit?: number
) {
  if (limit && width >= limit && height >= limit) {
    const ratio = width / height;

    if (ratio > 1) {
      return { height: limit, width: Math.round(limit / ratio) };
    }
    return { height: Math.round(limit * ratio), width: limit };
  }
  return { width, height };
}

/**
 * Parses the buffer data and returns it. If `limit` is set, it will make sure the smallest dimesion
 * will at most be of size `limit`.
 */
function parse(data: Buffer, limit: number): Promise<ImageData> {
  const { ext = "" } = imageType(data) || {};

  return new Promise((resolve, reject) => {
    if (ext === "bmp") {
      resolve(bmp.decode(data));
    } else {
      Canvas.loadImage(data)
        .then(img => {
          const { width, height } = getLimitDimensions(
            img.width,
            img.height,
            limit
          );
          const canvas = Canvas.createCanvas(width, height);
          const ctx = canvas.getContext("2d");

          ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height);

          return ctx.getImageData(0, 0, width, height);
        })
        .then(resolve)
        .catch(reject);
    }
  });
}

function loadUrl(url: string): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    http
      .get(url)
      .on("response", res => {
        const chunks: Buffer[] = [];

        res.on("data", data => chunks.push(data));
        res.on("end", () => {
          resolve(Buffer.concat(chunks));
        });
      })
      .on("error", reject);
  });
}

function loadFs(path: string): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    fs.readFile(path, (err, data) => {
      if (err) {
        reject(err);
        return;
      }

      resolve(data);
    });
  });
}

export function readpixels(
  url: string | Buffer,
  limit = 0
): Promise<ImageData> {
  let bufferPromise;

  if (Buffer.isBuffer(url)) {
    bufferPromise = Promise.resolve(url);
  } else if (typeof url === "string" && url.startsWith("http")) {
    bufferPromise = loadUrl(url);
  } else if (typeof url === "string") {
    bufferPromise = loadFs(url);
  } else {
    throw new Error("Invalid format used");
  }
  return bufferPromise.then(bufferData => parse(bufferData, limit));
}

Browsers

The easiest way to load the images on the browser is to rely on canvas and import them as a buffer something like this:

function readpixels(url, limit = 0) {
  const img = new Image()
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')

  return new Promise((resolve, reject) => {
    img.onload = () => {
      const { width, height } = getLimitDimensions(img.width, img.height, limit)

      if (width === 0 || height === 0) {
        return reject('Failed to load image')
      }

      canvas.width = width
      canvas.height = height

      ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width, height)
      return resolve(ctx.getImageData(0, 0, width, height))
    }
    img.onerror = reject
    img.src = url
  })
}
Clone this wiki locally