Python-OpenCV --F Function error--not finding right dir - python

Am trying to put a timelapse on my OpenCV code but it seems i am doing something that is not right and i don't know why i have the error.
The erorr i got is for this line: filename = f"{timelapse_img_dir}/{i}.jpg" What should i do in order not to have an error?
This is the error i have from my IDE:
File "/Users/Dropbox/OpenCV/src/timelapse.py", line 34
filename = f"{timelapse}/{i}.jpg"
^
SyntaxError: invalid syntax
[Finished in 0.1s with exit code 1]
[shell_cmd: python -u "/Users/Dropbox/OpenCV/src/timelapse.py"]
[dir: /Users/Dropbox/OpenCV/src]
[path: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin]
My code:
import os
import numpy as np
import cv2
import time
import datetime
from utils import CFEVideoConf, image_resize
import glob
cap = cv2.VideoCapture(0)
frames_per_seconds = 20
save_path='saved-media/timelapse.mp4'
config = CFEVideoConf(cap, filepath=save_path, res='720p')
out = cv2.VideoWriter(save_path, config.video_type, frames_per_seconds, config.dims)
timelapse_img_dir = '/Users/Dropbox/OpenCV/src/images/timelapse/'
seconds_duration = 20
seconds_between_shots = .25
if not os.path.exists(timelapse_img_dir):
os.mkdir(timelapse_img_dir)
now = datetime.datetime.now()
finish_time = now + datetime.timedelta(seconds=seconds_duration)
i = 0
while datetime.datetime.now() < finish_time:
'''
Ensure that the current time is still less
than the preset finish time
'''
ret, frame = cap.read()
# filename = f"{timelapse_img_dir}/{i}.jpg"
filename = f"{timelapse_img_dir}/{i}.jpg"
i += 1
cv2.imwrite(filename, frame)
time.sleep(seconds_between_shots)
if cv2.waitKey(20) & 0xFF == ord('q'):
break
def images_to_video(out, image_dir, clear_images=True):
image_list = glob.glob(f"{image_dir}/*.jpg")
sorted_images = sorted(image_list, key=os.path.getmtime)
for file in sorted_images:
image_frame = cv2.imread(file)
out.write(image_frame)
if clear_images:
'''
Remove stored timelapse images
'''
for file in image_list:
os.remove(file)
images_to_video(out, timelapse_img_dir)
# When everything done, release the capture
cap.release()
out.release()
cv2.destroyAllWindows()

I believe the format you are using is only supported on python 3.6 "Literal String Interpolation":
https://www.python.org/dev/peps/pep-0498/#raw-f-strings
filename = f"{timelapse_img_dir}/{i}.jpg"
here are 2 other options depends on what python you are using:
python 2.7:
filename = '%(timelapse_img_dir)s %(i)s' % locals()
python 3+
filename = '{timelapse_img_dir} {i}'.format(**locals())
Based on:
https://pyformat.info/

I dont know why i was not able to use the f function. I had to do another concatenation to work.
filename = timelapse_img_dir +str(i) +file_format
and for the rest i used the wild card
filetomake = image_dir + "/*.jpg"
image_list = glob.glob(filetomake)

Related

Remove background from a directory of JPEG images

I wrote a code to remove the background of 8000 images but that whole code is taking approximately 8 hours to give the result.
How to improve its time complexity as I have to work on a large dataset in future?
Or do I have to write a whole new code? If it is, please suggest some sample codes.
from rembg import remove
import cv2
import glob
for img in glob.glob('../images/*.jpg'):
a = img.split('../images/')
a1 = a[1].split('.jpg')
try:
cv_img = cv2.imread(img)
output = remove(cv_img)
except:
continue
cv2.imwrite('../output image/' + str(a1[0]) + '.png', output)
One simple approach would be to divide the work into multiple threads. See ThreadPoolExecutor for more.
You can play around with max_workers= to see what get's the best results. Note that max-workers can be any number between 1 and 32.
This sample code is ready to run. It assumes the image files are in the same directory as your main.py and the output_image directory exits.
import cv2
import rembg
import sys
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
out_dir = Path("output_image")
in_dir = Path(".")
def is_image(absolute_path: Path):
return absolute_path.is_file and str(absolute_path).endswith('.png')
input_filenames = [p for p in filter(is_image, Path(in_dir).iterdir())]
def process_image(in_dir):
try:
image = cv2.imread(str(in_dir))
if image is None or not image.data:
raise cv2.error("read failed")
output = rembg.remove(image)
in_dir = out_dir / in_dir.with_suffix(".png").name
cv2.imwrite(str(in_dir), output)
except Exception as e:
print(f"{in_dir}: {e}", file=sys.stderr)
executor = ThreadPoolExecutor(max_workers=4)
for result in executor.map(process_image, input_filenames):
print(f"Processing image: {result}")
Check out the U^2Net repository. Like u2net_test.py, Writing your own remove function and using dataloaders can speed up the process. if it is not necessary skip the alpha matting else you can add the alpha matting code from rembg.
def main():
# --------- 1. get image path and name ---------
model_name='u2net'#u2netp
image_dir = os.path.join(os.getcwd(), 'test_data', 'test_images')
prediction_dir = os.path.join(os.getcwd(), 'test_data', model_name + '_results' + os.sep)
model_dir = os.path.join(os.getcwd(), 'saved_models', model_name, model_name + '.pth')
img_name_list = glob.glob(image_dir + os.sep + '*')
print(img_name_list)
#1. dataloader
test_salobj_dataset = SalObjDataset(img_name_list = img_name_list,
lbl_name_list = [],
transform=transforms.Compose([RescaleT(320),
ToTensorLab(flag=0)])
)
test_salobj_dataloader = DataLoader(test_salobj_dataset,
batch_size=1,
shuffle=False,
num_workers=1)
for i_test, data_test in enumerate(test_salobj_dataloader):
print("inferencing:",img_name_list[i_test].split(os.sep)[-1])
inputs_test = data_test['image']
inputs_test = inputs_test.type(torch.FloatTensor)
if torch.cuda.is_available():
inputs_test = Variable(inputs_test.cuda())
else:
inputs_test = Variable(inputs_test)
d1,d2,d3,d4,d5,d6,d7= net(inputs_test)
# normalization
pred = d1[:,0,:,:]
pred = normPRED(pred)
# save results to test_results folder
if not os.path.exists(prediction_dir):
os.makedirs(prediction_dir, exist_ok=True)
save_output(img_name_list[i_test],pred,prediction_dir)
del d1,d2,d3,d4,d5,d6,d7
Try to use parallelization with multiprocessing like Mark Setchell mentioned in his comment. I rewrote your code according to Method 8 from here. Multiprocessing should speed up your execution time. I did not test the code, try if it works.
import glob
from multiprocessing import Pool
import cv2
from rembg import remove
def remove_background(filename):
a = filename.split("../images/")
a1 = a[1].split(".jpg")
try:
cv_img = cv2.imread(filename)
output = remove(cv_img)
except:
continue
cv2.imwrite("../output image/" + str(a1[0]) + ".png", output)
files = glob.glob("../images/*.jpg")
pool = Pool(8)
results = pool.map(remove_background, files)
Ah, you used the example from https://github.com/danielgatis/rembg#usage-as-a-library as template for your code. Maybe try the other example with PIL image instead of OpenCV. The latter is mostly less fast, but who knows. Try it with maybe 10 images and compare execution time.
Here is your code using PIL instead of OpenCV. Not tested.
import glob
from PIL import Image
from rembg import remove
for img in glob.glob("../images/*.jpg"):
a = img.split("../images/")
a1 = a[1].split(".jpg")
try:
cv_img = Image.open(img)
output = remove(cv_img)
except:
continue
output.save("../output image/" + str(a1[0]) + ".png")

Add a forEach or foor loop in python code

I am using this library midi2img to generate midi from images
From this library, this is the file am using:
from PIL import Image
import numpy as np
from music21 import instrument, note, chord, stream
lowerBoundNote = 21
def column2notes(column):
notes = []
for i in range(len(column)):
if column[i] > 255/2:
notes.append(i+lowerBoundNote)
return notes
resolution = 0.25
def updateNotes(newNotes,prevNotes):
res = {}
for note in newNotes:
if note in prevNotes:
res[note] = prevNotes[note] + resolution
else:
res[note] = resolution
return res
def image2midi(image_path):
with Image.open(image_path) as image:
im_arr = np.fromstring(image.tobytes(), dtype=np.uint8)
try:
im_arr = im_arr.reshape((image.size[1], image.size[0]))
except:
im_arr = im_arr.reshape((image.size[1], image.size[0],3))
im_arr = np.dot(im_arr, [0.33, 0.33, 0.33])
""" convert the output from the prediction to notes and create a midi file
from the notes """
offset = 0
output_notes = []
# create note and chord objects based on the values generated by the model
prev_notes = updateNotes(im_arr.T[0,:],{})
for column in im_arr.T[1:,:]:
notes = column2notes(column)
# pattern is a chord
notes_in_chord = notes
old_notes = prev_notes.keys()
for old_note in old_notes:
if not old_note in notes_in_chord:
new_note = note.Note(old_note,quarterLength=prev_notes[old_note])
new_note.storedInstrument = instrument.Piano()
if offset - prev_notes[old_note] >= 0:
new_note.offset = offset - prev_notes[old_note]
output_notes.append(new_note)
elif offset == 0:
new_note.offset = offset
output_notes.append(new_note)
else:
print(offset,prev_notes[old_note],old_note)
prev_notes = updateNotes(notes_in_chord,prev_notes)
# increase offset each iteration so that notes do not stack
offset += resolution
for old_note in prev_notes.keys():
new_note = note.Note(old_note,quarterLength=prev_notes[old_note])
new_note.storedInstrument = instrument.Piano()
new_note.offset = offset - prev_notes[old_note]
output_notes.append(new_note)
prev_notes = updateNotes(notes_in_chord,prev_notes)
midi_stream = stream.Stream(output_notes)
midi_stream.write('midi', fp=image_path.split("/")[-1].replace(".jpeg",".mid"))
import sys
image_path = sys.argv[1]
image2midi(image_path)
and this is the code I execute in terminal to geneate midi from image:
python img2midi.py samples/image.png
I need to make the above code to loop over all input images I put inside samples folder and generate midi for each, not just one file at a time.
Any help would be much appreciated.
You can do this by getting list of images from directory and iterate over them.
import sys
import os
sample_folder_path = sys.argv[1]
images = os.listdir(sample_folder_path) # getting all images stored in sample folder
images_path = [os.path.abspath(f"{sample_folder_path}/{image}") for image in images] # gets absolute path for all images
for image_path in images_path:
image2midi(image_path)
Usage:
python img2midi.py folder_path
Here folder_path is the path of folder that contains images. It can

Grab video frames from all videos in folder, process with openCV, save to own folder?

I have code like this. I know it's not correct, but I'm new at this and not sure how to fix it. How do I make it so cv2 runs on every video I have in a folder, taking a frame every 3 seconds, and saving the frame images in a new folder within /data/ --- ex. /data/rise_of_skywalker?
Thanks
import cv2
import numpy as np
import os
def frame_capture(file):
cap = cv2.VideoCapture(file)
try:
if not os.path.exists('data'):
os.makedirs('data')
except OSError:
print('Error: Creating directory of data')
currentFrame = 0
while(True):
# Capture frame by frame
ret, frame = cap.read()
'''
(((how do I change this block here to get it for every 3 seconds?)))
'''
if currentFrame == 5:
name = './data/frame' + str(currentFrame) + '.jpg'
print ('Creating...' + name)
cv2.imwrite(name, frame)
# To stop duplicate images
currentFrame += 1
cap.release()
cv2.destroyAllWindows()
print ("Done!")
for file in os.listdir("/users/x/Desktop/y/videos"):
if file.endswith(".mp4"):
path=os.path.join("/users/x/Desktop/y/videos", file))
frame_capture(path)
If I understand correctly, what you want to do is to change the line if currentFrame == 5: to if currentFrame % (3 * fps) == 0:
And before that line, outside the loop, add fps = cap.get(cv2.CAP_PROP_FPS)

OpenCV - Save video segments based on certion condition

Aim : Detect the motion and save only the motion periods in files with names of the starting time.
Now I met the issue about how to save the video to the files with video starting time.
What I tested :
I tested my program part by part. It seems that each part works well except the saving part.
Running status: No error. But in the saving folder, there is no video. If I use a static saving path instead, the video will be saved successfully, but the video will be override by the next video. My codes are below:
import cv2
import numpy as np
import time
cap = cv2.VideoCapture( 0 )
bgst = cv2.createBackgroundSubtractorMOG2()
fourcc=cv2.VideoWriter_fourcc(*'DIVX')
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
n = "start_time"
while True:
ret, frame = cap.read()
dst = bgst.apply(frame)
dst = np.array(dst, np.int8)
if np.count_nonzero(dst)>3000: # use this value to adjust the "Sensitivity“
print('something is moving %s' %(time.ctime()))
path = r'E:\OpenCV\Motion_Detection\%s.avi' %n
out = cv2.VideoWriter( path, fourcc, 50, size )
out.write(frame)
key = cv2.waitKey(3)
if key == 32:
break
else:
out.release()
n = time.ctime()
print("No motion Detected %s" %n)
What I meant is:
import cv2
import numpy as np
import time
cap = cv2.VideoCapture( 0 )
bgst = cv2.createBackgroundSubtractorMOG2()
fourcc=cv2.VideoWriter_fourcc(*'DIVX')
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
path = r'E:\OpenCV\Motion_Detection\%s.avi' %(time.ctime())
out = cv2.VideoWriter( path, fourcc, 16, size )
while True:
ret, frame = cap.read()
dst = bgst.apply(frame)
dst = np.array(dst, np.int8)
for i in range(number of frames in the video):
if np.count_nonzero(dst)<3000: # use this value to adjust the "Sensitivity“
print("No Motion Detected")
out.release()
else:
print('something is moving %s' %(time.ctime()))
#label each frame you want to output here
out.write(frame(i))
key = cv2.waitKey(1)
if key == 32:
break
cap.release()
cv2.destroyAllWindows()
If you see the code there will be a for loop, within which the process of saving is done.
I do not know the exact syntax involving for loop with frames, but I hope you have the gist of it. You have to find the number of frames present in the video and set that as the range in the for loop.
Each frame gets saved uniquely (see the else condition.) As I said I do not know the syntax. Please refer and follow this procedure.
Cheers!

openCV and filenames in python

anybody familiar with openCV know how to make it so the output is a different file each time. OR how to make it so the video appends to the original one.
here is the code I am working with
import RPi.GPIO as GPIO
import cv2
import numpy as np
import datetime
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(11,GPIO.IN)
import os
# Setup the camera such that it closes
# when we are done with it.
os.chdir ("/home/pi/Videos")
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
while True:
if GPIO.input(11) == 1:
filename = datetime.datetime.now() .strftime ("%Y-%m-%d-%H.%M.%$
print("about to record")
out = cv2.VideoWriter('video.avi', fourcc, 20.0, (640,480))
ret, frame = cap.read()
if ret==True:
out.appendleft(frame)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q') or GPIO.input(11) $
break
else:
break
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
thanks in advance for any and all help!
This function will store data in a progressive filename system
from os import path
def open_next_file(someData, template='incomingVideo{}.jpg'):
"""Creates a new file for storing new Firmware."""
## Store upto 100 file names
for serial in range(100):
if not path.exists(template.format(serial)):
## if file does not exist,
## enter data into the new file
with open(template.format(serial), 'wb+') as f:
f.write(someData)
break
else:
## if file already exists then,
## don't write on existing files
pass
return template.format(serial)
you can call this function everytime you want to write a new data to a new file viz. incomingVideo1.jpg or incomingVideo2.jpg
You can change the parameters according your needs.
Example
newFile = open_next_file(someDat="Write this inside")
print(newFile)
gives result as incomingVideoX.jpg where x depends on range() function in the loop

Categories

Resources