Can I apply a mask on an image with Ruby? - ruby-on-rails

I have a Rails app where users can upload images. After they upload an image, I need to apply a mask on that image, so parts of it become transparant. Is this possible?

RMagick (a Ruby image manipulation library) allows you to apply clipping path on objects : http://www.imagemagick.org/RMagick/doc/rvgclip.html
Basically, you'll need to :
create a clipping path instance
use the drawing API to draw your mask
apply the path to your image
A click on the keyhole image" there pops up an example of how to achieve this :
require 'rvg/rvg'
hat = Magick::Image.read('images/Flower_Hat.jpg').first
rvg = Magick::RVG.new(hat.columns, hat.rows) do |canvas|
keyhole = Magick::RVG::ClipPath.new do |path|
path.circle(60, canvas.width/2, 80)
path.polygon(canvas.width/2-10, 60, 40, 230, 160, 230, canvas.width/2+10, 60)
end
canvas.image(hat, nil, nil, 20, 20).styles(:clip_path=>keyhole)
end
rvg.draw.write('rvg_clippath.gif')
EDIT :
If you need to apply a transparent background the simplest approach should be to crop your image, then use the Image#composite! method to add a trnasparent layer.

Related

Generate QR codes with a logo or having background colors in Ruby

I'm using rQrCode for generating QR Codes in Ruby. But I want to change the background colors or add an image in the middle of that generated QR Codes. I did some research but found nothing.
Have anyone implemented it in Ruby yet? Or do you have any idea about it?
Thanks a lot.
I'm using rQrCode for generating QR Codes in Ruby. But I want to change the background colors [...]
Reviewing the rQrCode documentation you change the fill colour of QR codes using the following :color options when creating the QR code as either an image, ANSI, SVG or HTML table, as follows:
require "rqrcode"
qrcode = RQRCode::QRCode.new("http://stackoverflow.com/questions/42691235/generate-qr-codes-with-a-logo-or-having-background-colors-in-ruby")
image = qrcode.as_png(color: "000")
svg = qrcode.as_svg(color: "000")
html = qrcode.as_html # `td.black { border-color: #000; }` in stylesheet
string = qrcode.as_ansi(dark: "\033[40m")
You can also change the background colour, use the fill option instead
qrcode = RQRCode::QRCode.new("http://stackoverflow.com/questions/42691235/generate-qr-codes-with-a-logo-or-having-background-colors-in-ruby")
image = qrcode.as_png(fill: "000")
svg = qrcode.as_svg(fill: "000")
html = qrcode.as_html # `table { background-color: #000; }` in stylesheet
string = qrcode.as_ansi(light: "\033[40m")
These can either be used together or alone, its up to you.
However, I will warn you that depending on the contrast of the colours used, it may not be able readable by your, or someone else's, QR scanner. Also, change the QR code black squares to a different colour can break the QR code or not be register by the scanner.
As for appending or merging an image
[...] or add an image in the middle of that generated QR Codes.
Generate your QR code as before and give it a Transparent background colour
# require "chunky_png" # this already required by `rqrcode`
require "rqrcode"
qrcode = RQRCode::QRCode.new("http://stackoverflow.com/questions/42691235/generate-qr-codes-with-a-logo-or-having-background-colors-in-ruby")
TRANSPARENT = ::ChunkyPNG::Color::TRANSPARENT
qr_image = qrcode.as_png(fill: TRANSPARENT, module_px_size: 4)
_ You can ignore the module_px_size: 4 option, it was useful in my following example._
Then load your logo as a separate image and compose a new image with a reasonable width & height offset; I center the known smaller image within the larger image
logo = ChunkyPNG::Image.from_file("logo.png")
height = (logo.dimension.height / 2).floor - (qr_image.dimension.height / 2).floor
width = (logo.dimension.width / 2).floor - (qr_image.dimension.width / 2).floor
qr_composed = logo.compose(qr_image, width, height)
qr_composed.save("qr_composed.png")
I have omitted logic to determine which is the large image before composing them, otherwise you will likely in encounter an out of bounds error, like so
~/gems/2.3.0/gems/chunky_png-1.3.8/lib/chunky_png/canvas/operations.rb:393:in `check_size_constraints!': Background image width is too small! (ChunkyPNG::OutOfBounds)
However, you can create a new blank image and merge the logo & QR code images either horizontally; e.g.
new_width = logo.dimension.width + qr_image.dimension.width
qr_merged_horizontally = ChunkyPNG::Image.new(new_width, logo.dimension.height, ::ChunkyPNG::Color::WHITE)
qr_merged_horizontally.compose!(logo, 0, 0)
qr_merged_horizontally.compose!(qr_image, logo.dimension.width)
qr_merged_horizontally.save("qr_merged_horizontally.png")
Or vertically; e.g.
new_height = logo.dimension.height + qr_image.dimension.height
qr_merged_vertically = ChunkyPNG::Image.new(logo.dimension.width, new_height, ::ChunkyPNG::Color::WHITE)
qr_merged_vertically.compose!(logo, 0, 0)
qr_merged_vertically.compose!(qr_image, 0, logo.dimension.height)
qr_merged_vertically.save("qr_merged_vertically.png")
By combining their widths or heights respectively.
Otherwise, it will require more external information or manual manipulation to position the images amongst one another.
References:
File: README — Documentation for rqrcode (0.10.1)
Module: ChunkyPNG::Color — Documentation for chunky_png (1.2.0)
You can overlay any image in the middle as long as it covers less area than the QR code error correction level. It's pretty simple to do with the current version of RQRCode (2.0.0):
qr_code = RQRCode::QRCode.new("some data", level: :h)
qr_code_png = qr_code.as_png(size: 600)
logo = ChunkyPNG::Image.from_file('some/path/logo.png')
qr_code_png.compose!(logo, 200, 200) # adjust the coordinates according to the logo size
qr_code_png.to_blob # binary file result

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.

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")

How to crop image using opencv-ruby gem?

I wish to crop an image using the opencv-ruby gem, how can I do this?
If you just want to crop and resize you should probably use the rmagic gem instead (https://github.com/rmagick/rmagick, docs: http://studio.imagemagick.org/RMagick/doc/).
# note the .first, since read returns an array of layers/images
image = Magick::Image::read("my_file.jpg").first
cropped_image = image.crop(x_start, y_start, width, height, true);
cropped_image.write("my_file_cropped.jpg")
If you must use OpenCV, then this should get you there or close
image = IplImage::load("my_file.jpg")
sub = image.sub_rect(x,y, width, height)
sub.save_image("my_file_cropped.jpg")

Prawn: css like overflow: hidden for bounding boxes with images

I want to clip an image if it goes beyond the dimensions of a bounding box. Just like how CSS overflow: hidden would do it. Eg.
pdf.grid([0, 0], [3, 27]).bounding_box do
pdf.image image_file
end
Now currently, this image would overflow outside the bounding box if its larger than it. Is there any way to clip the image when it goes beyond the bounding box. ? I know this is possible for text when using text_box.
you can set the size of the image or get the image to scale so it fits within a certain area while maintaining proportions, do not believe you can crop an image.
If your page is not dynamic, that is the image area will always be the same this should be OK.
pdf.image "image_file_path_&_name", :position => :center, :fit => [100,450];
This is based on v0.8.4.
Unfortunately, there seems to exist no proper way to crop an image to a bounding box at the moment. Faced with this problem I figured out this beauty:
class SamplePdf
include Prawn::View
def initialize
crop_width = 100 # your width here
crop_height = 50 # your height here
image_path = '/path/to/your_image.jpg'
bounding_box [0, 0], width: crop_width, height: crop_height do
pdf_obj, _ = build_image_object(image_path)
x, y = document.send(:image_position, crop_width, crop_height, {})
document.send(:move_text_position, crop_height)
label = "I#{document.send(:next_image_id)}"
document.state.page.xobjects.merge!(label => pdf_obj)
cm_params = PDF::Core.real_params([crop_width, 0, 0, crop_height, x, y - crop_height])
document.renderer.add_content("\nq\n#{cm_params} cm\n/#{label} Do\nQ")
end
end
end
It basically adapts the Prawn::Images#image method, but skips the calculation of the image's dimensions and the scaling respectively.
It's not exactly a clean solution. Please keep me posted if you find a better one.
You should keep in mind though that this snippet leverages some implementation details which are not part of Prawn's public API and can change anytime.
At the time of writing Prawn 2.0.1 was the most recent version.

Resources