8

I have a image that needs to converted to Uint8Array.

For Example:

var image = new Image();
image.src="image13.jpg";

Now i need to transform this image variable to Uint8Array. Is there any function in JS, which allows to achieve this functionality?

Dumb_Shock
  • 1,050
  • 1
  • 13
  • 23

1 Answers1

7

You may use the Canvas API to retrieve the image data:

  1. Retrieving the data in the binary form of various image formats (PNG, JPEG, etc.) using canvas.toBlob() and blob.arrayBuffer():
async function imageToUint8Array(image, context) {
  context.width = image.width;
  context.height = image.height;
  context.drawImage(image, 0, 0);
  const blob = await context.canvas.toBlob(
    callback,
    "image/jpeg" // the MIME type of the image format
    1 // quality (in the range 0..1)
  );
  return new Uint8Array(await blob.arrayBuffer());
}

function toBlob(canvas, type = "image/png", quality = 1) {
  return new Promise((resolve) => canvas.toBlob(blob => resolve(blob)))
}

async function imageToUint8Array(image, context) {
  context.width = image.width;
  context.height = image.height;
  context.drawImage(image, 0, 0);
  const blob = await toBlob(context.canvas, "image/png");
  return new Uint8Array(await blob.arrayBuffer());
}

const image = new Image();
// stackoverflow logo
image.src = `data:image/png;base64,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`;

const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");

image.onload = async() => console.log(await imageToUint8Array(image, context));
  1. Retrieving the raw image pixels using canvas.getImageData():
function imageToUint8Array(image, context) {
  context.width = image.width;
  context.height = image.height;
  context.drawImage(image, 0, 0);
  // `getImageData().data` is a `Uint8ClampedArray`, which differs from `Uint8Array` only in
  // how data is treated when values are being *set*, so it is valid to perform the conversion
  // into a `Uint8Array`.
  return new Uint8Array(context.getImageData(0, 0, image.width, image.height).data.buffer);
}

function imageToUint8Array(image, context) {
  context.width = image.width;
  context.height = image.height;
  context.drawImage(image, 0, 0);
  // `getImageData().data` is a `Uint8ClampedArray`, which differs from `Uint8Array` only in
  // how data is treated when values are being *set*, so it is valid to perform the conversion
  // into a `Uint8Array`.
  return new Uint8Array(context.getImageData(0, 0, image.width, image.height).data.buffer);
}

const image = new Image();
// stackoverflow logo
image.src = `data:image/png;base64,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`;

const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");

image.onload = async() => console.log(imageToUint8Array(image, context));
tonayy
  • 173
  • 2
  • 12
  • 2
    "There is no extra processing" gosh there is... If it's synchronous it's because this method is old and because the slow operation is the reading of the buffer anyway, which needs to be done before the next call draws something new on it. Also, your first snippet does create a completely new image, which will "look" the same, but won't be the actual image's data. See https://stackoverflow.com/a/42916772/3702797 – Kaiido Oct 06 '21 at 08:02
  • Not sure what you mean. There isn't any processing done once you've drawn the image onto the canvas. The fact that it creates a new image is not important, since all we care about is the image's contents, so as long as it looks the same, that's good enough. – tonayy Oct 06 '21 at 08:54
  • 1
    Yes, there is a lot of processing being done when you call `ctx.getImageData()`: The bitmap that was until there stored on the GPU buffer has to be moved to the CPU, from there every pixels have to be read, and unmultiplied to RGBA before being stored in the ImageData's buffer. That's literally one of the slowest operations in the canvas API. And yes, it does matter that you are now generating a new bigger image with more encoding artifacts. – Kaiido Oct 06 '21 at 08:59
  • I think you must've misunderstood what I meant. What you're describing is a consequence of how the data is stored. Of course it has to be read, unpacked, and serialized into a JS object. What I mean is that it isn't additionally converted to PNG/JPEG/etc *on top of that*. That's why I wrote **extra processing**. The extra artifacts only matter if you're trying to represent *the exact same image*. OP's question is a little vague in this regard. – tonayy Oct 06 '21 at 09:19
  • 2
    "There is no extra processing, so this method is fully synchronous." This sentence clearly states that this method is synchronous *because* there is no extra processing. That's just not true, it's synchronous for other reasons. And "no extra processing" is in contrast with your first paragraph which stated "the data has to be processed, which may take a long time, and should not block the main thread." Here too the data has to be processed, a different processing but still a processing, that still "may take a long time" and could also "block the main thread". – Kaiido Oct 06 '21 at 09:30
  • nit: missing a comma after "image/jpeg" and before 1 – Andrew Halpern Feb 01 '23 at 23:02