AkashKumarave commited on
Commit
22fcb4a
·
verified ·
1 Parent(s): f7d9b8d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -142
app.py CHANGED
@@ -15,199 +15,126 @@ 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/multi-image2image"
19
 
20
  # ===== AUTHENTICATION =====
21
  def generate_jwt_token():
22
- """Generate JWT token for API authentication"""
23
  payload = {
24
  "iss": ACCESS_KEY_ID,
25
- "exp": int(time.time()) + 1800, # 30 minutes expiration
26
- "nbf": int(time.time()) - 5 # Not before 5 seconds ago
27
  }
28
  return jwt.encode(payload, ACCESS_KEY_SECRET, algorithm="HS256")
29
 
30
  # ===== IMAGE PROCESSING =====
31
  def prepare_image_base64(image_path):
32
  """Convert image to base64 without prefix"""
33
- try:
34
- with open(image_path, "rb") as img_file:
35
- return base64.b64encode(img_file.read()).decode('utf-8')
36
- except Exception as e:
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
- )
 
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():
 
22
  payload = {
23
  "iss": ACCESS_KEY_ID,
24
+ "exp": int(time.time()) + 1800,
25
+ "nbf": int(time.time()) - 5
26
  }
27
  return jwt.encode(payload, ACCESS_KEY_SECRET, algorithm="HS256")
28
 
29
  # ===== IMAGE PROCESSING =====
30
  def prepare_image_base64(image_path):
31
  """Convert image to base64 without prefix"""
32
+ with open(image_path, "rb") as img_file:
33
+ return base64.b64encode(img_file.read()).decode('utf-8')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ # ===== API CALLS =====
36
+ def create_face_transform_task(image_base64, prompt):
 
37
  headers = {
38
  "Authorization": f"Bearer {generate_jwt_token()}",
39
  "Content-Type": "application/json"
40
  }
41
 
 
 
 
 
 
 
 
 
 
 
 
42
  payload = {
43
+ "model_name": "kling-v2", # Best for face transformation
44
  "prompt": prompt,
45
+ "image": image_base64,
46
+ "image_reference": "face", # Critical for face transformation
47
+ "image_fidelity": 0.97, # 97% face similarity
48
+ "human_fidelity": 0.95, # High facial feature preservation
49
  "aspect_ratio": "1:1"
50
  }
51
 
52
  try:
53
  response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers)
54
  response.raise_for_status()
55
+ return response.json()
56
+ except Exception as e:
57
+ logger.error(f"API Error: {str(e)}")
58
+ return None
 
 
59
 
60
  def check_task_status(task_id):
 
61
  headers = {"Authorization": f"Bearer {generate_jwt_token()}"}
 
 
62
  try:
63
+ response = requests.get(f"{API_BASE_URL}/v1/images/generations/{task_id}", headers=headers)
64
  response.raise_for_status()
65
+ return response.json()
66
+ except Exception as e:
67
+ logger.error(f"Status Check Error: {str(e)}")
68
+ return None
69
 
70
+ # ===== MAIN FUNCTION =====
71
+ def transform_face(image_path, prompt):
72
+ if not image_path:
73
+ return None, "Please upload an image first"
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ try:
76
+ # Prepare image
77
+ image_base64 = prepare_image_base64(image_path)
78
+
79
+ # Create task
80
+ task_data = create_face_transform_task(image_base64, prompt)
81
+ if not task_data or task_data.get("code") != 0:
82
+ return None, "Failed to start transformation"
 
 
83
 
84
+ task_id = task_data["data"]["task_id"]
85
+
86
+ # Poll for results (max 2 minutes)
87
+ for _ in range(12):
88
+ time.sleep(10)
89
+ status_data = check_task_status(task_id)
90
+ if not status_data:
91
+ continue
92
+
93
+ if status_data["data"]["task_status"] == "succeed":
94
+ image_url = status_data["data"]["task_result"]["images"][0]["url"]
95
+ img_data = requests.get(image_url).content
96
+ output_path = f"/tmp/transformed_face_{task_id}.png"
97
  with open(output_path, "wb") as f:
98
+ f.write(img_data)
99
+ return output_path, None
 
 
100
 
101
+ return None, "Processing timed out"
102
+
103
+ except Exception as e:
104
+ return None, f"Error: {str(e)}"
 
 
 
105
 
106
  # ===== GRADIO INTERFACE =====
107
+ with gr.Blocks() as app:
108
+ gr.Markdown("# 🎭 Face Transformation")
109
+ gr.Markdown("Upload a clear face photo and describe your transformation")
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  with gr.Row():
112
  with gr.Column():
113
+ image_input = gr.Image(type="filepath", label="Upload Face Photo", sources=["upload"])
 
 
 
 
 
 
 
114
  prompt_input = gr.Textbox(
115
+ label="Transformation Prompt",
116
+ placeholder="e.g. 'cyberpunk character', 'renaissance painting'"
117
  )
118
+ generate_btn = gr.Button("Transform", variant="primary")
119
 
120
+ gr.Markdown("### Requirements")
 
 
121
  gr.Markdown("""
122
+ - Clear frontal face photo
123
+ - Single person only
124
+ - Max 10MB size (JPG/PNG)
125
+ - Min 300x300 resolution
 
126
  """)
127
 
128
  with gr.Column():
129
+ output_image = gr.Image(label="Transformed Result", interactive=False)
130
+ output_file = gr.File(label="Download")
 
131
  status_output = gr.Textbox(label="Status", interactive=False)
132
 
 
133
  generate_btn.click(
134
+ fn=lambda img, prompt: transform_face(img, prompt) + (None,), # Extra None for error placeholder
135
+ inputs=[image_input, prompt_input],
136
  outputs=[output_image, output_file, status_output]
137
  )
138
 
139
  if __name__ == "__main__":
140
+ app.launch(server_name="0.0.0.0", server_port=7860)