Search code examples
rubyminimagick

Saving pixel color in array Ruby


I use CarrierWave and MiniMagick for work with images. I upload image. Right now I need to get values of all pixels in image and store them into array

If I use next code it works well

def pixelcolor 
@image = MiniMagick::Image.open(image.path)     
return @image.get_pixels[56][34][1] # 56 and 34 - random numbers for row and column
end

next code works as well

def pixelcolor 
@image = MiniMagick::Image.open(image.path)     
width = @image.width
color = Array.new   
for i in 0..width
  color[i] = i
end
return color
end

But if I try to do next code it doesn't work

def pixelcolor 
@image = MiniMagick::Image.open(image.path)     
width = @image.width
color = Array.new
for i in 0..width
    color[i] = @image.get_pixels[45][65][0]
end
return color
end

As result I need something like this

def pixelcolor 
@image = MiniMagick::Image.open(image.path)     
width = @image.width
height = @image.height  
red = Array.new  
green = Array.new
blue = Array.new    
for i in 0..width
    for j in 0..height
      red[i][j] = @image.get_pixels[width][height][0]
      green[i][j] = @image.get_pixels[width][height][1]
      blue[i][j] = @image.get_pixels[width][height][2]
    end
end
return red, green, blue
end

Much time ago I have created very similar project in C++ for working with images so I have idea of what do to but not sure how do do it correct in Ruby as I'm new in it


Solution

  • The problem you're facing stems from the fact that Ruby doesn't have a concept of "multidimensional arrays", individual arrays are always one-dimensional.

    You can, however, have nested arrays, which are arrays that contain arrays, like [[], [], []].


    Let's have a look at two particular places:

    red = Array.new # Note: this could as well be `red = []`
    

    So you have assigned red a single empty array. Alright. Here's what you do later on:

    red[i][j] = ...
    

    We don't even need the rest of the line, it's visibly broken already.

    On the very first iteration red is still an empty array. Thus, red[i] is nil, since there's absolutely nothing inside. nil does not have array accessors ([] and []=), so you'll be getting errors trying to read or write to it as if it were an array.

    So you need red[i] to be an actual array, so you can put data into it. Let's make it happen.

    Since you know the size of your arrays in advance, you can just allocate and populate red with with arrays of appropriate length immediately:

    red = Array.new(width) { Array.new(height) }
    

    For instance, for width = 2 and height = 3 here's the result you'd get:

    [
      [nil, nil, nil],
      [nil, nil, nil]
    ]
    

    So now that you have allocated all the cells that you'll need, you can start overwriting the nils with something meaningful.

    See the docs for Array.new for the various ways to construct an array.


    Careful:

    I have used a "blocky" form of Array.new for a reason.

    Beginners often make a mistake of using a single value to populate the array like so:

    red = Array.new(width, Array.new(height))
    

    While this form has its uses, it won't have the effect that you need: all the entries in red will refer to one and the same array, so modifying any single row would look as if all of them are modified.


    Side note: you probably want to swap width and height in dimensions, since in graphics programs usually deal with arrays of rows, not columns. Depends, though. Might not have any effect on your particular problem.