Spaces:
Sleeping
Sleeping
File size: 6,102 Bytes
c77c587 573c3f1 70542da 47201a2 573c3f1 47201a2 d46ecd3 1c3330f c371552 573c3f1 cad3317 573c3f1 c77c587 573c3f1 47201a2 573c3f1 cbcb276 14400f2 c77c587 c371552 cbcb276 573c3f1 c77c587 c8a65fb 36a53ae 7d6a2fc c371552 c8a65fb c371552 c77c587 c371552 c77c587 1c3330f c77c587 bdd2388 87d3bd4 c371552 bacbeef c371552 bacbeef 1663e03 c371552 2726595 c371552 1663e03 768e0e2 14400f2 1663e03 7d6a2fc bacbeef c371552 2726595 b2f0be2 2726595 b32455d 2726595 14400f2 2726595 14400f2 113d1af 2726595 038be2c 2726595 c67f2ef 46a514f 2726595 c371552 14400f2 c371552 c77c587 c371552 46b8412 b9d04ad |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from PIL import Image
import cv2
from math import tau
import gradio as gr
from concurrent.futures import ThreadPoolExecutor
import tempfile
def process_image(input_image, img_size, blur_kernel_size, desired_range):
img = cv2.cvtColor(np.array(input_image), cv2.COLOR_RGB2BGR)
img = cv2.resize(img, (img_size, img_size), interpolation=cv2.INTER_AREA)
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(imgray, (blur_kernel_size, blur_kernel_size), 0)
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
largest_contour_idx = np.argmax([cv2.contourArea(c) for c in contours])
largest_contour = contours[largest_contour_idx]
verts = [tuple(coord) for coord in largest_contour.squeeze()]
xs, ys = np.asarray(list(zip(*verts)))
x_range, y_range = np.max(xs) - np.min(xs), np.max(ys) - np.min(ys)
scale_x, scale_y = desired_range / x_range, desired_range / y_range
xs = (xs - np.mean(xs)) * scale_x
ys = -(ys - np.mean(ys)) * scale_y
return xs, ys
def compute_cn(f_exp, n, t_values):
coef = np.trapz(f_exp * np.exp(-n * t_values * 1j), t_values) / tau
return coef
def calculate_fourier_coefficients(xs, ys, num_points, coefficients):
t_list = np.linspace(0, tau, len(xs))
t_values = np.linspace(0, tau, num_points)
f_precomputed = np.interp(t_values, t_list, xs + 1j * ys)
N = coefficients
indices = [0] + [j for i in range(1, N + 1) for j in (i, -i)]
with ThreadPoolExecutor(max_workers=2) as executor:
coefs = list(executor.map(lambda n: (compute_cn(f_precomputed, n, t_values), n), indices))
return coefs
def setup_animation_env(img_size, desired_range, coefficients):
fig, ax = plt.subplots()
circles = [ax.plot([], [], 'b-')[0] for _ in range(-coefficients, coefficients + 1)]
circle_lines = [ax.plot([], [], 'g-')[0] for _ in range(-coefficients, coefficients + 1)]
drawing, = ax.plot([], [], 'r-', linewidth=2)
ax.set_xlim(-desired_range, desired_range)
ax.set_ylim(-desired_range, desired_range)
ax.set_axis_off()
ax.set_aspect('equal')
fig.set_size_inches(15, 15)
fig.canvas.draw()
background = fig.canvas.copy_from_bbox(ax.bbox)
return fig, ax, background, circles, circle_lines, drawing
def animate(frame, coefs, frame_times, fig, ax, background, circles, circle_lines, drawing, draw_x, draw_y, coefs_static, theta):
fig.canvas.restore_region(background)
center = (0, 0)
for idx, (r, fr) in enumerate(coefs_static):
c_dynamic = coefs[idx][0] * np.exp(1j * (fr * tau * frame_times[frame]))
x, y = center[0] + r * np.cos(theta[frame]), center[1] + r * np.sin(theta[frame])
circle_lines[idx].set_data([center[0], center[0] + np.real(c_dynamic)], [center[1], center[1] + np.imag(c_dynamic)])
circles[idx].set_data([x], [y])
center = (center[0] + np.real(c_dynamic), center[1] + np.imag(c_dynamic))
draw_x.append(center[0])
draw_y.append(center[1])
drawing.set_data(draw_x, draw_y)
for circle in circles:
ax.draw_artist(circle)
for line in circle_lines:
ax.draw_artist(line)
ax.draw_artist(drawing)
fig.canvas.blit(ax.bbox)
# Convert canvas to PIL Image using buffer_rgba
#fig.canvas.draw()
pil_image = Image.frombuffer("RGBA", fig.canvas.get_width_height(), fig.canvas.buffer_rgba(), "raw", "RGBA", 0, 1)
return (pil_image, None)
def fourier_transform_drawing(input_image, frames, coefficients, img_size, blur_kernel_size, desired_range, num_points):
xs, ys = process_image(input_image, img_size, blur_kernel_size, desired_range)
coefs = calculate_fourier_coefficients(xs, ys, num_points, coefficients)
# Setup animation environment
fig, ax, background, circles, circle_lines, drawing = setup_animation_env(img_size, desired_range, coefficients)
coefs_static = [(np.linalg.norm(c), fr) for c, fr in coefs]
frame_times = np.linspace(0, 1, num=frames)
thetas = np.linspace(0, tau, num=frames)
draw_x, draw_y = [], []
# Create a temporary file for the video
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file:
video_path = temp_file.name
# Generate and save each frame as a PIL image, and ultimately the video
for frame in range(frames):
pil_image, _ = animate(frame, coefs, frame_times, fig, ax, background, circles, circle_lines, drawing, draw_x, draw_y, coefs_static, thetas)
#pil_image = input_image
yield pil_image, video_path
# Save the animation as a video
anim = animation.FuncAnimation(fig, animate, frames=frames, interval=5, fargs=(coefs, frame_times, fig, ax, background, circles, circle_lines, drawing, draw_x, draw_y, coefs_static, thetas))
anim.save(video_path, fps=15)
yield pil_image, video_path
def setup_gradio_interface():
interface = gr.Interface(
fn=fourier_transform_drawing,
inputs=[
gr.Image(label="Drawing Progress", sources=['upload'], type="pil"),
gr.Slider(minimum=5, maximum=500, value=100, label="Number of Frames"),
gr.Slider(minimum=1, maximum=500, value=50, label="Number of Coefficients"),
gr.Number(value=224, label="Image Size (px)", precision=0),
gr.Slider(minimum=3, maximum=11, step=2, value=5, label="Blur Kernel Size (odd number)"),
gr.Number(value=400, label="Desired Range for Scaling", precision=0),
gr.Number(value=1000, label="Number of Points for Integration", precision=0),
],
outputs=["image", gr.Video()],
title="Fourier Transform Drawing",
description="Upload an image and generate a Fourier Transform drawing animation."
)
return interface
if __name__ == "__main__":
interface = setup_gradio_interface()
interface.queue()
interface.launch() |