Spaces:
Runtime error
Runtime error
| import numpy as np | |
| import cv2 | |
| def crop_and_scaled_imgs(imgs): | |
| PAD = 5 | |
| # use the last image to find the bounding box of the non-white area and the transformation parameters | |
| # and then apply the transformation to all images | |
| img = imgs[-1] | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Threshold the image to create a binary mask | |
| _, binary_mask = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV) | |
| # Find the coordinates of non-zero pixels | |
| coords = cv2.findNonZero(binary_mask) | |
| # Get the bounding box of the non-zero pixels | |
| x, y, w, h = cv2.boundingRect(coords) | |
| x = max(0, x-PAD) | |
| y = max(0, y-PAD) | |
| x_end = min(img.shape[1], x+w+2*PAD) | |
| y_end = min(img.shape[0], y+h+2*PAD) | |
| w = x_end - x | |
| h = y_end - y | |
| SIZE = 400 | |
| # Calculate the position to center the ROI in the SIZExSIZE image | |
| start_x = max(0, (SIZE - w) // 2) | |
| start_y = max(0, (SIZE - h) // 2) | |
| # Create a new SIZExSIZE rgb images | |
| new_imgs = [np.ones((SIZE, SIZE, 3), dtype=np.uint8) * 255 for _ in range(len(imgs))] | |
| for i in range(len(imgs)): | |
| # Extract the ROI (region of interest) of the non-white area | |
| roi = imgs[i][y:y+h, x:x+w] | |
| # If the ROI is larger than 256x256, resize it | |
| if w > SIZE or h > SIZE: | |
| scale = min(SIZE / w, SIZE / h) | |
| new_w = int(w * scale) | |
| new_h = int(h * scale) | |
| roi = cv2.resize(roi, (new_w, new_h), interpolation=cv2.INTER_AREA) | |
| else: | |
| new_w = w | |
| new_h = h | |
| # new_imgs[i] = np.ones((256, 256), dtype=np.uint8) * 255 | |
| # centered_img = np.ones((256, 256), dtype=np.uint8) * 255 | |
| # Place the ROI in the centered position | |
| new_imgs[i][start_y:start_y+new_h, start_x:start_x+new_w] = roi | |
| return new_imgs | |
| HALF_INF = 63 | |
| INF = 126 | |
| EPS_DIST = 1/20 | |
| EPS_ANGLE = 2.86 | |
| SCALE = 15 | |
| MOVE_SPEED = 25 | |
| ROTATE_SPEED = 30 | |
| FPS = 24 | |
| class Turtle: | |
| def __init__(self, canvas_size=(1000, 1000)): | |
| self.x = canvas_size[0] // 2 | |
| self.y = canvas_size[1] // 2 | |
| self.heading = 0 | |
| self.canvas = np.ones((canvas_size[1], canvas_size[0], 3), dtype=np.uint8) * 255 | |
| self.is_down = True | |
| self.time_since_last_frame = 0 | |
| self.frames = [self.canvas.copy()] | |
| def forward(self, dist): | |
| # print('st', self.x, self.y) | |
| # self.forward_step(dist * SCALE) | |
| # print('ed', self.x, self.y) | |
| # return | |
| dist = dist * SCALE | |
| sign = 1 if dist > 0 else -1 | |
| abs_dist = abs(dist) | |
| if self.time_since_last_frame + abs_dist / MOVE_SPEED >= 1: | |
| dist1 = (1 - self.time_since_last_frame) * MOVE_SPEED | |
| self.forward_step(dist1 * sign) | |
| self.save_frame_with_turtle() | |
| self.time_since_last_frame = 0 | |
| # for loop to step forward | |
| num_steps = int((abs_dist - dist1) / MOVE_SPEED) | |
| for _ in range(num_steps): | |
| self.forward_step(MOVE_SPEED * sign) | |
| self.save_frame_with_turtle() | |
| last_abs_dist = abs_dist - dist1 - num_steps * MOVE_SPEED | |
| if last_abs_dist >= MOVE_SPEED: | |
| self.forward_step(MOVE_SPEED * sign) | |
| self.save_frame_with_turtle() | |
| last_abs_dist -= MOVE_SPEED | |
| self.forward_step(last_abs_dist * sign) | |
| self.time_since_last_frame = last_abs_dist / MOVE_SPEED | |
| else: | |
| self.forward_step(abs_dist * sign) | |
| # self.time_since_last_frame += abs_dist / MOVE_SPEED | |
| # if self.time_since_last_frame >= 1: | |
| # self.time_since_last_frame = 0 | |
| def forward_step(self, dist): | |
| # print('step', dist) | |
| if dist == 0: | |
| return | |
| x0, y0 = self.x, self.y | |
| x1 = (x0 + dist * np.cos(self.heading)) | |
| y1 = (y0 - dist * np.sin(self.heading)) | |
| if self.is_down: | |
| cv2.line(self.canvas, (int(np.rint(x0)), int(np.rint(y0))), (int(np.rint(x1)), int(np.rint(y1))), (0, 0, 0), 3) | |
| self.x, self.y = x1, y1 | |
| self.time_since_last_frame += abs(dist) / MOVE_SPEED | |
| # self.frames.append(self.canvas.copy()) | |
| # self.save_frame_with_turtle() | |
| # print(self.x, self.y) | |
| def save_frame_with_turtle(self): | |
| # save the current frame to frames buffer | |
| # also plot a red triangle to represent the turtle pointing to the current direction | |
| # draw the turtle | |
| x, y = self.x, self.y | |
| canvas_copy = self.canvas.copy() | |
| triangle_size = 10 | |
| x0 = int(np.rint(x + triangle_size * np.cos(self.heading))) | |
| y0 = int(np.rint(y - triangle_size * np.sin(self.heading))) | |
| x1 = int(np.rint(x + triangle_size * np.cos(self.heading + 2 * np.pi / 3))) | |
| y1 = int(np.rint(y - triangle_size * np.sin(self.heading + 2 * np.pi / 3))) | |
| x2 = int(np.rint(x + triangle_size * np.cos(self.heading - 2 * np.pi / 3))) | |
| y2 = int(np.rint(y - triangle_size * np.sin(self.heading - 2 * np.pi / 3))) | |
| x3 = int(np.rint(x - 0.25 * triangle_size * np.cos(self.heading))) | |
| y3 = int(np.rint(y + 0.25 * triangle_size * np.sin(self.heading))) | |
| # fill the triangle | |
| cv2.fillPoly(canvas_copy, [np.array([(x0, y0), (x1, y1), (x3, y3), (x2, y2)], dtype=np.int32)], (0, 0, 255)) | |
| self.frames.append(canvas_copy) | |
| def left(self, angle): | |
| # print('angel', angle) | |
| # print('ast', self.heading) | |
| # self.heading += angle * np.pi / 180 | |
| self.turn_to(angle) | |
| # print('aed', self.heading) | |
| def right(self, angle): | |
| # print('angel', angle) | |
| # print('ast', self.heading) | |
| # self.heading -= angle * np.pi / 180 | |
| self.turn_to(-angle) | |
| # print('aed', self.heading) | |
| def turn_to(self, angle): | |
| abs_angle = abs(angle) | |
| sign = 1 if angle > 0 else -1 | |
| if self.time_since_last_frame + abs(angle) / ROTATE_SPEED > 1: | |
| angle1 = (1 - self.time_since_last_frame) * ROTATE_SPEED | |
| self.turn_to_step(angle1 * sign) | |
| self.save_frame_with_turtle() | |
| self.time_since_last_frame = 0 | |
| num_steps = int((abs_angle - angle1) / ROTATE_SPEED) | |
| for _ in range(num_steps): | |
| self.turn_to_step(ROTATE_SPEED * sign) | |
| self.save_frame_with_turtle() | |
| last_abs_angle = abs_angle - angle1 - num_steps * ROTATE_SPEED | |
| if last_abs_angle >= ROTATE_SPEED: | |
| self.turn_to_step(ROTATE_SPEED * sign) | |
| self.save_frame_with_turtle() | |
| last_abs_angle -= ROTATE_SPEED | |
| self.turn_to_step(last_abs_angle * sign) | |
| self.time_since_last_frame = last_abs_angle / ROTATE_SPEED | |
| else: | |
| self.turn_to_step(abs_angle * sign) | |
| # self.time_since_last_frame += abs_angle / ROTATE_SPEED | |
| def turn_to_step(self, angle): | |
| # print('turn step', angle) | |
| self.heading += angle * np.pi / 180 | |
| self.time_since_last_frame += abs(angle) / ROTATE_SPEED | |
| def penup(self): | |
| self.is_down = False | |
| def pendown(self): | |
| self.is_down = True | |
| def save(self, path): | |
| if path: | |
| cv2.imwrite(path, self.canvas) | |
| return self.canvas | |
| def save_gif(self, path): | |
| import imageio.v3 as iio | |
| frames_rgb = [cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for frame in self.frames] | |
| print(f'number of frames: {len(frames_rgb)}') | |
| last = frames_rgb[-1] | |
| if len(frames_rgb)>=1000: | |
| skip_ratio = len(frames_rgb)//1000 | |
| frames_rgb = frames_rgb[0::skip_ratio] | |
| frames_rgb.extend(FPS*2 * [last]) | |
| print(f'number of frames: {len(frames_rgb)}') | |
| frames_rgb = crop_and_scaled_imgs(frames_rgb) | |
| # iio.imwrite(path, np.stack(frames_rgb), fps=30, plugin='pillow') | |
| return iio.imwrite('<bytes>', np.stack(frames_rgb), fps=FPS, loop=0, plugin='pillow', format='gif') | |
| class _TurtleState: | |
| def __init__(self, turtle): | |
| self.turtle = turtle | |
| self.position = None | |
| self.heading = None | |
| self.pen_status = None | |
| def __enter__(self): | |
| self.position = (self.turtle.x, self.turtle.y) | |
| self.heading = self.turtle.heading | |
| self.pen_status = self.turtle.is_down | |
| return self | |
| def __exit__(self, exc_type, exc_val, exc_tb): | |
| self.turtle.penup() | |
| self.turtle.x, self.turtle.y = self.position | |
| self.turtle.heading = self.heading | |
| if self.pen_status: | |
| self.turtle.pendown() | |
| if __name__ == "__main__": | |
| turtle = Turtle() | |
| def forward(dist): | |
| turtle.forward(dist) | |
| def left(angle): | |
| turtle.left(angle) | |
| def right(angle): | |
| turtle.right(angle) | |
| def penup(): | |
| turtle.penup() | |
| def pendown(): | |
| turtle.pendown() | |
| def save(path): | |
| turtle.save(path) | |
| def fork_state(): | |
| """ | |
| Clone the current state of the turtle. | |
| Usage: | |
| with clone_state(): | |
| forward(100) | |
| left(90) | |
| forward(100) | |
| """ | |
| return turtle._TurtleState(turtle) | |
| # Example usage | |
| def example_plot(): | |
| forward(5) | |
| with fork_state(): | |
| forward(10) | |
| left(90) | |
| forward(10) | |
| with fork_state(): | |
| right(90) | |
| forward(20) | |
| left(90) | |
| forward(10) | |
| left(90) | |
| forward(10) | |
| right(90) | |
| forward(50) | |
| save("test2.png") | |
| return turtle.frames | |
| def plot2(): | |
| for j in range(2): | |
| forward(2) | |
| left(0.0) | |
| for i in range(4): | |
| forward(2) | |
| left(90) | |
| forward(0) | |
| left(180.0) | |
| forward(2) | |
| left(180.0) | |
| FINAL_IMAGE = turtle.save("") | |
| def plot3(): | |
| frames = [] | |
| frames.append(np.array(turtle.save(""))) | |
| for j in range(2): | |
| forward(2) | |
| frames.append(np.array(turtle.save(""))) | |
| left(0.0) | |
| for i in range(4): | |
| forward(2) | |
| left(90) | |
| frames.append(np.array(turtle.save(""))) | |
| forward(0) | |
| left(180.0) | |
| forward(2) | |
| left(180.0) | |
| frames.append(np.array(turtle.save(""))) | |
| return frames | |
| def make_gif(frames, filename): | |
| import imageio | |
| frames_rgb = [cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for frame in frames] | |
| imageio.mimsave(filename, frames_rgb, fps=30) | |
| def make_gif2(frames, filename): | |
| import imageio.v3 as iio | |
| frames_rgb = [cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for frame in frames] | |
| print(f'number of frames: {len(frames_rgb)}') | |
| iio.imwrite(filename, np.stack(frames_rgb), fps=30, plugin='pillow') | |
| def make_gif3(frames, filename): | |
| from moviepy.editor import ImageSequenceClip | |
| clip = ImageSequenceClip(list(frames), fps=20) | |
| clip.write_gif(filename, fps=20) | |
| def make_gif4(frames, filename): | |
| from array2gif import write_gif | |
| write_gif(frames, filename, fps=20) | |
| def make_gif5(frames, filename): | |
| from PIL import Image | |
| frames_rgb = [cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for frame in frames] | |
| images = [Image.fromarray(frame) for frame in frames_rgb] | |
| images[0].save(filename, save_all=True, append_images=images[1:], duration=100, loop=0) | |
| def plot4(): | |
| # the following program draws a treelike pattern | |
| import random | |
| def draw_tree(level, length, angle): | |
| if level == 0: | |
| return | |
| else: | |
| forward(length) | |
| left(angle) | |
| draw_tree(level-1, length*0.7, angle*0.8) | |
| right(angle*2) | |
| draw_tree(level-1, length*0.7, angle*0.8) | |
| left(angle) | |
| forward(-length) | |
| random.seed(0) # Comment this line to change the randomness | |
| for _ in range(7): # Adjust the number to control the density | |
| draw_tree(5, 5, 30) | |
| forward(0) | |
| left(random.randint(0, 360)) | |
| turtle.save("test3.png") | |
| return turtle.frames | |
| def plot5(): | |
| for i in range(7): | |
| with fork_state(): | |
| for j in range(4): | |
| forward(3*i) | |
| left(90.0) | |
| return turtle.frames | |
| # make_gif2(plot5(), "test.gif") | |
| frames = plot5() | |
| # frames = [cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) for frame in frames] | |
| # breakpoint() | |
| # from moviepy.editor import ImageClip, concatenate_videoclips | |
| # clips = [ImageClip(frame).set_duration(1/24) for frame in frames] | |
| # concat_clip = concatenate_videoclips(clips, method="compose") | |
| # concat_clip.write_videofile("test.mp4", fps=24) | |
| img_bytes_string = turtle.save_gif("") | |
| # turtle.save('test3.png') | |
| with open("test5.gif", "wb") as f: | |
| f.write(img_bytes_string) | |
| # example_plot() | |
| # plot2() |