I'm doing image processing with ImageMagick commands and I would like to port them to RMagick. The goal of this task is to take a picture and to pixelate given areas (one or more) for privacy purpose.
Here is my bash script (script.sh
), which works very well using the convert
command:
convert invoice.png -scale 10% -scale 1000% pixelated.png
convert invoice.png -gamma 0 -fill white -draw "rectangle 35, 110, 215, 250" mask.png
convert invoice.png pixelated.png mask.png -composite result.png
Now I want to create the Ruby version of this script using ImageMagick. Here is what I have now:
require 'rmagick'
# pixelate_areas('invoice.png', [ [ x1, y1, width, height ] ])
def pixelate_areas(image_path, areas)
image = Magick::Image::read(image_path).first
pixelated = image.scale(0.1).scale(10)
mask = Magick::Image.new(image.columns, image.rows) { self.background_color = '#000' }
areas.each do |coordinates|
area = Magick::Image.new(coordinates[2], coordinates[3]) { self.background_color = '#fff' }
mask.composite!(area, coordinates[0], coordinates[1], Magick::OverCompositeOp)
end
# Now, how can I merge my 3 images?
# I need to extract the part of pixelated that overlap with the white part of the mask (everything else must be transparent).
# Then I have to superpose the resulting image to the original (it's the easy part).
end
As you can see, I'm stuck at the last step. What operation do I need to do with my original picture, my pixelated picture and my mask in order to have this result?
How can I build an image with just the overlapping of the white part of the mask and the pixelated picture. Just like this one but with transparency instead of black?
First, why are you porting commands to RMagick when you've got something that already works? The bash version is short and comprehensible. If this is just part of a larger script that you're porting, don't be afraid of system()
.
That said, here's a different tack that I believe accomplishes what you're trying to do in a more straightforward manner.
require 'RMagick'
def pixelate_area(image_path, x1, y1, x2, y2)
image = Magick::Image::read(image_path).first
sensitive_area = image.crop(x1, y1, x2 - x1, y2 - y1).scale(0.1).scale(10)
image.composite!(sensitive_area, x1, y1, Magick::AtopCompositeOp)
image.write('result.png') { self.depth = image.depth }
end
This then seems to do the same as your original bash commands:
pixelate_area('invoice.png', 35, 110, 215, 250)
Since it looks like you want to handle multiple areas to blur out, here's a version that takes an array of areas (each as [x1, y1, x2, y2]
):
def pixelate_areas(image_path, areas)
image = Magick::Image::read(image_path).first
areas.each do |area|
x1, y1, x2, y2 = area
sensitive_area = image.crop(x1, y1, x2 - x1, y2 - y1).scale(0.1).scale(10)
image.composite!(sensitive_area, x1, y1, Magick::AtopCompositeOp)
end
image.write('result.png') { self.depth = image.depth }
end