mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
185 lines
6 KiB
Python
185 lines
6 KiB
Python
import numpy as np
|
|
import itertools as it
|
|
import os
|
|
import sys
|
|
from PIL import Image
|
|
import cv2
|
|
from colour import Color
|
|
import progressbar
|
|
|
|
from helpers import *
|
|
|
|
class Camera(object):
|
|
CONFIG = {
|
|
"pixel_width" : DEFAULT_WIDTH,
|
|
"pixel_height" : DEFAULT_HEIGHT,
|
|
"space_height" : SPACE_HEIGHT,
|
|
"space_center" : ORIGIN,
|
|
"background_color" : BLACK,
|
|
}
|
|
|
|
def __init__(self, background = None, **kwargs):
|
|
digest_config(self, kwargs, locals())
|
|
self.init_background()
|
|
self.reset()
|
|
|
|
width_to_height = float(self.pixel_width) / self.pixel_height
|
|
self.space_width = self.space_height * width_to_height
|
|
|
|
def init_background(self):
|
|
if self.background:
|
|
shape = self.background.shape[:2]
|
|
self.pixel_height, self.pixel_width = shape
|
|
else:
|
|
background_color = Color(self.background_color)
|
|
background_rgb = np.array(background_color.get_rgb())
|
|
ones = np.ones(
|
|
(self.pixel_height, self.pixel_width, 1),
|
|
dtype = 'uint8'
|
|
)
|
|
self.background = np.dot(
|
|
ones, background_rgb.reshape((1, 3))
|
|
)
|
|
|
|
def get_image(self):
|
|
return self.pixel_array
|
|
|
|
def reset(self):
|
|
self.pixel_array = np.array(self.background)
|
|
|
|
# def get_pixels(image_array): #TODO, FIX WIDTH/HEIGHT PROBLEM HERE
|
|
# if image_array is None:
|
|
# return np.zeros(
|
|
# (DEFAULT_HEIGHT, DEFAULT_WIDTH, 3),
|
|
# dtype = 'uint8'
|
|
# )
|
|
# else:
|
|
# pixels = np.array(image_array).astype('uint8')
|
|
# 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:
|
|
# #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 capture_mobject(self, mobject):
|
|
return self.capture_mobjects([mobject])
|
|
|
|
def capture_mobjects(self, mobjects, include_sub_mobjects = True):
|
|
# pixels = get_pixels(image_array)
|
|
# height = pixels.shape[0]
|
|
# width = pixels.shape[1]
|
|
# space_height = SPACE_HEIGHT
|
|
# space_width = SPACE_HEIGHT * width / height
|
|
# pixels = pixels.reshape((pixels.size/3, 3)).astype('uint8')
|
|
|
|
if include_sub_mobjects:
|
|
all_families = [
|
|
mob.submobject_family()
|
|
for mob in mobjects
|
|
]
|
|
mobjects = reduce(op.add, all_families, [])
|
|
|
|
for mobject in mobjects:
|
|
self.display_points(
|
|
mobject.points, mobject.rgbs,
|
|
mobject.point_thickness
|
|
)
|
|
|
|
def display_points(self, points, rgbs, thickness):
|
|
points = self.project_onto_screen(points)
|
|
pixel_coordinates = self.pixel_coordinates_of_points(points)
|
|
rgbs = (255*rgbs).astype('uint8')
|
|
on_screen_indices = self.on_screen_pixels(pixel_coordinates)
|
|
pixel_coordinates = pixel_coordinates[on_screen_indices]
|
|
rgbs = rgbs[on_screen_indices]
|
|
|
|
flattener = np.array([1, self.width], dtype = 'int')
|
|
flattener = flattener.reshape((2, 1))
|
|
indices = np.dot(pixel_coordinates, flattener)[:,0]
|
|
|
|
pw, ph = self.pixel_width, self.pixel_height
|
|
self.pixel_array.reshape((pw*ph, 3))
|
|
self.pixel_array[indices] = rgbs.astype('uint8')
|
|
self.pixel_array.reshape((ph, pw, 3))
|
|
|
|
def project_onto_screen(points):
|
|
## TODO
|
|
points[:,2] = 0
|
|
|
|
def pixel_coordinates_of_points(self, points):
|
|
result = np.zeros((len(points), 2))
|
|
width_mult = self.pixel_width/self.space_width/2
|
|
width_add = self.pixel_width/2
|
|
height_mult = self.pixel_height/self.space_height/2
|
|
height_add = self.pixel_height/2
|
|
#Flip on y-axis as you go
|
|
height_mult *= -1
|
|
|
|
result[:,0] = points[:,0]*width_mult + width_add
|
|
result[:,1] = points[:,1]*height_mult + height_add
|
|
return result
|
|
|
|
def on_screen_pixels(self, pixel_coordinates):
|
|
return (pixel_coordinates[:,0] < 0) | \
|
|
(pixel_coordinates[:,0] >= width) | \
|
|
(pixel_coordinates[:,1] < 0) | \
|
|
(pixel_coordinates[:,1] >= height)
|
|
|
|
|
|
# def add_thickness(pixel_indices_and_rgbs, thickness, width, height):
|
|
# """
|
|
# Imagine dragging each pixel around like a paintbrush in
|
|
# a plus-sign-shaped pixel arrangement surrounding it.
|
|
|
|
# Pass rgb = None to do nothing to them
|
|
# """
|
|
# thickness = adjusted_thickness(thickness, width, height)
|
|
# original = np.array(pixel_indices_and_rgbs)
|
|
# n_extra_columns = pixel_indices_and_rgbs.shape[1] - 2
|
|
# for nudge in range(-thickness/2+1, thickness/2+1):
|
|
# if nudge == 0:
|
|
# continue
|
|
# for x, y in [[nudge, 0], [0, nudge]]:
|
|
# pixel_indices_and_rgbs = np.append(
|
|
# pixel_indices_and_rgbs,
|
|
# original+([x, y] + [0]*n_extra_columns),
|
|
# axis = 0
|
|
# )
|
|
# admissibles = (pixel_indices_and_rgbs[:,0] >= 0) & \
|
|
# (pixel_indices_and_rgbs[:,0] < width) & \
|
|
# (pixel_indices_and_rgbs[:,1] >= 0) & \
|
|
# (pixel_indices_and_rgbs[:,1] < height)
|
|
# return pixel_indices_and_rgbs[admissibles]
|
|
|
|
def adjusted_thickness(thickness, width, height):
|
|
big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"]
|
|
big_height = PRODUCTION_QUALITY_DISPLAY_CONFIG["height"]
|
|
factor = (big_width + big_height) / (width + height)
|
|
return 1 + (thickness-1)/facto
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|