I am writing a Python module which removes the background from animated GIF images. My problem function takes a directory path string and a URL string as input. The function uses an IF statement to pick either the URL or the path, based on if the URL is empty ("") or not. Pseudo-code is basically:
If URL != "", then get URL content and create a PIL.Image, else, search the directory path for gif and use to create PIL.Image.
In either case, a PIL.Image object called "im" is created and then "im" is referenced again after the IF block, which is where I get the error:
UnboundLocalError: local variable 'im' referenced before assignment
I thought that control blocks like IFs, Fors, Whiles, etc. were exempt from the variable scope restrictions that separate function variables from each other, but I am clearly missing something.
Below is my module code. I have put indicator comments around what I believe is the problem area (within function "gif_to_imgs").
src/gif_to_imgs.py
from PIL import Image
import os
import tempfile
import shutil
from rembg import remove
import glob
import contextlib
import requests
from io import BytesIO
def remove_img_bg(img_file, output_path):
output = remove(img_file)
output.save(output_path)
print(f"Background removed, output saved to: \"{output_path}\"")
def imgs_to_video(framesDir):
# https://stackoverflow.com/a/57751793/17312223
fp_in = f"{framesDir}/frame-*.png" # Will return all file paths obeying regex to a list var
fp_out = f"{framesDir}/modified.gif"
# use exit stack to automatically close opened images
with contextlib.ExitStack() as stack:
# lazily load images
imgs = (stack.enter_context(Image.open(f))
for f in sorted(glob.glob(fp_in)))
# extract first image from iterator
img = next(imgs)
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
img.save(fp=fp_out, format='GIF', disposal=2, append_images=imgs,
save_all=True, duration=100, loop=0)
def gif_to_imgs(imgDir,gif_url):
framesDir = f"{imgDir}/gif_frames"
# Delete frames folder if exists
if (os.path.exists(framesDir)):
tmp = tempfile.mktemp(dir=os.path.dirname(framesDir))
shutil.move(framesDir, tmp) # "Move" to temporary folder
shutil.rmtree(tmp) # Delete
os.mkdir(framesDir)
#==============================================================
# PROBLEM BEGINS HERE
#==============================================================
if (gif_url != ""):
response = requests.get(gif_url)
im = Image.open(BytesIO(response.content))
else:
for filename in os.listdir(imgDir):
if (filename == "*.gif"):
im = Image.open(f"{imgDir}/{filename}.gif")
print("Number of frames: " + str(im.n_frames)) #<---- "im" causes the error here
#==============================================================
# CODE FAILS AT THE ABOVE "PRINT()"
#==============================================================
for i in range(0, im.n_frames):
# Create sort-friendly numerical suffix for GIF frames
numSuffix = str(i)
while (len(numSuffix) < 4):
numSuffix = f"0{numSuffix}"
# Remove background from the frame
im.seek(i) # Iterate to specific frame
duration = im.info['duration']/1000 # Get frame duration
output_path = f"{framesDir}/frame-{numSuffix}-{duration}.png" # Set output path
remove_img_bg(im, output_path) # Remove background
imgs_to_video(framesDir)
In case you are curious where the entry point for this module is, see my main.py script that calls it below:
main.py
from src import gif_to_imgs
if __name__ == '__main__':
# gif_url = "https://media.tenor.com/tX_T48A14BwAAAAd/khaby-really.gif"
gif_url = ""
# takes path and url as input
gif_to_imgs.gif_to_imgs("assets", gif_url)