3b1b-manim/displayer.py

213 lines
6.1 KiB
Python
Raw Normal View History

import numpy as np
import itertools as it
import os
from PIL import Image
2015-03-26 22:49:22 -06:00
import subprocess
import cv2
from colour import Color
2015-04-03 16:41:25 -07:00
import progressbar
2015-04-03 16:41:25 -07:00
from mobject import *
2015-06-10 22:00:35 -07:00
from constants import *
2015-06-19 08:31:02 -07:00
def get_pixels(image_array): #TODO, FIX WIDTH/HEIGHT PROBLEM HERE
2015-03-26 22:49:22 -06:00
if image_array is None:
return np.zeros(
(DEFAULT_HEIGHT, DEFAULT_WIDTH, 3),
dtype = 'uint8'
)
2015-03-26 22:49:22 -06:00
else:
pixels = np.array(image_array).astype('uint8')
2015-03-26 22:49:22 -06:00
assert len(pixels.shape) == 3 and pixels.shape[2] == 3
return pixels
def paint_region(region, image_array = None, color = None):
pixels = get_pixels(image_array)
assert region.shape == pixels.shape[:2]
if color is None:
2015-05-17 15:08:51 -07:00
#Random dark color
rgb = 0.5 * np.random.random(3)
else:
rgb = np.array(Color(color).get_rgb())
pixels[region.bool_grid] = (255*rgb).astype('uint8')
return pixels
def paint_mobject(mobject, image_array = None):
2015-06-13 19:00:23 -07:00
pixels = get_pixels(image_array)
if mobject.get_num_points() == 0:
return pixels
2015-03-26 22:49:22 -06:00
height = pixels.shape[0]
width = pixels.shape[1]
space_height = SPACE_HEIGHT
2015-05-24 09:42:28 -07:00
space_width = SPACE_HEIGHT * width / height
# camera_distance = 10
2015-03-26 22:49:22 -06:00
points = np.array(mobject.points[:, :2])
2015-05-24 09:42:28 -07:00
# for i in range(2):
# points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2])
2015-04-03 16:41:25 -07:00
rgbs = np.array(mobject.rgbs)
#Flips y-axis
points[:,1] *= -1
2015-06-19 08:31:02 -07:00
#Removes points out of space
to_remove = (abs(points[:,0]) < SPACE_WIDTH) & \
(abs(points[:,1]) < SPACE_HEIGHT)
points = points[to_remove]
rgbs = rgbs[to_remove]
#Map points to pixel space, then create pixel array first in terms
#of its flattened version
2015-06-13 19:00:23 -07:00
try:
points += np.array(
[space_width, space_height]*points.shape[0]
).reshape(points.shape)
except:
print points.shape, mobject.points.shape
points *= np.array(
2015-03-26 22:49:22 -06:00
[width / (2.0 * space_width), height / (2.0 * space_height)]*\
points.shape[0]
).reshape(points.shape)
points = points.astype('int')
2015-03-26 22:49:22 -06:00
flattener = np.array([1, width], dtype = 'int').reshape((2, 1))
indices = np.dot(points, flattener)
indices = indices.reshape(indices.size)
if mobject.should_buffer_points:#Is this alright?
2015-04-03 16:41:25 -07:00
for tweak in [
indices + 1,
indices + width,
indices + width + 1
]:
indices = np.append(indices, tweak)
rgbs = np.array(list(rgbs) * 4)
2015-03-26 22:49:22 -06:00
admissibles = (indices < height * width) * (indices > 0)
indices = indices[admissibles]
2015-04-03 16:41:25 -07:00
rgbs = rgbs[admissibles]
rgbs = (rgbs * 255).astype(int)
2015-03-26 22:49:22 -06:00
pixels = pixels.reshape((height * width, 3))
2015-04-03 16:41:25 -07:00
pixels[indices] = rgbs.reshape((rgbs.size/3), 3)#WHY?
2015-03-26 22:49:22 -06:00
pixels = pixels.reshape((height, width, 3)).astype('uint8')
return pixels
def write_to_gif(scene, name):
#TODO, find better means of compression
if not name.endswith(".gif"):
name += ".gif"
filepath = os.path.join(GIF_DIR, name)
temppath = os.path.join(GIF_DIR, "Temp.gif")
print "Writing " + name + "..."
2015-03-26 22:49:22 -06:00
images = [Image.fromarray(frame) for frame in scene.frames]
2015-08-17 13:19:34 -07:00
writeGif(temppath, images, scene.frame_duration)
print "Compressing..."
os.system("gifsicle -O " + temppath + " > " + filepath)
os.system("rm " + temppath)
def write_to_movie(scene, name):
frames = scene.frames
progress_bar = progressbar.ProgressBar(maxval=len(frames))
progress_bar.start()
print "writing " + name + "..."
2015-03-26 22:49:22 -06:00
filepath = os.path.join(MOVIE_DIR, name)
filedir = "/".join(filepath.split("/")[:-1])
if not os.path.exists(filedir):
os.makedirs(filedir)
2015-08-17 13:19:34 -07:00
rate = int(1/scene.frame_duration)
2015-03-26 22:49:22 -06:00
tmp_stem = os.path.join(TMP_IMAGE_DIR, name.replace("/", "_"))
suffix = "-%04d.png"
image_files = []
for frame, count in zip(frames, it.count()):
progress_bar.update(int(0.9 * count))
2015-03-26 22:49:22 -06:00
Image.fromarray(frame).save(tmp_stem + suffix%count)
image_files.append(tmp_stem + suffix%count)
commands = [
"ffmpeg",
"-y",
"-loglevel",
"error",
"-i",
tmp_stem + suffix,
"-c:v",
"libx264",
"-vf",
2015-05-07 21:28:02 -07:00
"fps=%d,format=yuv420p"%rate,
2015-03-26 22:49:22 -06:00
filepath + ".mp4"
]
os.system(" ".join(commands))
for image_file in image_files:
os.remove(image_file)
progress_bar.finish()
2015-03-26 22:49:22 -06:00
# vs = VideoSink(scene.shape, filepath, rate)
# for frame in frames:
# vs.run(frame)
# vs.close()
# progress_bar.finish()
# filepath = os.path.join(MOVIE_DIR, name + ".mov")
# fourcc = cv2.cv.FOURCC(*"8bps")
# out = cv2.VideoWriter(
2015-03-26 22:49:22 -06:00
# filepath, fourcc, 1.0/scene.frame_duration, (DEFAULT_WIDTH, DEFAULT_HEIGHT), True
# )
# progress = 0
# for frame in frames:
# if progress == 0:
# print "Writing movie"
# progress_bar.update(progress)
# r, g, b = cv2.split(np.array(frame))
# bgr_frame = cv2.merge([b, g, r])
# out.write(bgr_frame)
# progress += 1
# out.release()
# progress_bar.finish()
2015-05-07 21:28:02 -07:00
# class VideoSink(object):
# def __init__(self, size, filename="output", rate=10, byteorder="bgra") :
# self.size = size
# cmdstring = [
# 'mencoder',
# '/dev/stdin',
# '-demuxer', 'rawvideo',
# '-rawvideo', 'w=%i:h=%i'%size[::-1]+":fps=%i:format=%s"%(rate,byteorder),
# '-o', filename+'.mp4',
# '-ovc', 'lavc',
# ]
# self.p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False)
# def run(self, image):
# """
# Image comes in as HEIGHTxWIDTHx3 numpy array, order rgb
# """
# assert image.shape == self.size + (3,)
# r, g, b = [image[:,:,i].astype('uint32') for i in range(3)]
# a = np.ones(image.shape[:2], dtype = 'uint32')
# #hacky
# image = sum([
# arr << 8**i
# for arr, i in zip(range(4), [a, r, g, b])
# ])
# self.p.stdin.write(image.tostring())
# def close(self):
# self.p.stdin.close()