ll3ll commited on
Commit
7863e99
·
verified ·
1 Parent(s): 225ad76

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -0
app.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import io
4
+ from PIL import Image, ImageOps
5
+ import base64
6
+ import time
7
+
8
+ def load_image_from_url(url):
9
+ try:
10
+ response = requests.get(url)
11
+ response.raise_for_status()
12
+ image = Image.open(io.BytesIO(response.content))
13
+ return image
14
+ except Exception as e:
15
+ return None, f"Error: {e}"
16
+
17
+ def send_to_api(key, prompt, image_url, mask_base64, path_points):
18
+ """Send the image and mask to the API endpoint."""
19
+ url = "https://api.goapi.ai/api/v1/task"
20
+ payload = {
21
+ "model": "kling",
22
+ "task_type": "video_generation",
23
+ "input": {
24
+ "prompt": prompt,
25
+ "negative_prompt": "",
26
+ "cfg_scale": 0.5,
27
+ "duration": 5,
28
+ "image_url": image_url,
29
+ "image_tail_url": "",
30
+ "mode": "std",
31
+ "version": "1.0",
32
+ "motion_brush": {
33
+ "mask_url": f"data:image/png;base64,{mask_base64}",
34
+ "static_masks": [{"points": []}],
35
+ "dynamic_masks": [{"points": path_points}]
36
+ }
37
+ }
38
+ }
39
+
40
+ headers = {
41
+ "x-api-key": key
42
+ }
43
+
44
+ response = requests.post(url, headers=headers, json=payload)
45
+ if response.status_code == 200:
46
+ data = response.json()
47
+ task_id = data.get("data", {}).get("task_id")
48
+ return task_id if task_id else None
49
+ else:
50
+ return f"Request failed, status code: {response.status_code}", None
51
+
52
+ def fetch_api(task_id, key):
53
+ """Fetch task status and return video URL, retrying every 20 seconds until task is completed."""
54
+ url = f"https://api.goapi.ai/api/v1/task/{task_id}"
55
+ headers = {
56
+ "x-api-key": key
57
+ }
58
+
59
+ while True:
60
+ response = requests.get(url, headers=headers)
61
+ if response.status_code == 200:
62
+ data = response.json()
63
+ status = data.get("data", {}).get("status", "")
64
+ if status == "completed":
65
+ video_url = data.get("data", {}).get("output", {}).get("video_url", "Error video URL")
66
+ return video_url
67
+ else:
68
+ print(f"Task status is '{status}'. Retrying in 10 seconds...")
69
+ else:
70
+ return f"Request failed, status code: {response.status_code}", None
71
+
72
+ time.sleep(10)
73
+
74
+ def image_to_base64(image):
75
+ """Convert a PIL Image to a base64-encoded PNG string."""
76
+ buffered = io.BytesIO()
77
+ image.save(buffered, format="PNG")
78
+ img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
79
+ return img_base64
80
+
81
+ def generate_mask_and_path(editor_value, path_direction, key, prompt, original_image_url):
82
+ layers = editor_value.get("layers", [])
83
+ if len(layers) < 3:
84
+ return None
85
+
86
+ green_layer = layers[0]
87
+ green_mask = ImageOps.colorize(
88
+ ImageOps.grayscale(green_layer), black="black", white="green"
89
+ )
90
+
91
+ black_layer = layers[1]
92
+ black_mask = ImageOps.colorize(
93
+ ImageOps.grayscale(green_layer), black="black", white="green"
94
+ )
95
+
96
+ width, height = green_mask.size
97
+ composite_image = Image.new("RGB", (width, height), "white")
98
+ composite_image.paste(green_mask, mask=green_layer)
99
+ composite_image.paste(black_mask, mask=black_layer)
100
+
101
+ path_layer = layers[2]
102
+ path_array = path_layer.load()
103
+ path_points = []
104
+
105
+ # Generate path points based on selected direction
106
+ if path_direction == "Left to Right":
107
+ for y in range(height):
108
+ for x in range(width):
109
+ if path_array[x, y] == (255, 255, 255, 255):
110
+ path_points.append({"x": x, "y": y})
111
+ elif path_direction == "Right to Left":
112
+ for y in range(height):
113
+ for x in range(width - 1, -1, -1):
114
+ if path_array[x, y] == (255, 255, 255, 255):
115
+ path_points.append({"x": x, "y": y})
116
+ elif path_direction == "Top to Bottom":
117
+ for x in range(width):
118
+ for y in range(height):
119
+ if path_array[x, y] == (255, 255, 255, 255):
120
+ path_points.append({"x": x, "y": y})
121
+ elif path_direction == "Bottom to Top":
122
+ for x in range(width):
123
+ for y in range(height - 1, -1, -1):
124
+ if path_array[x, y] == (255, 255, 255, 255):
125
+ path_points.append({"x": x, "y": y})
126
+
127
+ selected_points = []
128
+ if path_points:
129
+ step = max(len(path_points) // 10, 1)
130
+ selected_points = path_points[::step][:10]
131
+
132
+ original_image = original_image_url
133
+ mask_base64 = image_to_base64(composite_image)
134
+
135
+ task_id = send_to_api(key, prompt, original_image, mask_base64, selected_points)
136
+ video_url = fetch_api(task_id, key)
137
+
138
+ return composite_image, selected_points, task_id, video_url
139
+
140
+ with gr.Blocks() as interface:
141
+ gr.Markdown("# Video Motion Generation Tool")
142
+
143
+ gr.Markdown("---")
144
+ gr.Markdown("### 1. Input Background Image URL")
145
+ with gr.Row():
146
+ url_input = gr.Textbox(label="Input Background Image URL", placeholder="Enter the image URL")
147
+ load_image_btn = gr.Button("Load Image")
148
+
149
+ gr.Markdown("---")
150
+ gr.Markdown("### 2. Use the Brush Tool to Edit the Image")
151
+ gr.Markdown("Layer 1 will generate a dynamic mask, Layer 2 is a static mask, and Layer 3 will generate path points.")
152
+ with gr.Row():
153
+ image_editor = gr.ImageEditor(
154
+ type="pil",
155
+ brush=gr.Brush(default_size=20, colors=["#FFFFFF"], color_mode="fixed"),
156
+ layers=True,
157
+ interactive=True,
158
+ label="Drawing Tool to Generate Mask and Path"
159
+ )
160
+ with gr.Row():
161
+ prompt_input = gr.Textbox(label="Prompt", placeholder="Enter Prompt")
162
+
163
+ with gr.Row():
164
+ key_input = gr.Textbox(label="API Key", placeholder="Enter PiAPI Key")
165
+
166
+
167
+ with gr.Row():
168
+ direction_input = gr.Dropdown(
169
+ choices=["Left to Right", "Right to Left", "Top to Bottom", "Bottom to Top"], label="Select Path Direction"
170
+ )
171
+ submit_btn = gr.Button("Generate")
172
+
173
+ with gr.Row():
174
+ output_composite_image = gr.Image(label="Generated Composite Image")
175
+ output_path_points = gr.Textbox(label="Path Point Data")
176
+ output_task_id = gr.Textbox(label="Task ID")
177
+ output_video = gr.Video(label="Generated Video Link")
178
+
179
+
180
+ load_image_btn.click(
181
+ fn=load_image_from_url,
182
+ inputs=[url_input],
183
+ outputs=[image_editor],
184
+ )
185
+
186
+ submit_btn.click(
187
+ fn=generate_mask_and_path,
188
+ inputs=[image_editor, direction_input, key_input, prompt_input, url_input],
189
+ outputs=[output_composite_image, output_path_points, output_task_id, output_video],
190
+ )
191
+
192
+ interface.launch()