diff --git a/camera/camera.py b/camera/camera.py index 645d608b..f23a76eb 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -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 diff --git a/camera/mapping_camera.py b/camera/mapping_camera.py index 9efdea31..d85f51ca 100644 --- a/camera/mapping_camera.py +++ b/camera/mapping_camera.py @@ -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, diff --git a/camera/multi_camera.py b/camera/multi_camera.py index af9ccd9a..42b428cd 100644 --- a/camera/multi_camera.py +++ b/camera/multi_camera.py @@ -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: diff --git a/camera/three_d_camera.py b/camera/three_d_camera.py index 55cd1967..2bcb750a 100644 --- a/camera/three_d_camera.py +++ b/camera/three_d_camera.py @@ -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 = { diff --git a/constants.py b/constants.py index 927daf86..c8470519 100644 --- a/constants.py +++ b/constants.py @@ -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 diff --git a/old_projects/WindingNumber.py b/old_projects/WindingNumber.py index c12c5cbd..b94a5ef6 100644 --- a/old_projects/WindingNumber.py +++ b/old_projects/WindingNumber.py @@ -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" ) diff --git a/scene/scene.py b/scene/scene.py index f136982f..364e6348 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -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, diff --git a/utils/output_directory_getters.py b/utils/output_directory_getters.py index 8011392e..14d317fc 100644 --- a/utils/output_directory_getters.py +++ b/utils/output_directory_getters.py @@ -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))