-2

I'm working on a project and I have an image from the user in the data:image/png form. I need it to be a 28x28 image. I'm trying to convert the 280x280 image from to user to 28x28, and then into a 784x1 array with each pixel value.

I have tried a number of packages and none of them seem to work.

Amukh1
  • 9
  • 1
  • 2
    How's this related to ReactJS? – Christian Vincenzo Traina Jan 03 '23 at 22:03
  • I'm getting the image from the user on a react site I'm building, and trying to convert the image within the react code. – Amukh1 Jan 03 '23 at 22:04
  • See [Resize image with javascript canvas (smoothly)](https://stackoverflow.com/questions/19262141/resize-image-with-javascript-canvas-smoothly) and [Drawing an image from a data URL to a canvas](https://stackoverflow.com/questions/4773966/drawing-an-image-from-a-data-url-to-a-canvas) – Spectric Jan 03 '23 at 22:08
  • Does this answer your question? [How to scale (resize) image keep aspect ratio in javascript](https://stackoverflow.com/questions/39790874/how-to-scale-resize-image-keep-aspect-ratio-in-javascript) – PM 77-1 Jan 03 '23 at 22:08
  • I'm not trying to display the resized image in a canvas though, so nothing about canvases will help me. I'm just trying to get the image data into a 28x28 form so I can turn it into an array/tensor. – Amukh1 Jan 03 '23 at 22:09

1 Answers1

2

So you're in a browser environment and have a data: URL describing an image? You don't need a package.

  • Have the browser parse the URL and read it as an image with i = new Image(), i.addEventListener("load", listener), i.src = 'data:...'.
  • Within listener, create a canvas = new HTMLCanvasElement(), and set its dimensions (width, height) to 28x28. The element doesn't need to be in the document, we'll just use it to draw things.
  • Call ctx = canvas.getContext("2d") to get a 2D drawing canvas.
  • Call ctx.fillRect(0, 0, 28, 28) to fill the canvas to a solid color, just in case the user's image contains an alpha channel you don't care about.. (Set fillColor first if black isn't your thing.)
  • Call ctx.drawImage(i, 0, 0, 28, 28) to draw the original image onto your context.
  • Call ctx.getImageData(0, 0, 28, 28) to get an ImageData object describing the image.
  • The ImageData object's data is now a 28 * 28 * 4 = 3136 byte array in RGBARGBARGBA... order. You wanted 784 (28 * 28 * 1) bytes, so either choose a channel (0, 1, 2) from the data and copy with that offset and a stride of 4 to a 784-byte Uint8ClampedArray, or average every 3 bytes to take a lazy grayscale of the RGB image data. (For a more correct grayscale, use e.g. v = 0.299 * r + 0.587 * g + 0.114 * b for the YUV brightness scale.)
  • Put the resulting 784-byte array where you need it to be.

(Since you mention you're working with tensors, and 28x28 sounds a lot like MNIST images, you may want to add additional logic to invert the grayscale if it looks like it's black-on-white; the MNIST images are white-on-black, and you'll get weird results if they're not the same.)

AKX
  • 152,115
  • 15
  • 115
  • 172
  • Thanks @AKX! I followed everything and it worked! I'm having a little trouble moving from the 3136 RGB(...) Array to the 784x1 array by "choosing a channel". I am working in MNIST, how do I convert/invert the grayscale? – Amukh1 Jan 03 '23 at 22:43
  • To fill your 784-byte array, use a good ol' for loop, and assign `out[i] = imageData.data[i * 4]` to grab the first (red) channel of each pixel. If the original image is monochrome, it'll work out like that. – AKX Jan 03 '23 at 22:50