Panda3d rendered window to numpy array - python

I was following this topic to try to get the buffer data from a camera and save it as a numpy array. Following the topic I reused this code:
base.graphicsEngine.renderFrame()
dr = base.camNode.getDisplayRegion(0)
tex = dr.getScreenshot()
data = tex.getRamImage()
image = np.frombuffer(data,np.uint8)
image.shape = (tex.getYSize(),tex.getXSize(),tex.getNumComponents())
print(image)
However, I get this error:
File "main.py", line 137, in __init__
image = np.frombuffer(data,np.uint8)
AttributeError: 'panda3d.core.ConstPointerToArray_unsigned_char' object has no attribute '__buffer__'
Any advice?

Solved it by changing the original code to the following:
base.graphicsEngine.renderFrame()
dr = base.camNode.getDisplayRegion(0)
tex = dr.getScreenshot()
data = tex.getRamImage()
v = memoryview(data).tolist()
img = np.array(v,dtype=np.uint8)
img = img.reshape((tex.getYSize(),tex.getXSize(),4))
img = img[::-1]
cv2.imshow('img',img)
cv2.waitKey(0)
The image ends up being flipped for whatever reason after reshaping the numpy array hence the 3rd line from the bottom. You should see an identical image of whatever your camera sees when you run this snippet as an accept key or something. Hope this helps someone having the same issues.

I was having trouble with a few bugs on pyrender, so i wanted to use panda3d as a offscreen render, and this was very usefull, expanding on what Masa Hu posted, here is the full example on how to do that:
from direct.showbase.ShowBase import ShowBase
import cv2
import numpy as np
base = ShowBase( windowType='offscreen')
box = base.loader.loadModel("meshes/box.egg")
box.setPos(0,10,0)
box.reparentTo(base.render)
base.graphicsEngine.renderFrame()
dr = base.camNode.getDisplayRegion(0)
tex = dr.getScreenshot()
data = tex.getRamImage()
v = memoryview(data).tolist()
img = np.array(v,dtype=np.uint8)
img = img.reshape((tex.getYSize(),tex.getXSize(),4))
img = img[::-1]
cv2.imshow('img',img)
cv2.waitKey(0)

Related

cv2 treshold does not work correctly on second image

I am new to python and I was playing around with background subtraction to visualize changes in pre and post change images.
I wrote a short and simple script using the cv2 library:
#!/usr/bin/env python
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
#GRAYSCALE ONLY FOR TESTING
#Test with person appearing in image
img1 = cv.imread("images/1.jpg", 0)
img2 = cv.imread("images/2.jpg", 0)
img3 = cv.subtract(img1, img2)
ret,thresh1 = cv.threshold(img3,90,255,cv.THRESH_BINARY)
#Test with satelite image of japan landslide changes after earthquake
jl_before = cv.imread("images/japan_earthquake_before.jpg",0)
jl_after = cv.imread("images/japan_earthquake_after.jpg",0)
jl_subtraction = cv.subtract(jl_before, jl_after)
ret,thresh2 = cv.threshold(img3,20,255,cv.THRESH_BINARY)
images = [img1, img2, thresh1, jl_before, jl_after, thresh2]
titles = ["Image1", "Image2", "Changes", "Japan_Before", "Japan_After", "Japan_Changes" ]
for i in range(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
The result looks like this:
Why is the mask with changes from the first set of images present in the mask of the second set of images?
I used different variables, thresh1 and thresh2.
Any help would be greatly appreciated as I can't seem to find the problem.
Because you missed a change when copy pasting:
ret,thresh2 = cv.threshold(img3,20,255,cv.THRESH_BINARY)
^^^^

Want to append colored images to a list and convert that list to grayscale using OpenCV

So basically I'm trying to convert a set of RGB images to grayscale using cv2.cvtColor and python is throwing the following error:
Traceback (most recent call last):
File "MCG.py", line 53, in
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
TypeError: src is not a numpy array, neither a scalar.
This here is the code:
import numpy as np
import cv2
import dlib
import sys
import skimage
from PIL import Image
import os
import glob
folderpath = sys.argv[1]
cascPath = sys.argv[2]
imageformat = ".tif"
path = folderpath
imfilelist = [os.path.join(path,f) for f in os.listdir(path) if f.endswith(imageformat)]
data = []
for IMG in imfilelist:
print IMG
image = cv2.imread(IMG)
data.append(image)
cv2.imshow('Image', image)
cv2.waitKey(0)
faceCascade = cv2.CascadeClassifier(cascPath)
predictor = dlib.shape_predictor(PREDICTOR_PATH)
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.05,
minNeighbors=5,
minSize=(100,100)
)
As you can see, I'm trying to append all these images to a list, which will then be converted using the cv2.cvtColor function. However, that error is thrown. What am I doing wrong? Thank you.
P.S if anyone is wondering why I imported modules that don't seem to be used in this code, this code is just a segment of the whole thing and all of those modules have are being utilized in one way or the other.
If you read the cv2.cvtColor documentation, you can see that the first parameter is the Src 8-bit single channel image. However, in your case you are giving an entire list of images.
So change the code as
gray = []
for j in range(0,len(data)):
gray.append(cv2.cvtColor(np.array(data[j]), cv2.COLOR_BGR2GRAY))
I guess this should work.
You are collecting the images into a list with
data = []
for IMG in imfilelist:
...
data.append(image)
....
and then trying to convert the list with
gray = cv2.cvtColor(data, cv2.COLOR_BGR2GRAY)
This is why you are getting the error - the error is telling you that data is not an image (numpy array) but is a list. You need to convert one image at a time with cv2.cvtColor().
You could try
gray = []
for img in data:
gray.append(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY))
This would give you a list of greyscaled images, which is what I think you want to do.

How to make a movie out of images in python

I currently try to make a movie out of images, but i could not find anything helpful .
Here is my code so far:
import time
from PIL import ImageGrab
x =0
while True:
try:
x+= 1
ImageGrab().grab().save('img{}.png'.format(str(x))
except:
movie = #Idontknow
for _ in range(x):
movie.save("img{}.png".format(str(_)))
movie.save()
You could consider using an external tool like ffmpeg to merge the images into a movie (see answer here) or you could try to use OpenCv to combine the images into a movie like the example here.
I'm attaching below a code snipped I used to combine all png files from a folder called "images" into a video.
import cv2
import os
image_folder = 'images'
video_name = 'video.avi'
images = [img for img in os.listdir(image_folder) if img.endswith(".png")]
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape
video = cv2.VideoWriter(video_name, 0, 1, (width,height))
for image in images:
video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
video.release()
It seems that the most commented section of this answer is the use of VideoWriter. You can look up it's documentation in the link of this answer (static) or you can do a bit of digging of your own. The first parameter is the filename, followed by an integer (fourcc in the documentation, the codec used), the FPS count and a tuple of the dimensions of the frame. If you really like digging in that can of worms, here's the fourcc video codecs list.
Thanks , but i found an alternative solution using ffmpeg:
def save():
os.system("ffmpeg -r 1 -i img%01d.png -vcodec mpeg4 -y movie.mp4")
But thank you for your help :)
Here is a minimal example using moviepy. For me this was the easiest solution.
import os
import moviepy.video.io.ImageSequenceClip
image_folder='folder_with_images'
fps=1
image_files = [os.path.join(image_folder,img)
for img in os.listdir(image_folder)
if img.endswith(".png")]
clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile('my_video.mp4')
I use the ffmpeg-python binding. You can find more information here.
import ffmpeg
(
ffmpeg
.input('/path/to/jpegs/*.jpg', pattern_type='glob', framerate=25)
.output('movie.mp4')
.run()
)
When using moviepy's ImageSequenceClip it is important that the images are in an ordered sequence.
While the documentation states that the frames can be ordered alphanumerically under the hood, I found this not to be the case.
So, if you are having problems, make sure to manually order the frames first.
#Wei Shan Lee (and others): Sure, my whole code looks like this
import os
import moviepy.video.io.ImageSequenceClip
from PIL import Image, ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
image_files = []
for img_number in range(1,20):
image_files.append(path_to_images + 'image_folder/image_' + str(img_number) + '.png')
fps = 30
clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(image_files, fps=fps)
clip.write_videofile(path_to_videos + 'my_new_video.mp4')
I've created a function to do this. Similar to the first answer (using opencv) but wanted to add that for me, ".mp4" format did not work. That's why I use the raise within the function.
import cv2
import typing
def write_video(video_path_out:str,
frames_sequence:typing.Tuple[np.ndarray,...]):
if ".mp4" in video_path_out: raise ValueError("[ERROR] This method does not support .mp4; try .avi instead")
height, width, _ = frames_sequence[0].shape
# 0 means no preprocesing
# 1 means each image will be played with 1 sec delay (1fps)
out = cv2.VideoWriter(video_path_out,0, 1,(width,height))
for frame in frames_sequence:
out.write(frame)
out.release()
# you can use as much images as you need, I just use 3 for this example
# put your img1_path,img2_path, img3_path
img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)
img3 = cv2.imread(img3_path)
# img1 can be cv2.imread out; which is a np.ndarray; you can also se PIL
# if you'd like to.
frames_sequence = [img1,img2,img3]
write_video(video_path_out = "mypath_outvideo.avi",
frames_sequence = frames_sequence
)
Hope it's useful!
Little hacky but avoids creating the file and just lets you watch it in real time.
import glob
from PIL import Image
import cv2
import numpy as np
import time
####### PARAMS
imgs_path = "/Users/user/Desktop/lidar_rig/ouster_data_wide_angle_cam_v9/imgs/*"
cur_img_index = 0
ds_st_index = 0
ds_en_index = -1
fps = 35 # tweak this
###### PARAMS
def cnvt_pil_to_cv2(pil_img):
open_cv_image = np.array(pil_img)
# Convert RGB to BGR
open_cv_image = open_cv_image[:, :, ::-1].copy()
return open_cv_image
img_files = sorted(glob.glob(imgs_path), key = lambda x: int(x.split('/')[-1].split('.')[0]))[ds_st_index:ds_en_index][cur_img_index:]
cnt = 0
for img_pth in img_files:
if not cnt %50: ## DDD -- avoid mem overflow
cv2.destroyAllWindows()
img = Image.open(img_pth).resize((750,750))
cv2.imshow(img_pth.split("/")[-1], cnvt_pil_to_cv2(img))
time.sleep(float(1.0/float(fps)))
cnt+=1

Python creating video from images using opencv

I was trying to create a video to show the dynamic variation of the data, like just continuously showing the images one by one quickly, so I used images (the images just called 1,2,3,4,.....) and wrote the following code:
import cv2
import numpy as np
img=[]
for i in range(0,5):
img.append(cv2.imread(str(i)+'.png'))
height,width,layers=img[1].shape
video=cv2.VideoWriter('video.avi',-1,1,(width,height))
for j in range(0,5):
video.write(img)
cv2.destroyAllWindows()
video.release()
and a error was raised:
TypeError: image is not a numpy array, neither a scalar
I think I used the list in a wrong way but I'm not sure. So where did I do wrong?
You are writing the whole array of frames. Try to save frame by frame instead:
...
for j in range(0,5):
video.write(img[j])
...
reference
You can read the frames and write them to video in a loop. Following is your code with a small modification to remove one for loop.
import cv2
import numpy as np
# choose codec according to format needed
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video = cv2.VideoWriter('video.avi', fourcc, 1, (width, height))
for j in range(0,5):
img = cv2.imread(str(i) + '.png')
video.write(img)
cv2.destroyAllWindows()
video.release()
Alternatively, you can use skvideo library to create video form sequence of images.
import numpy as np
import skvideo.io
out_video = np.empty([5, height, width, 3], dtype = np.uint8)
out_video = out_video.astype(np.uint8)
for i in range(5):
img = cv2.imread(str(i) + '.png')
out_video[i] = img
# Writes the the output image sequences in a video file
skvideo.io.vwrite("video.mp4", out_video)
You can use this pip package. It provides CLI commands to make video from images.
img_to_vid.py -f images_directory

Using Global Contrast Normalization - Python pylearn2

Im attempting to input my image to this method, but when i try to draw the image, it comes totally black.
I tried inputing just one image and inputing the whole MNIST dataset.
Same result.
https://github.com/lisa-lab/pylearn2/blob/master/pylearn2/expr/preprocessing.py
if GCN is True:
trainingFolder = "../inputData/converted_training/GCN/"
testingFolder = "../inputData/converted_testing/GCN/"
img0 = (data[1,1:]).reshape((28,28)).astype('uint8')*255
im = Image.fromarray(img0)
im.show()
#GCN#
img_gcn = global_contrast_normalize(data)
img_gcn_1 = Image.fromarray(img_gcn[1,1:].reshape((28,28)).astype('uint8')*255)
img_gcn_1.show()
The second image, which is img_gcn_1 comes blacked.
What am i doing wrong?
Have you tried to visualize the image without multiplying by 255? i.e.,
import matplotlib.pyplot as plt
img = img_gcn[:, 0]
img = img.reshape(28, 28, order='F')
plt.imshow(img, cmap=plt.get_cmap('gray'))
I think that procedure should work.

Categories