How can I get the frames per sec from the header of an avi xvid video file? - avi

The following is a hex-ascii dump of the first 300 bytes of an avi xvid file:
|52|49|46|46|8c|79|95|00|41|56|49|20|4c|49|53|54|94|6a|00|00 RIFF.y..AVI LIST.j..
|68|64|72|6c|61|76|69|68|38|00|00|00|40|9c|00|00|00|ec|36|00 hdrlavih8...#.....6.
|00|00|00|00|10|00|00|00|81|01|00|00|00|00|00|00|01|00|00|00 ....................
|df|9e|01|00|d0|02|00|00|40|02|00|00|00|00|00|00|00|00|00|00 ........#...........
|00|00|00|00|00|00|00|00|4c|49|53|54|3c|69|00|00|73|74|72|6c ........LIST<i..strl
|73|74|72|68|38|00|00|00|76|69|64|73|78|76|69|64|00|00|00|00 strh8...vidsxvid....
|00|00|00|00|00|00|00|00|e8|03|00|00|a8|61|00|00|00|00|00|00 .............a......
|81|01|00|00|df|9e|01|00|10|27|00|00|00|00|00|00|00|00|00|00 .........'..........
|d0|02|40|02|73|74|72|66|28|00|00|00|28|00|00|00|d0|02|00|00 ..#.strf(...(.......
|40|02|00|00|01|00|18|00|58|56|49|44|00|f8|25|00|00|00|00|00 #.......XVID..%.....
|00|00|00|00|00|00|00|00|00|00|00|00|73|74|72|64|c0|28|00|00 ............strd.(..
|00|00|00|00|bc|02|00|00|90|b2|08|00|2e|5c|76|69|64|65|6f|2e .............\video.
|70|61|73|73|00|00|2e|00|70|00|61|00|73|00|73|00|00|00|00|00 pass....p.a.s.s.....
|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00 ....................
|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00 ....................
In this particular case I know that the video has a frame rate of 25 frames per second. How can I use this dump to obtain the frame rate? Note, it is straightforward to obtain the frame rate for an avi msvc video file since the data structure is well documented. However, I have yet to find any document that defines the data structure for an avi xvid file.

The following is an improved version (IMHO) of my previous answer. This code finds 5 key features of AVI files.
"""
Purpose: To extract 5 important features from AVI files: time between frames,
frames per second, number of frames, width and height of each frame.
Author: V. Stokes (vs#it.uu.se)
Version: 2014.01.01.1
Note:
1. DT (time between samples) in microseconds, bytes: 32,33,34,35
of all AVI files that I tested. Byte 0 is the first byte in the AVI file.
2. SR (sample rate) is equivalent to fps (frames per second) and can be
calculated from the inverse of DT.
3. NF (number of frames), bytes: 48,49,50,51.
4. WD (width of each frame) in pixels, bytes: 64,65,66,67
5. HT (height of each frame) in pixels, bytes: 68,69,70,71
6. This python script works on all the AVI files that I have tested (so far),
which suggests there is some consistency in the different AVI formats.
"""
import os
# laptop desktop
dirpaths = [r'D:/Videos/', r'C:/Videos/']
for path in dirpaths:
if os.path.exists(path):
defDir = path
## All the following videos were "test AVI video downloads" from the Web
#video = 'julius.avi' #CRAM ( 56 frames,320x240,15fps, 66667 microseconds between frames)
#video = 'flyby-xvid.avi' #DIVX (495 frames,320x240,30fps, 33333 microseconds between frames)
#video = 'flyby-xvid-realtime.avi' #DIVX (495 frames,320x240,30fps, 33333 microseconds between frames)
#video = 'flyby-xvid-q5.avi' #DIVX
#video = 'flyby-xvid-mpeg.avi' #DIVX
#video = 'flyby-xvid-mpeg-q2.avi' #DIVX
#video = 'flyby-xvid-default.avi' #DIVX
#video = 'flyby-xvid-2p.avi' #DIVX
#video = 'flyby-divx.avi' #DX50
#video = 'ubAVIxvid10.avi' #XVID (849 frames,640x480,10fps,100000 microseconds between frames)
#video = 'toy_plane_liftoff.mp4' # This is not an AVI and doesn't work
video = 'toy_plane_liftoff.avi' #MJPG (100 frames,640x480,25fps, 40000 microsseconds between frames)
inFile = defDir + video
print 'Input file: %s' %inFile
data = [0,0,0,0]
with open(inFile,'r') as f:
f.seek(32,0) # start reading offset byte 32
data[0] = f.read(4) # get 4 bytes (32,33,34,35) -- DT
f.seek(48,0)
data[1] = f.read(4) # get 4 bytes (48,49,50,51) -- NF
f.seek(64,0)
data[2] = f.read(4) # get 4 bytes (64,65,66,67) -- Width
data[3] = f.read(4) # get 4 bytes (68,69,70,71) -- Height
f.close()
def bufferToHex(num):
for k in range(num):
accumulator = ''
buffer = data[k]
for item in range(4):
accumulator += '%s' % buffer[item]
data[k] = accumulator
# Get 4 hex strings (4 bytes in each string)
bufferToHex(4)
for k in range(4): # loop on DT,NF,WD,HT
prev_kvs = ''
for hexval in data[k]: # loop on each group of 4 bytes
strr = hexval.encode('hex') # hexidecimal string of length 2
kvs = strr + prev_kvs
prev_kvs = kvs
intVal = int(kvs,16)
if k == 0:
# DT
print 'DT = %6d (microseconds)' %intVal
print 'SR = %6d (frames per second)' %round(10.0**6/intVal)
elif k == 1:
# NF
print 'NF = %6d (number of frames)' %intVal
elif k == 2:
# Width
print 'Width = %6d (width in pixels)' %intVal
else:
# Height
print 'Height = %6d (height in pixels)' %intVal

The following Python script (Python 2.7, Windows Vista-32 bit) can be used to extract the time between frames from AVI files and in turn the frame rate.
"""
Purpose: To extract the time between frames from AVI files
Author: V. Stokes (vs#it.uu.se)
Version: 2013.12.31.1
Note:
1. DT (time between samples) in microseconds is stored in bytes: 32,33,34,35
of all AVI files that I tested. Byte 0 is the first byte in the AVI file.
2. SR (sample rate) is equivalent to fps (frames per second) and can be
calculated from the inverse of DT.
3. DT is stored as 4 bytes (long C integer) which are in reverse order.
4. This python script works on all the AVI files that I have tested (so far),
which suggests there is some consistency in the different AVI formats.
5. All of the AVI files used for testing can be obtained from me via
an email request.
"""
import os
# laptop desktop
dirpaths = [r'D:/Videos/', r'C:/Videos/']
for path in dirpaths:
if os.path.exists(path):
defDir = path
#
## All of the following files were downloaded from different web "test" AVI file sites
## Bytes: 32,33,34,35 = 35,82,00,00
#video = 'flyby-xvid.avi' #DIVX (495 frames,320x240,30fps, 33333 microseconds between frames)
#video = 'flyby-xvid-realtime.avi' #DIVX (495 frames,320x240,30fps, 33333 microseconds between frames)
#video = 'flyby-xvid-q5.avi' #DIVX
#video = 'flyby-xvid-mpeg.avi' #DIVX
#video = 'flyby-xvid-mpeg-q2.avi' #DIVX
#video = 'flyby-xvid-default.avi' #DIVX
#video = 'flyby-xvid-2p.avi' #DIVX
## Bytes: 32,33,34,35 = 35,82,00,00
#video = 'flyby-divx.avi' #DX50
## Bytes: 32,33,34,35 = a0,86,01,00
#video = 'ubAVIxvid10.avi' #XVID (849 frames,640x480,10fps,100000 microseconds between frames)
#video = 'toy_plane_liftoff.mp4' # This is not an AVI and doesn't work!
#video = 'toy_plane_liftoff.avi' #MJPG (100 frames,640x480,25fps, 40000 microsseconds between frames)
## Bytes: 32,33,34,35 = 6b,04,01,00
video = 'julius.avi' #CRAM ( 56 frames,320x240,15fps, 66667 microseconds between frames)
inFile = defDir+video
print 'Input file: %s' %inFile
with open(inFile,'r') as f:
# Get time between frames (in hex format)
f.seek(32,0) # start reading at byte 32
data = f.read(4) # get 4 bytes (32,33,34,35)
f.close()
def bufferToHex(buffer, start, count):
accumulator = ''
for item in range(count):
accumulator += '%s' % buffer[start + item]
return accumulator
hexc = bufferToHex(data, 0, 4)
timeBetweenFrames = 0
k = -2
for hexval in hexc:
k += 2 # two hex characters
kvs = hexval.encode('hex') # hexidecimal string of length 2
kv = int(kvs,16) # convert to integer base-16 (hexidecimal)
kp = 16**k
timeBetweenFrames += kv*kp
print 'DT = %6d (microseconds)' %timeBetweenFrames
print 'SR = %6d (frames per second)' %round(10.0**6/timeBetweenFrames)

Related

Numbering tickets on a PDF

I have a PDF with the art of a ticket for a fundraising dinner. I am providing a mock-up here so you can reproduce my problem:
mock up ticket
I would like to run the following pseudocode:
for i in 1:200
copy "mock up.pdf" to $i.pdf
inject $i into $i.pdf using font "OpenDyslexic" # place the ticket number in the pdf
end
create "final.pdf"
i = 0
for p in 1:20
add page to "final.pdf"
for column in 1:2
for row in 1:5
i = i + 1
inject $i.pdf in "final.pdf" in row, column of page p
end
end
end
Thank you!
I might have a solution:
#!/bin/env python3
# adapted from https://pymupdf.readthedocs.io/en/latest/faq.html#how-to-insert-text
# and https://pymupdf.readthedocs.io/en/latest/faq.html#how-to-combine-single-pages
import fitz # in fact, pip install pymupdf
#from sys import argv # in a future, I might get all the parameter via command line
width, height = fitz.paper_size("A4")
r = fitz.Rect(0, 0, width, height)
doc = fitz.open("mock up.pdf")
page = doc[0]
print("input file information:")
print(doc.metadata)
print("rotation=",page.rotation)
print("cropbox=",page.cropbox)
print("mediabox=",page.mediabox)
print("rect=",page.rect)
page.set_rotation(0)
artsize=page.rect
(nx, ny) = (200,140) # position of the ticket number inside the pdf
(dx, dy) = page.mediabox_size # the displacement of the ticket inside the output pdf
ntickets=25
nrows=5 # of tickets vertically
ncols=2 # of tickets horizontally
ntickets_per_page = nrows*ncols
outpdf = fitz.open()
nrow = ncol = 0
i = 0
while i < ntickets:
if i % ntickets_per_page == 0:
#print("new page for ticket #",i)
newpage = outpdf.new_page()
nrow, ncol = 0, 0
for ncol in range(1,ncols+1):
for nrow in range(1,nrows+1):
i += 1
if i > ntickets:
break
text = "{:04d}".format(i)
locr = fitz.Rect((ncol-1)*dx,(nrow-1)*dy,ncol*dx,nrow*dy)
#print("location of the ticket:", locr)
newpage.show_pdf_page(locr,doc,0)
p = fitz.Point(nx+(ncol-1)*dx,ny+(nrow-1)*dy)
#print("location of the number for ticket ", i, ": ", p)
rc = newpage.insert_text(p, # bottom left of 1st char
text,
fontname="tibo", # Times, bold
fontsize=12,
rotate=0,
)
i -= 1
print("%i lines printed on %i tickets." % (rc, i))
outpdf.save("tmp{:04d}.pdf".format(i))

Pigs counting when crossing a line using OpenCV

I'm trying to count the number of piglets that enter and leave a zone. This is important because, in my project, there is a balance underneath the zone that computes the weight of the animals. My goal is to find the pig's weight, so, to achieve that, I will count the number of piglets that enter the zone, and if this number is zero, I have the pig's weight, and according to the number of piglets that get in I will calculate the weight of each as well.
But the weight history is for the future. Currently, I need help in the counting process.
The video can be seen here. The entrance occurs from the minute 00:40 until 02:00 and the exit starts on the minute 03:54 and goes all the way through the video because the piglets start, at this point, to enter and exit the zone.
I've successfully counted the entrance with the code below. I defined a region of interest, very small, and filter the pigs according to their colors. It works fine until the piglets start to move around and get very active, leaving and entering the zone all the time.
I'm out of ideas to proceed with this challenge. If you have any suggestions, please, tell me!
Thanks!!
import cv2
FULL_VIDEO_PATH = "PATH TO THE FULL VIDEO"
MAX_COLOR = (225, 215, 219)
MIN_COLOR = (158, 141, 148)
def get_centroid(x, y, w, h):
x1 = int(w / 2)
y1 = int(h / 2)
cx = x + x1
cy = y + y1
return cx, cy
def filter_mask(frame):
# create a copy from the ROI to be filtered
ROI = (frame[80:310, 615:620]).copy()
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# create a green rectangle on the structure that creates noise
thicker_line_filtered = cv2.rectangle(ROI, (400, 135), (0, 165), (20, 200, 20), -1)
closing = cv2.morphologyEx(thicker_line_filtered, cv2.MORPH_CLOSE, kernel)
opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel)
dilation = cv2.dilate(opening, kernel, iterations=2)
# Filter the image according to the colors
segmented_line = cv2.inRange(dilation, MIN_COLOR, MAX_COLOR)
# Resize segmented line only for plot
copy = cv2.resize(segmented_line, (200, 400))
cv2.imshow('ROI', copy)
return segmented_line
def count_pigs():
cap = cv2.VideoCapture(FULL_VIDEO_PATH)
ret, frame = cap.read()
total_pigs = 0
frames_not_seen = 0
last_center = 0
is_position_ok = False
is_size_ok = False
total_size = 0
already_counted = False
while ret:
# Window interval used for counting
count_window_interval = (615, 0, 620, 400)
# Filter frame
fg_mask = filter_mask(frame)
# Draw a line on the frame, which represents when the pigs will be counted
frame_with_line = cv2.line(frame, count_window_interval[0:2], count_window_interval[2:4],(0,0,255), 1)
contours, _ = cv2.findContours(fg_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# If no contour is found, increments the variable
if len(contours) == 0:
frames_not_seen += 1
# If no contours are found within 5 frames, set last_center to 0 to generate the position difference when
# a new counter is found.
if frames_not_seen > 5:
last_center = 0
for c in contours:
frames_not_seen = 0
# Find the contour coordinates
(x, y, w, h) = cv2.boundingRect(c)
# Calculate the rectangle's center
centroid = get_centroid(x, y, w, h)
# Get the moments from the contour to calculate its size
moments = cv2.moments(c)
# Get contour's size
size = moments['m00']
# Sum the size until count the current pig
if not already_counted:
total_size += size
# If the difference between the last center and the current one is bigger than 80 - which means a new pig
# enter the counting zone - set the position ok and set the already_counted to False to mitigate noises
# with significant differences to be counted
if abs(last_center - centroid[1]) > 80:
is_position_ok = True
already_counted = False
# Imposes limits to the size to evaluate if the contour is consistent
# Min and Max value determined experimentally
if 1300 < total_size < 5500:
is_size_ok = True
# If all conditions are True, count the pig and reset all of them.
if is_position_ok and is_size_ok and not already_counted:
is_position_ok = False
is_size_ok = False
already_counted = True
total_size = 0
total_pigs += 1
last_center = centroid[1]
frame_with_line = cv2.putText(frame_with_line, f'Pigs: {total_pigs}', (100, 370) , cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,0), 2)
cv2.imshow('Frame', frame_with_line)
cv2.moveWindow('ROI', 1130, 0)
cv2.moveWindow('Frame', 0, 0)
k = cv2.waitKey(15) & 0xff
if k == 27:
break
elif k == 32:
cv2.waitKey() & 0xff
ret, frame = cap.read()
cv2.destroyAllWindows()
cap.release()
if __name__ == '__main__':
count_pigs()

Combining multiple values from database into one image

I'm trying to take 5 consecutive pixels from each image of a database, and position them consecutively to create a new image of 250x250px. all images in the database are 250x250px.
The Numpy array I'm getting has only 250 items in it, although the database has about 13,000 photos in it. Can someone help me spot the problem?
Current output for 'len(new_img_pxl)' = 250
Illustration
#edit:
from imutils import paths
import cv2
import numpy as np
# access database
database_path = list(paths.list_images('database'))
#grey scale database
img_gray = []
x = -5
y = 0
r = 0
new_img_pxl = []
# open as grayscale, resize
for img_path in database_path:
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
img_resize = cv2.resize(img, (250, 250))
img_gray.append(img_resize)
# take five consecutive pixel from each image
for item in img_gray:
x += 5
y += 5
five_pix = item[[r][x:y]]
for pix in five_pix:
new_img_pxl.append(pix)
if y == 250:
r += 1
x = -5
y = 0
# convert to array
new_img_pxl_array = np.array(new_img_pxl)
reshape_new_img = new_img_pxl_array.reshape(25,10)
# Convert the pixels into an array using numpy
array = np.array(reshape_new_img, dtype=np.uint8)
new_img_output = cv2.imwrite('new_output_save/001.png',reshape_new_img)
your bug is in the second loop.
for item in img_gray:
for every image (i) in the list img_gray you do:
for a in item:
for each row (j) in the image (i), extract 5 pixels and append them to new_img_pxl.
the first bug is that you don't take just 5 pixels from each image, you take 5 pixels from each row of each image.
your 2nd bug is that after extracting 250 pixels the values of the variables x and y are higher than 250 (the length of a row). As a result, when you try to access the pixels [250:255] and so on you get 'None'.
If I understand your intentions, then the way you should have implemented this is as follows:
r = 0
# As Mark Setchell suggested, you might want to change iterating
# over a list of images to iterating over the list of paths
# for img_path in database_path:
for item in img_gray:
# As Mark Setchell suggested, you might wat to load and
# process your image here, overwriting the past image and
# having the memory released
x += 5
y += 5
# when you finish a row jump to the next?
if x==250:
x = 0
y = 5
r+=1
# not sure what you wanna do when you get to the end of the image.
# roll back to the start?
if r==249 && x==250:
r = 0
x = 0
y = 5
five_pix = a[r, x:y]
for pix in five_pix:
new_img_pxl.append(pix)

Increasing the volume of recording in Real Time of after saving it?

I used python to make a prototype, to increase the volume of audio signal in real time. It worked by using new_data = audioop.mul(data, 4, 4) where 'data' is chunks from Pyaudio in streaming.
Now, I have to apply similar in ObjectiveC, and even after searching I am unable to find it. How can it be done in Objective C? Do we have such control over data flow in Objective C and If we can't, Is there anyway that a recorded sample's volume can be increased?
import pyaudio
import wave
import audioop
import sys
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = 7
WAVE_OUTPUT_FILENAME1 = sys.argv[1]
WAVE_OUTPUT_FILENAME2 = sys.argv[2]
device_index = 2
print("----------------------record device list---------------------")
audio = pyaudio.PyAudio()
print(audio)
info = audio.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
if (audio.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
print ("Input Device id ", i, " - ", audio.get_device_info_by_host_api_device_index(0, i).get('name'))
print("-------------------------------------------------------------")
index = int((input()))
print(type(index))
print("recording via index "+str(index))
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,input_device_index = index,
frames_per_buffer=CHUNK)
print ("recording started")
Recordframes = []
Recordframes2= []
print(int(RATE / CHUNK * RECORD_SECONDS))
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
new_data = audioop.mul(data, 4, 4)
print("hshsh")
Recordframes.append(data)
Recordframes2.append(new_data)
# data = stream.read(CHUNK)
# print("hshsh")
# Recordframes.append(data)
# print ("recording stopped")
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME1, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(Recordframes))
waveFile2 = wave.open(WAVE_OUTPUT_FILENAME2, 'wb')
waveFile2.setnchannels(CHANNELS)
waveFile2.setsampwidth(audio.get_sample_size(FORMAT))
waveFile2.setframerate(RATE)
waveFile2.writeframes(b''.join(Recordframes2))
waveFile.close()
waveFile2.close()
You can use AVAudioEngine (link) to tap into the raw audio data. Alternatively, still using AVAudioEngine, you could add an AVAudioUnitEQ (link) node to your audio graph and use that boost the gain.
Using either method, you can then write out to a file using AVAudioFile (link).

How to properly select wanted data and discard unwanted data from binary files

I'm working on a project where I'm trying to convert old 16bit binary data files into 32bit data files for later use.
Straight conversion is no issue, but then i noticed i needed to remove header data from the data-file's.
The data consists of 8206 bytes long frames, each frame consists of 14 byte long header and 4096 bytes long data -block, depending on file, there are either 70313 or 70312 frames in each file.
i couldn't find a neat way to find all the header and remove them and save only the data-block to a new file.
so heres what I did:
results_array = np.empty([0,1], np.uint16)
for filename in file_list:
num_files += 1
# read data from file as 16bit's and save it as 32bit
data16 = np.fromfile(data_dir + "/" + filename, dtype=np.uint16)
filesize = np.prod(data16.shape)
if filesize == 288494239:
total_frames = 70313
#total_frames = 3000
else:
total_frames = 70312
#total_frames = 3000
frame_count = 0
chunksize = 4103
with open(data_dir + "/" + filename, 'rb') as file:
while frame_count < total_frames:
frame_count += 1
read_data = file.read(chunksize)
if not read_data:
break
data = read_data[7:4103]
results_array = np.append(results_array,data)
converted = np.frombuffer(results_array, np.uint16)
print(str(frame_count) + "/" + str(total_frames))
converted = np.frombuffer(results_array, np.uint16)
data32 = converted.astype(dtype=np.uint32) * 256
It works (i think it does atleast), but it is very very slow.
So question is, is there a way to do the above much faster, maybe some build-in function in numpy or something else perhaps?
Thanks in advance
Finally managed to crack this one, and it is 100x faster than initial approach :)
data = np.fromfile(read_dir + "/" + file, dtype=np.int16)
frames = len(data) // 4103 # framelenght
# Reshape into array such that each row is a frame
data = np.reshape(data[:frames * 4103], (frames, 4103))
# Remove headers and convert to int32
data = data[:, 7:].astype(np.int32) * 256