Start tracking pixel_height and pixel_width instead of pixel_shape, since all uses of it involved unpacking anyway, and the ordering makes it harder to read and edit.

This commit is contained in:
Grant Sanderson
2018-05-14 13:52:44 -07:00
parent f04b2e270c
commit ecd48d885e
8 changed files with 64 additions and 36 deletions

View File

@ -29,8 +29,10 @@ from utils.space_ops import angle_of_vector
class Camera(object): class Camera(object):
CONFIG = { CONFIG = {
"background_image": None, "background_image": None,
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH), "pixel_height": DEFAULT_PIXEL_HEIGHT,
# Note: frame height and width will be resized to match pixel_shape "pixel_width": DEFAULT_PIXEL_WIDTH,
# Note: frame height and width will be resized to match
# the pixel aspect ratio
"frame_height": FRAME_HEIGHT, "frame_height": FRAME_HEIGHT,
"frame_width": FRAME_WIDTH, "frame_width": FRAME_WIDTH,
"frame_center": ORIGIN, "frame_center": ORIGIN,
@ -62,12 +64,19 @@ class Camera(object):
self.canvas = None self.canvas = None
return copy.copy(self) return copy.copy(self)
def reset_pixel_shape(self, new_shape): def reset_pixel_shape(self, new_height, new_width):
self.pixel_shape = tuple(new_shape) self.pixel_width = new_width
self.pixel_height = new_height
self.init_background() self.init_background()
self.resize_frame_shape() self.resize_frame_shape()
self.reset() self.reset()
def get_pixel_height(self):
return self.pixel_height
def get_pixel_width(self):
return self.pixel_width
def get_frame_height(self): def get_frame_height(self):
return self.frame_height return self.frame_height
@ -89,11 +98,12 @@ class Camera(object):
def resize_frame_shape(self, fixed_dimension=0): def resize_frame_shape(self, fixed_dimension=0):
""" """
Changes frame_shape to match the aspect ratio Changes frame_shape to match the aspect ratio
of pixel_shape, where fixed_dimension determines of the pixels, where fixed_dimension determines
whether frame_height or frame_width whether frame_height or frame_width
remains fixed while the other changes accordingly. remains fixed while the other changes accordingly.
""" """
pixel_height, pixel_width = self.pixel_shape pixel_height = self.get_pixel_height()
pixel_width = self.get_pixel_width()
frame_height = self.get_frame_height() frame_height = self.get_frame_height()
frame_width = self.get_frame_width() frame_width = self.get_frame_width()
aspect_ratio = fdiv(pixel_width, pixel_height) aspect_ratio = fdiv(pixel_width, pixel_height)
@ -105,10 +115,11 @@ class Camera(object):
self.set_frame_width(frame_width) self.set_frame_width(frame_width)
def init_background(self): def init_background(self):
height = self.get_pixel_height()
width = self.get_pixel_width()
if self.background_image is not None: if self.background_image is not None:
path = get_full_raster_image_path(self.background_image) path = get_full_raster_image_path(self.background_image)
image = Image.open(path).convert(self.image_mode) image = Image.open(path).convert(self.image_mode)
height, width = self.pixel_shape
# TODO, how to gracefully handle backgrounds # TODO, how to gracefully handle backgrounds
# with different sizes? # with different sizes?
self.background = np.array(image)[:height, :width] self.background = np.array(image)[:height, :width]
@ -118,7 +129,7 @@ class Camera(object):
self.background_color, self.background_opacity self.background_color, self.background_opacity
) )
self.background = np.zeros( self.background = np.zeros(
list(self.pixel_shape) + [self.n_rgb_coords], (height, width, self.n_rgb_coords),
dtype=self.pixel_array_dtype dtype=self.pixel_array_dtype
) )
self.background[:, :] = background_rgba self.background[:, :] = background_rgba
@ -388,7 +399,8 @@ class Camera(object):
pixel_coords = pixel_coords[on_screen_indices] pixel_coords = pixel_coords[on_screen_indices]
rgbas = rgbas[on_screen_indices] rgbas = rgbas[on_screen_indices]
ph, pw = self.pixel_shape ph = self.get_pixel_height()
pw = self.get_pixel_width()
flattener = np.array([1, pw], dtype='int') flattener = np.array([1, pw], dtype='int')
flattener = flattener.reshape((2, 1)) flattener = flattener.reshape((2, 1))
@ -433,9 +445,8 @@ class Camera(object):
# TODO, there is no accounting for a shear... # TODO, there is no accounting for a shear...
# Paste into an image as large as the camear's pixel array # Paste into an image as large as the camear's pixel array
h, w = self.pixel_shape
full_image = Image.fromarray( full_image = Image.fromarray(
np.zeros((h, w)), np.zeros((self.get_pixel_height(), self.get_pixel_width())),
mode="RGBA" mode="RGBA"
) )
new_ul_coords = center_coords - np.array(sub_image.size) / 2 new_ul_coords = center_coords - np.array(sub_image.size) / 2
@ -479,7 +490,8 @@ class Camera(object):
shifted_points = points - self.get_frame_center() shifted_points = points - self.get_frame_center()
result = np.zeros((len(points), 2)) result = np.zeros((len(points), 2))
pixel_height, pixel_width = self.pixel_shape pixel_height = self.get_pixel_height()
pixel_width = self.get_pixel_width()
frame_height = self.get_frame_height() frame_height = self.get_frame_height()
frame_width = self.get_frame_width() frame_width = self.get_frame_width()
width_mult = pixel_width / frame_width width_mult = pixel_width / frame_width
@ -496,14 +508,22 @@ class Camera(object):
def on_screen_pixels(self, pixel_coords): def on_screen_pixels(self, pixel_coords):
return reduce(op.and_, [ return reduce(op.and_, [
pixel_coords[:, 0] >= 0, pixel_coords[:, 0] >= 0,
pixel_coords[:, 0] < self.pixel_shape[1], pixel_coords[:, 0] < self.get_pixel_width(),
pixel_coords[:, 1] >= 0, pixel_coords[:, 1] >= 0,
pixel_coords[:, 1] < self.pixel_shape[0], pixel_coords[:, 1] < self.get_pixel_height(),
]) ])
def adjusted_thickness(self, thickness): def adjusted_thickness(self, thickness):
big_shape = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_shape"] # TODO: This seems...unsystematic
factor = sum(big_shape) / sum(self.pixel_shape) big_sum = op.add(
PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"],
PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"],
)
this_sum = op.add(
self.get_pixel_height(),
self.get_pixel_width(),
)
factor = fdiv(big_sum, this_sum)
return 1 + (thickness - 1) / factor return 1 + (thickness - 1) / factor
def get_thickening_nudges(self, thickness): def get_thickening_nudges(self, thickness):
@ -525,12 +545,16 @@ class Camera(object):
self.get_frame_width(), self.get_frame_width(),
self.get_frame_height() self.get_frame_height()
]) ])
full_pixel_dims = np.array(self.pixel_shape)[::-1] full_pixel_dims = np.array([
self.get_pixel_width(),
self.get_pixel_height()
])
# These are addressed in the same y, x order as in pixel_array, but the values in them # These are addressed in the same y, x order as in pixel_array, but the values in them
# are listed in x, y order # are listed in x, y order
uncentered_pixel_coords = np.indices(self.pixel_shape)[ uncentered_pixel_coords = np.indices(
::-1].transpose(1, 2, 0) [self.get_pixel_height(), self.get_pixel_width()]
)[::-1].transpose(1, 2, 0)
uncentered_space_coords = fdiv( uncentered_space_coords = fdiv(
uncentered_pixel_coords * full_space_dims, uncentered_pixel_coords * full_space_dims,
full_pixel_dims) full_pixel_dims)
@ -540,7 +564,8 @@ class Camera(object):
# overflow is unlikely to be a problem) # overflow is unlikely to be a problem)
centered_space_coords = ( centered_space_coords = (
uncentered_space_coords - fdiv(full_space_dims, 2)) uncentered_space_coords - fdiv(full_space_dims, 2)
)
# Have to also flip the y coordinates to account for pixel array being listed in # Have to also flip the y coordinates to account for pixel array being listed in
# top-to-bottom order, opposite of screen coordinate convention # top-to-bottom order, opposite of screen coordinate convention

View File

@ -53,8 +53,8 @@ class OldMultiCamera(Camera):
"camera": camera_with_start_positions[0], "camera": camera_with_start_positions[0],
"start_x": camera_with_start_positions[1][1], "start_x": camera_with_start_positions[1][1],
"start_y": camera_with_start_positions[1][0], "start_y": camera_with_start_positions[1][0],
"end_x": camera_with_start_positions[1][1] + camera_with_start_positions[0].pixel_shape[1], "end_x": camera_with_start_positions[1][1] + camera_with_start_positions[0].get_pixel_width(),
"end_y": camera_with_start_positions[1][0] + camera_with_start_positions[0].pixel_shape[0], "end_y": camera_with_start_positions[1][0] + camera_with_start_positions[0].get_pixel_height(),
}) })
for camera_with_start_positions in cameras_with_start_positions for camera_with_start_positions in cameras_with_start_positions
] ]
@ -103,13 +103,10 @@ class SplitScreenCamera(OldMultiCamera):
self.left_camera = left_camera self.left_camera = left_camera
self.right_camera = right_camera self.right_camera = right_camera
half_width = self.pixel_shape[1] / 2 half_width = self.get_pixel_width() / 2
for camera in [self.left_camera, self.right_camera]: for camera in [self.left_camera, self.right_camera]:
# TODO: Round up on one if width is odd # TODO: Round up on one if width is odd
camera.pixel_shape = (self.pixel_shape[0], half_width) camera.reset_pixel_shape(camera.get_pixel_height(), half_width)
camera.init_background()
camera.resize_frame_shape()
camera.reset()
OldMultiCamera.__init__( OldMultiCamera.__init__(
self, self,

View File

@ -30,10 +30,10 @@ class MultiCamera(MovingCamera):
imfc.camera.frame.get_height(), imfc.camera.frame.get_height(),
imfc.camera.frame.get_width(), imfc.camera.frame.get_width(),
) )
imfc.camera.reset_pixel_shape(( imfc.camera.reset_pixel_shape(
int(pixel_height * imfc.get_height() / self.get_frame_height()), int(pixel_height * imfc.get_height() / self.get_frame_height()),
int(pixel_width * imfc.get_width() / self.get_frame_width()), int(pixel_width * imfc.get_width() / self.get_frame_width()),
)) )
def reset(self): def reset(self):
for imfc in self.image_mobjects_from_cameras: for imfc in self.image_mobjects_from_cameras:

View File

@ -12,6 +12,8 @@ from utils.bezier import interpolate
from utils.space_ops import rotation_about_z from utils.space_ops import rotation_about_z
from utils.space_ops import rotation_matrix from utils.space_ops import rotation_matrix
# TODO: Make sure this plays well with latest camera updates
class CameraWithPerspective(Camera): class CameraWithPerspective(Camera):
CONFIG = { CONFIG = {

View File

@ -17,17 +17,20 @@ LOW_QUALITY_FRAME_DURATION = 1. / 15
MEDIUM_QUALITY_FRAME_DURATION = 1. / 30 MEDIUM_QUALITY_FRAME_DURATION = 1. / 30
PRODUCTION_QUALITY_FRAME_DURATION = 1. / 60 PRODUCTION_QUALITY_FRAME_DURATION = 1. / 60
# There might be other configuration than pixel_shape later... # There might be other configuration than pixel shape later...
PRODUCTION_QUALITY_CAMERA_CONFIG = { PRODUCTION_QUALITY_CAMERA_CONFIG = {
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH), "pixel_height": DEFAULT_PIXEL_HEIGHT,
"pixel_width": DEFAULT_PIXEL_WIDTH,
} }
MEDIUM_QUALITY_CAMERA_CONFIG = { MEDIUM_QUALITY_CAMERA_CONFIG = {
"pixel_shape": (720, 1280), "pixel_height": 720,
"pixel_width": 1280,
} }
LOW_QUALITY_CAMERA_CONFIG = { LOW_QUALITY_CAMERA_CONFIG = {
"pixel_shape": (480, 854), "pixel_height": 480,
"pixel_width": 854,
} }
DEFAULT_POINT_DENSITY_2D = 25 DEFAULT_POINT_DENSITY_2D = 25

View File

@ -668,7 +668,7 @@ class ColorMappedByFuncScene(Scene):
# We hash just based on output image # We hash just based on output image
# Thus, multiple scenes with same output image can re-use it # Thus, multiple scenes with same output image can re-use it
# without recomputation # without recomputation
full_hash = hash((func_hash, self.camera.pixel_shape)) full_hash = hash((func_hash, self.camera.get_pixel_width()))
self.background_image_file = self.short_path_to_long_path( self.background_image_file = self.short_path_to_long_path(
"color_mapped_bg_hash_" + str(full_hash) + ".png" "color_mapped_bg_hash_" + str(full_hash) + ".png"
) )

View File

@ -572,7 +572,8 @@ class Scene(Container):
self.args_to_rename_file = (temp_file_path, file_path) self.args_to_rename_file = (temp_file_path, file_path)
fps = int(1 / self.frame_duration) fps = int(1 / self.frame_duration)
height, width = self.camera.pixel_shape height = self.camera.get_pixel_height()
width = self.camera.get_pixel_width()
command = [ command = [
FFMPEG_BIN, FFMPEG_BIN,

View File

@ -34,7 +34,7 @@ def get_scene_output_directory(scene_class):
def get_movie_output_directory(scene_class, camera_config, frame_duration): def get_movie_output_directory(scene_class, camera_config, frame_duration):
directory = get_scene_output_directory(scene_class) directory = get_scene_output_directory(scene_class)
sub_dir = "%dp%d" % ( sub_dir = "%dp%d" % (
camera_config["pixel_shape"][0], camera_config["pixel_height"],
int(1.0 / frame_duration) int(1.0 / frame_duration)
) )
return guarantee_existance(os.path.join(directory, sub_dir)) return guarantee_existance(os.path.join(directory, sub_dir))