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
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 nil
s with something meaningful.
See the docs for Array.new
for the various ways to construct an array.
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.