def _convert_2d_to_3d_point_undistort(point_group, camera, fbw, fbh, lcox, lcoy, camera_fov, frame, pos, depth): """ Convert a 2D point (undistorted) into a 3D point, in world space. :param point_group: Camera Point Group for camera. :param camera: The camera to use for rolling shutter calculations. :param fbw: Camera's film back width value. :param fbh: Camera's film back height value. :param lcox: Camera lens lens center offset X value. :param lcoy: Camera lens lens center offset Y value. :param camera_fov: Camera Field of View as list of left, right, bottom and top. :param frame: The 2D point's frame number (in internal 3DE frame numbers). :param pos: Input 2D data. :param depth: The content distance to calculate rolling shutter at. :return: Corrected 2D point. :rtype: vec2d """ focal = tde4.getCameraFocalLength(camera, frame) r3d = vl_sdv.mat3d(tde4.getPGroupRotation3D(point_group, camera, frame)) p3d = vl_sdv.vec3d(tde4.getPGroupPosition3D(point_group, camera, frame)) left, right, bottom, top = camera_fov p2d = [0, 0] p2d[0] = (pos[0] - left) / (right - left) p2d[1] = (pos[1] - bottom) / (top - bottom) p2d = tde4.removeDistortion2D(camera, frame, p2d) p2d_cm = vl_sdv.vec2d((p2d[0] - 0.5) * fbw - lcox, (p2d[1] - 0.5) * fbh - lcoy) homogeneous_point = r3d * vl_sdv.vec3d(p2d_cm[0], p2d_cm[1], -focal).unit() out_point = homogeneous_point * depth + p3d return out_point
def _generate_v2(point_group, camera, points, start_frame=None, undistort=False): """ Generate the UV file format contents, using JSON format. :param point_group: The 3DE Point Group containing 'points' :type point_group: str :param camera: The 3DE Camera containing 2D 'points' data. :type camera: str :param points: The list of 3DE Points representing 2D data to save. :type points: list of str :param start_frame: The frame number to be considered at 'first frame'. Defaults to 1001 if set to None. :type start_frame: None or int :param undistort: Should we apply undistortion to the 2D points data? Yes or no. :type undistort: bool Each point will store: - Point name - X, Y position (in UV coordinates, per-frame) - Point weight (per-frame) - Point Set name - Point 'Persistent ID' """ assert isinstance(point_group, basestring) assert isinstance(camera, basestring) assert isinstance(points, (list, tuple)) assert start_frame is None or isinstance(start_frame, int) assert isinstance(undistort, bool) if start_frame is None: start_frame = 1001 data = UV_TRACK_HEADER_VERSION_2.copy() cam_num_frames = tde4.getCameraNoFrames(camera) if len(points) == 0: return '' frame0 = int(start_frame) frame0 -= 1 data['num_points'] = len(points) data['is_undistorted'] = bool(undistort) data['points'] = [] for point in points: point_data = {} # Query point information name = tde4.getPointName(point_group, point) uid = None if SUPPORT_PERSISTENT_ID is True: uid = tde4.getPointPersistentID(point_group, point) point_set = tde4.getPointSet(point_group, point) point_set_name = None if point_set is not None: point_set_name = tde4.getSetName(point_group, point_set) point_data['name'] = name point_data['id'] = uid point_data['set_name'] = point_set_name # Write per-frame position data frame = 1 # 3DE starts at frame '1' regardless of the 'start frame'. point_data['per_frame'] = [] pos_block = tde4.getPointPosition2DBlock(point_group, point, camera, 1, cam_num_frames) for pos in pos_block: if pos[0] == -1.0 or pos[1] == -1.0: # No valid data here. frame += 1 continue # Does the 2D point go outside the camera FOV? Is that ok? valid_2d = tde4.isPointPos2DValid(point_group, point, camera, frame) if valid_2d != 1: # No valid data here. frame += 1 continue f = frame + frame0 if undistort is True: pos = tde4.removeDistortion2D(camera, frame, pos) weight = 1.0 if SUPPORT_POINT_WEIGHT_BY_FRAME is True: weight = tde4.getPointWeightByFrame(point_group, point, camera, frame) frame_data = {'frame': f, 'pos': pos, 'weight': weight} point_data['per_frame'].append(frame_data) frame += 1 data['points'].append(point_data) data_str = json.dumps(data) return data_str
def _generate_v1(point_group, camera, points, start_frame=None, undistort=False): """ Generate the UV file format contents, using a basic ASCII format. :param point_group: The 3DE Point Group containing 'points' :type point_group: str :param camera: The 3DE Camera containing 2D 'points' data. :type camera: str :param points: The list of 3DE Points representing 2D data to save. :type points: list of str :param start_frame: The frame number to be considered at 'first frame'. Defaults to 1001 if set to None. :type start_frame: None or int :param undistort: Should we apply undistortion to the 2D points data? Yes or no. :type undistort: bool Each point will store: - Point name - X, Y position (in UV coordinates, per-frame) - Point weight (per-frame) """ assert isinstance(point_group, basestring) assert isinstance(camera, basestring) assert isinstance(points, (list, tuple)) assert start_frame is None or isinstance(start_frame, int) assert isinstance(undistort, bool) if start_frame is None: start_frame = 1001 data_str = '' cam_num_frames = tde4.getCameraNoFrames(camera) if len(points) == 0: return data_str frame0 = int(start_frame) frame0 -= 1 data_str += '{0:d}\n'.format(len(points)) for point in points: name = tde4.getPointName(point_group, point) c2d = tde4.getPointPosition2DBlock(point_group, point, camera, 1, cam_num_frames) # Write per-frame position data num_valid_frame = 0 pos_list = [] weight_list = [] frame = 1 # 3DE starts at frame '1' regardless of the 'start-frame'. for v in c2d: if v[0] == -1.0 or v[1] == -1.0: # No valid data here. frame += 1 continue # Does the 2D point go outside the camera FOV? Is that ok? valid = tde4.isPointPos2DValid(point_group, point, camera, frame) if valid == 0: # No valid data here. frame += 1 continue # Number of points with valid positions num_valid_frame += 1 f = frame + frame0 if undistort is True: v = tde4.removeDistortion2D(camera, frame, v) weight = 1.0 if SUPPORT_POINT_WEIGHT_BY_FRAME is True: weight = tde4.getPointWeightByFrame(point_group, point, camera, frame) pos_list.append((f, v)) weight_list.append((f, weight)) frame += 1 # add data data_str += name + '\n' data_str += '{0:d}\n'.format(num_valid_frame) for pos_data, weight_data in zip(pos_list, weight_list): f = pos_data[0] v = pos_data[1] w = weight_data[1] assert f == weight_data[0] data_str += '%d %.15f %.15f %.8f\n' % (f, v[0], v[1], w) return data_str
def _generate_v2_v3_and_v4(point_group, camera, points, version=None, **kwargs): """ Generate the UV file format contents, using JSON format. Set the individual _generate_v2 or _generate_v3 functions for details of what is stored. :param point_group: The 3DE Point Group containing 'points' :type point_group: str :param camera: The 3DE Camera containing 2D 'points' data. :type camera: str :param points: The list of 3DE Points representing 2D data to save. :type points: list of str :param version: The version of file to generate, UV_TRACK_FORMAT_VERSION_2 or UV_TRACK_FORMAT_VERSION_3. :type version: int :param start_frame: Format v2 and v3; The frame number to be considered at 'first frame'. Defaults to 1001 if set to None. :type start_frame: None or int :param undistort: Format v2; Should we apply undistortion to the 2D points data? Yes or no. :type undistort: bool :returns: A JSON format string, with the UV Track data in it. :rtype: str """ assert isinstance(point_group, basestring) assert isinstance(camera, basestring) assert isinstance(points, (list, tuple)) assert isinstance(version, (int, long)) assert version in [ UV_TRACK_FORMAT_VERSION_2, UV_TRACK_FORMAT_VERSION_3, UV_TRACK_FORMAT_VERSION_4 ] start_frame = kwargs.get('start_frame') assert start_frame is None or isinstance(start_frame, int) if start_frame is None: start_frame = 1001 undistort = None if version == UV_TRACK_FORMAT_VERSION_2: undistort = kwargs.get('undistort') assert isinstance(undistort, bool) data = None if version == UV_TRACK_FORMAT_VERSION_2: data = UV_TRACK_HEADER_VERSION_2.copy() elif version == UV_TRACK_FORMAT_VERSION_3: data = UV_TRACK_HEADER_VERSION_3.copy() elif version == UV_TRACK_FORMAT_VERSION_4: data = UV_TRACK_HEADER_VERSION_4.copy() else: raise ValueError("Version number is invalid; %r" % version) cam_num_frames = tde4.getCameraNoFrames(camera) camera_fov = tde4.getCameraFOV(camera) if len(points) == 0: return '' frame0 = int(start_frame) frame0 -= 1 data['num_points'] = len(points) data['is_undistorted'] = None if version == UV_TRACK_FORMAT_VERSION_2: data['is_undistorted'] = bool(undistort) data['points'] = [] for point in points: point_data = {} # Query point information name = tde4.getPointName(point_group, point) uid = None if SUPPORT_PERSISTENT_ID is True: uid = tde4.getPointPersistentID(point_group, point) point_set = tde4.getPointSet(point_group, point) point_set_name = None if point_set is not None: point_set_name = tde4.getSetName(point_group, point_set) point_data['name'] = name point_data['id'] = uid point_data['set_name'] = point_set_name valid_mode = _get_point_valid_mode(point_group, point) # Get the 3D point position if version in [UV_TRACK_FORMAT_VERSION_3, UV_TRACK_FORMAT_VERSION_4]: point_data['3d'] = _get_3d_data_from_point(point_group, point) # Write per-frame position data frame = 1 # 3DE starts at frame '1' regardless of the 'start frame'. point_data['per_frame'] = [] pos_block = tde4.getPointPosition2DBlock(point_group, point, camera, 1, cam_num_frames) for pos in pos_block: if pos[0] == -1.0 or pos[1] == -1.0: # No valid data here. frame += 1 continue # Is the 2D point obscured? valid = tde4.isPointPos2DValid(point_group, point, camera, frame) if valid == 0: # No valid data here. frame += 1 continue # Check if we're inside the FOV / Frame or not. valid_pos = _is_valid_position(pos, camera_fov, valid_mode) if valid_pos is False: frame += 1 continue pos_undist = pos if undistort is True or undistort is None: pos_undist = tde4.removeDistortion2D(camera, frame, pos) weight = _get_point_weight(point_group, point, camera, frame) f = frame + frame0 frame_data = {'frame': f, 'pos': pos_undist, 'weight': weight} if version in [ UV_TRACK_FORMAT_VERSION_3, UV_TRACK_FORMAT_VERSION_4 ]: frame_data['pos_dist'] = pos point_data['per_frame'].append(frame_data) frame += 1 data['points'].append(point_data) if version == UV_TRACK_FORMAT_VERSION_4: lens = tde4.getCameraLens(camera) data['camera'] = _generate_camera_data(camera, lens, frame0) data_str = json.dumps(data) return data_str
def _generate_v1(point_group, camera, points, start_frame=None, undistort=False, rs_distance=None): """ Generate the UV file format contents, using a basic ASCII format. Each point will store: - Point name - X, Y position (in UV coordinates, per-frame) - Point weight (per-frame) :param point_group: The 3DE Point Group containing 'points' :type point_group: str :param camera: The 3DE Camera containing 2D 'points' data. :type camera: str :param points: The list of 3DE Points representing 2D data to save. :type points: list of str :param start_frame: The frame number to be considered at 'first frame'. Defaults to 1001 if set to None. :type start_frame: None or int :param undistort: Should we apply undistortion to the 2D points data? Yes or no. :type undistort: bool :param rs_distance: If not None, correct rolling shutter effects on the 2D points at the content distance rs_distance. :type rs_distance: None or float :returns: A ASCII format string, with the UV Track data in it. :rtype: str """ assert isinstance(point_group, TEXT_TYPE) assert isinstance(camera, TEXT_TYPE) assert isinstance(points, (list, tuple)) assert start_frame is None or isinstance(start_frame, int) assert isinstance(undistort, bool) assert rs_distance is None or isinstance(rs_distance, float) if start_frame is None: start_frame = 1001 data_str = '' cam_num_frames = tde4.getCameraNoFrames(camera) camera_fov = tde4.getCameraFOV(camera) if len(points) == 0: return data_str frame0 = int(start_frame) frame0 -= 1 data_str += '{0:d}\n'.format(len(points)) for point in points: name = tde4.getPointName(point_group, point) c2d = tde4.getPointPosition2DBlock( point_group, point, camera, 1, cam_num_frames ) valid_mode = _get_point_valid_mode(point_group, point) # Write per-frame position data num_valid_frame = 0 pos_list = [] weight_list = [] frame = 1 # 3DE starts at frame '1' regardless of the 'start-frame'. for v in c2d: if v[0] == -1.0 or v[1] == -1.0: # No valid data here. frame += 1 continue # Does the 2D point go outside the camera FOV? Is that ok? valid = tde4.isPointPos2DValid( point_group, point, camera, frame ) if valid == 0: # No valid data here. frame += 1 continue # Check if we're inside the FOV / Frame or not. valid_pos = _is_valid_position(v, camera_fov, valid_mode) if valid_pos is False: frame += 1 continue # Number of points with valid positions num_valid_frame += 1 f = frame + frame0 if rs_distance is not None: v = vl_sdv.vec2d(v[0], v[1]) v = _remove_rs_from_2d_point( point_group, camera, frame, v, rs_distance) if undistort is True: v = tde4.removeDistortion2D(camera, frame, v) weight = _get_point_weight(point_group, point, camera, frame) pos_list.append((f, v)) weight_list.append((f, weight)) frame += 1 # add data data_str += name + '\n' data_str += '{0:d}\n'.format(num_valid_frame) for pos_data, weight_data in zip(pos_list, weight_list): f = pos_data[0] v = pos_data[1] w = weight_data[1] assert f == weight_data[0] data_str += '%d %.15f %.15f %.8f\n' % (f, v[0], v[1], w) return data_str
def getDistortionBoundingBox(camera, lco_x, lco_y): # We genererate a number of samples around the image in normalized coordinates. # These samples are later unwarped, and the unwarped points # will be used to create a bounding box. In general, it is *not* sufficient to # undistort only the corners, because distortion might be moustache-shaped. # This is our list of samples: warped = [] for i in range(10): warped.append([i / 10.0,0.0]) warped.append([(i + 1) / 10.0,1.0]) warped.append([0.0,i / 10.0]) warped.append([1.0,(i + 1) / 10.0]) # The lens center is by definition the fixed point of the distortion mapping. elc = [0.5 + lco_x,0.5 + lco_y] # Image size w_px = tde4.getCameraImageWidth(camera) h_px = tde4.getCameraImageHeight(camera) # The bounding boxes for non-symmetrized and symmetrized cased. bb_nonsymm = bbdld_bounding_box() bb_symm = bbdld_bounding_box() # Run through the frames of this camera n_frames = tde4.getCameraNoFrames(camera) for i_frame in range(n_frames): # 3DE4 counts from 1. frame = i_frame + 1 # Now we undistort all edge points for the given # camera and frame and extend the bounding boxes. for p in warped: p_unwarped = tde4.removeDistortion2D(camera,frame,p) # Accumulate bounding boxes bb_nonsymm.extend(p_unwarped[0],p_unwarped[1]) bb_symm.extend_symm(p_unwarped[0],p_unwarped[1],elc[0],elc[1]) # Scale to pixel coordinates and extend to pixel-aligned values bb_nonsymm.scale(w_px,h_px) bb_nonsymm.extend_to_integer() # Image width and height for the non-symmetrized case w_nonsymm_px = bb_nonsymm.dx() h_nonsymm_px = bb_nonsymm.dy() # Lower left corner for the symmetrized case. This tells us # how the undistorted image is related to the distorted image. x_nonsymm_px = bb_nonsymm.x_min() y_nonsymm_px = bb_nonsymm.y_min() # Scale to pixel coordinates and extend to pixel-aligned values bb_symm.scale(w_px,h_px) bb_symm.extend_to_integer() # Image width and height for the symmetrized case w_symm_px = bb_symm.dx() h_symm_px = bb_symm.dy() # Lower left corner for the symmetrized case. This tells us # how the undistorted image is related to the distorted image. x_symm_px = bb_symm.x_min() y_symm_px = bb_symm.y_min() return x_symm_px,y_symm_px,w_symm_px,h_symm_px