1

I'm using the vips library for manipulating some images, specifically its Lua binding, lua-vips, and I'm trying to find a way to do a feather effect on the edge of an image.

It's the first time I try a library for this kind of task and I've been looking at this list of functions available, but still no idea on how to it. It's not complex shape, just a basic rectangular image whose top and bottom edges should blend smoothly with the background (another image that I'm currently using vips_composite() on).

Supposing that a "feather_edges" method existed, it would be something like:

local bg = vips.Image.new_from_file("foo.png")
local img = vips.Image.new_from_file("bar.png") --smaller than `bg`
img = img:feather_edges(6) --imagine a 6px feather
bg:composite(img, 'over')

But still it would be nice to specify what parts of the image should be feathered. Any ideas on how to do it?

PiFace
  • 526
  • 3
  • 19

2 Answers2

1

You need to pull the alpha out of the top image, mask off the edges with a black border, blur the alpha to feather the edges, reattach, then compose.

Something like:

#!/usr/bin/luajit

vips = require 'vips'

function feather_edges(image, sigma)
    -- split to alpha + image data 
    local alpha = image:extract_band(image:bands() - 1)
    local image = image:extract_band(0, {n = image:bands() - 1})

    -- we need to place a black border on the alpha we can then feather into,
    -- and scale this border with sigma
    local margin = sigma * 2
    alpha = alpha
        :crop(margin, margin,
            image:width() - 2 * margin, image:height() - 2 * margin)
        :embed(margin, margin, image:width(), image:height())
        :gaussblur(sigma)

    -- and reattach
    return image:bandjoin(alpha)
end

bg = vips.Image.new_from_file(arg[1], {access = "sequential"})
fg = vips.Image.new_from_file(arg[2], {access = "sequential"})
fg = feather_edges(fg, 10)
out = bg:composite(fg, "over", {x = 100, y = 100})
out:write_to_file(arg[3])
jcupitt
  • 10,213
  • 2
  • 23
  • 39
  • It's almost what I need. One problem is that the black border becomes visible. But you gave me great advices, because I couldn't figure it out how to work on specific bands. – PiFace Mar 11 '19 at 02:27
  • I managed to do a small adjustment to your function and it solved that problem! Should I post it as an answer to my own question or just edit my post? – PiFace Mar 11 '19 at 03:30
  • Hi PiFace, I would post as an answer to your own question, then accept that. – jcupitt Mar 11 '19 at 06:57
  • I updated with a solution to the black border issue. – jcupitt Mar 15 '19 at 08:15
1

As jcupitt said, we need to pull the alpha band from the image, blur it, join it again and composite it with the background, but using the function as it was, left a thin black border around the foreground image.

To overcome that, we need to copy the image, resize it according to the sigma parameter, extract the alpha band from the reduced copy, blur it, and replace the alpha band of the original image with it. Like this, the border of the original image will be completely covered by the transparent parts of the alpha.

local function featherEdges(img, sigma)
    local copy = img:copy()
        :resize(1, { vscale = (img:height() - sigma * 2) / img:height() })
        :embed(0, sigma, img:width(), img:height())
    local alpha = copy
        :extract_band(copy:bands() - 1)
        :gaussblur(sigma)
    return img
        :extract_band(0, { n = img:bands() - 1 })
        :bandjoin(alpha)
end
PiFace
  • 526
  • 3
  • 19
  • Nice! But rather than shrinking the alpha, how about masking off the edges? It'll be much quicker, and it'll work for images with complex shapes (since you won't lose the alignment between image and alpha). I updated my answer with another alternative. – jcupitt Mar 15 '19 at 08:13
  • You could remove the `copy` as well --- all libvips operations are non-destructive and return new images rather than modifying their arguments. – jcupitt Mar 15 '19 at 08:16
  • ooh I see, that's true, copying is not necessary. And I didn't think about cropping instead of resizing :P By the way, what kind of complex shapes would that work with? – PiFace Mar 15 '19 at 13:55
  • I usually use this for testing complex shape masking https://en.wikipedia.org/wiki/Portable_Network_Graphics#/media/File:PNG_transparency_demonstration_1.png – jcupitt Mar 15 '19 at 17:06