Unable to retain transparency using ImageMagick's composite - imagemagick

I have two images (both png) with transparency. I am using the MiniMagick gem to crop two copies of a single image into two other images. I'm then wanting to compose one of these images on top of the other, retaining the transparency all the way down.
Using the following code, it is respecting the transparency of image2, but once it is placed on top of image1 (which is what I'm after), the transparency of image1 is changed to black! I need to retain the transparency, but I'm really not sure how to properly use the alpha transparency stuff, if that is even the proper tool here.
image = MiniMagick::Image.open("skin.png")
image1 = MiniMagick::Image.open(image.path)
image2 = MiniMagick::Image.open(image.path)
# Crop and scale image1
MiniMagick::Tool::Mogrify.new do |m|
m.crop '8x8+8+8'
m.scale '144x144'
m.background 'transparent'
m.extent '160x160-8-8'
m << image1.path
end
# Crop and scale image2
MiniMagick::Tool::Mogrify.new do |m|
m.crop '8x8+40+8'
m.scale '160x160'
m << image2.path
end
result = image1.composite(image2) do |c|
c.compose 'Over'
c.alpha 'On'
end
result.write "public/skins/#{profile}.png"
send_file "public/skins/#{profile}.png"
Thanks.

MiniMagick's composite is processed as jpg with initial value.
http://www.rubydoc.info/github/probablycorey/mini_magick/MiniMagick/Image#composite-instance_method
The code below is working for me.
result = image1.composite(image2, 'png') do |c|
c.channel "A"
c.alpha 'Activate'
c.compose 'Over'
end

Related

problem with the 2d interpolation method using scipy.ndimage.zoom

I have an image which is in gray scale.
I wanted to upsamle the image, so I used the following code,
img = cv2.imread('unnamed.jpg')
img_1 = scipy.ndimage.zoom(img,3, order=1)
print(img.shape, img_1.shape)
and the output is
(187, 250, 3) (561, 750, 9)
For some reason, I cannot use plt.imshow(img_1) as it gives error,
TypeError: Invalid shape (561, 750, 9) for image data
I'd appreciate it if somebody could help me with it.
It looks like your image has 3 channels, which means it is not in grayscale. So, either convert it to grayscale first, and apply zoom, or, in case you want to keep the image in color mode, don't apply zoom on the image channels, because it does not make much sense.
# 1st option returns grayscale image
img = cv2.imread('unnamed.jpg',0) # returns grayscale image
img_1 = scipy.ndimage.zoom(img,3, order=1)
# 2nd option returns BGR image
img = cv2.imread('unnamed.jpg',1) # returns RGB image
img_1 = scipy.ndimage.zoom(img,[3,3,1], order=1) # zoom should contain one value for each axis.

Imagemagick rails image composition adding unwanted white background

I have the following code:
begin
big_image = Magick::ImageList.new
#this is an image containing first row of images
first_row = Magick::ImageList.new
#adding images to the first row (Image.read returns an Array, this is why .first is needed)
first_row.push(Magick::Image.read(Rails.root.join("app","assets","images","logo.png")).first)
if #model.avatar.exists?
image = Magick::Image.read(#model.avatar.path).first
image = image.resize_to_fit("450", "401")
first_row.push(image)
end
#adding first row to big image and specify that we want images in first row to be appended in a single image on the same row - argument false on append does that
big_image.push (first_row.append(false))
fileName = #model.id.to_s + ".png"
big_image.append(true).write(Rails.root.join("app","assets","images","shared_logo",fileName))
rescue => e
puts "Errors! -- #{e.inspect}"
end
The codes puts two image on the same row. The images are png. The problem is that the second image has an height less than the first one. Image magick fill the remaining part with an unwanted white background. I want to keep the transparency on the combined image.
You could do something like:
manipulate! do |img|
img.combine_options do |c|
c.background "transparent"
c.gravity "center"
c.extent "450x401"
end
end
This is for RMagick if I'm not mistaken, the names might be slightly different (although I think combine options is using mongrifies methods?).

Crop an Image to the shape of a Vector or Overlay a Shape

I imagine this is a shot in the dark, but is it possible to have a vector file of a shape (in this case a hexagon with rounded corners), and pass an image through some code and have it coming out cropped in the shape of that vector?
I'm attempting to utilize hexagons in my design and have gone through every page I possibly can. I've seen the many HTML and CSS solutions, but none of them achieve what I'm looking for flawlessly.
Another idea I have is maybe overlaying a transparent hexagon shape with white corners on top of the existing image with imagemagick, and then going through and making any white transparent. Thoughts?
I don't have any code for cropping in the shape of a vector file, but here's what I have for overlaying an outline of the shape I want on top of the other picture:
imgfile = "public/" + SecureRandom.uuid + ".png"
SmartCropper.from_file(art.url(:original)).smart_square.resize(225,225).write(imgfile)
overlay = Magick::Image.read("app/assets/images/overlay.png")
img = Magick::Image.read(imgfile)
img.composite(overlay,0,0, Magick::OverCompositeOp)
Right now it's giving me an undefined method error for composite, which is strange because I've followed some other stack overflow questions using the same thing in their models.
Any help is appreciated!
You have fallen for a common ImageMagick trap - the objects you get from the .read method are not Magick::Image objects but Magick::ImageList ones, and for most image types you want the first item from the list.
Without knowing how you have set up your overlay.png file, it is difficult to tell what the best composite option is. However, in a similar situation I found CopyOpacityCompositeOp to be useful, and to have the overlay's transparency control the transparency in the final image.
I tested the following code and it looks like it would do what you want if overlay.png was set up that way:
require 'smartcropper'
imgfile = "test_square.png"
SmartCropper.from_file( 'test_input.png' ).
smart_square.resize( 225, 225 ).write( imgfile )
overlay = Magick::Image.read( 'overlay.png' ).first
img = Magick::Image.read( imgfile ).first
img.composite( overlay, 0, 0, Magick::CopyOpacityCompositeOp ).
write( "test_result.png" )
Instead of reading overlay from a file, you could create it using Magick::Draw like this:
overlay = Magick::Image.new( 225, 225 ) do |i|
i.background_color= "Transparent"
end
gc = Magick::Draw.new
gc.stroke('white').stroke_width(10)
gc.fill('white')
gc.polygon(97.5, 26.25, 178.5, 73.125, 178.5, 167,
97.5, 213.75, 16.5, 167, 16.5, 73.125)
gc.draw( overlay )
NB That's a hexagon, but I've not bothered centering it.

Take center part of image while resizing

I have a problem with resizing image with Carrierwave-MiniMagick-ImageMagick.
I wrote custom method of resizing, cause I need to merge 2 images together and make some processing on them, so standard process methods from MiniMagick are not enough. The problem is with resize method. I need to take center part of the image, but it returns me the top part.
def merge
manipulate! do |img|
img.resize '180x140^' # problem is here
...
img
end
end
Thanks for any help!
I would approach this as follows:
Resize image to a 180x180 square
Remove (180-140)/2 from the top
Remove (180-140)/2 from the bottom
Something like this should do it:
def merge
manipulate! do |img|
img.resize '180x180' # resize to 180px square
img.shave '0x20' # Removes 20px from top and bottom edges
img # Returned image should be 180x140, cropped from the centre
end
end
Of course, this assumes your input image is always a square. If it wasn't square and you've got your heart set on the 180x140 ratio, you could do something like this:
def merge
manipulate! do |img|
if img[:width] <= img[:height]
# Image is tall ...
img.resize '180' # resize to 180px wide
pixels_to_remove = ((img[:height] - 140)/2).round # calculate amount to remove
img.shave "0x#{pixels_to_remove}" # shave off the top and bottom
else
# Image is wide
img.resize 'x140' # resize to 140px high
pixels_to_remove = ((img[:width] - 180)/2).round # calculate amount to remove
img.shave "#{pixels_to_remove}x0" # shave off the sides
end
img # Returned image should be 180x140, cropped from the centre
end
end
This is what resize_to_fill does:
Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension.
Example:
image = ImageList.new(Rails.root.join('app/assets/images/image.jpg'))
thumb = image.resize_to_fill(1200, 630)
thumb.write('thumb.jpg')
The method takes a third argument which is gravity, but it's CenterGravity by default.
You should use crop instead of resize.
Take a look at crop ImageMagick description of crop command here:
http://www.imagemagick.org/script/command-line-options.php#crop
MiniMagick just a wrapper around ImageMagick, so all arguments are the same.

RMagick - how to create a thumbnail with an automatic height?

I am using RMagick for creating thumbnails like this:
img = Magick::Image.read(image_url).first
target = Magick::Image.new(110, 110) do
self.background_color = 'white'
end
img.resize_to_fit!(110, 110)
target.composite(img, Magick::CenterGravity, Magick::CopyCompositeOp).write(thumb_path)
This works well - I'll load the current image, create a "space" for the new thumb and then will place it there.
However, I would need to create a thumb where would be the width 110px and the height would be automatically counted... How to do this?
Thank you
You'd rather use resize_to_fill!
Doc here
image = Magick::Image.read(image_url).first
image.format = "JPG"
image.change_geometry!("110X110") { |cols, rows| image.thumbnail! cols, rows }
image.write("<path to save thumbnail>")
This turns out to be super easy! ImageMagick and GraphicsMagick both maintain aspect ratios properly, so in your case, just give the max width you want the image to be. See http://www.imagemagick.org/script/command-line-processing.php#geometry to learn more about the magick dimension operators.
If you find that you're ruby process' RAM consumption is growing, you may want to switch to an external-exec image library, like https://github.com/mceachen/micro_magick. Also, switching to GraphicsMagick is an all-around win, BTW, giving better image encoding and in less time.
require 'micro_magick'
img = MicroMagick::Convert.new("input.png")
img.resize("110") # this restricts to width, if you want to restrict to height, use "x345"
img.unsharp(1.5) # This runs an "unsharp mask" convolution filter, and is optional
img.write("output.png")

Resources