Rmagick setting opacity in watermark with transparency - ruby

I am trying to create a watermark with different opacity values (from 0 opaque value to 1 totally transparent).
I have the following method for RMagick in ruby:
# 0 = opaque (Magick::OpaqueOpacity) 1= transparent (Magick::TransparentOpacity)
def watermark(opacity = 0.99, size = 'm')
manipulate! do |img|
logo = Magick::Image.read("#{Rails.root}/app/assets/images/watermark#{size}.png").first
logo.alpha(Magick::ActivateAlphaChannel)
logo.opacity = (1 - opacity.to_f) * Magick::QuantumRange
img.alpha(Magick::ActivateAlphaChannel)
img = img.composite(logo, Magick::NorthWestGravity, 0, 0, Magick::OverCompositeOp)
end
end
My problem is that it seems to work, but the composite mode or the alpha composite or setting the opacity or alpha is failing, because I get a black transparency in the image. For example if my watermark is a totally transparent image with a text, that I put over a car image, then I get a more dark or nightly image with the watermark, so the background of the watermark it is not blending properly.
Any suggestions to set properly the opacity in the watermark image? Maybe some method to disolve the watermark?
EDIT: Adding image examples:
http://uppix.com/f-watermarkg53925b100016ab8e.png (watermark)
http://oi62.tinypic.com/2us8rxl.jpg (base image)
http://oi60.tinypic.com/2pt6mg3.jpg (composition)

Thanks to Neil Slater, I finally found the right solution. I need a combination of composite operation of DstIn + Over in my finalt result:
def watermark(opacity = 0.99, size = 'm')
manipulate! do |img|
logo = Magick::Image.read("#{Rails.root}/app/assets/images/watermark#{size}.png").first
logo.alpha(Magick::ActivateAlphaChannel)
white_canvas = Magick::Image.new(logo.columns, logo.rows) { self.background_color = "none" }
white_canvas.alpha(Magick::ActivateAlphaChannel)
white_canvas.opacity = Magick::QuantumRange - (Magick::QuantumRange * opacity)
# Important: DstIn composite operation (white canvas + watermark)
logo_opacity = logo.composite(white_canvas, Magick::NorthWestGravity, 0, 0, Magick::DstInCompositeOp)
logo_opacity.alpha(Magick::ActivateAlphaChannel)
# Important: Over composite operation (original image + white canvas watermarked)
img = img.composite(logo_opacity, Magick::NorthWestGravity, 0, 0, Magick::OverCompositeOp)
end
end

Related

Add padding to object in 4-channel image

I have a 4-channel image (.png, .tif) like this one:
I am using OpenCV, and I would like to add padding of type BORDER_REFLECT around the flower. copyMakeBorder is not useful, since it adds padding to the edges of the image.
I can add certain padding if I split the image in bgr + alpha and apply dilate with BORDER_REFLECT option on the bgr image, but that solution spoils all the pixels of the flower.
Is there any way to perform a selective BORDER_REFLECT padding addition on a ROI defined by a binary mask?
EDIT:
The result I expect is something like (sorry I painted it very quickly with GIMP) :
I painted two black lines to delimit the old & new contour of the flower after the padding, but of course those lines should not appear in the final result. The padding region (inside the two black lines) must be composed by mirrored pixels from the flower (I painted it yellow to make it understandable).
A simple python script to resize the image and copy the original over the enlarged one will do the trick.
import cv2
img = cv2.imread('border_reflect.png', cv2.IMREAD_UNCHANGED)
pad = 20
sh = img.shape
sh_pad = (sh[0]+pad, sh[1]+pad)
imgpad = cv2.resize(img, sh_pad)
imgpad[20:20+sh[0], 20:20+sh[1], :][img[:,:,3]==255] = img[img[:,:,3]==255]
cv2.imwrite("padded_image.png", imgpad)
Here is the result
But that doesn't look very 'centered'. So I modified the code to detect and account for the offsets while copying.
import cv2
img = cv2.imread('border_reflect.png', cv2.IMREAD_UNCHANGED)
pad = 20
sh = img.shape
sh_pad = (sh[0]+pad, sh[1]+pad)
imgpad = cv2.resize(img, sh_pad)
def get_roi(img):
cimg = img[:,:,3].copy()
contours,hierarchy = cv2.findContours(cimg,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#Remove the tiny pixel noises that get detected as contours
contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 10]
x,y,w,h = cv2.boundingRect(cnt)
roi=img[y:y+h,x:x+w]
return roi
roi = get_roi(img)
roi2 = get_roi(imgpad)
sh = roi.shape
sh2 = roi2.shape
o = ((sh2[0]-sh[0])/2, (sh2[1]-sh[1])/2)
roi2[o[0]:o[0]+sh[0], o[1]:o[1]+sh[1], :][roi[:,:,3]==255] = roi[roi[:,:,3]==255]
cv2.imwrite("padded_image.png", imgpad)
Looks much better now
The issue has been already addressed and solved here:
http://answers.opencv.org/question/90229/add-padding-to-object-in-4-channel-image/

Is there a max image size (pixel width and height) within wx where png images lose there transparency?

Initially, I loaded in 5 .png's with transparent backgrounds using wx.Image() and every single one kept its transparent background and looked the way I wanted it to on the canvas (it kept the background of the canvas). These png images were about (200,200) in size. I proceeded to load a png image with a transparent background that was about (900,500) in size onto the canvas and it made the transparency a black box around the image. Next, I opened the image up with gimp and exported the transparent image as a smaller size. Then when I loaded the image into python the image kept its transparency. Is there a max image size (pixel width and height) within wx where png images lose there transparency? Any info would help. Keep in mind that I can't resize the picture before it is loaded into wxpython. If I do that, it will have already lost its transparency.
import wx
import os
def opj(path):
return apply(os.path.join, tuple(path.split('/')))
def saveSnapShot(dcSource):
size = dcSource.Size
bmp= wx.EmptyBitmap(size.width, size.height)
memDC = wx.MemoryDC()
memDC.SelectObject(bmp)
memDC.Blit(0, 0, size.width, size.height, dcSource, 0,0)
memDC.SelectObject(wx.NullBitmap)
img = bmp.ConvertToImage()
img.SaveFile('path to new image created', wx.BITMAP_TYPE_JPEG)
def main():
app = wx.App(None)
testImage = wx.Image(opj('path to original image'), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
draw_bmp = wx.EmptyBitmap(1500, 1500)
canvas_dc = wx.MemoryDC(draw_bmp)
background = wx.Colour(208, 11, 11)
canvas_dc.SetBackground(wx.Brush(background))
canvas_dc.Clear()
canvas_dc.DrawBitmap(testImage,0, 0)
saveSnapShot(canvas_dc)
if __name__ == '__main__':
main()
I don't know if I got this right. But if I convert your example from MemoryDC to PaintDC, then I could fix the transparency issue. The key was to pass True to useMask in DrawBitmap method. If I omit useMask parameter, it will default to False and no transparency will be used.
The documentation is here: http://www.wxpython.org/docs/api/wx.DC-class.html#DrawBitmap
I hope this what you wanted to do...
import wx
class myFrame(wx.Frame):
def __init__(self, testImage):
wx.Frame.__init__(self, None, size=testImage.Size)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.testImage = testImage
self.Show()
def OnPaint(self, event):
dc = wx.PaintDC(self)
background = wx.Colour(255, 0, 0)
dc.SetBackground(wx.Brush(background))
dc.Clear()
#dc.DrawBitmap(self.testImage, 0, 0) # black background
dc.DrawBitmap(self.testImage, 0, 0, True) # transparency on, now red
def main():
app = wx.App(None)
testImage = wx.Image(r"path_to_image.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap()
Frame = myFrame(testImage)
app.MainLoop()
if __name__ == '__main__':
main()
(Edit) Ok. I think your original example can be fixed in a similar way
memDC.Blit(0, 0, size.width, size.height, dcSource, 0,0, useMask=True)
canvas_dc.DrawBitmap(testImage,0, 0, useMask=True)
Just making sure that useMask is True was enough to fix the transparency issue in your example, too.

Rmagick - image with transparent background from text

I am trying to create an image file from text using Rmagick in ruby. Additionally I need it's background transparent. My code:
canvas = Image.new(400, 60)
watermark_text = Draw.new
watermark_text.annotate(canvas, 0,0,0,0, text) do
self.gravity = WestGravity
self.pointsize = 50
self.font = "whatever.ttf"
self.fill = 'black'
self.stroke = "none"
end
canvas.write(#path)
It works, but the background of the image is white and I need it to be transparent. Any ideas? File is saved as png.
When you create an image, the default background is white. You can tell rmagick you want a different background:
canvas = Image.new(400, 60) do |c|
c.background_color= "Transparent"
end

Corona - how to make sprite sheets compatible with dynamic image resolution

Corona has a method for creating images that will be displayed dynamically based on device resolution:
img = display.newImageRect("image.png", 100, 100)
Great, but what if all your images are in a sprite sheet, which is recommended for performance? Then you have to do something like this to display the image:
local data = require("sheet1")
local tileSheet = sprite.newSpriteSheetFromData("sheet1.png", data.getSpriteSheetData())
local tileSet = sprite.newSpriteSet(tileSheet, 1, 3)
local img = sprite.newSprite(tileSet)
img.currentFrame = 1
How do you create dynamically sized images from sprite sheets?
use display.contentScaleX http://developer.anscamobile.com/reference/index/displaycontentscalex
here's how http://developer.anscamobile.com/forum/2010/12/08/dynamic-retina-spritesheets-heres-how
Here's how I resized my background animation. It consisted of 2 frames, each 794 x 446. It needed to be full-screen, landscape mode. Refer to step 6 below.
--> Initialize background animations
-- 1. Data = define size and frames
local bgData = { width=794, height=446, numFrames=2, sheetContentWidth=1588, sheetContentHeight=446 }
-- 2. Sheet = define the sprite sheet
local bgSheet = graphics.newImageSheet( "hkc.png", bgData )
-- 3. Animation = define animation characteristics
local bgAnim = {
{ name = "bgAnimation", start = 1, count = 2, time = 800, loopCount = 0, -- repeat forever
loopDirection = "forward"
}
}
-- 4. Display the sprite (can't set size here unlike display.newImageRect)
local bg = display.newSprite( bgSheet, bgAnim )
-- 5. Center the animation
bg:setReferencePoint( display.CenterReferencePoint )
bg.x = display.contentCenterX
bg.y = display.contentCenterY
-- 6. Resize to match screen size based on a ratio with the actual background pixel dimensions
bg:scale( display.contentWidth / 794, display.contentHeight / 446 )
-- 7. Play the animation
bg:play()

rmagick auto scaling with proportions

Is there a way for me to scale and image in rmagick where i set the width, and the height auto scales so the image contain the same proportions?
I use the resize_to_fit method, which will use the parameters supplied as the maximum width/height, but will keep the aspect ration. So, something like this:
#scaled = #image.resize_to_fit 640 640
That will ensure that either the width or height is no greater than 640, but will not stretch the image making it look funny. So, you might end up with 640x480 or 480x640. There is also a resize_to_fit! method that converts in place
If you want to resize to a given width without respect to a bounding box, you will have to write a helper function. Something like this:
#img = Magick::Image::read(file_name).first
def resize_by_width image new_width
#new_height = new_width * image.x_resolution.to_f / image.y_resolution.to_f
new_image = image.scale(new_width, new_height)
return new_image
end
#resized = resize_by_width #img 1024
Hope that helps!

Resources