is there a frame size limitation in opencv VideoWriter? - python

When using opencv to save a bunch of pictures to a video file the file won't open unless I resize the image:
(Windows error - "This item is in a format we don't support. 0xc00d36b4")
My code looks like this:
import cv2
import os
input_path = "input_images_folder_path"
imgs_lst = os.listdir(r"C:\Users\...\input_path")
outvid_path = r"C:\Users\...\output.avi"
image0 = input_path +"\\"+ imgs_lst[0]
img0 = cv2.imread(image)
size = (int(img0.shape[1]), int(img0.shape[0]))
fps = 12.0
is_color = True
fourcc = cv2.VideoWriter_fourcc(*"XVID")
vid = cv2.VideoWriter(outvid_path, fourcc, 10.0, size, True)
for i in range(0,50):# int(len(imgs_lst))):
image = input_path +"\\"+ imgs_lst[i]
img = cv2.imread(image,1)
img = cv2.resize(img, size) #tried to comment this out... wont work either
cv2.imshow("img", img)
cv2.waitKey(1)
vid.write(img)
vid.release()
if I resize -
size = (int(img0.shape[1]/2), int(img0.shape[0]/2))
all works well.
My question is whether or not there is a limit on the frame size. I found some other answers regarding the output file size limitation, but nothing about the single frame shape.
(changing fps, format didn't work either)

Related

convert numpy.ndarray into video

In my code i'm looping over frames of a video, and trying to generate another mp4 video.
This is my code:
cap = cv2.VideoCapture(args.video)
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = int(cap.get(cv2.CAP_PROP_FPS))
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (frame_width, frame_height))
while cap.isOpened():
ret, img = cap.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
out.release()
break
#<code>...
#<code>...
print(type(my_image))
out.write(my_image)
The output of print(type(my_image)) is numpy.ndarray for each frame. When I ran the code, i got output_video.mp4 file, but weights only 300 kb (it needs to be about 50 mb).
I tried to save each frame as an image, and to see if it will work, and it did. This is the code:
img = Image.fromarray(my_image, 'RGB')
img.save('frameeeee-%s.png'%i)
I coded this function to solve a similiar problem, you need to save the images singularly into a folder and then you can use frames2video to convert it into a video.
def frames2video( path_in = "/content/original_frames" , path_out = "/content/outputvideo",
frame_rate = 30 , video_name="output_video" ):
"""
Given an input path to a folder that contains a set of frames, this function
convert them into a video and then save it in the path_out.
You need to know the fps of the original video, are 30 by default.
"""
img_path_list = natsorted(os.listdir(path_in))
assert(len(img_path_list)>0)
img_array = []
print("[F2V] Frames to video...", end="\n\n")
with tqdm(total=len(img_path_list)) as pbar:
for count,filename in enumerate(img_path_list):
img = cv2.imread(path_in+"/"+filename)
if(img is None):break
height, width, layers = img.shape
img_array.append(img)
size = (width,height)
pbar.update()
if os.path.exists(path_out): shutil.rmtree(path_out)
os.mkdir(path_out)
out = cv2.VideoWriter(path_out+"/"+str(video_name)+'.mp4', cv2.VideoWriter_fourcc(*'DIVX'), frame_rate, size)
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
print("\n[F2V] Video made from "+str(count+1)+" frames", end="\n\n")
For completeness, i post also the viceversa, a function that given a video extract the frames.
def n_frames(video):
"""
Given an input video returns the EXACT number of frames(CV2 was not precise)
"""
success = True
count = 0
while success:
success,image = video.read()
if success == False: break
count+=1
return count
def video2frames( path_in = "/content/video.mp4" , path_out = "/content/original_frames",
n_of_frames_to_save = 999999, rotate=True, frames_name = "OrigFrame" ):
"""
Given a video from path_in saves all the frames inside path_out.
The number of frames(in case of long videos) can be truncated with
the n_of_frames_to_save parameter. Rotate is used to save rotated
frames by 90 degree. All the frames are named frames_name with an
index
"""
blur_threshold = 0
if os.path.exists(path_out): shutil.rmtree(path_out)
os.mkdir(path_out)
count = 0
success = True
vidcap = cv2.VideoCapture(path_in)
v2 = cv2.VideoCapture(path_in)
fps = vidcap.get(cv2.CAP_PROP_FPS)
if(fps>120):
print("CAP_PROP_FPS > 120, probabily you are using a webcam. Setting fps manually")
fps = 25
n_of_frames = n_frames(v2) # #int(video.get(cv2.CAP_PROP_FRAME_COUNT)) is not accurate, https://stackoverflow.com/questions/31472155/python-opencv-cv2-cv-cv-cap-prop-frame-count-get-wrong-numbers
if(n_of_frames_to_save < n_of_frames): n_of_frames = n_of_frames_to_save
print("[V2F] Dividing the video in " + str(n_of_frames) + " frames", end="\n\n")
for count in trange(n_of_frames):
success,image = vidcap.read()
if not success: break
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if(rotate): image = cv2.rotate(image,cv2.ROTATE_90_CLOCKWISE)
plt.imsave("%s/%s%d.png" % (path_out,frames_name+"_", count), image)
count+=1
print("\n[V2F] "+str(count)+" frames saved",end="\n\n")
return fps
Ok, I found a solution. I noticed that I had resize function in my code:
my_image = cv2.resize(image_before, (1280, 720))
So I changed
out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (frame_width, frame_height))
to
out = cv2.VideoWriter('outputttttt.mp4', fourcc, fps, (1280, 720))
And it works (:

Passing an image as an argument to a function in python

How can I create a function that takes an image file (not image filename) in python. Simply, like the following:
FaceController.py
import cv2
from Computer_Vision import Face_Detector as FD
def detectface():
img = cv2.imread('DSC_1902.JPG')
FD.detect(img)
detectface()
Face_Detector.py
import cv2
def detect(img):
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
cv2.namedWindow('img',cv2.WINDOW_NORMAL)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('messigray.png', img)
return img
Error:
OpenCV Error: Assertion failed (!empty()) in cv::CascadeClassifier::detectMultiScale, file C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect.cpp, line 1698
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
cv2.error: C:\projects\opencv-python\opencv\modules\objdetect\src\cascadedetect.cpp:1698: error: (-215) !empty() in function cv::CascadeClassifier::detectMultiScale
You can pass a pointer pointing to the image instead of the image or the filename of the image
EDIT
def image_function(imagePointer):
#DO SOMETHING WITH THE IMAGE
#HERE IS THE IMAGE POINTER
image = open('your_image.png')
#CALLING THE FUNCTION
image_function(image)
Sorry, I don't know opencv so I can not help in your code :(
You can actually pass the image as a tensor.
with cv2.imread() and torch.
Which is easy, useful.
short answer:
load with cv2.imread()
transform to tensor with
img = torch.Tensor(img)/255.
That works for my application.
Yours might be a little different.
Code answer:
from Computer_Vision import Face_Detector as FD
def detectface():
import cv2
import torch
folder = r"This Folder/"
image_file = folder+"image.png"
# or
# file = r"image.png"
# image_file = os.path.join(folder, file)
img = imread(image_file)
img = torch.Tensor(img)/255. # THE KEY LINE HERE.
FDdetect(img):
"""Do stuff with object detection..."""
result =
return result

Eigenfaces training image pixel size error

Hello to all senior programmer! I have an error on eigenfaces image training part.
The error is : OpenCV Error: Unsupported format or combination of formats (In the Eigenfaces method all input samples (training images) must be of equal size! Expected 27889 pixels, but was 27556 pixels.) in cv::face::Eigenfaces::train, file C:\projects\opencv-python\opencv_contrib\modules\face\src\eigen_faces.cpp, line 68
Which mean my pictures don't be in equal size. I try cv2.rezise() when I capture picture from camera but it still doesn't work.
here is my capture code :
import cv2
cam = cv2.VideoCapture(0)
detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
Id = input('enter your id: ')
sampleNum = 0
while(True):
ret, img = cam.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = detector.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
sampleNum = sampleNum+1
cv2.imwrite("dataSet/user."+Id+'.'+str(sampleNum)+".jpg",cv2.resize
(gray[y:y+h,x:x+w],(70,70)))
cv2.imshow('frame',img)
if cv2.waitKey(100) & 0xFF == ord('q'):#waitKey is for delay in video capture
break
elif sampleNum >= 50:#how many picture capture?
break
cam.release()
cv2.destroyAllWindows()
and here is training part:
import cv2,os
import numpy as np
recognizer = cv2.face.EigenFaceRecognizer_create()
detector= cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
def getImagesAndLabels(path):
imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
faceSamples=[]
Ids=[]
for imagePath in imagePaths:
pilImage = Image.open(imagePath).convert('L')
imageNp = np.array(pilImage,'uint8')
Id = int(os.path.split(imagePath)[-1].split(".")[1])
faces = detector.detectMultiScale(imageNp)
for (x,y,w,h) in faces:
faceSamples.append(imageNp[y:y+h,x:x+w])
Ids.append(Id)
return faceSamples,Ids
faces,Ids = getImagesAndLabels('dataSet')
recognizer.train(faces, np.array(Ids))
recognizer.write('trainner/trainnerEi.yml')
PS. I adapt this code from LBPHFaceRecognizer
Thank you!*3
Okay, so EigenFaces only works if the dimension of all the images is same in pixel space
Which means if one image used in training is of size 28x28 then every other image in the training as well as in testing has to be of size 28x28
If the image size is not same then opencv will throw you that error
The error simply says that one of the image was of 27889 dimensions in pixel space and other was 27556 dimensions pixel space.
I would recommend you to use cv2.resize() function to make all images of the same size
Use the below code as a reference for you training part:
import cv2,os
import numpy as np
recognizer = cv2.face.EigenFaceRecognizer_create()
detector= cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
def getImagesAndLabels(path):
width_d, height_d = 280, 280 # Declare your own width and height
imagePaths=[os.path.join(path,f) for f in os.listdir(path)]
faceSamples=[]
Ids=[]
for imagePath in imagePaths:
pilImage = Image.open(imagePath).convert('L')
imageNp = np.array(pilImage,'uint8')
Id = int(os.path.split(imagePath)[-1].split(".")[1])
faces = detector.detectMultiScale(imageNp)
for (x,y,w,h) in faces:
########################################
# The line to be changed by cv2.resize()
########################################
faceSamples.append(cv2.resize(imageNp[y:y+h,x:x+w], (width_d, height_d))
Ids.append(Id)
return faceSamples,Ids
faces,Ids = getImagesAndLabels('dataSet')
recognizer.train(faces, np.array(Ids))
recognizer.write('trainner/trainnerEi.yml')
Keep in mind even the test images has to be of same size

Convert CGImageRef to PIL

How could I convert a CGImageRef to PIL without saving the image to disk on osx?
I though about getting the raw pixel data from the CGImageRef and using Image.fromstring() to make the PIL image by doing
import mss
import Quartz.CoreGraphics as CG
from PIL import Image
mss = mss.MSSMac()
for i, monitor in enumerate(mss.enum_display_monitors(0)):
imageRef = mss.get_pixels(monitor)
pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(imageRef))
img = Image.fromstring("RGB", (monitor[b'width'], monitor[b'height']), pixeldata)
img.show()
but this doesn't give me the correct image.
This is the image I expect:
and this is the image I get in PIL:
The screencapture from CG doesn't necessarily use the RGB colorspace. It may use RGBA or something else. Try changing:
img = Image.fromstring("RGB", (monitor[b'width'], monitor[b'height']), pixeldata)
to
img = Image.fromstring("RGBA", (monitor[b'width'], monitor[b'height']), pixeldata)
Here is how I detect which colorspace is actually being captured:
bpp = CG.CGImageGetBitsPerPixel(imageRef)
info = CG.CGImageGetBitmapInfo(imageRef)
pixeldata = CG.CGDataProviderCopyData(CG.CGImageGetDataProvider(imageRef))
img = None
if bpp == 32:
alphaInfo = info & CG.kCGBitmapAlphaInfoMask
if alphaInfo == CG.kCGImageAlphaPremultipliedFirst or alphaInfo == CG.kCGImageAlphaFirst or alphaInfo == CG.kCGImageAlphaNoneSkipFirst:
img = Image.fromstring("RGBA", (CG.CGImageGetWidth(imageRef), CG.CGImageGetHeight(imageRef)), pixeldata, "raw", "BGRA")
else:
img = Image.fromstring("RGBA", (CG.CGImageGetWidth(imageRef), CG.CGImageGetHeight(imageRef)), pixeldata)
elif bpp == 24:
img = Image.fromstring("RGB", (CG.CGImageGetWidth(imageRef), CG.CGImageGetHeight(imageRef)), pixeldata)
It was a bug I fixed some time ago. Here is how to achieve what you want using the latest mss version (2.0.22):
from mss.darwin import MSS
from PIL import Image
with MSS() as mss:
for monitor in mss.enum_display_monitors(0):
pixeldata = mss.get_pixels(monitor)
img = Image.frombytes('RGB', (mss.width, mss.height), pixeldata)
img.show()
Note that pixeldata is just a reference to mss.image, you can use it directly.

Converting an UploadedFile to PIL image in Django

I'm trying to check an image's dimension, before saving it. I don't need to change it, just make sure it fits my limits.
Right now, I can read the file, and save it to AWS without a problem.
output['pic file'] = request.POST['picture_file']
conn = myproject.S3.AWSAuthConnection(aws_key_id, aws_key)
filedata = request.FILES['picture'].read()
content_type = 'image/png'
conn.put(
bucket_name,
request.POST['picture_file'],
myproject.S3.S3Object(filedata),
{'x-amz-acl': 'public-read', 'Content-Type': content_type},
)
I need to put a step in the middle, that makes sure the file has the right size / width dimensions. My file isn't coming from a form that uses ImageField, and all the solutions I've seen use that.
Is there a way to do something like
img = Image.open(filedata)
image = Image.open(file)
#To get the image size, in pixels.
(width,height) = image.size()
#check for dimensions width and height and resize
image = image.resize((width_new,height_new))
I've done this before but I can't find my old snippet... so here we go off the top of my head
picture = request.FILES.get['picture']
img = Image.open(picture)
#check sizes .... probably using img.size and then resize
#resave if necessary
imgstr = StringIO()
img.save(imgstr, 'PNG')
imgstr.reset()
filedata = imgstr.read()
The code bellow creates the image from the request, as you want:
from PIL import ImageFile
def image_upload(request):
for f in request.FILES.values():
p = ImageFile.Parser()
while 1:
s = f.read(1024)
if not s:
break
p.feed(s)
im = p.close()
im.save("/tmp/" + f.name)

Categories