0

I am trying to figure out how to load a PNG image and create a matrix containing the RGB values of each pixel. I am currently using the following method to load the file and get the various RGB values:

  def to_pixels
    File.open(@file, 'r') do |file|
      byte_block = file.read
      byte_block.each_byte do |byte|
        @pixels << byte
      end
    end
  end

From my understanding, each pixel contains 3-bytes representing the R,G, and B value. I initially tried taking the output array @pixels and grouping into sub-groups of 3 elements assuming that the pixel order and RGB value of each pixel was preserved in my output. E.g.:

@pixels = @pixels.each_slice(3).to_a

The length of the array that I created was nearly the same length as the total number of pixels in my original image, so I was encouraged. However, I used ChunkyPNG to take my RGB pixel array and print back to an image, and it looks like random color noise. Could some of the bytes being input into @pixels represent metadata? Or perhaps would the bytes being output not be ordered as R,G, then B values of individual pictures, but perhaps all the R bytes, then all the G bytes, then all the B bytes for example?

I would like to figure out how to transform the byte array into an array of arrays grouping RGB values of the image in some logical order (start at top left and work across to the right, or start in top left and work down, etc)

michjo
  • 407
  • 2
  • 17
  • The [PNG data format](http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html) is not just a simple array of RGB values and it might be compressed too. – spickermann Sep 08 '18 at 14:02
  • ...and it **is** compressed too. Yes, you are reading in metadata and other stuff too. Use a PNG library to read PNG files, there is no point in reinventing the wheel. – Cris Luengo Sep 08 '18 at 16:35

1 Answers1

1

The chunky_png gem can do this. https://github.com/wvanbergen/chunky_png

Something like:

img = ChunkyPNG::Image.from_file(@file)
img.pixels.each do |pixel|
 puts ChunkyPNG::Color.to_hex(pixel) # would spit out a hex string like "#b8e1f6ff"
end

There are a number of other methods if you want different formats: to_grayscale, to_grayscale_alpha_bytes, to_grayscale_bytes, to_hex, to_hsb, to_hsl, to_hsv, to_s, to_truecolor_alpha_bytes, to_truecolor_bytes.

Philip Hallstrom
  • 19,673
  • 2
  • 42
  • 46
  • This helped me out after hours of trying to do it in a bunch of other ways! Ultimately, I am building a Chrome extension and wanting to change the extension icon based on whether the extension is active or not. To do this, you have to be a service_worker context which means you don't have the DOM and all its useful Canvas helpers. Ultimately, I needed a pixel array to initialize an ImageData (javascript) object. And so I used the `to_truecolor_alpha_bytes` helper to get the pixel array that can be used for ImageData – Peter P. Mar 27 '22 at 01:09