Using `Image.avatar` with PNGs
jarrodmoldrich opened this issue · 4 comments
Hi @kipcole9 ,
I was trying the avatar function lately and wondering why it wasn't working for me. It turned out that when I switch the input to jpeg it worked. I looked a little deeper and it's because the PNG already has an alpha channel which is not removed before the bandjoin
. Compositing the alpha channels is way too complex for the use case, so simply removing this channel in the vips code below fixes the problem.
Mix.install([{:image, "~> 0.24.0"}])
defmodule Test do
def vips_avatar(in_file, out_file) do
{:ok, image} = Vix.Vips.Image.new_from_file(in_file)
# mask
{:ok, {circle, _}} = Vix.Vips.Operation.svgload_buffer("<svg viewBox=\"0 0 512 512\"><circle style=\"fill: black; stroke: none\" cx=\"256\" cy=\"256\" r=\"256\"/></svg>")
true = Vix.Vips.Image.has_alpha?(circle)
{:ok, mask} = Vix.Vips.Operation.extract_band(circle, Vix.Vips.Image.bands(circle) - 1)
# write
{:ok, image_without_alpha} = ensure_alpha_removed(image) # <-------------------
{:ok, final_image} = Vix.Vips.Operation.bandjoin([image_without_alpha, mask])
Vix.Vips.Image.write_to_file(final_image, out_file)
end
def ensure_alpha_removed(image) do
band_count = Vix.Vips.Image.bands(image)
if band_count == 4,
do: Vix.Vips.Operation.extract_band(image, 0, n: band_count - 1),
else: {:ok, image}
end
def image_avatar(in_file, out_file) do
Image.open!(in_file)
|> Image.avatar!()
|> Image.write!(out_file)
end
end
Test.vips_avatar("broken_example.png", "test_output_vips_png.png")
Test.vips_avatar("broken_example.jpeg", "test_output_vips_jpeg.png")
Test.image_avatar("broken_example.png", "test_output_image_png.png")
Test.image_avatar("broken_example.jpeg", "test_output_image_jpeg.png")
Script is here with test images.
I've switched to jpeg, so it's not a problem for me, but it may confuse future developers, so I'm leaving this here. Let me know if you'd like a PR.
Cheers,
Jarrod
@jarrodmoldrich thanks very much for getting to the bottom of this. I feel as if the right approach is to flatten out the image before compositing (ie with Image.flatten
) so I'll experiment with that first and then seek your review to check its ok.
I've pushed a commit that flattens the image before compositing the mask which I think should resolve this issue. Would you mind confirming by configuring {:image, github: "elixir-image/image"}
and checking with your test image?
That's fixed, thank you!
Thanks @jarrodmoldrich, closing for now and publish an update in the next hour.