I am trying to use gradio for a web-based gif editor. The user uploads a gif and then performs various manipulations on it.
My problem is that once the gif is uploaded as input, the resulting object is just a static image of the first GIF frame. To be clear, the web gui will display the animated gif, but the underlying code object is not a gif. When I open a GIF in PIL, it is loaded as a multiframe image that you can perform .seek on to advance frames. In Gradio, the image uploads as a simple Image object, whereas pure Pillow will upload a gif as a gif plugin object.
I can't find any resources that address this, as it seems most are focused on static images and AI in Gradio right now.
I have explained the issue. The desired outcome I am looking for is a way to get the actual uploaded gif as a multi-frame image object in my python script.
Here comes my code. Problem arises in 'z_gif_handler.py' when I attempt to get the n_frame property of the PIL image. Traceback included at bottom:
main.py
from imageEditUI.main_webui import *
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
app()
main_webui.py
import gradio as gr
import numpy as np
from imageEditUI.z_image_editing import *
from imageEditUI.z_gif_handler import *
def app():
def sepia_image(input_img):
# choose: sepia, bw, or negative (so far)
return color_filters(input_img, 'sepia')
def flip_image(x):
return np.fliplr(x)
def rembg_image(input_img, rgb_high, rgb_low):
rgb_high = rgb_high.lstrip('#')
rgb_low = rgb_low.lstrip('#')
high = tuple(int(rgb_high[i:i + 2], 16) for i in (0, 2, 4))
low = tuple(int(rgb_low[i:i + 2], 16) for i in (0, 2, 4))
return remColor(input_img, high, low)
def rembg_gif(input_img, rgb_high, rgb_low):
rgb_high = rgb_high.lstrip('#')
rgb_low = rgb_low.lstrip('#')
high = tuple(int(rgb_high[i:i + 2], 16) for i in (0, 2, 4))
low = tuple(int(rgb_low[i:i + 2], 16) for i in (0, 2, 4))
return master(input_img, high, low)
def floodFill_ui(image_input_remBGCol):
return floodFill(image_input_remBGCol)
def gif_rembgcolor(image_input_remBGCol, high, low):
print(type(image_input_remBGCol))
return gif_main(image_input_remBGCol, high, low)
with gr.Blocks() as demo:
gr.Markdown("Select your edits below:")
# set output image height var:
output_height = 400
with gr.Tab("GIF"):
with gr.Row():
gif_input = gr.Image(type='pil', interactive=True)
gif_output = gr.Image(height=output_height)
with gr.Row():
high_gif = gr.ColorPicker(label="Choose high color")
low_gif = gr.ColorPicker(label="Choose low color")
gif_button = gr.Button("Remove Background Color")
gif_button2 = gr.Button("Remove Background Color v2")
with gr.Tab("Color"):
with gr.Row():
image_input_remBGCol = gr.Image()
image_output_remBGCol = gr.Image(height=output_height)
with gr.Row():
rgb_high = gr.ColorPicker(label="Choose high color")
rgb_low = gr.ColorPicker(label="Choose low color")
remBGCol_button = gr.Button("Remove Background Color")
selectArea_button = gr.Button("Select area of image")
with gr.Tab("Flip Image"):
# filter: FLIP
with gr.Row():
image_input = gr.Image()
image_output = gr.Image(height=output_height)
image_button = gr.Button("Flip")
remBGCol_button.click(rembg_image,
inputs=[image_input_remBGCol, rgb_high, rgb_low],
outputs=image_output_remBGCol)
selectArea_button.click(floodFill_ui, inputs=image_input_remBGCol, outputs=image_output_remBGCol)
image_button.click(flip_image, inputs=image_input, outputs=image_output)
gif_button.click(rembg_gif, inputs=[gif_input, high_gif, low_gif], outputs=gif_output)
gif_button2.click(gif_rembgcolor, inputs=[gif_input, high_gif, low_gif], outputs=gif_output)
demo.launch()
z_gif_handler.py
from PIL import Image, ImageFile
########################################################################################################################
# Manages the splitting of gifs into images, delivering individual images to the image editor
# module, and recompiling gifs before sending back to webui
########################################################################################################################
def split_gif(im):
# loop through frames saving each to bytesio
myImages = []
i = 0
for i in range(i, im.n_frames):
im.seek(i)
myImages.append(im)
return myImages
def deliver_to_editor(gif_frames, tool_selection):
if tool_selection == 'chroma':
return
def gif_main(input_image, choice, high=(165, 253, 171), low=(1, 77, 0)):
gif_frames = split_gif(input_image)
gif_frames[0].save('assets/images/final.gif', save_all=True, append_images=gif_frames[1:], duration=100, loop=0)
Traceback error
C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Scripts\python.exe C:\Users\Admin\PycharmProjects\ImageEditUI\imageEditUI\main.py
Running on local URL: http://127.0.0.1:7860
To create a public link, set `share=True` in `launch()`.
<class 'PIL.Image.Image'>
Traceback (most recent call last):
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\gradio\routes.py", line 437, in run_predict
output = await app.get_blocks().process_api(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\gradio\blocks.py", line 1352, in process_api
result = await self.call_function(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\gradio\blocks.py", line 1077, in call_function
prediction = await anyio.to_thread.run_sync(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\anyio\to_thread.py", line 33, in run_sync
return await get_asynclib().run_sync_in_worker_thread(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 877, in run_sync_in_worker_thread
return await future
^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\anyio\_backends\_asyncio.py", line 807, in run
result = context.run(func, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\imageEditUI\main_webui.py", line 38, in gif_rembgcolor
return gif_main(image_input_remBGCol, high, low)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\imageEditUI\z_gif_handler.py", line 40, in gif_main
gif_frames = split_gif(input_image)
^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\imageEditUI\z_gif_handler.py", line 21, in split_gif
for i in range(i, im.n_frames):
^^^^^^^^^^^
File "C:\Users\Admin\PycharmProjects\ImageEditUI\venv\Lib\site-packages\PIL\Image.py", line 528, in __getattr__
raise AttributeError(name)
AttributeError: n_frames