Sequence of images to video in OpenCV - python

I have a series of images in *.tif format that I want to use to create a video. I am using OpenCV 3.1.0 in Python 2.7. Below is a snippet of my code:
import cv2
import numpy as np
nIMAGES = 10
files = glob.glob(DIR + '\\' + tpname +'\\*.tif' )
image_stack = np.empty((500, 220, nIMAGES))
mov = DIR + '\\' + tpname + '\\' + tpname + '_mov.avi'
MOV = cv2.VideoWriter(filename=mov, fourcc=cv2.VideoWriter_fourcc('F', 'M', 'P', '4'), fps=2, frameSize=(220, 500)) # frame size is 220 x 500
for i in np.arange(0, nIMAGES):
print 'Working on: ' + files[i][-14:-4]
image = cv2.imread(files[i], 0)
crop_image = image[50:550, 252:472] #crop y:h, x:w
# now let's create the movie:
crop_image = cv2.applyColorMap(crop_image, cv2.COLORMAP_JET)
MOV.write(crop_image)
MOV.release()
When I run this code, I create an AVI file that is 0 Kb (it hasn't saved anything to it).
I believe I am missing something like frame = cv2.VideoCapture.read(crop_image) in which case I would convert the write line to MOV.write(frame). However, I get an AttributeError in that the VideoCapture.read is not an attribute.
I am using this OpenCV webpage as my guide: http://docs.opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html

I had to make two changes to get this to work:
Following the advice on this question: OpenCV 2.4 VideoCapture not working on Windows I had to copy the fmpeg.dll files to my Python directory (C:\Anaconda). I also had to relabel the folders to include the version of my opencv (3.1.0) e.g. opencv_ffmpeg310_64.dll
The other change I needed to make was to change my codec to MJPG
Much appreciation goes to #Micka for helping me very quickly.

Related

Split video into images with ffmpeg-python

As far as I understand ffmpeg-python is main package in Python to operate ffmpeg directly.
Now I want to take a video and save it's frames as separate files at some fps.
There are plenty of command line ways to do it, e.g. ffmpeg -i video.mp4 -vf fps=1 img/output%06d.png described here
But I want to do it in Python. Also there are solutions [1] [2] that use Python's subprocess to call ffmpeg CLI, but it looks dirty for me.
Is there any way to to make it using ffmpeg-python?
The following works for me:
ffmpeg
.input(url)
.filter('fps', fps='1/60')
.output('thumbs/test-%d.jpg',
start_number=0)
.overwrite_output()
.run(quiet=True)
I'd suggest you try imageio module and use the following code as a starting point:
import imageio
reader = imageio.get_reader('imageio:cockatoo.mp4')
for frame_number, im in enumerate(reader):
# im is numpy array
if frame_number % 10 == 0:
imageio.imwrite(f'frame_{frame_number}.jpg', im)
You can also use openCV for that.
Reference code:
import cv2
video_capture = cv2.VideoCapture("your_video_path")
video_capture.set(cv2.CAP_PROP_FPS, <your_desired_fps_here>)
saved_frame_name = 0
while video_capture.isOpened():
frame_is_read, frame = video_capture.read()
if frame_is_read:
cv2.imwrite(f"frame{str(saved_frame_name)}.jpg", frame)
saved_frame_name += 1
else:
print("Could not read the frame.")
#norus solution is actually good, but for me it was missing the ss and r parameters in the input. I used a local file instead of a url.
This is my solution:
ffmpeg.input(<path/to/file>, ss = 0, r = 1)\
.filter('fps', fps='1/60')\
.output('thumbs/test-%d.jpg', start_number=0)\
.overwrite_output()]
.run(quiet=True)
ss is the starting second in the above code starts on 0
r is the ration, because the filter fps is set to 1/60 an r of 1 will return 1 frame per second, of 2 1 frame every 2 seconds, 0.5 a frame every half second....

name 'openpyxl' not defined error

Still learning the ins and outs of python here. I've had minor success with using openpyxl library and started this pet project of using excel file as a "canvas" to read a 1 bit bmp file from directory then to "duplicate-display" the image on the worksheet by getting rid of the guides, decreasing the sizes of each cell and finally by adding a small image in each cell with list of binary data from getdata method from pillow.
I have not gone through the entire logic, but I am having trouble with below code for two nights and I still have no clue why I am getting name 'openpyxl' not defined error in the line almost at the bottom withe the code sheet.add_image(openpyxl.drawing.image.Image('square_blue.jpg'), colnum_string(col)+str(row))
I had success with similar project using almost identical importing of both openpyxl and PIL libraries.
from openpyxl import Workbook
import os
from PIL import Image
# for file in os.listdir():
# if file.endswith('bmp'):
os.chdir('c:/users/Sam/Desktop/py')
img = Image.open('trump.bmp')
img_width, img_height = img.size
pix_val = list(img.getdata())
wb = Workbook()
def colnum_string(n):
string = ""
while n > 0:
n, remainder = divmod(n - 1, 26)
string = chr(65 + remainder) + string
return string
r_height = 3
c_width = 3
sheet = wb.create_sheet("Mysheet")
for col in range(1, img_height):
sheet.row_dimensions[img_height].height = r_height
for row in range(1, img_width):
sheet.column_dimensions[colnum_string(col)] = c_width
sheet.add_image(openpyxl.drawing.image.Image('square_blue.jpg'), colnum_string(col)+str(row))
wb.save('out.xlsx')
Can anyone help me out please?
You only import ''Workbook'' sub-module from openpyxel. Try:
from openpyxel import Workbook, drawing # also import drawing namespace
[...]
sheet.add_image(drawing.image.Image('square_blue.jpg'), colnum_string(col)+str(row))
[...]

Convert three I;16B images into one image

I have 3 images of type I;16B and I am correctly reading them into Python via PIL:
#!/usr/bin/en python
import sys
from PIL import Image
mode2bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, 'I':32, 'F':32}
if __name__=="__main__":
print "Working!"
basedir = sys.argv[1]
imname = sys.argv[2]
Rc = sys.argv[3]
Gc = sys.argv[4]
Bc = sys.argv[5]
Zstack = sys.argv[6]
Rtif = basedir+"/"+imname+"-"+Rc+"/Data-"+Rc+"-Z"+Zstack+".tif"
Gtif = basedir+"/"+imname+"-"+Gc+"/Data-"+Gc+"-Z"+Zstack+".tif"
Btif = basedir+"/"+imname+"-"+Bc+"/Data-"+Bc+"-Z"+Zstack+".tif"
Rim = Image.open(Rtif)
Gim = Image.open(Gtif)
Bim = Image.open(Btif)
print Rim
print Rim.mode
This shows me that the data is I;16B but I am having to read them as 3 different images (one per channel). How should I go about combining these 3 channels into one image and writing a .tif file as output?
For now, Pillow doesn't support multichannel images with more than 8 bits per channel. You can only convert every image to 'L' mode and merge they together with Image.merge().

Reading bmp files in Python

Is there a way to read in a bmp file in Python that does not involve using PIL? PIL doesn't work with version 3, which is the one I have. I tried to use the Image object from graphics.py, Image(anchorPoint, filename), but that only seems to work with gif files.
In Python it can simply be read as:
import os
from scipy import misc
path = 'your_file_path'
image= misc.imread(os.path.join(path,'image.bmp'), flatten= 0)
## flatten=0 if image is required as it is
## flatten=1 to flatten the color layers into a single gray-scale layer
I realize that this is an old question, but I found it when solving this problem myself and I figured that this might help someone else in the future.
It's pretty easy actually to read a BMP file as binary data. Depending on how broad support and how many corner-cases you need to support of course.
Below is a simple parser that ONLY works for 1920x1080 24-bit BMP's (like ones saved from MS Paint). It should be easy to extend though. It spits out the pixel values as a python list like (255, 0, 0, 255, 0, 0, ...) for a red image as an example.
If you need more robust support there's information on how to properly read the header in answers to this question: How to read bmp file header in python?. Using that information you should be able to extend the simple parser below with any features you need.
There's also more information on the BMP file format over at wikipedia https://en.wikipedia.org/wiki/BMP_file_format if you need it.
def read_rows(path):
image_file = open(path, "rb")
# Blindly skip the BMP header.
image_file.seek(54)
# We need to read pixels in as rows to later swap the order
# since BMP stores pixels starting at the bottom left.
rows = []
row = []
pixel_index = 0
while True:
if pixel_index == 1920:
pixel_index = 0
rows.insert(0, row)
if len(row) != 1920 * 3:
raise Exception("Row length is not 1920*3 but " + str(len(row)) + " / 3.0 = " + str(len(row) / 3.0))
row = []
pixel_index += 1
r_string = image_file.read(1)
g_string = image_file.read(1)
b_string = image_file.read(1)
if len(r_string) == 0:
# This is expected to happen when we've read everything.
if len(rows) != 1080:
print "Warning!!! Read to the end of the file at the correct sub-pixel (red) but we've not read 1080 rows!"
break
if len(g_string) == 0:
print "Warning!!! Got 0 length string for green. Breaking."
break
if len(b_string) == 0:
print "Warning!!! Got 0 length string for blue. Breaking."
break
r = ord(r_string)
g = ord(g_string)
b = ord(b_string)
row.append(b)
row.append(g)
row.append(r)
image_file.close()
return rows
def repack_sub_pixels(rows):
print "Repacking pixels..."
sub_pixels = []
for row in rows:
for sub_pixel in row:
sub_pixels.append(sub_pixel)
diff = len(sub_pixels) - 1920 * 1080 * 3
print "Packed", len(sub_pixels), "sub-pixels."
if diff != 0:
print "Error! Number of sub-pixels packed does not match 1920*1080: (" + str(len(sub_pixels)) + " - 1920 * 1080 * 3 = " + str(diff) +")."
return sub_pixels
rows = read_rows("my image.bmp")
# This list is raw sub-pixel values. A red image is for example (255, 0, 0, 255, 0, 0, ...).
sub_pixels = repack_sub_pixels(rows)
Use pillow for this. After you installed it simply import it
from PIL import Image
Then you can load the BMP file
img = Image.open('path_to_file\file.bmp')
If you need the image to be a numpy array, use np.array
img = np.array(Image.open('path_to_file\file.bmp'))
The numpy array will only be 1D. Use reshape() to bring it into the right shape in case your image is RGB. For example:
np.array(Image.open('path_to_file\file.bmp')).reshape(512,512,3)
I had to work on a project where I needed to read a BMP file using python, it was quite interesting, actually the best way is to have a review on the BMP file format (https://en.wikipedia.org/wiki/BMP_file_format) then reading it as binairy file, to extract the data.
You will need to use the struct python library to perform the extraction
You can use this tutorial to see how it proceeds https://youtu.be/0Kwqdkhgbfw
Use the excellent matplotlib library
import matplotlib.pyplot as plt
im = plt.imread('image.bmp')
It depends what you are trying to achieve and on which platform?
Anyway using a C library to load BMP may work e.g. http://code.google.com/p/libbmp/ or http://freeimage.sourceforge.net/, and C libraries can be easily called from python e.g. using ctypes or wrapping it as a python module.
or you can compile this version of PIL https://github.com/sloonz/pil-py3k
If you're doing this in Windows, this site, should allow you to get PIL (and many other popular packages) up and running with most versions of Python: Unofficial Windows Binaries for Python Extension Packages
The common port of PIL to Python 3.x is called "Pillow".
Also I would suggest pygame library for simple tasks. It is a library, full of features for creating games - and reading from some common image formats is among them. Works with Python 3.x as well.

How can I obtain the image size using a standard Python class (without using an external library)?

I am using Python 2.5. And using the standard classes from Python, I want to determine the image size of a file.
I've heard PIL (Python Image Library), but it requires installation to work.
How might I obtain an image's size without using any external library, just using Python 2.5's own modules?
Note I want to support common image formats, particularly JPG and PNG.
Here's a Python 3 script that returns a tuple containing an image height and width for .png, .gif and .jpeg without using any external libraries (i.e., what Kurt McKee referenced). It should be relatively easy to transfer it to Python 2.
import struct
import imghdr
def get_image_size(fname):
'''Determine the image type of fhandle and return its size.
from draco'''
with open(fname, 'rb') as fhandle:
head = fhandle.read(24)
if len(head) != 24:
return
if imghdr.what(fname) == 'png':
check = struct.unpack('>i', head[4:8])[0]
if check != 0x0d0a1a0a:
return
width, height = struct.unpack('>ii', head[16:24])
elif imghdr.what(fname) == 'gif':
width, height = struct.unpack('<HH', head[6:10])
elif imghdr.what(fname) == 'jpeg':
try:
fhandle.seek(0) # Read 0xff next
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf:
fhandle.seek(size, 1)
byte = fhandle.read(1)
while ord(byte) == 0xff:
byte = fhandle.read(1)
ftype = ord(byte)
size = struct.unpack('>H', fhandle.read(2))[0] - 2
# We are at a SOFn block
fhandle.seek(1, 1) # Skip `precision' byte.
height, width = struct.unpack('>HH', fhandle.read(4))
except Exception: #IGNORE:W0703
return
else:
return
return width, height
Kurt's answer needed to be slightly modified to work for me.
First, on Ubuntu: sudo apt-get install python-imaging
Then:
from PIL import Image
im = Image.open(filepath)
im.size # (width,height) tuple
Check out the handbook for more information.
Here's a way to get dimensions of a PNG file without needing a third-party module. From Python - verify a PNG file and get image dimensions:
import struct
def get_image_info(data):
if is_png(data):
w, h = struct.unpack('>LL', data[16:24])
width = int(w)
height = int(h)
else:
raise Exception('not a png image')
return width, height
def is_png(data):
return (data[:8] == '\211PNG\r\n\032\n'and (data[12:16] == 'IHDR'))
if __name__ == '__main__':
with open('foo.png', 'rb') as f:
data = f.read()
print is_png(data)
print get_image_info(data)
When you run this, it will return:
True
(x, y)
And another example that includes handling of JPEGs as well:
http://markasread.net/post/17551554979/get-image-size-info-using-pure-python-code
While it's possible to call open(filename, 'rb') and check through the binary image headers for the dimensions, it seems much more useful to install PIL and spend your time writing great new software! You gain greater file format support and the reliability that comes from widespread usage. From the PIL documentation, it appears that the code you would need to complete your task would be:
from PIL import Image
im = Image.open('filename.png')
print 'width: %d - height: %d' % im.size # returns (width, height) tuple
As for writing code yourself, I'm not aware of a module in the Python standard library that will do what you want. You'll have to open() the image in binary mode and start decoding it yourself. You can read about the formats at:
PNG file format documentation
Notes on the JPEG file format headers
Regarding Fred the Fantastic's answer:
Not every JPEG marker between C0-CF are SOF markers; I excluded DHT (C4), DNL (C8) and DAC (CC). Note that I haven't looked into whether it is even possible to parse any frames other than C0 and C2 in this manner. However, the other ones seem to be fairly rare (I personally haven't encountered any other than C0 and C2).
Either way, this solves the problem mentioned in comments by Malandy with Bangles.jpg (DHT erroneously parsed as SOF).
The other problem mentioned with 1431588037-WgsI3vK.jpg is due to imghdr only being able detect the APP0 (EXIF) and APP1 (JFIF) headers.
This can be fixed by adding a more lax test to imghdr (e.g. simply FFD8 or maybe FFD8FF?) or something much more complex (possibly even data validation). With a more complex approach I've only found issues with: APP14 (FFEE) (Adobe); the first marker being DQT (FFDB); and APP2 and issues with embedded ICC_PROFILEs.
Revised code below, also altered the call to imghdr.what() slightly:
import struct
import imghdr
def test_jpeg(h, f):
# SOI APP2 + ICC_PROFILE
if h[0:4] == '\xff\xd8\xff\xe2' and h[6:17] == b'ICC_PROFILE':
print "A"
return 'jpeg'
# SOI APP14 + Adobe
if h[0:4] == '\xff\xd8\xff\xee' and h[6:11] == b'Adobe':
return 'jpeg'
# SOI DQT
if h[0:4] == '\xff\xd8\xff\xdb':
return 'jpeg'
imghdr.tests.append(test_jpeg)
def get_image_size(fname):
'''Determine the image type of fhandle and return its size.
from draco'''
with open(fname, 'rb') as fhandle:
head = fhandle.read(24)
if len(head) != 24:
return
what = imghdr.what(None, head)
if what == 'png':
check = struct.unpack('>i', head[4:8])[0]
if check != 0x0d0a1a0a:
return
width, height = struct.unpack('>ii', head[16:24])
elif what == 'gif':
width, height = struct.unpack('<HH', head[6:10])
elif what == 'jpeg':
try:
fhandle.seek(0) # Read 0xff next
size = 2
ftype = 0
while not 0xc0 <= ftype <= 0xcf or ftype in (0xc4, 0xc8, 0xcc):
fhandle.seek(size, 1)
byte = fhandle.read(1)
while ord(byte) == 0xff:
byte = fhandle.read(1)
ftype = ord(byte)
size = struct.unpack('>H', fhandle.read(2))[0] - 2
# We are at a SOFn block
fhandle.seek(1, 1) # Skip `precision' byte.
height, width = struct.unpack('>HH', fhandle.read(4))
except Exception: #IGNORE:W0703
return
else:
return
return width, height
Note: Created a full answer instead of a comment, since I'm not yet allowed to.
If you happen to have ImageMagick installed, then you can use 'identify'. For example, you can call it like this:
path = "//folder/image.jpg"
dim = subprocess.Popen(["identify","-format","\"%w,%h\"",path], stdout=subprocess.PIPE).communicate()[0]
(width, height) = [ int(x) for x in re.sub('[\t\r\n"]', '', dim).split(',') ]
I found a nice solution in another Stack Overflow post (using only standard libraries + dealing with JPEG as well): JohnTESlade answer
And another solution (the quick way) for those who can afford running the 'file' command within the Python interpreter, run:
import os
info = os.popen("file foo.jpg").read()
print info
Output:
foo.jpg: JPEG image data...density 28x28, segment length 16, baseline, precision 8, 352x198, frames 3
All you have got to do now is to format the output to capture the dimensions. 352x198 in my case.
That code does accomplish two things:
Getting the image dimension
Find the real EOF of a JPEG file
Well, when googling, I was more interested in the latter one.
The task was to cut out a JPEG file from a data stream. Since I didn't find any way to use Pythons 'image' to a way to get the EOF of so a JPEG file, I made up this.
Interesting things /changes/notes in this sample:
extending the normal Python file class with the method uInt16,
making the source code better readable and maintainable.
Messing around with struct.unpack() quickly makes the code look ugly
Replaced read over'uninteresting' areas/chunk with seek
In case you just like to get the dimensions, you may remove the line:
hasChunk = ord(byte) not in range(0xD0, 0xDA) + [0x00]
-> since that only gets important when reading over the image data chunk
and comment in
#break
to stop reading as soon as the dimension were found.
...but smile what I'm telling. You're the voder ;)
import struct
import io, os
class myFile(file):
def byte(self):
return file.read(self, 1);
def uInt16(self):
tmp = file.read(self, 2)
return struct.unpack(">H", tmp)[0];
jpeg = myFile('grafx_ui.s00_\\08521678_Unknown.jpg', 'rb')
try:
height = -1
width = -1
EOI = -1
type_check = jpeg.read(2)
if type_check != b'\xff\xd8':
print("Not a JPG")
else:
byte = jpeg.byte()
while byte != b"":
while byte != b'\xff': byte = jpeg.byte()
while byte == b'\xff': byte = jpeg.byte()
# FF D8 SOI Start of Image
# FF D0..7 RST DRI Define Restart Interval inside CompressedData
# FF 00 Masked FF inside CompressedData
# FF D9 EOI End of Image
# http://en.wikipedia.org/wiki/JPEG#Syntax_and_structure
hasChunk = ord(byte) not in range(0xD0, 0xDA) + [0x00]
if hasChunk:
ChunkSize = jpeg.uInt16() - 2
ChunkOffset = jpeg.tell()
Next_ChunkOffset = ChunkOffset + ChunkSize
# Find bytes \xFF \xC0..C3. That marks the start of the frame
if (byte >= b'\xC0' and byte <= b'\xC3'):
# Found SOF1..3 data chunk - Read it and quit
jpeg.seek(1, os.SEEK_CUR)
h = jpeg.uInt16()
w = jpeg.uInt16()
#break
elif (byte == b'\xD9'):
# Found end of image
EOI = jpeg.tell()
break
else:
# Seek to the next data chunk
print "Pos: %.4x %x" % (jpeg.tell(), ChunkSize)
if hasChunk:
jpeg.seek(Next_ChunkOffset)
byte = jpeg.byte()
width = int(w)
height = int(h)
print("Width: %s, Height: %s JpgFileDataSize: %x" % (width, height, EOI))
finally:
jpeg.close()
It depends on the output of file which I am not sure is standardized on all systems. Some JPEGs don't report the image size
import subprocess, re
image_size = list(map(int, re.findall('(\d+)x(\d+)', subprocess.getoutput("file" + filename))[-1]))
I stumbled upon this one, but you can get it by using the following as long as you import NumPy.
import numpy as np
[y, x] = np.shape(img[:, :, 0])
It works because you ignore all but one color, and then the image is just 2D, so shape() tells you how big it is. I am still kind of new to Python, but it seems like a simple way to do it.

Categories