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):
CONFIG = {
"background_image": None,
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
# Note: frame height and width will be resized to match pixel_shape
"pixel_height": DEFAULT_PIXEL_HEIGHT,
"pixel_width": DEFAULT_PIXEL_WIDTH,
# Note: frame height and width will be resized to match
# the pixel aspect ratio
"frame_height": FRAME_HEIGHT,
"frame_width": FRAME_WIDTH,
"frame_center": ORIGIN,
@ -62,12 +64,19 @@ class Camera(object):
self.canvas = None
return copy.copy(self)
def reset_pixel_shape(self, new_shape):
self.pixel_shape = tuple(new_shape)
def reset_pixel_shape(self, new_height, new_width):
self.pixel_width = new_width
self.pixel_height = new_height
self.init_background()
self.resize_frame_shape()
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):
return self.frame_height
@ -89,11 +98,12 @@ class Camera(object):
def resize_frame_shape(self, fixed_dimension=0):
"""
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
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_width = self.get_frame_width()
aspect_ratio = fdiv(pixel_width, pixel_height)
@ -105,10 +115,11 @@ class Camera(object):
self.set_frame_width(frame_width)
def init_background(self):
height = self.get_pixel_height()
width = self.get_pixel_width()
if self.background_image is not None:
path = get_full_raster_image_path(self.background_image)
image = Image.open(path).convert(self.image_mode)
height, width = self.pixel_shape
# TODO, how to gracefully handle backgrounds
# with different sizes?
self.background = np.array(image)[:height, :width]
@ -118,7 +129,7 @@ class Camera(object):
self.background_color, self.background_opacity
)
self.background = np.zeros(
list(self.pixel_shape) + [self.n_rgb_coords],
(height, width, self.n_rgb_coords),
dtype=self.pixel_array_dtype
)
self.background[:, :] = background_rgba
@ -388,7 +399,8 @@ class Camera(object):
pixel_coords = pixel_coords[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 = flattener.reshape((2, 1))
@ -433,9 +445,8 @@ class Camera(object):
# TODO, there is no accounting for a shear...
# Paste into an image as large as the camear's pixel array
h, w = self.pixel_shape
full_image = Image.fromarray(
np.zeros((h, w)),
np.zeros((self.get_pixel_height(), self.get_pixel_width())),
mode="RGBA"
)
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()
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_width = self.get_frame_width()
width_mult = pixel_width / frame_width
@ -496,14 +508,22 @@ class Camera(object):
def on_screen_pixels(self, pixel_coords):
return reduce(op.and_, [
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] < self.pixel_shape[0],
pixel_coords[:, 1] < self.get_pixel_height(),
])
def adjusted_thickness(self, thickness):
big_shape = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_shape"]
factor = sum(big_shape) / sum(self.pixel_shape)
# TODO: This seems...unsystematic
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
def get_thickening_nudges(self, thickness):
@ -525,12 +545,16 @@ class Camera(object):
self.get_frame_width(),
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
# are listed in x, y order
uncentered_pixel_coords = np.indices(self.pixel_shape)[
::-1].transpose(1, 2, 0)
uncentered_pixel_coords = np.indices(
[self.get_pixel_height(), self.get_pixel_width()]
)[::-1].transpose(1, 2, 0)
uncentered_space_coords = fdiv(
uncentered_pixel_coords * full_space_dims,
full_pixel_dims)
@ -540,7 +564,8 @@ class Camera(object):
# overflow is unlikely to be a problem)
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
# top-to-bottom order, opposite of screen coordinate convention

View file

@ -53,8 +53,8 @@ class OldMultiCamera(Camera):
"camera": camera_with_start_positions[0],
"start_x": camera_with_start_positions[1][1],
"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_y": camera_with_start_positions[1][0] + camera_with_start_positions[0].pixel_shape[0],
"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].get_pixel_height(),
})
for camera_with_start_positions in cameras_with_start_positions
]
@ -103,13 +103,10 @@ class SplitScreenCamera(OldMultiCamera):
self.left_camera = left_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]:
# TODO: Round up on one if width is odd
camera.pixel_shape = (self.pixel_shape[0], half_width)
camera.init_background()
camera.resize_frame_shape()
camera.reset()
camera.reset_pixel_shape(camera.get_pixel_height(), half_width)
OldMultiCamera.__init__(
self,

View file

@ -30,10 +30,10 @@ class MultiCamera(MovingCamera):
imfc.camera.frame.get_height(),
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_width * imfc.get_width() / self.get_frame_width()),
))
)
def reset(self):
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_matrix
# TODO: Make sure this plays well with latest camera updates
class CameraWithPerspective(Camera):
CONFIG = {

View file

@ -17,17 +17,20 @@ LOW_QUALITY_FRAME_DURATION = 1. / 15
MEDIUM_QUALITY_FRAME_DURATION = 1. / 30
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 = {
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
"pixel_height": DEFAULT_PIXEL_HEIGHT,
"pixel_width": DEFAULT_PIXEL_WIDTH,
}
MEDIUM_QUALITY_CAMERA_CONFIG = {
"pixel_shape": (720, 1280),
"pixel_height": 720,
"pixel_width": 1280,
}
LOW_QUALITY_CAMERA_CONFIG = {
"pixel_shape": (480, 854),
"pixel_height": 480,
"pixel_width": 854,
}
DEFAULT_POINT_DENSITY_2D = 25

View file

@ -668,7 +668,7 @@ class ColorMappedByFuncScene(Scene):
# We hash just based on output image
# Thus, multiple scenes with same output image can re-use it
# 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(
"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)
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 = [
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):
directory = get_scene_output_directory(scene_class)
sub_dir = "%dp%d" % (
camera_config["pixel_shape"][0],
camera_config["pixel_height"],
int(1.0 / frame_duration)
)
return guarantee_existance(os.path.join(directory, sub_dir))