I am trying to make animation using lottie frame work in python. The script I have made can show only one image. I don't know how to animate image sequence in lottie frame work by python. Any help would be appreciated.
from lottie.utils import script
from lottie import objects
from lottie import Point, Color
import os
image_filename1 = os.path.dirname(os.path.realpath(__file__))+'/images1/pngImages'+'/'+'2020-05-01'+"_new.png"
last_frame = 60
an = objects.Animation(last_frame, 1)
an.width = 720
an.height = 360
image1 = objects.assets.Image().load(image_filename1)
an.assets.append(image1)
an.add_layer(objects.ImageLayer(image1.id))
script.script_main(an, path="/Users/ihasan/", basename='sample2', formats=['html'])
After searching on the lottie-python examples script here, I found a way to animate image sequence. The code I am sharing here is composed of two image. It can be simplified using a loop with more image.
from lottie.utils import script
from lottie import objects
from lottie import Point, Color
import os
image_filename1 = "/../" # image1 directory
image_filename2 = "/../" # image2 directory
image1 = objects.assets.Image().load(image_filename1)
image2 = objects.assets.Image().load(image_filename2)
lastFrame = 60
an = objects.Animation(lastFrame)
an.width = 720
an.height = 360
an.assets.append(image1)
an.assets.append(image2)
precomp = objects.Precomp("myid", an)
layer1 = objects.ImageLayer(image1.id)
layer1.out_point = 20
precomp.add_layer(layer1)
pcl0 = an.add_layer(objects.PreCompLayer("myid"))
pcl0.width = 720
pcl0.height = 360
pcl0.start_time = 0
layer2 = objects.ImageLayer(image2.id)
layer2.out_point = 40
precomp.add_layer(layer2)
pcl1 = an.add_layer(objects.PreCompLayer("myid"))
pcl1.start_time = 20
pcl1.width = 720
pcl0.height = 360
script.script_main(an, path="../../", basename='samplePrecomp', formats=['json'])
Related
I am trying to generate 3D point cloud from 4 RGB-D images. I am able to do that with Open3D but I am unable to maintain the position of the images. You can find the camera_parameters.json here.
import open3d as o3d
import cv2
import os
import numpy as np
import matplotlib.pyplot as plt
import json
def load_json(path):
with open(path) as f:
return json.load(f)
def parse_camera_params(camera):
param = {}
param['camera'] = camera['camera']
param['depth'] = {}
param['depth']['fx'] = camera['K_depth'][0][0]
param['depth']['fy'] = camera['K_depth'][1][1]
param['depth']['cx'] = camera['K_depth'][0][2]
param['depth']['cy'] = camera['K_depth'][1][2]
param['depth']['K'] = camera['K_depth']
# ignore distCoeffs_depth's 5th (1000) and 6th (0) element
# since they are strange
param['depth']['distCoeffs'] = np.array(camera['distCoeffs_depth'][:5])
param['depth_width'] = camera['depth_width']
param['depth_height'] = camera['depth_height']
param['color'] = {}
param['color']['fx'] = camera['K_color'][0][0]
param['color']['fy'] = camera['K_color'][1][1]
param['color']['cx'] = camera['K_color'][0][2]
param['color']['cy'] = camera['K_color'][1][2]
param['color']['K'] = camera['K_color']
# ignore distCoeffs_color's 5th (1000) and 6th (0) element
# since they are strange
param['color']['distCoeffs'] = np.array(camera['distCoeffs_color'][:5])
param['color_width'] = camera['color_width']
param['color_height'] = camera['color_height']
# world to depth
w2d_T = np.array(camera['M_world2sensor'])
param['w2d_R'] = w2d_T[0:3, 0:3]
param['w2d_t'] = w2d_T[0:3, 3]
param['w2d_T'] = camera['M_world2sensor']
d2w_T = np.linalg.inv(w2d_T)
param['d2w_R'] = d2w_T[0:3, 0:3]
param['d2w_t'] = d2w_T[0:3, 3]
param['d2w_T'] = d2w_T
return param
if __name__ == '__main__':
data_dir = "data/"
camera_params = load_json(os.path.join(data_dir,
'camera_parameters.json'))
SVC_NUM = 4
pcd_combined = o3d.geometry.PointCloud()
for i in range(SVC_NUM):
param = parse_camera_params(camera_params['sensors'][i])
color = cv2.imread(os.path.join(data_dir, 'color_{:05d}.png'.format(i)))
color = cv2.cvtColor(color, cv2.COLOR_BGR2RGB)
depth = cv2.imread(os.path.join(data_dir, 'depth_{:05d}.png'.format(i)), -1)
# depth = depth * 0.001 # factor to scale the depth image from carla
o3d_color = o3d.geometry.Image(color)
o3d_depth = o3d.geometry.Image(depth)
rgbd_image = o3d.geometry.RGBDImage.create_from_tum_format(o3d_color, o3d_depth, False)
h, w = depth.shape
dfx, dfy, dcx, dcy = param['depth']['fx'], param['depth']['fy'], param['depth']['cx'], param['depth']['cy']
intrinsic = o3d.camera.PinholeCameraIntrinsic(w, h, dfx,dfy, dcx, dcy)
intrinsic.intrinsic_matrix = param['depth']['K']
cam = o3d.camera.PinholeCameraParameters()
cam.intrinsic = intrinsic
cam.extrinsic = np.array(param['w2d_T'])
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd_image, cam.intrinsic, cam.extrinsic)
o3d.io.write_point_cloud("svc_{:05d}_v13.pcd".format(i), pcd)
pcd_combined += pcd
o3d.io.write_point_cloud("svc_global_v13.pcd", pcd_combined)
With the above code I am getting output of svc_global_v13.pcd like below
As you can see, all the images are projected into center. As indicated in the json file, I would like the images to be positioned as left, right, front and rear in the 3D point cloud.
May I know what is it I am missing here?
I have a python flask-restful API, I am trying to add the feature to allow users to upload GIFs, the client will upload a base64-encoded GIF to the API for that, so on the API I need to be able to decode base64 to GIF and also be able to do operations on it such as resizing and compression then convert it to bytes
I tried to do with PIL like this:
img_bytes = BytesIO()
# I specified the duration to be 67 because I want the GIF to play at 15 FPS, 1000 / 15 = 66.66
self.image.save(img_bytes, append_images=self.frames[1:], format=self.format, save_all=True,
optimize=False, duration=67, loop=0)
img_bytes.seek(0)
The problem with that implementation is that I find a lot of distortion and black pixels in the GIFs and inconsistent framerate/animation speed, it's overall pretty bad and it makes a 75 KBs GIF be 1.8 MBs, any other solutions?
Try the code below. It works for me.
from PIL import Image
import base64
import os
myimagestring = "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"
myimagedata = base64.b64decode(myimagestring)
myimagefile= open("myimage.gif","wb")
myimagefile.write(myimagedata)
myimagefile.close()
myimageobject = PhotoImage(file="myimage.gif")
And here is a function to resize the image if needed:
from PIL import Image
def get_resized_img(img_path, picture_size):
img = Image.open(img_path)
width, height = picture_size # these are the MAX dimensions
picture_ratio = width / height
img_ratio = img.size[0] / img.size[1]
if picture_ratio >= 1: # the picture is wide
if img_ratio <= picture_ratio: # image is not wide enough
width_new = int(height * img_ratio)
size_new = width_new, height
else: # image is wider than picture
height_new = int(width / img_ratio)
size_new = width, height_new
else: # the picture is tall
if img_ratio >= picture_ratio: # image is not tall enough
height_new = int(width / img_ratio)
size_new = width, height_new
else: # image is taller than picture
width_new = int(height * img_ratio)
size_new = width_new, height
return img.resize(size_new, resample=Image.LANCZOS)
I try to print an image from python script on Debian 10 using cups:
import cups
def printImageLinux(image_name):
conn = cups.Connection()
printer = conn.getDefault()
conn.printFile(printer, image_name, 'suo_ticket', {})
Finally, the image went to print but I see canceling of printing and error in Cups user interface (localhost:631) with a message:
"The page setup information was not valid."
CUPS message screenshot
I suppose that I should make some preparation of image sending to print, but I didn't find any information about it.
I can print the same image from Windows using win32print module and code:
import os, sys
from win32 import win32api, win32print
from PIL import Image, ImageWin
def printImage(image_name):
# Constants for GetDeviceCaps
# HORZRES / VERTRES = printable area
HORZRES = 8
VERTRES = 10
# LOGPIXELS = dots per inch
LOGPIXELSX = 88
LOGPIXELSY = 90
# PHYSICALWIDTH/HEIGHT = total area
PHYSICALWIDTH = 110
PHYSICALHEIGHT = 111
# PHYSICALOFFSETX/Y = left / top margin
PHYSICALOFFSETX = 112
PHYSICALOFFSETY = 113
printer_name = win32print.GetDefaultPrinter ()
file_name = image_name
#
# You can only write a Device-independent bitmap
# directly to a Windows device context; therefore
# we need (for ease) to use the Python Imaging
# Library to manipulate the image.
#
# Create a device context from a named printer
# and assess the printable size of the paper.
#
hDC = win32ui.CreateDC ()
hDC.CreatePrinterDC (printer_name)
printable_area = hDC.GetDeviceCaps (HORZRES), hDC.GetDeviceCaps (VERTRES)
printer_size = hDC.GetDeviceCaps (PHYSICALWIDTH), hDC.GetDeviceCaps (PHYSICALHEIGHT)
printer_margins = hDC.GetDeviceCaps (PHYSICALOFFSETX), hDC.GetDeviceCaps (PHYSICALOFFSETY)
#
# Open the image, rotate it if it's wider than
# it is high, and work out how much to multiply
# each pixel by to get it as big as possible on
# the page without distorting.
#
bmp = Image.open (file_name)
if bmp.size[0] > bmp.size[1]:
bmp = bmp.rotate (90)
ratios = [1.0 * printable_area[0] / bmp.size[0], 1.0 * printable_area[1] / bmp.size[1]]
scale = min (ratios)
#
# Start the print job, and draw the bitmap to
# the printer device at the scaled size.
#
hDC.StartDoc (file_name)
hDC.StartPage ()
dib = ImageWin.Dib (bmp)
scaled_width, scaled_height = [int (scale * i) for i in bmp.size]
x1 = int ((printer_size[0] - scaled_width) / 2)
y1 = int ((printer_size[1] - scaled_height) / 2)
x2 = x1 + scaled_width
y2 = y1 + scaled_height
dib.draw (hDC.GetHandleOutput (), (x1, y1, x2, y2))
hDC.EndPage ()
hDC.EndDoc ()
hDC.DeleteDC ()
It works correctly.
Can I make the same operations with the image in Debian?
Thank you for any help!
Problem was solved with using PIL module. Working code:
import cups
from PIL import Image
def printImageLinux(image_name):
conn = cups.Connection()
printer = conn.getDefault()
image1 = Image.open(image_name)
im1 = image1.convert('RGB')
im1.save('temp_image.pdf')
conn.printFile(printer, 'temp_image.pdf', 'suo_ticket', {'fit-to-page':'True'})
I am trying to make an image mosaic generator using pyvips. So basically, given an image (called original in the following) create a new, bigger, image that resembles the original one except each pixel (or more realistically groups of pixels) are replaced by smaller distinct image tiles.
I was drawn to pyvips because it is said it can handle huge images and that it can process images without having to load them completely into memory.
However, I am having an issue creating a blank mosaic to then populate with tile images.
In the code below I try joining tiles together row by row to create a mosaic but this code unfortunately eats through my RAM and always segfaults.
import os
import pyvips
from os.path import join
from scipy.spatial import cKDTree
class Mosaic(object):
def __init__(self, dir_path, original_path, tree=None, averages=None):
self.dir_path = dir_path
self.original = original_path
self.tree = tree
if averages:
self.averages = averages
else:
self.averages = {}
def get_image(self, path):
return pyvips.Image.new_from_file(path, access="sequential")
def build_tree(self):
for root, dirs, files in os.walk(self.dir_path):
print('Loading images from', root, '...')
for file_name in files:
path = join(root, file_name)
try:
image = pyvips.Image.new_from_file(path)
self.averages[self.avg_rgb(image)] = path
except pyvips.error.Error:
print('File', path, 'not recognized as an image.')
self.tree = cKDTree(self.averages.keys())
print('Loaded', len(self.averages), 'images.')
def avg_rgb(self, image):
m = image.stats()
return tuple(m(4,i)[0] for i in range(1,4))
def get_tile_name(self, patch):
avg = self.avg_rgb(patch)
index = self.tree.query(avg)[1]
return self.averages[tuple(self.tree.data[index])]
def get_tile(self, x, y, step):
patch = self.get_image(self.original).crop(x, y, step, step)
patch_name = self.get_tile_name(patch)
return pyvips.Image.new_from_file(patch_name, access="sequential")
def make_mosaic(self, tile_num, tile_size, mosaic_path):
original = self.get_image(self.original)
mosaic = None
step = min(original.height, original.width) / tile_num
for y in range(0, original.height, step):
mosaic_row = None
print('Building row', y/step, '/', original.height/step)
for x in range(0, original.width, step):
tile = self.get_tile(x, y, step)
tile = tile.resize(float(tile_size) / float(min(tile.width, tile.height)))
tile = tile.crop(0, 0, tile_size, tile_size)
#mosaic.draw_image(tile, x, y)
mosaic_row = tile if not mosaic_row else mosaic_row.join(tile, "horizontal")
mosaic = mosaic_row if not mosaic else mosaic.join(mosaic_row, "vertical")
mosaic.write_to_file(mosaic_path)
I have also tried creating a mosaic by resizing the original image and then using draw_image like the following but this also crashes.
mosaic = self.get_image(self.original).resize(tile_size)
mosaic.draw_image(tile, x, y)
Finally, I have tried creating the mosaic from new_temp_file and I am having trouble writing to the temp image.
How can I make this mosaic program work?
libvips uses a recursive algorithm to work out which pixels to compute next, so for very long pipelines you can overflow the C stack and get a crash.
The simplest solution would be to use arrayjoin. This is a libvips operator which can join many images in a single call:
http://jcupitt.github.io/libvips/API/current/libvips-conversion.html#vips-arrayjoin
There's an example on the libvips github of using it to join 30,000 images at once:
https://github.com/jcupitt/libvips/issues/471
(though that's using the previous version of the libvips Python binding)
I adapted your program to use arrayjoin, and changed the way it loaded images. I noticed you were also reloading the original image for each output tile, so removing that gave a nice speedup.
#!/usr/bin/python2
from __future__ import print_function
import os
import sys
import pyvips
from os.path import join
from scipy.spatial import cKDTree
class Mosaic(object):
def __init__(self, dir_path, original_path, tile_size=128, tree=None, averages=None):
self.dir_path = dir_path
self.original_path = original_path
self.tile_size = tile_size
self.tree = tree
if averages:
self.averages = averages
else:
self.averages = {}
def avg_rgb(self, image):
m = image.stats()
return tuple(m(4,i)[0] for i in range(1,4))
def build_tree(self):
for root, dirs, files in os.walk(self.dir_path):
print('Loading images from', root, '...')
for file_name in files:
path = join(root, file_name)
try:
# load image as a square image of size tile_size X tile_size
tile = pyvips.Image.thumbnail(path, self.tile_size,
height=self.tile_size,
crop='centre')
# render into memory
tile = tile.copy_memory()
self.averages[self.avg_rgb(tile)] = tile
except pyvips.error.Error:
print('File', path, 'not recognized as an image.')
self.tree = cKDTree(self.averages.keys())
print('Loaded', len(self.averages), 'images.')
def fetch_tree(self, patch):
avg = self.avg_rgb(patch)
index = self.tree.query(avg)[1]
return self.averages[tuple(self.tree.data[index])]
def make_mosaic(self, tile_num, mosaic_path):
mosaic = None
original = pyvips.Image.new_from_file(self.original_path)
step = min(original.height, original.width) / tile_num
tiles_across = original.width / step
tiles_down = original.height / step
tiles = []
for y in range(0, tiles_down):
print('Building row', y, '/', tiles_down)
for x in range(0, tiles_across):
patch = original.crop(x * step, y * step,
min(step, original.width - x * step),
min(step, original.height - y * step))
tile = self.fetch_tree(patch)
tiles.append(tile)
mosaic = pyvips.Image.arrayjoin(tiles, across=tiles_across)
print('writing ', mosaic_path)
mosaic.write_to_file(mosaic_path)
mosaic = Mosaic(sys.argv[1], sys.argv[2])
mosaic.build_tree()
mosaic.make_mosaic(200, sys.argv[3])
I can run it like this:
$ time ./mosaic2.py samples/ k2.jpg x.png
Loading images from samples/ ...
Loaded 228 images.
Building row 0 / 292
...
Building row 291 / 292
writing x.png
real 7m19.333s
user 7m27.322s
sys 0m30.578s
making a 26496 x 37376 pixel image, in this case, and it runs in about 150mb of memory.
How to write the code? I could only come up with this:
def rotateImage90CW(image):
pic = FileImage(image)
oldw = pic.getWidth()
oldh = pic.getHeight()
newIm = EmptyImage(oldw,oldh)
for row in range (oldh):
for col in range(oldw):
oldPixel = pic.getPixel(col,row)
newIm.setPixel(oldw-row,col,oldPixel)
newIm.draw(myWin)
If you use PIL/pillow:
from PIL import Image
im = Image.open(image)
im.rotate(90).show()
In your example, oldw-row should be „row”