Muhammad Waqas commited on
Commit
d9bc1e6
·
1 Parent(s): 2bc3ef7

Added: Generate image to video

Browse files
app.py CHANGED
@@ -10,13 +10,16 @@ import uuid
10
  from dotenv import load_dotenv
11
  from flask import Flask, request, jsonify, render_template, send_file
12
  from PIL import Image
 
13
 
14
- # Load environment variables from the .env file.
15
  load_dotenv()
16
 
17
  # Initialize Flask app
18
  app = Flask(__name__)
19
 
 
 
20
  # Set server and websocket addresses from environment variables
21
  server_address = os.getenv("SERVER_ADDRESS")
22
  ws_address = os.getenv("WS_ADDRESS")
@@ -24,10 +27,35 @@ ws_address = os.getenv("WS_ADDRESS")
24
  # Generate a unique client ID
25
  client_id = str(uuid.uuid4())
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def make_request(url, data=None, headers=None):
28
  req = urllib.request.Request(url, data=data, headers=headers)
29
- with urllib.request.urlopen(req) as response:
30
- return json.loads(response.read())
 
 
 
 
 
 
 
 
31
 
32
  def queue_prompt(prompt, token):
33
  payload = {"prompt": prompt, "client_id": client_id}
@@ -82,6 +110,12 @@ def get_images(ws, prompt, token):
82
 
83
  return output_images
84
 
 
 
 
 
 
 
85
  # Default route for home welcome
86
  @app.route('/')
87
  def home():
@@ -124,14 +158,21 @@ def generate_image():
124
  # seednum = random.randint(1, 9999999999999)
125
  # prompt["3"]["inputs"]["seed"] = seednum
126
 
127
- # For model Flux1.dev
 
 
128
 
129
  # Generate a random 15-digit seed as an integer
130
  seednum = random.randint(100000000000000, 999999999999999)
131
  prompt["31"]["inputs"]["seed"] = seednum
132
 
133
  ws = websocket.WebSocket()
134
- ws.connect(f"{ws_address}?clientId={client_id}&token={token}")
 
 
 
 
 
135
  images = get_images(ws, prompt, token)
136
  ws.close()
137
 
@@ -151,5 +192,77 @@ def generate_image():
151
  def get_image_file(filename):
152
  return send_file(filename, mimetype='image/png')
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  if __name__ == '__main__':
155
  app.run(host='0.0.0.0', port=7860) # Removed 'debug=True'
 
10
  from dotenv import load_dotenv
11
  from flask import Flask, request, jsonify, render_template, send_file
12
  from PIL import Image
13
+ from werkzeug.utils import secure_filename
14
 
15
+ # Load environment variables from the .env file
16
  load_dotenv()
17
 
18
  # Initialize Flask app
19
  app = Flask(__name__)
20
 
21
+ ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'webp'} # Define supported image types
22
+
23
  # Set server and websocket addresses from environment variables
24
  server_address = os.getenv("SERVER_ADDRESS")
25
  ws_address = os.getenv("WS_ADDRESS")
 
27
  # Generate a unique client ID
28
  client_id = str(uuid.uuid4())
29
 
30
+ def allowed_file(filename):
31
+ """Check if the uploaded file has an allowed extension."""
32
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
33
+
34
+ def save_base64_image(b64_string):
35
+ """Decode a base64 string and save it as an image."""
36
+ header, encoded = b64_string.split(',', 1) # Handle data URI schemes if provided
37
+ image_data = base64.b64decode(encoded)
38
+
39
+ # Determine image extension from data URI or use a default one
40
+ ext = header.split('/')[1].split(';')[0] if '/' in header else 'png'
41
+ image_path = f"/tmp/{uuid.uuid4()}.{ext}"
42
+
43
+ with open(image_path, 'wb') as f:
44
+ f.write(image_data)
45
+ return image_path
46
+
47
  def make_request(url, data=None, headers=None):
48
  req = urllib.request.Request(url, data=data, headers=headers)
49
+ try:
50
+ with urllib.request.urlopen(req) as response:
51
+ response_body = response.read().decode() # Decode the response
52
+ # print(response_body)
53
+ return json.loads(response_body) # Convert to JSON if valid
54
+ except urllib.error.HTTPError as e:
55
+ print(f"HTTPError: {e.code}, {e.reason}")
56
+ print(e.read().decode()) # Print detailed error response
57
+ except urllib.error.URLError as e:
58
+ print(f"URLError: {e.reason}")
59
 
60
  def queue_prompt(prompt, token):
61
  payload = {"prompt": prompt, "client_id": client_id}
 
110
 
111
  return output_images
112
 
113
+ def fetch_video(video_data, token):
114
+ video_url = f"{server_address}/download?file={video_data['filename']}"
115
+ req = urllib.request.Request(video_url)
116
+ req.add_header("Authorization", f"Bearer {token}")
117
+ return urllib.request.urlopen(req).read()
118
+
119
  # Default route for home welcome
120
  @app.route('/')
121
  def home():
 
158
  # seednum = random.randint(1, 9999999999999)
159
  # prompt["3"]["inputs"]["seed"] = seednum
160
 
161
+ #######################
162
+ # For model Flux1.dev #
163
+ #######################
164
 
165
  # Generate a random 15-digit seed as an integer
166
  seednum = random.randint(100000000000000, 999999999999999)
167
  prompt["31"]["inputs"]["seed"] = seednum
168
 
169
  ws = websocket.WebSocket()
170
+
171
+ try:
172
+ ws.connect(f"{ws_address}?clientId={client_id}&token={token}")
173
+ except websocket.WebSocketException as e:
174
+ return jsonify({'error': f'WebSocket connection failed: {str(e)}'}), 500
175
+
176
  images = get_images(ws, prompt, token)
177
  ws.close()
178
 
 
192
  def get_image_file(filename):
193
  return send_file(filename, mimetype='image/png')
194
 
195
+ @app.route('/image_to_video', methods=['POST'])
196
+ def image_to_video():
197
+ data = request.json
198
+
199
+ # Extract token from headers
200
+ token = request.headers.get('Authorization')
201
+ if not token or not token.startswith("Bearer "):
202
+ return jsonify({'error': 'Invalid or missing token'}), 400
203
+ token = base64.b64decode(token.split(" ")[1]).decode("utf-8")
204
+
205
+ # Extract text prompt
206
+ text_prompt = data.get('text_prompt')
207
+ if not text_prompt:
208
+ return jsonify({'error': 'Text prompt is required'}), 400
209
+
210
+ # Handle uploaded image or base64-encoded image
211
+ image_file = request.files.get('image')
212
+ base64_image = data.get('base64_image')
213
+
214
+ if image_file:
215
+ # Validate and save uploaded image
216
+ if not allowed_file(image_file.filename):
217
+ return jsonify({'error': 'Unsupported image format'}), 400
218
+ filename = secure_filename(image_file.filename)
219
+ image_path = f"/tmp/{uuid.uuid4()}_{filename}"
220
+ image_file.save(image_path)
221
+
222
+ elif base64_image:
223
+ # Save base64-encoded image
224
+ try:
225
+ image_path = save_base64_image(base64_image)
226
+ except Exception as e:
227
+ return jsonify({'error': f'Invalid base64 image data: {str(e)}'}), 400
228
+ else:
229
+ return jsonify({'error': 'Image is required (either file or base64)'}), 400
230
+
231
+ # Get the path to the workflow configuration file
232
+ current_dir = os.path.dirname(os.path.abspath(__file__))
233
+ file_path = os.path.join(current_dir, 'workflows/cogvideox_image_to_video_workflow_api.json')
234
+
235
+ # Load and modify workflow
236
+ with open(file_path, 'r', encoding='utf-8') as file:
237
+ workflow = json.load(file)
238
+ workflow["30"]["inputs"]["prompt"] = text_prompt # Text prompt
239
+ workflow["36"]["inputs"]["upload"] = image_path # Image path
240
+ workflow["31"]["inputs"]["prompt"] = "Low quality, watermark, strange motion" # Negative prompt
241
+
242
+ seed = random.randint(1e14, 9e14)
243
+ workflow["57"]["inputs"]["seed"] = seed # Set reproducibility seed
244
+
245
+ # WebSocket connection to trigger workflow
246
+ ws = websocket.WebSocket()
247
+ ws.connect(f"{ws_address}?clientId={client_id}&token={token}")
248
+ ws.send(json.dumps({"workflow": workflow})) # Send the modified workflow
249
+
250
+ # Receive video processing result
251
+ while True:
252
+ out = ws.recv()
253
+ message = json.loads(out)
254
+ if message.get('type') == 'completed':
255
+ video_data = message['data']
256
+ break
257
+
258
+ # Fetch and return the generated video
259
+ video_content = fetch_video(video_data, token)
260
+ return send_file(
261
+ io.BytesIO(video_content),
262
+ mimetype='video/mp4',
263
+ as_attachment=True,
264
+ download_name='generated_video.mp4'
265
+ )
266
+
267
  if __name__ == '__main__':
268
  app.run(host='0.0.0.0', port=7860) # Removed 'debug=True'
workflows/cogvideox_image_to_video_workflow_api.json ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": {
3
+ "inputs": {
4
+ "model": "THUDM/CogVideoX-5b-I2V",
5
+ "precision": "bf16",
6
+ "fp8_transformer": "disabled",
7
+ "compile": "disabled",
8
+ "enable_sequential_cpu_offload": false
9
+ },
10
+ "class_type": "DownloadAndLoadCogVideoModel",
11
+ "_meta": {
12
+ "title": "(Down)load CogVideo Model"
13
+ }
14
+ },
15
+ "20": {
16
+ "inputs": {
17
+ "clip_name": "t5\\google_t5-v1_1-xxl_encoderonly-fp8_e4m3fn.safetensors",
18
+ "type": "sd3"
19
+ },
20
+ "class_type": "CLIPLoader",
21
+ "_meta": {
22
+ "title": "Load CLIP"
23
+ }
24
+ },
25
+ "30": {
26
+ "inputs": {
27
+ "prompt": "Darth Vader is a vampire and blood is dripping from his steel fangs. The camera is slowly rotating around him. The background is filled with smoke and a strong light.",
28
+ "strength": 1,
29
+ "force_offload": true,
30
+ "clip": [
31
+ "20",
32
+ 0
33
+ ]
34
+ },
35
+ "class_type": "CogVideoTextEncode",
36
+ "_meta": {
37
+ "title": "CogVideo TextEncode"
38
+ }
39
+ },
40
+ "31": {
41
+ "inputs": {
42
+ "prompt": "The video is not of a high quality, it has a low resolution. Watermark present in each frame. Strange motion trajectory. ",
43
+ "strength": 1,
44
+ "force_offload": true,
45
+ "clip": [
46
+ "20",
47
+ 0
48
+ ]
49
+ },
50
+ "class_type": "CogVideoTextEncode",
51
+ "_meta": {
52
+ "title": "CogVideo TextEncode"
53
+ }
54
+ },
55
+ "36": {
56
+ "inputs": {
57
+ "image": "Webimage-1-720x480.jpg",
58
+ "upload": "image"
59
+ },
60
+ "class_type": "LoadImage",
61
+ "_meta": {
62
+ "title": "Load Image"
63
+ }
64
+ },
65
+ "37": {
66
+ "inputs": {
67
+ "width": 720,
68
+ "height": 480,
69
+ "upscale_method": "lanczos",
70
+ "keep_proportion": false,
71
+ "divisible_by": 16,
72
+ "crop": "disabled",
73
+ "image": [
74
+ "36",
75
+ 0
76
+ ]
77
+ },
78
+ "class_type": "ImageResizeKJ",
79
+ "_meta": {
80
+ "title": "Resize Image"
81
+ }
82
+ },
83
+ "44": {
84
+ "inputs": {
85
+ "frame_rate": 24,
86
+ "loop_count": 0,
87
+ "filename_prefix": "CogVideoX-I2V",
88
+ "format": "video/h264-mp4",
89
+ "pix_fmt": "yuv420p",
90
+ "crf": 19,
91
+ "save_metadata": true,
92
+ "pingpong": false,
93
+ "save_output": true,
94
+ "images": [
95
+ "56",
96
+ 0
97
+ ]
98
+ },
99
+ "class_type": "VHS_VideoCombine",
100
+ "_meta": {
101
+ "title": "Video Combine 🎥🅥🅗🅢"
102
+ }
103
+ },
104
+ "56": {
105
+ "inputs": {
106
+ "enable_vae_tiling": false,
107
+ "tile_sample_min_height": 96,
108
+ "tile_sample_min_width": 96,
109
+ "tile_overlap_factor_height": 0.083,
110
+ "tile_overlap_factor_width": 0.083,
111
+ "auto_tile_size": true,
112
+ "pipeline": [
113
+ "57",
114
+ 0
115
+ ],
116
+ "samples": [
117
+ "57",
118
+ 1
119
+ ]
120
+ },
121
+ "class_type": "CogVideoDecode",
122
+ "_meta": {
123
+ "title": "CogVideo Decode"
124
+ }
125
+ },
126
+ "57": {
127
+ "inputs": {
128
+ "height": 480,
129
+ "width": 720,
130
+ "num_frames": 49,
131
+ "steps": 50,
132
+ "cfg": 6,
133
+ "seed": 65334758276105,
134
+ "scheduler": "DPM",
135
+ "denoise_strength": 16,
136
+ "pipeline": [
137
+ "1",
138
+ 0
139
+ ],
140
+ "positive": [
141
+ "30",
142
+ 0
143
+ ],
144
+ "negative": [
145
+ "31",
146
+ 0
147
+ ],
148
+ "image_cond_latents": [
149
+ "58",
150
+ 0
151
+ ]
152
+ },
153
+ "class_type": "CogVideoSampler",
154
+ "_meta": {
155
+ "title": "CogVideo Sampler"
156
+ }
157
+ },
158
+ "58": {
159
+ "inputs": {
160
+ "chunk_size": 16,
161
+ "enable_tiling": true,
162
+ "pipeline": [
163
+ "1",
164
+ 0
165
+ ],
166
+ "image": [
167
+ "37",
168
+ 0
169
+ ]
170
+ },
171
+ "class_type": "CogVideoImageEncode",
172
+ "_meta": {
173
+ "title": "CogVideo ImageEncode"
174
+ }
175
+ }
176
+ }
workflows/deliberate_v6_workflow_api.json ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "3": {
3
+ "inputs": {
4
+ "seed": 156680208700286,
5
+ "steps": 20,
6
+ "cfg": 8,
7
+ "sampler_name": "euler",
8
+ "scheduler": "normal",
9
+ "denoise": 1,
10
+ "model": [
11
+ "4",
12
+ 0
13
+ ],
14
+ "positive": [
15
+ "6",
16
+ 0
17
+ ],
18
+ "negative": [
19
+ "7",
20
+ 0
21
+ ],
22
+ "latent_image": [
23
+ "5",
24
+ 0
25
+ ]
26
+ },
27
+ "class_type": "KSampler",
28
+ "_meta": {
29
+ "title": "KSampler"
30
+ }
31
+ },
32
+ "4": {
33
+ "inputs": {
34
+ "ckpt_name": "Deliberate_v6.safetensors"
35
+ },
36
+ "class_type": "CheckpointLoaderSimple",
37
+ "_meta": {
38
+ "title": "Load Checkpoint"
39
+ }
40
+ },
41
+ "5": {
42
+ "inputs": {
43
+ "width": 512,
44
+ "height": 512,
45
+ "batch_size": 1
46
+ },
47
+ "class_type": "EmptyLatentImage",
48
+ "_meta": {
49
+ "title": "Empty Latent Image"
50
+ }
51
+ },
52
+ "6": {
53
+ "inputs": {
54
+ "text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
55
+ "clip": [
56
+ "4",
57
+ 1
58
+ ]
59
+ },
60
+ "class_type": "CLIPTextEncode",
61
+ "_meta": {
62
+ "title": "CLIP Text Encode (Prompt)"
63
+ }
64
+ },
65
+ "7": {
66
+ "inputs": {
67
+ "text": "text, watermark",
68
+ "clip": [
69
+ "4",
70
+ 1
71
+ ]
72
+ },
73
+ "class_type": "CLIPTextEncode",
74
+ "_meta": {
75
+ "title": "CLIP Text Encode (Prompt)"
76
+ }
77
+ },
78
+ "8": {
79
+ "inputs": {
80
+ "samples": [
81
+ "3",
82
+ 0
83
+ ],
84
+ "vae": [
85
+ "4",
86
+ 2
87
+ ]
88
+ },
89
+ "class_type": "VAEDecode",
90
+ "_meta": {
91
+ "title": "VAE Decode"
92
+ }
93
+ },
94
+ "9": {
95
+ "inputs": {
96
+ "filename_prefix": "ComfyUI",
97
+ "images": [
98
+ "8",
99
+ 0
100
+ ]
101
+ },
102
+ "class_type": "SaveImage",
103
+ "_meta": {
104
+ "title": "Save Image"
105
+ }
106
+ }
107
+ }