Find every pixels RGB value from multiple frames - python

I have two videos. One at 10 fps and other one is the same video and 10 fps but it is interpolated from the same videos 5 fps version. I want to see how accurate the frame interpolation by comparing the RGB values of every frame. I can extract every frame from both videos. However, I can only get the RGB values of only 1 frame. I use the following code:
from PIL import Image
im = Image.open('frame1.jpg')
pix = im.load()
for x in range(0,640):
for y in range(0,480):
print pix[x,y]
This code can only found the RGB values in 1 frame and I have hundreds of frames. The frames of my original video are named frame1.jpg frame2.jpg ... frame 100.jpg etc. and other videos frames are saved as frames1.jpg frames2.jpg ... frames100.jpg etc. Is there a way to automate it

You can use glob (comes natively with Python) to load all the images and store them simultaneously, or process them one at a time.
import glob
from PIL import Image
for frames in glob.glob({path}+"*.jpg"):
im = Image.open(frames)
pix=im.load()
for x in range(0,640):
for y in range(0,480):
print pix[x,y]
This will do what you did, but it will loop over all files in path with JPG format. If you want something more specific, you can add requests and I'll add to my answer. But this will sequentially load all images, allowing you to process them subsequently.

Put all that stuff in a loop.
for i in range(1, 101):
file = 'frame%d.jpg'%i
im = Image.open(file)
# ...

Related

Ignore image name while getting hash

I'm coding a program which'll take an image for an input, check it against images in a database and output the image with the same hash
However, when using hash("imagepath") 2 of the same images give different hashes, even when the only difference is the image's name, which makes me believe the name is the issue
Is there a way to easily ignore the name of the image? (png)
How I solved it:
I ended up not using "hashing" but the average pixel by scrambeling pieces of code together, and then find an image with the same average pixel (the average pixels are in a list so it gets the index which it then uses to find a name)
import requests
#Database of possible image average pixels
clone_imgs = [88.0465, 46.2568, 102.6426 ...]
image = <image url>
img_data = requests.get(image).content
with open('image.png', 'wb') as handler: #Download the image as "image.png" (Replace "image.png" with the path where you want to save it)
handler.write(img_data)
img = Image.open(r"image.png") #Open the image for reading
img = img.resize((100, 100), Image.ANTIALIAS) #A series of compressions to the image
img = img.convert("L")
img_pixel_data = list(spawn.getdata())
img_avg_pixel = sum(spawn_pixel_data)/len(spawn_pixel_data) #Get the average pixel values
clone_img_index = clone_imgs.index(img_avg_pixel) #Find the same pixel value in the database
This worked for me but it has a few downsides:
The images need to be 100% the same in color (A single pixel off can ruin it)
One of these average pixels can make an infinite amount of images, my database only contained 800 so it still worked (However I had to go from compression to 10x10 to 100x100 to not end up with clones)

How to shrink data set output from avi file

I'm trying to create a data set from an avi file I have and I know I've made a mistake somewhere.
The Avi file I have is 1,827 KB (4:17) but after running my code to convert the frames into arrays of number I now have a file that is 1,850,401 KB. This seems a little large to me.
How can I reduce the size of my data set / where did I go wrong?
# Program To Read video
# and Extract Frames
import cv2
import numpy as np
import time
# Function to extract frames
def FrameCapture(path):
# Path to video file
vidObj = cv2.VideoCapture(path)
# Used as counter variable
count = 0
# checks whether frames were extracted
success = 1
newDataSet = []
try:
while success:
# vidObj object calls read
# function extract frames
success, image = vidObj.read()
img_reverted = cv2.bitwise_not(image)
new_img = img_reverted / 255.0
newDataSet.append(new_img)
#new_img >> "frame%d.txt" % count
# Saves the frames with frame-count
#cv2.imwrite("frame%d.jpg" % count, image)
count += 1
except:
timestr = time.strftime("%Y%m%d-%H%M%S")
np.save("DataSet" + timestr , newDataSet)
# Driver Code
if __name__ == '__main__':
# Calling the function
FrameCapture("20191212-150041output.avi")
I'm going to guess that the video mainly consist of similar pixels blocked together that the video have compressed to such a low file size. When you load single images into arrays all that compression goes away and depending on the fps of the video you will have thousands of uncompressed images. When you first load an image it will be saved as a numpy array of dtype uint8 and the image size will be WIDTH * HEIGHT * N_COLOR_CHANNELS bytes. After you divide it with 255.0 to normalize between 0 and 1 the dtype changes to float64 and the image size increases eightfold. You can use this information to calculate expected size of the images.
So your options is to either decrease the height and width of your images (downscale), change to grayscale or if your application allows it to stick with uint8 values. If the images doesn't change too much and you don't need thousands of them you could also only save every 10th or whatever seems reasonable. If you need them all as is but they don't fit in memory consider using a generator to load them on demand. It will be slower but at least it will run.

Get frames per second of a gif in python?

In python, im loading in a gif with PIL. I extract the first frame, modify it, and put it back. I save the modified gif with the following code
imgs[0].save('C:\\etc\\test.gif',
save_all=True,
append_images=imgs[1:],
duration=10,
loop=0)
Where imgs is an array of images that makes up the gif, and duration is the delay between frames in milliseconds. I'd like to make the duration value the same as the original gif, but im unsure how to extract either the total duration of a gif or the frames displayed per second.
As far as im aware, the header file of gifs does not provide any fps information.
Does anyone know how i could get the correct value for duration?
Thanks in advance
Edit: Example of gif as requested:
Retrived from here.
In GIF files, each frame has its own duration. So there is no general fps for a GIF file. The way PIL supports this is by providing an info dict that gives the duration of the current frame. You could use seek and tell to iterate through the frames and calculate the total duration.
Here is an example program that calculates the average frames per second for a GIF file.
import os
from PIL import Image
FILENAME = os.path.join(os.path.dirname(__file__),
'Rotating_earth_(large).gif')
def get_avg_fps(PIL_Image_object):
""" Returns the average framerate of a PIL Image object """
PIL_Image_object.seek(0)
frames = duration = 0
while True:
try:
frames += 1
duration += PIL_Image_object.info['duration']
PIL_Image_object.seek(PIL_Image_object.tell() + 1)
except EOFError:
return frames / duration * 1000
return None
def main():
img_obj = Image.open(FILENAME)
print(f"Average fps: {get_avg_fps(img_obj)}")
if __name__ == '__main__':
main()
If you assume that the duration is equal for all frames, you can just do:
print(1000 / Image.open(FILENAME).info['duration'])

Python: How to get some images from a video in Python3?

How can I extract frames from a video file using Python3?
For example, I want to get 16 picture from a video and combine them into a 4x4 grid.
I don't want 16 separate images at the end, I want one image containing 16 frames from the video.
----Edit----
import av
container = av.open('/home/uguraba/Downloads/equals/equals.mp4')
video = next(s for s in container.streams)
for packet in container.demux(video):
for frame in packet.decode():
if frame.index %3000==0:
frame.to_image().save('/home/uguraba/Downloads/equals/frame-%04d.jpg' % frame.index)
By using this script i can get frames. There will be lots of frames saved. Can i take specific frames like 5000-7500-10000 ?
Also my question is how can i see the total frame number ?
Use PyMedia or PyAV to access image data and PIL or Pillow to manipulate it in desired form(s).
These libraries have plenty of examples, so with basic knowledge about the video muxing/demuxing and picture editing you should be able to do it pretty quickly. It's not so complicated as it would seem at first.
Essentially, you demux the video stream into frames, going frame by frame.
You get the picture either in its original (e.g. JPEG) or raw form and push it into PIL/Pillow.
You do with it what you want, resizing etc... - PIL provides all necessary stuff.
And then you paste it into one big image at desired position.
That's all.
You can do that with OpenCV3, the Python wrapper and Numpy.
First you need to do is capture the frames then save them separately and finally paste them all in a bigger matrix.
import numpy as np
import cv2
cap = cv2.VideoCapture(video_source)
# capture the 4 frames
_, frame1 = cap.read()
_, frame2 = cap.read()
_, frame3 = cap.read()
_, frame4 = cap.read()
# 'glue' the frames using numpy and vertigal/horizontal stacks
big_frame = np.vstack((np.hstack((frame1, frame2)),
np.hstack((frame3, frame4))))
# Show a 4x4 unique frame
cv2.imshow('result', big_frame)
cv2.waitKey(1000)
To compile and install OpenCV3 and Numpy in Python3 you can follow this tutorial.
You can implement a kind of "control panel" from 4 different video sources with something like that:
import numpy as np
import cv2
cam1 = cv2.VideoCapture(video_source1)
cam2 = cv2.VideoCapture(video_source2)
cam3 = cv2.VideoCapture(video_source3)
cam4 = cv2.VideoCapture(video_source4)
while True:
more1, frame_cam1 = cam1.read()
more2, frame_cam2 = cam2.read()
more3, frame_cam3 = cam3.read()
more4, frame_cam4 = cam4.read()
if not all([more1, more2, more3, more4]) or cv2.waitKey(1) & 0xFF in (ord('q'), ord('Q')):
break
big_frame = np.vstack((np.hstack((frame_cam1, frame_cam2)),
np.hstack((frame_cam3, frame_cam4))))
# Show a 4x4 unique frame
cv2.imshow('result', big_frame)
print('END. One or more sources ended.')

Python JPEG to movie

I am looking for a way to concatenate a directory of images files (e.g., JPEGs) to a movie file (MOV, MP4, AVI) with Python. Ideally, this would also allow me to take multiple JPEGs from that directory and "paste" them into a grid which is one frame of a movie file. Which modules could achieve this?
You could use the Python interface of OpenCV, in particular a VideoWriter could probably do the job. From what I understand of the doc, the following would do what you want:
w = cvCreateVideoWriter(filename, -1, <your framerate>,
<your frame size>, is_color=1)
and, in a loop, for each file:
cvWriteFrame(w, frame)
Note that I have not tried this code, but I think that I got the idea right. Please tell me if it works.
here's a cut-down version of a script I have that took frames from one video and them modified them(that code taken out), and written to another video. maybe it'll help.
import cv2
fourcc = cv2.cv.CV_FOURCC(*'XVID')
out = cv2.VideoWriter('out_video.avi', fourcc, 24, (704, 240))
c = cv2.VideoCapture('in_video.avi')
while(1):
_, f = c.read()
if f is None:
break
f2 = f.copy() #make copy of the frame
#do a bunch of stuff (missing)
out.write(f2) #write frame to the output video
out.release()
cv2.destroyAllWindows()
c.release()
If you have a bunch of images, load them in a loop and just write one image after another to your vid.
I finally got into a working version of the project that got me into this question.
Now I want to contribute with the knowledge I got.
Here is my solution for getting all pictures in current directory and converting into a video having then centralized in a black background, so this solution works for different size images.
import glob
import cv2
import numpy as np
DESIRED_SIZE = (800, 600)
SLIDE_TIME = 5 # Seconds each image
FPS = 24
fourcc = cv2.VideoWriter.fourcc(*'X264')
writer = cv2.VideoWriter('output.avi', fourcc, FPS, DESIRED_SIZE)
for file_name in glob.iglob('*.jpg'):
img = cv2.imread(file_name)
# Resize image to fit into DESIRED_SIZE
height, width, _ = img.shape
proportion = min(DESIRED_SIZE[0]/width, DESIRED_SIZE[1]/height)
new_size = (int(width*proportion), int(height*proportion))
img = cv2.resize(img, new_size)
# Centralize image in a black frame with DESIRED_SIZE
target_size_img = np.zeros((DESIRED_SIZE[1], DESIRED_SIZE[0], 3), dtype='uint8')
width_offset = (DESIRED_SIZE[0] - new_size[0]) // 2
height_offset = (DESIRED_SIZE[1] - new_size[1]) // 2
target_size_img[height_offset:height_offset+new_size[1],
width_offset:width_offset+new_size[0]] = img
for _ in range(SLIDE_TIME * FPS):
writer.write(target_size_img)
writer.release()
Is it actually important to you that the solution should use python and produce a movie file? Or are these just your expectations of what a solution would look like?
If you just want to be able to play back a bunch of jpeg files as a movie, you can do it without using python or cluttering up your computer with .avi/.mov/mp4 files by going to vidmyfigs.com and using your mouse to select image files from your hard drive. The "movie" plays back in your Web browser.

Categories