Extract key frames from GIF using Python - python

I want to compress a GIF image by extracting 15 frames from the GIF that preferably should be distinct.
I'm using Python and Pillow library and I didn't find any way to get the number of frames a GIF has in the Pillow docs. Neither did I find how to extract a specific frame from a GIF, because Pillow restricts that.
Is there any way to extract frames without iterating through each frame consequently?
Is there a more advanced Python library for GIF processing?

Here is an extension of #radarhere's answer that divides the .gif into num_key_frames different parts and saves each part to a new image.
from PIL import Image
num_key_frames = 8
with Image.open('somegif.gif') as im:
for i in range(num_key_frames):
im.seek(im.n_frames // num_key_frames * i)
im.save('{}.png'.format(i))
The result is somegif.gif broken into 8 pieces saved as 0..7.png.

For the number of frames, you are looking for n_frames. Take a look at here.
from PIL import Image
im = Image.open('test.gif')
print("Number of frames: "+str(im.n_frames))
For extracting a single frame -
im.seek(20)
im.save('frame20.jpg')

The real working solution to extract proper frames of any GIF file:
BigglesZX/gifextract.py

If you have tf imported you can:
def load_gif(file_path):
with tf.io.gfile.GFile(file_path, 'rb') as f:
video = tf.io.decode_gif(f.read())
return np.array(video)

Related

How to convert multi-page Image bytes into PDF or TIFF bytes in memory with Python?

I am looking for a solution to convert Image bytes into PDF bytes in memory only.
For my web application, it takes in pdf/tiff documents (can be multi-paged) for information extraction.
I am adding in an image preprocessing step at the start of the pipeline. However, this step is only applicable for images as I am using OpenCV2. Thus, the pdf/tiff file is converted into image(s) for preprocessing. However, to send the file for information extraction I will need to join them back together, as there is a different logic flow for the first vs the subsequent pages.
I was previously using a workaround (referencing local path of merged pdf) but now I would like to remove the dependency and do everything in-memory. This is so that I will be able to deploy the application on the cloud.
image = Image.open(io.BytesIO(file_str))
num_frames = image.n_frames
# Loop through each page of a tif file
for i in range(num_frames):
image.seek(i)
file_array = np.array(image)
file_array = file_array.astype(np.uint8) * 255
# Preprocessing (removed for simplicity)
# TODO: Merge back into PDF file
Edit:
Simple answer: I can't do this in memory. Instead, I have used the tempfile library to help me to save the files there and delete the temporary directory after I am done. That, in some way has helped to achieve the "in memory" aspect.
Writing (not reading) multi-page PDF files is possible using Pillow. For the below solution, I used pdf2image for converting some multi-page PDF file to a list of Pillow Image objects. So, please adapt that according to your existing code.
from PIL import Image
import pdf2image
import numpy as np
# Read pages from PDF to Pillow Image objects
frames_in = pdf2image.convert_from_path('path/to/your/file.pdf')
# Enumerate frames, and preprocess
frames_out = []
for i, frame in enumerate(frames_in):
# Convert to NumPy array
frame = np.array(frame)
# Preprocessing for the first page
if i == 0:
frame[:100, ...] = [255, 0, 0]
# Preprocessing for the other pages
else:
frame[:100, ...] = [0, 0, 255]
# Convert back to Pillow Image object, and append to output list
frames_out.append(Image.fromarray(frame))
frames_out[0].save('output.pdf', save_all=True, append_images=frames_out[1:])
When using some sample PDF, the output looks the same, but with a red rectangle on the first page, and a blue rectangle on the second page.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
PyCharm: 2021.1.1
NumPy: 1.20.2
pdf2image 1.14.0
Pillow: 8.2.0
----------------------------------------

How to save (write) a list of images from a dataset into a new folder - openCV Python?

I'm so much newbie in openCV/Python tasks. I use Python 3.7 and openCV 4 running by a JNotebook. The question: I wanna save just 1,000 images from a dataset with 10,000 pictures, extracting them from it and write only those 1,000.jpeg in a new folder, is it possible using openCV package in Python? I've already had a list of names (1,000 images).
If you need just to copy files, you even don't need OpenCV tools:
out_folder_path = '...'
in_folder_path = '...'
images_to_save_names = [...]
for image_name in images_to_save_names:
cur_image_path = os.path.join(in_folder_path, image_name)
cur_image_out_path = os.path.join(out_folder_path, image_name)
shutil.copyfile(cur_image_path, cur_image_out_path)
If you have image names and their binary data from some specific DS file(.csv, .hdf, e.t.c.), you can use cv2.imwrite(path, image) instead of copying.
Assuming you have OpenCV correctly installed on your machine, you can first read the images with img = cv.imread(filename) and then write them with cv.imwrite(filename, img).

Merge multiple base64 images into one

If I have multiple base64 strings that are images (one string = one image). Is there a way to combine them and decode to a single image file? i.e. from multiple base64 strings, merge and output a single image file.
I'm not sure how I would approach this using Pillow (or if I even need it).
Further clarification:
The source images are TIFFs that are encoded into base64
When I say "merge", I mean turning multiple images into a multi-page image like you see in a multi-page PDF
I dug through the Pillow documentation (v5.3) and found something that seems to work. Basically, there are two phases to this:
Save encoded base64 strings as TIF
Append them together and save to disk
Example using Python 3.7:
from PIL import Image
import io
import base64
base64_images = ["asdfasdg...", "asdfsdafas..."]
image_files = []
for base64_string in base64_images:
buffer = io.BytesIO(base64.b64decode(base64_string))
image_file = Image.open(buffer)
image_files.append(image_file)
combined_image = images_files[0].save(
'output.tiff',
save_all=True,
append_images=image_files[1:]
)
In the above code, I first create PIL Image objects from a bytes buffers in order to do this whole thing in-memory but you can probably use .save() and create a bunch of tempfiles instead if I/O isn't a concern.
Once I have all the PIL Image objects, I choose the first image (assuming they were in desired order in base64_images list) and append the rest of the images with append_images flag. The resulting image has all the frames in one output file.
I assume this pattern is extensible to any image format that supports the save_all and append_images keyword arguments. The Pillow documentation should let you know if it is supported.

Moviepy : How to read greyscale movies frame by frame?

Disclaimer : I have no experience in computer vision or image processing, but I need to process videos to obtain data for machine learning.
I wish to read a greyscale movie (I made it using greyscale images) - frame by frame using moviepy. For further processing, I need greyscale frames. Here is my code:
clip = VideoFileClip('movie.mp4')
count =1
for frames in clip.iter_frames():
print frames.shape
count+=1
print count
The frame shapes come out to be (360L, 480L, 3L) while I was expecting (360L, 480L). And this puzzles me. Is there a way to get the "expected" shape? Python OpenCV ideas may work too, but I would prefer moviepy.
If your are dealing with videos and images, OpenCV is your friend:
import cv2
from moviepy.editor import VideoFileClip
clip = VideoFileClip('movie.mp4')
count =1
for frames in clip.iter_frames():
gray_frames = cv2.cvtColor(frames, cv2.COLOR_RGB2GRAY)
print frames.shape
print gray_frames.shape
count+=1
print count

Image processing 1 bit images

I am trying to understand the basics for image processing an image of a corridor. I have have used PIL to convert find the edges in an image, then I have converted it to a 1 bit image. I know what I want to be able to extract - the longest horizontal and diagonal lines that can be found in the image. Any ideas?
from PIL import *
import Image, ImageFilter
im = im.open("c:\Python26\Lib\site-packages\PIL\corridor.jpg")
imageInfo=list(im.getdata())
im.putdata(imageInfo)
print pic.size
for i in imageInfo2[180:220]:
if i==0:
print "This is a BLACK pixel"
elif i==255:
print "This is a WHITE pixel"
else:
print "ERROR"
First don't call them 1 bit images - that normally refers to images (like icons) where each pixel is 1bit and so 8pixels can be packed into a single byte.
Images with only two levels are normally called 'binary' in image processing.
Now you only have to learn the science of image processing !
A good place to start is opencv a free image processing library that also works with python and interfaces reasonably well with PIL.
You shoudl also read their book - or one of the other good books on image processing

Categories