I am creating a video from 5100 full size images (each about 5000 X 3000 px) using OpenCV and Python. Works great, however, at around image 3000 (with the AVI file at about 160 MB) the write() method slows down significant.
I'm cropping the image, and then I'm resizing before I write. I've put a lot of timing code below.
CPU isn't the issue... lots of disk space... Has anyone seen this before? (I've searched high and low) I've been looking for memory leaks, but the code is too simple for that.
import numpy as np
import cv2, glob, time
files = glob.glob("./pics/*.JPG")
video = cv2.VideoWriter('test.avi',cv2.cv.CV_FOURCC('m', 'p', '4', 'v'),120,(1920,1080))
print "INDEX, IMREAD, CROP_IMG, RESIZE, WRITE, TOTAL"
for idx, file in enumerate(files):
offset=idx/500 #just an offset to make sure that it reflects my real world.
start = time.time()
img = cv2.imread(file)
imread_dur = time.time() - start
start = time.time()
crop_img = img[1000+offset:2050+offset, 500-offset:2370-offset]
crop_dur = time.time() - start
start = time.time()
resized_image = cv2.resize(crop_img, (1920, 1080))
resize_dur = time.time() - start
start = time.time()
video.write(resized_image)
write_dur = time.time() - start
print "%5d, %3.6f, %3.6f, %3.6f, %3.6f, %3.6f " % (idx,imread_dur, crop_dur, resize_dur, write_dur, imread_dur+crop_dur+resize_dur+write_dur)
video.release()
At around the 2900th image, it goes from about 200ms to video.write() to around 2000ms or more per image. It feels like a buffer problem on the output video file, but nothing seems configurable. (The CPU goes from almost a full core to ~3% as the write() slows down.) At 3000, it is taking 13s for a write()
INDEX, IMREAD, CROP_IMG, RESIZE, WRITE, TOTAL
...
2847, 0.232032, 0.004127, 0.015886, 0.214999, 0.467044
2848, 0.233260, 0.003745, 0.013973, 0.214703, 0.465681
2849, 0.228818, 0.003882, 0.016408, 0.251602, 0.500710
...
3096, 0.238894, 0.003780, 0.013936, 13.183812, 13.440422
3097, 0.253249, 0.004052, 0.015534, 13.668831, 13.941666
I've looked under the covers and nothing is popping up. Anyone seen this?
Related
I have a directory of images and an image that I know is in this image directory there is a similar image in the directory saved in a different format and scaled differently, but I dont know where (about 100 000 images).
I want to look for the image and find out its filename inside this directory.
I am looking for a mostly already made soulution which I couldn't find. I found OpenCV but I would need to write code around that. Is there a project like that out there?
If there isn't could you help me make a simple C# console app using OpenCV, I tried their templates but never managed to get SURF or CudaSURF working.
Thanks
Edited as per #Mark Setchell's comment
If the image is identical, the fastest way is to get the file size of the image you are looking for and compare it with the file sizes of the images amongst which you are searching.
I suggest this first because, as Christoph clarifies in the comments, it doesn't require reading the file at all - it is just metadata.
If that yields more than one matching answer, calculate a hash (MD5 or other) and pick the filename that produces the same hash.
Again, as mentioned by Christoph in the comments, this doesn't require decoding the image, or holding the decompressed image in RAM, just checksumming it.
So in the end I used this site and modified the python code used there for searching a directory instead of a single image. There is not much code so the full thing is below:
import argparse
from ast import For, arg
import cv2
from os import listdir
from os.path import isfile, join
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str, required=True,
help="path to input image where we'll apply template matching")
ap.add_argument("-t", "--template", type=str, required=True,
help="path to template image")
args = vars(ap.parse_args())
# load the input image and template image from disk
print("[INFO] loading template...")
template = cv2.imread(args["template"])
cv2.namedWindow("Output")
cv2.startWindowThread()
# Display an image
cv2.imshow("Output", template)
cv2.waitKey(0)
# convert both the image and template to grayscale
templateGray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
imageFileNames = [f for f in listdir(args["image"]) if isfile(join(args["image"], f))]
for imageFileName in imageFileNames:
try:
imagePath = args["image"] + imageFileName
print("[INFO] Loading " + imagePath + " from disk...")
image = cv2.imread(imagePath)
print("[INFO] Converting " + imageFileName + " to grayscale...")
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print("[INFO] Performing template matching for " + imageFileName + "...")
result = cv2.matchTemplate(imageGray, templateGray,
cv2.TM_CCOEFF_NORMED)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(result)
(startX, startY) = maxLoc
endX = startX + template.shape[1]
endY = startY + template.shape[0]
if maxVal > 0.75:
print("maxVal = " + str(maxVal))
# draw the bounding box on the image
cv2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 3)
# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)
cv2.imshow("Output", template)
except KeyboardInterrupt:
break
except:
print(imageFileName)
print("Error")
cv2.destroyAllWindows()
The code above shows any image with match value (what I guess is how much similarity there is between source and template) greater than 0.75
Probably still too low but if you want to use it tweak it to your liking.
Note that this WILL NOT work if the image is rotated and if, like me, you have a bright light source in the template other lightsources will come up as false positives
As for time it took me about 7 hours, where the script paused about every 20 minutes for a false positive until I found my image. I got through about 2/3 of all images.
as a sidenote it took 10 minutes to just build the array of files inside the directory, and it took about 500mb of ram once done
This is not the best answer so if anyone more qualified finds this feel free to write another answer.
I've written a tkinter script with animation, that works fine on Xubuntu, but when I run it on Mac, the animation doesn't work. Here's a little script that demonstrates the problem:
import tkinter as tk
from time import sleep
root = tk.Tk()
canvas = tk.Canvas(root, height=200, width = 200)
canvas.pack()
this = canvas.create_rectangle(25,25, 75, 75, fill='blue')
that = canvas.create_rectangle(125, 125, 175, 175, fill = 'red')
def blink(event):
global this, that
for _ in range(9):
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill = 'blue')
canvas.update_idletasks()
sleep(.4)
this, that = that, this
canvas.bind('<ButtonRelease-1>', blink)
root.mainloop()
This draws a red square and a blue square on a canvas. When the user clicks the canvas, the squares repeatedly switch colors. On Xubuntu, it works as intended.
On Mac, when I click the canvas, I get spinning beach ball, and after a few seconds, we see that squares have switched colors, because they switch colors an odd number of times in the code.
It seems to me that update_idletasks isn't working. Is there some way to fix this? I am running python 3.9.5 with Tk 8.6 on Big Sur.
I think what you can do is avoid tasks that will block the mainloop, in this case time.sleep(). So your code can be remade by emulating a for loop with after, and I see nothing that stops this general code from running OS independent:
count = 0 # Think of this as the `_` in for _ in range(9)
def blink(event=None):
global this, that, count
if count < 9: # Basically repeats everytime `count` is less than 9, like a for loop
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill='blue')
this, that = that, this
count += 1 # Increase count
root.after(400,blink) # Repeat this code block every 400 ms or 0.4 seconds
else:
count = 0 # After it is 9, set it to 0 for the next click to be processed
I found that using update instead of update_idletasks works on both platforms. It's my understanding though, that the latter is much preferred. See the accepted answer to this question for example. This solves my immediate problem, but does anyone know if update_idletasks ever works on the Mac?
I would like to extract one image per minute from an mp4 video recording and I have a code for that already, however, I also would like to add a starting frame from where the extraction should start and either set an ending frame or the number of images that should be extracted.
This is because I would like to avoid trimming the videos as it takes a very long time.
My existing code to extract the images is the following:
cap = cv2.VideoCapture(r'C:my_folder/my_video.mp4')
i = 1
while (cap.isOpened()):
ret, frame = cap.read()
if ret == False:
break
if i % 1800 == 0:
cv2.imwrite(r'C:/my_folder/pictures/' + str(i) + '.jpg', frame)
i += 1
cap.release()
cv2.destroyAllWindows()
Let's say I would like to start the extraction from frame 2700 and from that every 1800th frame until I have 30 frames.
Is there a way to do this? Every help would be much appreciated!
Change the if condition to the following:
if i > 2700 and i % 1800 == 0:
[EDIT2] I am using the code below to plot a real time signal from serial port.
The issue I have is that the plot lags a lot even though I am not replotting the whole screen.
How can I speed up the process?
function timerCallback(~,~,hObject)
handles = guidata(hObject);
[r,w]=size(handles.y);
if isfield(handles,'s')
try
readasync(handles.s);
n = handles.s.BytesAvailable;
if n > 0
in = fscanf(handles.s,'%d');
handles.y=[handles.y,in];
guidata(hObject,handles);
set(handles.h,'yData',(handles.y-99)*26/855);
startSpot = (w)-1000;
axis([ startSpot, (w+50), 0 , 30 ]);
drawnow
end
catch
end
end
Timer is set to fixedRate at 0.001s.
[EDIT2]
I have moved the plot line outside the timer callback. I can't barely see any difference in performance.
It is very critical to learn the start time of each frame of a video.
I need to determine the starting point manually ( for example 848 here) by using below matlab code:
v = VideoReader('video1.avi','CurrentTime',848);
while hasFrame(v)
video_frame = readFrame(v);
counter=counter+1;
if counter==1
imshow(video_frame)
imhist(video_frame(:,:,1))
end
end
What I want is to distinguish some video frame from the others by using histogram. At the end my aim is to reach the exact showing time of the distinguished frames.
After editting:
This is frame histogram outputs:
Histogram size of the some frames are different from the previous one, do you know the reason?
difference=[difference sum(abs(histcounts(video_frame)-histcounts(lastframe)))];
Because of the taking the difference of the I had remove the different histogram sized frames but it causes missing some frames.
i havent found an video example that looks like what you discribe. please condsider always to have an example.
This example code calculates the differences in the histcounts. please notice that waitforbuttonpressis in the loop so you have to click for each frame while testing or remove it when the video is too long. Does this works on your file?
v = VideoReader('sample.avi','CurrentTime',1);
figure1=figure('unit','normalized','Position',[0.2 0.2 0.4 0.6]);
axes1=subplot(3,1,1);
axes2=subplot(3,1,2);
axes3 = subplot(3,1,3);
counter=0;
difference=[];
video_frame=readFrame(v);
while hasFrame(v)
lastframe=video_frame;
video_frame = readFrame(v);
counter=counter+1;
imshow(video_frame,'Parent',axes1);
[a,b]=histcounts(video_frame(:,:,1));
plot(b(1:end-1),a,'Parent',axes2);
difference=[difference sum(abs(histcounts(video_frame,0:255)-histcounts(lastframe,0:255)))];
bar(1:counter,difference,'Parent',axes3);
waitforbuttonpress
end
[~,onedistinguished]=max(difference);
%defining a threshold like every value that is bigger 4000
multidistinguished=find(difference>4000);
disp(['majorly changed at: ' num2str(distinguished)]);