-
I am developing an Avalonia based desktop app for generative art using NetVips, which needs to composite images to create gif frames, join the frames to make a multiple page image, then call gifsave to generate the gif. I have encountered a case where gifsave would generate black and white gif but after I convert the joined image to png buffer than reload it to vips image, gifsave would create colored gif correctly.
It seems the vips image with joined frames might need to be tuned to include certain color or band information for gifsave to work correctly. This problem would be fixed by above code of converting to png then reload to buffer. I have created a reporduce repo and posted the issue in NetVips but it looks now it is might not be a NetVips issue. The reproduce repo: https://github.com/lanyusan/net-vips-avanolia-bug
Please run |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 10 replies
-
Hi @lanyusan, This is usually a problem with the image = image.Copy(interpretation=Vips.Enums.Interpretation.sRGB) Just before the save. Current libvips has better rules for deciding whether to write (mono + many alpha) or (rgb + alpha), so you can skip this extra set-interpretation step. Or just run current libvips on all of your systems, of course. |
Beta Was this translation helpful? Give feedback.
-
... hmm actually, I'm wrong. I made a version of your code in python: #!/usr/bin/python3
import sys
import re
import pyvips
pngs = [pyvips.Image.new_from_file(filename)
for filename in sys.argv[1:] if re.match(".*\.png", filename)]
gifs = [pyvips.Image.new_from_file(filename, n=-1)
for filename in sys.argv[1:] if re.match(".*\.gif", filename)]
# number of pages all gifs have in common
n_pages = min([gif.get("n-pages") for gif in gifs])
# split gifs to pages
gifs = [[gif.crop(0, i * gif.get("page-height"),
gif.width, gif.get("page-height"))
for i in range(n_pages)]
for gif in gifs]
# composite to a list of frames
frames = []
for i in range(n_pages):
base_image = pngs[0]
for_layering = pngs[1:]
for gif in gifs:
for_layering.append(gif[i])
blended_frame = base_image.composite(for_layering, "over")
frames.append(blended_frame)
# and write as a GIF
image = pyvips.Image.arrayjoin(frames, across=1)
image = image.copy(interpretation="srgb")
image.set_type(pyvips.GValue.gint_type, "loop", 0)
image.set_type(pyvips.GValue.array_int_type, "delay", [10] * n_pages)
image.set_type(pyvips.GValue.gint_type, "page-height", frames[0].height)
print("writing x.gif ...")
image.write_to_file("x.gif") And it still outputs a mono GIF. I'll investigate a bit more. |
Beta Was this translation helpful? Give feedback.
-
I think I found the error, for my test images at least. The final gifsave is inheriting palettes from the input GIFs, and one of those gifs was monochrome. The solution is to set the This program: #!/usr/bin/python3
import sys
import re
import pyvips
pngs = [pyvips.Image.new_from_file(filename)
for filename in sys.argv[2:] if re.match(".*\.png", filename)]
gifs = [pyvips.Image.new_from_file(filename, n=-1)
for filename in sys.argv[2:] if re.match(".*\.gif", filename)]
# number of pages all gifs have in common
n_pages = min([gif.get("n-pages") for gif in gifs])
# split gifs to pages
gifs = [[gif.crop(0, i * gif.get("page-height"),
gif.width, gif.get("page-height"))
for i in range(n_pages)]
for gif in gifs]
# composite to a list of frames
frames = []
for i in range(n_pages):
base_image = pngs[0]
for_layering = pngs[1:]
for gif in gifs:
for_layering.append(gif[i])
blended_frame = base_image.composite(for_layering, "over")
frames.append(blended_frame)
# and write as a GIF
image = pyvips.Image.arrayjoin(frames, across=1)
image.set_type(pyvips.GValue.gint_type, "loop", 0)
image.set_type(pyvips.GValue.array_int_type, "delay", [10] * n_pages)
image.set_type(pyvips.GValue.gint_type, "page-height", frames[0].height)
print(f"writing {sys.argv[1]} ...")
image.write_to_file(sys.argv[1]) With these inputs: Run like this:
Makes this: Which I think is correct. |
Beta Was this translation helpful? Give feedback.
-
@kleisauke @dloebl I think cases like this mean we should flip the sense of How about deprecating This is a compatibility break, of course, which is bad :( But probably best done as soon as possible. What do you think? |
Beta Was this translation helpful? Give feedback.
@kleisauke @dloebl I think cases like this mean we should flip the sense of
reoptimise
for 8.14 -- it's probably too dangerous and confusing to have as a default behaviour.How about deprecating
reoptimise
and changing the default behaviour to always recompute the palette. As an option, we add a new flag called perhapscopy_palette
(maybe? any better ideas?) which will skip the reoptimisation step and reuse the palette from the metadata, if available.This is a compatibility break, of course, which is bad :( But probably best done as soon as possible.
What do you think?