Converting video to images using OpenCV library problem - python-3.x

I have this code which converts .mov videos to images with the specified frequency (e.g. every 30 seconds here).
import cv2
# the path to take the video from
vidcap = cv2.VideoCapture(r"C:\Users\me\Camera_videos\Images_30sec\PIR-206_7.MOV")
def getFrame(sec):
vidcap.set(cv2.CAP_PROP_POS_MSEC,sec*1000)
hasFrames,image = vidcap.read()
if hasFrames:
cv2.imwrite("image"+str(count)+".jpg", image) # save frame as JPG file
return hasFrames
sec = 0
frameRate = 30 #//it will capture image every 30 seconds
count= 1
success = getFrame(sec)
while success:
count = count + 1
sec = sec + frameRate
sec = round(sec, 2)
success = getFrame(sec)
I have no problem with smaller files. A 5min long .mov file for example, produces 11 images as expected (5 x 60 seconds / 30 seconds = about 10 images with the first image taken at 0 seconds).
However, when I tried a bigger file, which is 483 MB and is about 32mins long, I have encountered a problem.
It is expected to generate some 32 x 60/30 = 64 images.
However, it runs and runs generating some 40'000 images until I manually stop the program. it seems to be stuck at one of the last images??
I have uploaded both .mov files to my google drive, if anyone wants to have a look.
small file
https://drive.google.com/file/d/1guKtLgM-vwt-5fG3_suJrhVbtwMSjMQe/view?usp=sharing
large file
https://drive.google.com/file/d/1V_HVRM29qwlsU0vCyWiOuBP-tkjdokul/view?usp=sharing
Can somebody advise on what's going on here?

Related

tkinter animation speed on windows and linux

I was trying to create simple 2d game using tkinter, but faced with interesting problem: animation speed is quite different on various computers.
To test this, I've create script, that measures time of animation
import tkinter as tk
import datetime
root = tk.Tk()
can = tk.Canvas(height=500, width=1000)
can.pack()
rect = can.create_rectangle(0, 240, 20, 260, fil='#5F6A6A')
def act():
global rect, can
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1)
act()
def key_down(key):
t = datetime.datetime.now()
act()
print(datetime.datetime.now() - t)
can.bind("<Button-1>", key_down)
root.mainloop()
and get these results:
i3-7100u ubuntu 20.04 laptop python3.8.5 - 0.5 seconds
i3-7100u windows 10 laptop python3.9.4 - 3 seconds
i3-6006u ubuntu 20.10 laptop python3.9.x - 0.5 seconds
i3-6006u windows 10 laptop python3.8.x - 3 seconds
i5-7200u windows 10 laptop python3.6.x - 3 seconds
i5-8400 windows 10 desktop python3.9.x - 3 seconds
fx-9830p windows 10 laptop python3.8.x - 0.5 seconds
tkinter vesrion is the same - 8.6
How can be it fixed or at least explained?
tkinter.Canvas.after should be used like so:
def act():
global rect, can
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1, act)
The after method is not like time.sleep. Rather than recursively calling the function, the above code schedules it to be called later, so this will break your timing code.
If you want to time it again, try this:
def act():
global rect, can, t
pos = can.coords(rect)
if pos[2] < 1000:
can.move(rect, 5, 0)
can.update()
can.after(1, act)
else:
print(datetime.datetime.now() - t)
def key_down(key):
global t
t = datetime.datetime.now()
act()
This may still take different amounts of time on different machines. This difference can be caused by a variety of things like CPU speed, the implementation of tkinter for your OS etc. The difference can be reduced by increasing the delay between iterations: tkinter.Canvas.after takes a time in milliseconds, so a delay of 16 can still give over 60 frames per seconds.
If keeping the animation speed constant is important, I would recommend you use delta time in your motion calculations rather than assuming a constant frame rate.
AS you can see from your data it doesent matter which python version. It appears that ubuntu systems can help process python easier. However, im pretty sure its just the processer or how much ram the computer has.

Azure FaceAPI limits iteration to 20 items

I have a list of image urls from which I use MS Azure faceAPI to extract some features from the photos. The problem is that whenever I iterate more than 20 urls, it seems not to work on any url after the 20th one. There is no error shown. However, when I manually changed the range to iterate the next 20 urls, it worked.
Side note, on free version, MS Azure Face allows only 20 requests/minute; however, even when I let time sleep up to 60s per 10 requests, the problem still persists.
FYI, I have 360,000 urls in total, and sofar I have made only about 1000 requests.
Can anyone help tell me why this happens and how to solve this? Thank you so much!
# My codes
i = 0
for post in list_post[800:1000]:
i += 1
try:
image_url = post['photo_url']
headers = {'Ocp-Apim-Subscription-Key': KEY}
params = {
'returnFaceId': 'true',
'returnFaceLandmarks': 'false',
'returnFaceAttributes': 'age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise',
}
response = requests.post(face_api_url, params=params, headers=headers, json={"url": image_url})
post['face_feature'] = response.json()[0]
except (KeyError, IndexError):
continue
if i % 10 == 0:
time.sleep(60)
The free version has a max of 30 000 request per month, your 356 000 faces will therefore take a year to run.
The standard version costs USD 1 per 1000 requests, giving a total cost of USD 360. This option supports 10 transactions per second.
https://azure.microsoft.com/en-au/pricing/details/cognitive-services/face-api/

NodeJS Sharp Provides Incorrect Quality For JPEG Images

I am working with sharp node package to calculate the quality of an image.
I couldn't find any API in the package which would calculate the quality.
So I came up with an implementation that follows following steps -
Accept incoming image buffer & create sharp instance
Create another instance from this instance by setting quality to 100
Compare the size of original sharp instance and new sharp instance
If there is a difference in the size, update the quality and execute step 2 with updated quality
Return the quality once comparison in step 4 gives smallest difference
I tested this approach by using an image of known quality i.e. 50 (confirmed)
EDIT - I generated images with different quality values using Photoshop
However, the above logic returns the quality as 82 (expected is something close to 50)
Problem
So the problem is I am not able to figure out the quality of image.
It is fine if the above logic returns a closest value such as 49 or 51.
However the result is totally different.
Results
As per this logic, I get following results for a given quality -
Actual Quality 50 - Result 82
Actual Quality 60 - Result 90
Actual Quality 70 - Result 90
Actual Quality 80 - Result 94
Actual Quality 90 - Result 98
Actual Quality 100 - Result 98
Code
The following code snippet is used to calculate the quality.
I do understand that it is needs improvements for precise results.
But it should at least provide close values.
async function getJpegQuality(image: sharp.Sharp, min_quality: number, max_quality: number): Promise<number> {
if (Math.abs(max_quality - min_quality) <= 1) {
return max_quality;
}
const updated_image: sharp.Sharp = sharp(await image.jpeg({ quality: max_quality }).toBuffer());
const [metadata, updated_metadata]: sharp.Metadata[] = await Promise.all([image.metadata(), updated_image.metadata()]);
// update quality as per size comparison
if (metadata.size > updated_metadata.size) {
const temp_max = Math.round(max_quality);
max_quality = Math.round((max_quality * 2) - min_quality);
min_quality = Math.round(temp_max);
} else {
max_quality = Math.round((min_quality + max_quality) / 2);
min_quality = Math.round((min_quality + max_quality) / 2);
}
// recursion
return await getJpegQuality(image, min_quality, max_quality);
}
Usage
const image: sharp.Sharp = sharp(file.originalImage.buffer);
const quality = await getJpegQuality(image, 1, 100);
console.log(quality);
Thanks!

How to get the duration of an mp3 file

I tried many methods but did not get the exact length value of an mp3 file.
With moviepy:
audiofile = AudioFileClip(url)
print("duration moviepy: " + str(audiofile.duration))
I get result:
duration moviepy: 183.59
With mutagen:
from mutagen.mp3 import MP3
audio = MP3(url)
print("duration mutagen: " + str(audio.info.length))
I received another value of duration:
duration mutagen: 140.93416666666667
Actual duration value when I open the file using windows media player: 2m49s
I don't know what happens to my audio file, I test a few files from the music website and still get the correct value.
This is my audio file
try pyxsox
I tried to use pysox to the audio file which includes this question post
note: pysox needs SOX cli.
how to use it is like this.
import sox
mp3_path = "YOUR STRANGE MP3 FILEPATH"
length = sox.file_info.duration(mp3_path)
print("duration sec: " + str(length))
print("duration min: " + str(int(length/60)) + ':' + str(int(length%60)))
results are
duration sec: 205.347982
duration min: 3:25
and the others information on that mp3 file's duration.
ID3 information => 2.49
mutagen => 2:20
pysox => 3:25
actural length => 3:26
mutagen seems just to read ID3 information.
Using Mutagen
pip install mutagen
python:
import os
from mutagen.mp3 import MP3
def convert_seconds(seconds):
hours = seconds // 3600
seconds %= 3600
minutes = seconds // 60
seconds %= 60
return "%02d:%02d:%02d" % (hours, minutes, seconds)
path = "Your mp3 files floder."
total_length = 0
for root, dirs, files in os.walk(os.path.abspath(path)):
for file in files:
if file.endswith(".mp3"):
audio = MP3(os.path.join(root, file))
length = audio.info.length
total_length += length
hours, minutes, seconds = convert_seconds(total_length).split(":")
print("total duration: " + str(int(hours)) + ':' + str(int(minutes)) + ':' + str(int(seconds)))

Overwrite GPS coordinates in Image Exif using Python 3.6

I am trying to transform image geotags so that images and ground control points lie in the same coordinate system inside my software (Pix4D mapper).
The answer here says:
Exif data is standardized, and GPS data must be encoded using
geographical coordinates (minutes, seconds, etc) described above
instead of a fraction. Unless it's encoded in that format in the exif
tag, it won't stick.
Here is my code:
import os, piexif, pyproj
from PIL import Image
img = Image.open(os.path.join(dirPath,fn))
exif_dict = piexif.load(img.info['exif'])
breite = exif_dict['GPS'][piexif.GPSIFD.GPSLatitude]
lange = exif_dict['GPS'][piexif.GPSIFD.GPSLongitude]
breite = breite[0][0] / breite[0][1] + breite[1][0] / (breite[1][1] * 60) + breite[2][0] / (breite[2][1] * 3600)
lange = lange[0][0] / lange[0][1] + lange[1][0] / (lange[1][1] * 60) + lange[2][0] / (lange[2][1] * 3600)
print(breite) #48.81368778730952
print(lange) #9.954511162420633
x, y = pyproj.transform(wgs84, gk3, lange, breite) #from WGS84 to GaussKrüger zone 3
print(x) #3570178.732528623
print(y) #5408908.20172699
exif_dict['GPS'][piexif.GPSIFD.GPSLatitude] = [ ( (int)(round(y,6) * 1000000), 1000000 ), (0, 1), (0, 1) ]
exif_bytes = piexif.dump(exif_dict) #error here
img.save(os.path.join(outPath,fn), "jpeg", exif=exif_bytes)
I am getting struct.error: argument out of range in the dump method. The original GPSInfo tag looks like: {0: b'\x02\x03\x00\x00', 1: 'N', 2: ((48, 1), (48, 1), (3449322402, 70000000)), 3: 'E', 4: ((9, 1), (57, 1), (1136812930, 70000000)), 5: b'\x00', 6: (3659, 10)}
I am guessing I have to offset the values and encode them properly before writing, but have no idea what is to be done.
It looks like you are already using PIL and Python 3.x, not sure if you want to continue using piexif but either way, you may find it easier to convert the degrees, minutes, and seconds into decimal first. It looks like you are trying to do that already but putting it in a separate function may be clearer and account for direction reference.
Here's an example:
def get_decimal_from_dms(dms, ref):
degrees = dms[0][0] / dms[0][1]
minutes = dms[1][0] / dms[1][1] / 60.0
seconds = dms[2][0] / dms[2][1] / 3600.0
if ref in ['S', 'W']:
degrees = -degrees
minutes = -minutes
seconds = -seconds
return round(degrees + minutes + seconds, 5)
def get_coordinates(geotags):
lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])
lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])
return (lat,lon)
The geotags in this example is a dictionary with the GPSTAGS as keys instead of the numeric codes for readability. You can find more detail and the complete example from this blog post: Getting Started with Geocoding Exif Image Metadata in Python 3
After much hemming & hawing I reached the pages of py3exiv2 image metadata manipulation library. One will find exhaustive lists of the metadata tags as one reads through but here is the list of EXIF tags just to save few clicks.
It runs smoothly on Linux and provides many opportunities to edit image-headers. The documentation is also quite clear. I recommend this as a solution and am interested to know if it solves everyone else's problems as well.

Resources