def main(): host = '' port = 7777 server = None board = PyMata3(5) shoulder = Actuator(board, 9) arm = Actuator(board, 10) elbow = Actuator(board, 11, min_angle=-90, max_angle=90, offset=-90) global mech_arm mech_arm = MechanicalArm( [shoulder, arm, elbow], Fabrik( joint_positions=[Vector2(0, 0), Vector2(53, 0), Vector2(100, 0)], link_lengths=[53, 47], tolerance=0.1 ) ) sleep(2) while server is None: try: server = TCPServer((host, port), ConnectionHandler) except OSError: port += 1 continue print("Serving on: {}".format(port)) server.serve_forever() server.server_close()
def test_cross(self): vector2 = Vector2(5, 0) vector2_2 = Vector2(1, 0) crossResult = vector2.cross(vector2_2) self.assertEqual(crossResult[0], 0) self.assertEqual(crossResult[1], 0) self.assertEqual(crossResult[2], 0) with self.assertRaises(TypeError): dotResult2 = vector2.cross("Banana")
def __init__(self, mass: float, max_velocity: Optional[Vector2] = None) -> None: self.velocity = Vector2(0, 0) self.mass = mass self._max_velocity = max_velocity self._forces = Vector2(0, 0) self._acceleration = Vector2(0, 0)
def reposition(self, velocity: Vector2, dt: float) -> None: self.left += velocity.x * dt self.right = self.left + self.width self.top += velocity.y * dt self.bottom = self.top + self.height self.topleft = Vector2(self.left, self.top) self.topright = Vector2(self.left + self.width, self.top) self.bottomleft = Vector2(self.left, self.top + self.height) self.bottomright = Vector2(self.left + self.width, self.top + self.height) self.center = Vector2(self.left + self.width / 2, self.top + self.height / 2)
def _get_data(self, pose): image = skimage.io.imread(pose[self.IMAGE]) image = skimage.img_as_float(image) image = image.astype(np.float32) depth = skimage.io.imread(pose[self.DEPTH]) if depth.shape[-1] == 4: # has alpha channel depth = depth[:, :, 0] depth = skimage.img_as_float(depth) depth = depth.astype(np.float32) box_size = self.WIDTH resize_ratio = box_size / float(self.DEPTH_WIDTH) heatmaps = np.zeros(shape=(self.DEPTH_HEIGHT, self.DEPTH_WIDTH, pose[self.NUMBER_OF_JOINT]), dtype=np.float32) for idx in range(pose[self.NUMBER_OF_JOINT]): # num of joint keypoint = Vector2(pose[self.POSITION][idx]) # keypoint -= (center - box_size / 2) keypoint /= resize_ratio # space change: original image >> crop image if min(keypoint) < 0 or max(keypoint) >= 64: continue heatmaps[:, :, idx] = generate_heatmap(64, keypoint.y, keypoint.x) return image, depth, heatmaps
def rotate(vec, theta): '''rotate vector clockwise by theta radians''' sintheta = sin(theta) costheta = cos(theta) return Vector2( vec.x * costheta - vec.y * sintheta, vec.x * sintheta + vec.y * costheta, )
def test_view_types(self): v1 = Vector2Array(np.random.rand(100, 2)) self.assertTrue(isinstance(v1, Vector2Array)) self.assertTrue(isinstance(v1[1:2], Vector2Array)) self.assertTrue(isinstance(v1[1:50:2], Vector2Array)) self.assertTrue(isinstance(v1[4], Vector2)) self.assertTrue(isinstance(v1[4, :], np.ndarray)) self.assertTrue(isinstance(v1.x, np.ndarray)) self.assertTrue(isinstance(v1[1:30, :], np.ndarray)) a1 = np.array([1., 2., 3]) with self.assertRaises(ValueError): a1.view(Vector2) with self.assertRaises(ValueError): a1.view(Vector2Array) a1 = np.array([1., 2.]) self.assertTrue(isinstance(a1.view(Vector2), Vector2)) with self.assertRaises(ValueError): a1.view(Vector2Array) a1 = np.array([[1., 2.]]) with self.assertRaises(ValueError): a1.view(Vector2) self.assertTrue(isinstance(a1.view(Vector2Array), Vector2Array)) with self.assertRaises(ValueError): v1.view(Vector3Array) self.assertTrue(isinstance(v1.view(Vector2Array), Vector2Array)) with self.assertRaises(ValueError): v1.view(Vector3) with self.assertRaises(ValueError): v1.view(Vector2) v1 = Vector2([1., 2.]) with self.assertRaises(ValueError): v1.view(Vector3Array) with self.assertRaises(ValueError): v1.view(Vector2Array) with self.assertRaises(ValueError): v1.view(Vector3) self.assertTrue(isinstance(v1.view(Vector2), Vector2)) v1 = np.kron(Vector2([1., 0.]), np.atleast_2d(np.ones(10)).T) self.assertFalse(isinstance(v1, Vector2))
def calculate_remaining(self): """Calculate the remaining points and vectors based on point0, point1, target_a and target_b.""" # target_a and target_b first need to be calculated before this function can be executed if self.target_a is None or self.target_b is None: raise ValueError( "target_a and/or target_b are undefined. Use .set_targets() first." ) # Vectors from p1 to ta and tb p1_tb_vector = Vector2(self.target_b[0] - self.point1[0], self.target_b[1] - self.point1[1]) p1_ta_vector = Vector2(self.target_a[0] - self.point1[0], self.target_a[1] - self.point1[1]) # Determine the vector from p1 to p2 p1_ta_angle = p1_tb_vector.angle( p1_ta_vector ) # Calculate the angle between the path to tb and the path to ta p1_ta_proj = p1_ta_vector.length * np.cos( p1_ta_angle) # Calculate at which height ta lies on the path to tb p1_p2_angle = p1_ta_angle * 1.3 # Scale the angle to p2 up so you do not hit ta self.p1_to_p2 = Vector2( p1_ta_proj / np.cos(p1_p2_angle), # Calculate length of path from p1 to p2 p1_tb_vector.theta + p1_p2_angle, # Calculate angle of global x axis and path from p1 to p2 polar=True) # Submit as polar coordinates self.point2 = self.p1_to_p2 + self.point1 # Store the coordinates of point2 self.p1_to_p2_path = np.array( [self.point1, self.p1_to_p2 + self.point1]) # Store the path from p1 to p2 # Determine the vector from p2 to tb and scale it so that the robot stops before hitting target_b p2_tb_vector = Vector2(self.target_b[0] - self.point2[0], self.target_b[1] - self.point2[1]) self.p2_to_p3 = p2_tb_vector.as_length(p2_tb_vector.length - self.DISTANCE_FROM_TARGET_B) self.point3 = self.p2_to_p3 + self.point2 # Store the coordinates of p3 self.p2_to_p3_path = np.array( [self.point2, self.p2_to_p3 + self.point2]) # Store the path from p2 to p3
def __init__( self, settings, svg_path, gcode_path, plot_from_origin, x_offset_mm, y_offset_mm, x_size_mm, y_size_mm, rotate, ): # Check File Validity if not os.path.isfile(svg_path): raise ValueError('File \''+svg_path+'\' not found.') if not svg_path.endswith('.svg'): raise ValueError('File \''+svg_path+'\' is not an SVG file.') self.settings = settings self.svg_path = svg_path self.gcode_path = gcode_path self.gcode_file = GCodeFile(self.gcode_path, self.settings) # Get the svg Input File input_file = open(self.svg_path, 'r') self.svg_root = ET.parse(input_file).getroot() input_file.close() self.rotate_rads = rotate * pi / 180 self.svg_bounding_box = self.get_svg_bounding_box() bed_area_mm = Vector2(self.settings.bed_area_mm) self.plot_bed_mm = Rect(Vector2(), bed_area_mm) self.plot_size_mm = Vector2(x_size_mm or bed_area_mm.x, y_size_mm or bed_area_mm.y) self.offset_mm = Vector2(x_offset_mm, y_offset_mm) self.plot_from_origin = plot_from_origin self.scale, self.offset = self.get_transform()
def __init__(self, left: float, top: float, width: float, height: float) -> None: if width > 0 and height > 0: self.width = width self.height = height self.center = Vector2(left + width / 2, top + height / 2) self.topleft = Vector2(left, top) self.topright = Vector2(left + width, top) self.bottomleft = Vector2(left, top + height) self.bottomright = Vector2(left + width, top + height) self.left = left self.right = left + width self.top = top self.bottom = top + height else: raise ValueError( "`width` and `height` must be greater than 0, got ({}, {})". format(width, height))
def wander(self, distance, radius): '''wander''' center_circle = self.velocity.normalize center_circle = center_circle.scalarmult(distance) displacement = Vector2((0, 1)) dis = displacement.scalarmult(radius) wander_angle = 0 wander_angle = wander_angle + (random.randrange(0, 10) * 1) - (1 * .5) dis.x = math.cos(wander_angle) * dis.magnitude dis.y = math.sin(wander_angle) * dis.magnitude self.wanderforce = center_circle + dis return self.wanderforce
def move(self, x: float, y: float, z: float): y *= -1 # reversed value due to contruction old_joints_pos = [*self.ik.joints] self.ik.move(Vector2(math.sqrt(x**2 + y**2), z)) angles = [math.degrees(math.atan2(y, x)), *self.ik.angles_deg] angles[1] = 180 - angles[1] # reversed rotation due to contruction if all(self._set_angles(angles, dry_run=True)): self._set_angles(angles, dry_run=False) else: self.ik.joints = old_joints_pos print('base: {} \t arm: {} \t elbow: {}'.format(*angles))
def get_coordinates(self): if self.angle_in_p0 is None and self.angle_in_p1 is None: raise ValueError( "One or two angles are undefined. Use .set_angle() first.") alpha0 = self.angle_in_p0 alpha1 = np.pi - self.angle_in_p1 path_vector = Vector2(self.point1 - self.point0) # Determine path length using the law of sines target_vector_length = path_vector.length * np.sin(alpha1) / np.sin( np.pi - alpha0 - alpha1) # Create a vector using the length (rho) and the angle w.r.t. the x axis (theta) target_vector = Vector2(target_vector_length, path_vector.theta + alpha0, polar=True) self.coordinates = np.array([ target_vector.x + self.point0[0], target_vector.y + self.point0[1] ]) return self.coordinates
def crop_image(image_path, center, scale, rotate, resolution=256): image = Image.open(image_path) width, height = image.size center = Vector2(center) # assign new array crop_ratio = 200 * scale / resolution if crop_ratio >= 2: # if box size is greater than two time of resolution px # scale down image height = math.floor(height / crop_ratio) width = math.floor(width / crop_ratio) if max([height, width]) < 2: # Zoomed out so much that the image is now a single pixel or less raise ValueError("Width or height is invalid!") image = image.resize((width, height), Image.BILINEAR) center /= crop_ratio scale /= crop_ratio ul = (center - 200 * scale / 2).astype(int) br = (center + 200 * scale / 2).astype(int) # Vector2 if crop_ratio >= 2: # force image size 256 x 256 br -= (br - ul - resolution) pad_length = math.ceil(((ul - br).length - (br.x - ul.x)) / 2) if rotate != 0: ul -= pad_length br += pad_length crop_src = [max(0, ul.x), max(0, ul.y), min(width, br.x), min(height, br.y)] crop_dst = [max(0, -ul.x), max(0, -ul.y), min(width, br.x) - ul.x, min(height, br.y) - ul.y] crop_image = image.crop(crop_src) new_image = Image.new("RGB", (br.x - ul.x, br.y - ul.y)) new_image.paste(crop_image, box=crop_dst) if rotate != 0: new_image = new_image.rotate(rotate, resample=Image.BILINEAR) new_image = new_image.crop(box=(pad_length, pad_length, new_image.width - pad_length, new_image.height - pad_length)) if crop_ratio < 2: new_image = new_image.resize((resolution, resolution), Image.BILINEAR) return new_image
def part2(target): velocities = [] for x in range(1, int(target.lower_right.x) + 1): for y in range(int(target.lower_right.y), abs(int(target.lower_right.y))): try: vel = Vector2(x, y) calculate_highest(vel.copy(), target) velocities.append(vel) except TooFar: pass except TooShort: continue return len(velocities)
def part1(target): max = 0 for x in range(1, int(target.lower_right.x)): if ((x * (x + 1)) / 2) < target.upper_left.x: continue for y in range(1, abs(int(target.lower_right.y))): try: height = calculate_highest(Vector2(x, y), target) if height > max: max = height except TooFar: pass except TooShort: continue return max
def _get_elem_extents(self, elem): elem_extents = Rect.far_extents() tag_suffix = elem.tag.split('}')[-1] if tag_suffix not in SVG_TAGS: return elem_extents shape_class = getattr(shapes, tag_suffix) shape_obj = shape_class(elem) path = shape_obj.d_path() mtx = shape_obj.transformation_matrix() if path: points = shapes.point_generator(path, mtx, self.settings.smoothness) for point in points: elem_extents = elem_extents.expand_to(rotate(Vector2(point), self.rotate_rads)) return elem_extents
def crop_image(image, center, scale, rotate, resolution): center = Vector2(center) # assign new array height, width, channel = image.shape crop_ratio = 200 * scale / resolution if crop_ratio >= 2: # if box size is greater than two time of resolution px # scale down image height = math.floor(height / crop_ratio) width = math.floor(width / crop_ratio) if max([height, width]) < 2: # Zoomed out so much that the image is now a single pixel or less raise ValueError("Width or height is invalid!") # image = skimage.transform.resize(image, (height, width), mode='constant') image = image.resize(image, (height, width), mode='constant') center /= crop_ratio scale /= crop_ratio ul = (center - 200 * scale / 2).astype(int) br = (center + 200 * scale / 2).astype(int) # Vector2 if crop_ratio >= 2: # force image size 256 x 256 br -= (br - ul - resolution) pad_length = math.ceil((ul - br).length - (br.x - ul.x) / 2) if rotate != 0: ul -= pad_length br += pad_length src = [max(0, ul.y), min(height, br.y), max(0, ul.x), min(width, br.x)] dst = [max(0, -ul.y), min(height, br.y) - ul.y, max(0, -ul.x), min(width, br.x) - ul.x] new_image = np.zeros([br.y - ul.y, br.x - ul.x, channel], dtype=np.float32) new_image[dst[0]:dst[1], dst[2]:dst[3], :] = image[src[0]:src[1], src[2]:src[3], :] if rotate != 0: new_image = skimage.transform.rotate(new_image, rotate) new_height, new_width, _ = new_image.shape new_image = new_image[pad_length:new_height - pad_length, pad_length:new_width - pad_length, :] if crop_ratio < 2: # new_image = skimage.transform.resize(new_image, (resolution, resolution), mode='constant') new_image = image.resize(new_image, (resolution, resolution), mode='constant') return new_image
def __getitem__(self, index): data = self.test_data[index] center = Vector2(data['center'][0], data['center'][1]) scale = data['scale'] * 1.25 rotate = 0 img_name = data['img_name'] img_idx = data['img_idx'] r_idx = data['r_idx'] image_path = '{data_dir}/images/{image_name}'.format( data_dir=config.hourglass.data_dir, image_name=img_name) image = crop_image(image_path, center, scale, rotate) return self.to_tensor(image), np.asarray( [center.x, center.y], dtype=np.float32), scale, img_idx, r_idx
def calculate_highest(vel, target): pos = Vector2(0, 0) heights = [] while pos.x <= target.lower_right.x and pos.y >= target.lower_right.y: pos += vel heights.append(pos.y) vel.x = max(vel.x - 1, 0) vel.y -= 1 if target.upper_left.x <= pos.x <= target.lower_right.x and target.lower_right.y <= pos.y <= target.upper_left.y: return max(heights) if vel.x == 0 and pos.x < target.upper_left.x: raise TooShort if pos.x > target.lower_right.x: raise TooFar if pos.x < target.upper_left.x: raise TooShort if pos.y < target.lower_right.y: raise TooFar
def apply_acceleration(self, dt: float) -> None: self.velocity += self._acceleration * dt self._forces = Vector2( 0, 0 ) # Clear the forces acting upon this object for next frame/whatever # Keeping velocity in bounds if self._max_velocity is not None: if abs(self.velocity.x) > self._max_velocity.x: if self.velocity.x > 0: self.velocity.x = self._max_velocity.x elif self.velocity.x < 0: self.velocity.x = -self._max_velocity.x if abs(self.velocity.y) > self._max_velocity.y: if self.velocity.y > 0: self.velocity.y = self._max_velocity.y elif self.velocity.y < 0: self.velocity.y = -self._max_velocity.y
def path_to_gcode(self, path, mtx): '''Convert a single svg path to a gcode shape''' result = '' new_shape = True points = shapes.point_generator(path, mtx, self.settings.smoothness) for point in points: plot_point = self.scale * rotate(Vector2(point), self.rotate_rads) + self.offset if self.plot_bed_mm >= plot_point: # true if the plot point is within the plot bed result += f'G01 X{plot_point.x} Y{plot_point.y}\n' if new_shape: # move to position, put the pen down result += f'{self.settings.TOOL_ON_CMD}\n' new_shape = False else: print(f'\t--POINT OUT OF RANGE: {plot_point}') import ipdb as pdb; pdb.set_trace() sys.exit(1) return result
def test_2d_correctly_moves_the_joints(): poss = [Vector2(0, 0), Vector2(10, 0), Vector2(20, 0)] fab = Fabrik2D(poss, 0.01) assert fab.move_to(Vector2(20, 0)) == 0 assert fab.angles_deg == [0.0, 0.0] print(fab.angles_deg) assert fab.move_to(Vector2(60, 60)) == 249 assert fab.angles_deg == [43.187653094161064, 3.622882738369357] print(fab.angles_deg) assert fab.move_to(Vector2(0, 20)) == 250 assert fab.angles_deg == [88.19119752090381, 3.6158044811401675] print(fab.angles_deg) assert fab.move_to(Vector2(0, 10)) == 5 assert fab.angles_deg == [30.05682734132901, 119.97158632933548] print(fab.angles_deg)
def __init__(self, point0, point1, target_a=None, target_b=None): """ Initialize the trajectory. The trajectory needs to receive its first two points. It is optional to provide the target points, they can also be added later. The trajectory will calculate the remaining points on the trajectory when all data is provided. All coordinates are considered in CM :param point0: Origin of the robot. This point is where the first measurement of target_a and target_b locations is taken. :param point1: First destination point. This point is where the second measurement of target_a and target_b locations is taken. :param target_a: (optional) Coordinates of target_a (not the destination target). :param target_b: (optional) Coordinates of target_b (destination target). """ self.point0 = point0 self.point1 = point1 self.target_a = target_a self.target_b = target_b # Next destination points self.point2 = None self.point3 = None # Vectors to destinations (these vectors assume <current point>_<next point>) self.p0_to_p1 = Vector2(self.point1[0] - self.point0[0], self.point1[1] - self.point0[1]) self.p1_to_p2 = None self.p2_to_p3 = None # Vector paths (2D array containing the starting point and the destination point of each path segment) self.p0_to_p1_path = np.array( [self.point0, self.p0_to_p1 + self.point0]) self.p1_to_p2_path = None self.p2_to_p3_path = None
def __init__(self): self.position = Vector2((0, 300)) self.velocity = Vector2((300, 0)) self.maxvelocity = 250.0 self.mass = 1 self.force = Vector2((0, 0)) self.heading = self.velocity.normalize self.direction = Vector2((0, 0)) self.acceleration = Vector2((0, 0)) self.surface = pygame.Surface((100, 50), pygame.SRCALPHA) self.surface.fill((0, 0, 0)) points = [(0, 0), (75, 25), (0, 50), (0, 0)] pygame.draw.lines(self.surface, constants.RED, False, points, 1) self.Seek = False self.Flee = False self.Wander = False self.targetagent = None self.radius = 100 self.distance = 5 self.wanderforce = Vector2((0, 0))
def test_polar(self): # polar <-> cartesian conversions cases = [ # ((rho, theta), (x, y)) ((1, 0), (1, 0)), ((1, np.pi), (-1, 0)), ((2, -np.pi / 2), (0, -2)), ((1, np.pi * 3 / 4), (-1 / np.sqrt(2), 1 / np.sqrt(2))), ((3, np.pi / 4), (3 / np.sqrt(2), 3 / np.sqrt(2))), ] for polar, cartesian in cases: rho, theta = polar x, y = cartesian v = Vector2(rho, theta, polar=True) self.assertAlmostEqual(v.x, x) self.assertAlmostEqual(v.y, y) v = Vector2(x, y) self.assertAlmostEqual(v.rho, rho) self.assertAlmostEqual(v.theta, theta) # degrees -> radians cases = [ # (degrees, radians) (0, 0), (90, np.pi / 2), (-90, -np.pi / 2), (45, np.pi / 4), (180, np.pi), ] for deg, rad in cases: v = Vector2(1, deg, polar=True, unit='deg') self.assertAlmostEqual(v.theta, rad) self.assertAlmostEqual(v.theta_deg, deg) # faulty input with self.assertRaises(ValueError): Vector2(1, np.pi, polar=True, unit='invalid_unit') with self.assertRaises(ValueError): v = Vector2(1, np.pi, polar=True) # copying doesn't support polar=True Vector2(v, polar=True)
def test_angle(self): # test a unit vector along each coordinate v1 = Vector2(1, 0) # x-coordinate, use this as datum v = [Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1)] angles_deg = [0, 90, 180, 90] angles_rad = [0, np.pi / 2, np.pi, np.pi / 2] for k in range(4): a_deg = v1.angle(v[k], unit='deg') a_rad0 = v1.angle(v[k], unit='rad') a_rad1 = v1.angle(v[k]) self.assertEqual(a_deg, angles_deg[k]) self.assertEqual(a_rad0, angles_rad[k]) self.assertEqual(a_rad1, angles_rad[k]) # verify the associative property self.assertEqual(v1.angle(v[k]), v[k].angle(v1)) with self.assertRaises(TypeError): angleResult = v1.angle('anything but Vector2') with self.assertRaises(ValueError): angleResult = v1.angle(v[0], unit='invalid entry') with self.assertRaises(ZeroDivisionError): angleResult = v1.angle(Vector2(0, 0))
def vector_to(self, other): if other == self: return None opp = other.y - self.y adj = other.x - self.x return Vector2(adj, opp)
from colorama import Fore from numpy import pi from vectormath import Vector2 from ibidem.advent_of_code.board import Board from ibidem.advent_of_code.util import get_input_name COLORS = [ a for a in dir(Fore) if a.isupper() and a not in ("RESET", "BLACK", "LIGHTWHITE_EX", "WHITE") ] STYLES = [(getattr(Fore, color), char) for char in "#@¤+*%$<>XOABCDEFGHIJKLMNPRSTUVWYZ" for color in COLORS] SENTINEL = object() UP = Vector2(0, -1) class Asteroid(object): def __init__(self, x, y, color=Fore.LIGHTWHITE_EX, char="#"): self.x = x self.y = y self.color = color self.char = char self.lines_of_sight = defaultdict(list) def vector_to(self, other): if other == self: return None opp = other.y - self.y adj = other.x - self.x
def _get_augment_image(image_path, center, scale, angle=0, hflip=False, transform=None, target_res=256): image = Image.open(image_path) width, height = image.size center = Vector2(center) # assign new array crop_ratio = 200 * scale / target_res if crop_ratio >= 2: # if box size is greater than two time of resolution px # scale down image height = math.floor(height / crop_ratio) width = math.floor(width / crop_ratio) if max([height, width]) < 2: # Zoomed out so much that the image is now a single pixel or less raise ValueError("Width or height is invalid!") image = image.resize((width, height), Image.BILINEAR) center /= crop_ratio scale /= crop_ratio ul = (center - 200 * scale / 2).astype(int) br = (center + 200 * scale / 2).astype(int) # Vector2 if crop_ratio >= 2: # force image size 256 x 256 br -= (br - ul - target_res) pad_length = math.ceil(((ul - br).length - (br.x - ul.x)) / 2) if angle != 0: ul -= pad_length br += pad_length crop_src = [ max(0, ul.x), max(0, ul.y), min(width, br.x), min(height, br.y) ] crop_dst = [ max(0, -ul.x), max(0, -ul.y), min(width, br.x) - ul.x, min(height, br.y) - ul.y ] crop_image = image.crop(crop_src) new_image = Image.new("RGB", (br.x - ul.x, br.y - ul.y)) new_image.paste(crop_image, box=crop_dst) if hflip: new_image = new_image.transpose(Image.FLIP_LEFT_RIGHT) if angle != 0: new_image = new_image.rotate(angle, resample=Image.BILINEAR) new_image = new_image.crop(box=(pad_length, pad_length, new_image.width - pad_length, new_image.height - pad_length)) if crop_ratio < 2: new_image = new_image.resize((target_res, target_res), Image.BILINEAR) if transform is not None: new_image = transform(new_image) return new_image