AkashKumarave commited on
Commit
c245dea
·
verified ·
1 Parent(s): 013dbb5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -96
app.py CHANGED
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
  ACCESS_KEY_ID = "AFyHfnQATghFdCMyAG3gRPbNY4TNKFGB"
16
  ACCESS_KEY_SECRET = "TTepeLyBterLNM3brYPGmdndBnnyKJBA"
17
  API_BASE_URL = "https://api-singapore.klingai.com"
18
- CREATE_TASK_ENDPOINT = f"{API_BASE_URL}/v1/images/generations" # SINGLE image endpoint
19
 
20
  # ===== AUTHENTICATION =====
21
  def generate_jwt_token():
@@ -37,146 +37,177 @@ def prepare_image_base64(image_path):
37
  logger.error(f"Image processing failed: {str(e)}")
38
  return None
39
 
40
- def validate_face_image(image_path):
41
- """Validate the image meets face transformation requirements"""
42
  try:
43
- # Check file exists
44
- if not os.path.exists(image_path):
45
- return False, "Image file not found"
46
-
47
- # Check file size (max 10MB)
48
- file_size = os.path.getsize(image_path) / (1024 * 1024)
49
- if file_size > 10:
50
  return False, "Image too large (max 10MB)"
51
-
 
52
  return True, ""
53
  except Exception as e:
54
- return False, f"Validation error: {str(e)}"
55
 
56
  # ===== API FUNCTIONS =====
57
- def create_face_task(image_base64, prompt):
58
- """Create face transformation task with 97% fidelity"""
59
  headers = {
60
  "Authorization": f"Bearer {generate_jwt_token()}",
61
  "Content-Type": "application/json"
62
  }
63
 
 
 
 
 
 
 
 
 
 
 
 
64
  payload = {
65
- "model_name": "kling-v2.1", # Best for face preservation
66
  "prompt": prompt,
67
- "image": image_base64,
68
- "image_reference": "face", # Critical for face control
69
- "image_fidelity": 0.97, # 97% similarity
70
- "human_fidelity": 0.97, # 97% facial features
71
- "aspect_ratio": "1:1",
72
- "n": 1
73
  }
74
 
75
  try:
76
  response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers)
77
  response.raise_for_status()
78
- return response.json()
79
- except Exception as e:
80
- logger.error(f"API Error: {str(e)}")
81
- return None
 
 
82
 
83
  def check_task_status(task_id):
 
84
  headers = {"Authorization": f"Bearer {generate_jwt_token()}"}
 
 
85
  try:
86
- response = requests.get(
87
- f"{API_BASE_URL}/v1/images/generations/{task_id}",
88
- headers=headers
89
- )
90
  response.raise_for_status()
91
- return response.json()
92
- except Exception as e:
93
- logger.error(f"Status Check Error: {str(e)}")
94
- return None
95
 
96
- # ===== MAIN FUNCTION =====
97
- def transform_face(image_path, prompt):
98
- """Full transformation workflow"""
99
- # Validate image
100
- is_valid, error_msg = validate_face_image(image_path)
101
- if not is_valid:
102
- return None, error_msg
 
 
103
 
104
- try:
105
- # Prepare image
106
- image_base64 = prepare_image_base64(image_path)
107
- if not image_base64:
108
- return None, "Failed to process image"
109
-
110
- # Create task
111
- task_data = create_face_task(image_base64, prompt)
112
- if not task_data or task_data.get("code") != 0:
113
- return None, "Failed to start transformation"
114
-
115
- task_id = task_data["data"]["task_id"]
116
- logger.info(f"Task created: {task_id}")
 
 
 
 
 
117
 
118
- # Check results (max 3 minutes)
119
- for _ in range(18): # 18 attempts × 10 seconds
120
- time.sleep(10)
121
- status_data = check_task_status(task_id)
122
- if not status_data:
123
- continue
124
-
125
- if status_data["data"]["task_status"] == "succeed":
126
- image_url = status_data["data"]["task_result"]["images"][0]["url"]
127
- img_data = requests.get(image_url).content
128
- output_path = f"/tmp/face_result_{task_id}.png"
129
  with open(output_path, "wb") as f:
130
- f.write(img_data)
131
- return output_path, None
 
 
132
 
133
- elif status_data["data"]["task_status"] in ("failed", "canceled"):
134
- error_msg = status_data["data"].get("task_status_msg", "Task failed")
135
- return None, error_msg
136
-
137
- return None, "Processing timed out"
138
-
139
- except Exception as e:
140
- return None, f"Error: {str(e)}"
141
 
142
  # ===== GRADIO INTERFACE =====
143
- with gr.Blocks(title="Face Transformer") as app:
144
- gr.Markdown("# 🎭 Exact Face Transformation (97% Match)")
145
- gr.Markdown("Upload ONE face photo for style transformation (97% similarity)")
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  with gr.Row():
148
  with gr.Column():
149
- image_input = gr.Image(
150
- type="filepath",
151
- label="Upload Face Photo",
152
- sources=["upload"],
153
- height=300
154
- )
 
 
155
  prompt_input = gr.Textbox(
156
- label="Style Prompt",
157
- placeholder="e.g. 'anime character', 'watercolor portrait'"
158
  )
159
- generate_btn = gr.Button("Transform", variant="primary")
160
 
161
- gr.Markdown("### Requirements")
 
 
162
  gr.Markdown("""
163
- - **Single clear face photo**
164
- - Front-facing works best
165
- - No glasses/masks
166
- - Max 10MB (JPG/PNG)
167
- - Min 300x300px
168
  """)
169
 
170
  with gr.Column():
171
- output_image = gr.Image(label="Result", interactive=False, height=400)
 
172
  output_file = gr.File(label="Download Result")
173
  status_output = gr.Textbox(label="Status", interactive=False)
174
 
 
175
  generate_btn.click(
176
- fn=lambda img, prompt: transform_face(img, prompt) + (None,),
177
- inputs=[image_input, prompt_input],
178
  outputs=[output_image, output_file, status_output]
179
  )
180
 
181
  if __name__ == "__main__":
182
- app.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
15
  ACCESS_KEY_ID = "AFyHfnQATghFdCMyAG3gRPbNY4TNKFGB"
16
  ACCESS_KEY_SECRET = "TTepeLyBterLNM3brYPGmdndBnnyKJBA"
17
  API_BASE_URL = "https://api-singapore.klingai.com"
18
+ CREATE_TASK_ENDPOINT = f"{API_BASE_URL}/v1/images/multi-image2image"
19
 
20
  # ===== AUTHENTICATION =====
21
  def generate_jwt_token():
 
37
  logger.error(f"Image processing failed: {str(e)}")
38
  return None
39
 
40
+ def validate_image(image_path):
41
+ """Validate image meets API requirements"""
42
  try:
43
+ # Check file size
44
+ size_mb = os.path.getsize(image_path) / (1024 * 1024)
45
+ if size_mb > 10:
 
 
 
 
46
  return False, "Image too large (max 10MB)"
47
+
48
+ # Check dimensions (basic check - should use PIL for actual dimensions)
49
  return True, ""
50
  except Exception as e:
51
+ return False, f"Image validation error: {str(e)}"
52
 
53
  # ===== API FUNCTIONS =====
54
+ def create_multi_image_task(subject_images, prompt):
55
+ """Create multi-image generation task"""
56
  headers = {
57
  "Authorization": f"Bearer {generate_jwt_token()}",
58
  "Content-Type": "application/json"
59
  }
60
 
61
+ # Prepare subject images list
62
+ subject_image_list = []
63
+ for img_path in subject_images:
64
+ if img_path: # Skip empty/None images
65
+ base64_img = prepare_image_base64(img_path)
66
+ if base64_img:
67
+ subject_image_list.append({"subject_image": base64_img})
68
+
69
+ if len(subject_image_list) < 2:
70
+ return None, "At least 2 subject images required"
71
+
72
  payload = {
73
+ "model_name": "kling-v2",
74
  "prompt": prompt,
75
+ "subject_image_list": subject_image_list,
76
+ "n": 1,
77
+ "aspect_ratio": "1:1"
 
 
 
78
  }
79
 
80
  try:
81
  response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers)
82
  response.raise_for_status()
83
+ return response.json(), None
84
+ except requests.exceptions.RequestException as e:
85
+ logger.error(f"API request failed: {str(e)}")
86
+ if hasattr(e, 'response') and e.response:
87
+ logger.error(f"API response: {e.response.text}")
88
+ return None, f"API Error: {str(e)}"
89
 
90
  def check_task_status(task_id):
91
+ """Check task completion status"""
92
  headers = {"Authorization": f"Bearer {generate_jwt_token()}"}
93
+ status_url = f"{API_BASE_URL}/v1/images/multi-image2image/{task_id}"
94
+
95
  try:
96
+ response = requests.get(status_url, headers=headers)
 
 
 
97
  response.raise_for_status()
98
+ return response.json(), None
99
+ except requests.exceptions.RequestException as e:
100
+ return None, f"Status check failed: {str(e)}"
 
101
 
102
+ # ===== MAIN PROCESSING =====
103
+ def generate_image(subject_images, prompt):
104
+ """Handle complete image generation workflow"""
105
+ # Validate images
106
+ for img in subject_images:
107
+ if img: # Only validate non-empty images
108
+ is_valid, error_msg = validate_image(img)
109
+ if not is_valid:
110
+ return None, error_msg
111
 
112
+ # Create task
113
+ task_response, error = create_multi_image_task(subject_images, prompt)
114
+ if error:
115
+ return None, error
116
+
117
+ if task_response.get("code") != 0:
118
+ return None, f"API error: {task_response.get('message', 'Unknown error')}"
119
+
120
+ task_id = task_response["data"]["task_id"]
121
+ logger.info(f"Task created: {task_id}")
122
+
123
+ # Poll for results (max 10 minutes)
124
+ for _ in range(60):
125
+ task_data, error = check_task_status(task_id)
126
+ if error:
127
+ return None, error
128
+
129
+ status = task_data["data"]["task_status"]
130
 
131
+ if status == "succeed":
132
+ image_url = task_data["data"]["task_result"]["images"][0]["url"]
133
+ try:
134
+ response = requests.get(image_url)
135
+ response.raise_for_status()
136
+ output_path = Path(f"/tmp/kling_output_{task_id}.png")
 
 
 
 
 
137
  with open(output_path, "wb") as f:
138
+ f.write(response.content)
139
+ return str(output_path), None
140
+ except Exception as e:
141
+ return None, f"Failed to download result: {str(e)}"
142
 
143
+ elif status in ("failed", "canceled"):
144
+ error_msg = task_data["data"].get("task_status_msg", "Unknown error")
145
+ return None, f"Task failed: {error_msg}"
146
+
147
+ time.sleep(10)
148
+
149
+ return None, "Task timed out after 10 minutes"
 
150
 
151
  # ===== GRADIO INTERFACE =====
152
+ def process_interface(subject_image1, subject_image2, subject_image3, subject_image4, prompt):
153
+ # Filter out None/empty images
154
+ subject_images = [img for img in [subject_image1, subject_image2, subject_image3, subject_image4] if img]
155
+
156
+ if len(subject_images) < 2:
157
+ return None, None, "Please upload at least 2 subject images"
158
+
159
+ output_path, error = generate_image(subject_images, prompt)
160
+ if error:
161
+ return None, None, error
162
+
163
+ return output_path, output_path, "Generation successful!"
164
+
165
+ with gr.Blocks(title="Kling AI Multi-Image Generator") as app:
166
+ gr.Markdown("## 🖼️ Kling AI Multi-Image to Image")
167
+ gr.Markdown("Combine features from multiple images into one result")
168
 
169
  with gr.Row():
170
  with gr.Column():
171
+ gr.Markdown("### Input Settings")
172
+ with gr.Row():
173
+ subject_image1 = gr.Image(type="filepath", label="Subject Image 1 *")
174
+ subject_image2 = gr.Image(type="filepath", label="Subject Image 2 *")
175
+ with gr.Row():
176
+ subject_image3 = gr.Image(type="filepath", label="Subject Image 3 (Optional)")
177
+ subject_image4 = gr.Image(type="filepath", label="Subject Image 4 (Optional)")
178
+
179
  prompt_input = gr.Textbox(
180
+ label="Transformation Prompt",
181
+ placeholder="Describe how to combine these images"
182
  )
 
183
 
184
+ generate_btn = gr.Button("Generate", variant="primary")
185
+
186
+ gr.Markdown("### Requirements (* = required)")
187
  gr.Markdown("""
188
+ - **At least 2 subject images** (marked with *)
189
+ - Max 4 images total
190
+ - Max size: 10MB per image
191
+ - Formats: JPG, PNG
192
+ - Min dimensions: 300x300px
193
  """)
194
 
195
  with gr.Column():
196
+ gr.Markdown("### Output")
197
+ output_image = gr.Image(label="Generated Image", interactive=False, height=400)
198
  output_file = gr.File(label="Download Result")
199
  status_output = gr.Textbox(label="Status", interactive=False)
200
 
201
+ # Modified inputs to accept individual components
202
  generate_btn.click(
203
+ fn=process_interface,
204
+ inputs=[subject_image1, subject_image2, subject_image3, subject_image4, prompt_input],
205
  outputs=[output_image, output_file, status_output]
206
  )
207
 
208
  if __name__ == "__main__":
209
+ app.launch(
210
+ server_name="0.0.0.0",
211
+ server_port=7860,
212
+ share=False
213
+ )