import gradio as gr import requests import base64 import os import time import jwt import logging from pathlib import Path # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # ===== API CONFIGURATION ===== ACCESS_KEY_ID = "AFyHfnQATghFdCMyAG3gRPbNY4TNKFGB" ACCESS_KEY_SECRET = "TTepeLyBterLNM3brYPGmdndBnnyKJBA" API_BASE_URL = "https://api-singapore.klingai.com" CREATE_TASK_ENDPOINT = f"{API_BASE_URL}/v1/images/generations" # SINGLE image endpoint # ===== AUTHENTICATION ===== def generate_jwt_token(): payload = { "iss": ACCESS_KEY_ID, "exp": int(time.time()) + 1800, "nbf": int(time.time()) - 5 } return jwt.encode(payload, ACCESS_KEY_SECRET, algorithm="HS256") # ===== IMAGE PROCESSING ===== def prepare_image_base64(image_path): """Convert image to base64 without prefix""" with open(image_path, "rb") as img_file: return base64.b64encode(img_file.read()).decode('utf-8') # ===== API CALLS ===== def create_face_transform_task(image_base64, prompt): headers = { "Authorization": f"Bearer {generate_jwt_token()}", "Content-Type": "application/json" } payload = { "model_name": "kling-v2", # Best for face transformation "prompt": prompt, "image": image_base64, "image_reference": "face", # Critical for face transformation "image_fidelity": 0.97, # 97% face similarity "human_fidelity": 0.95, # High facial feature preservation "aspect_ratio": "1:1" } try: response = requests.post(CREATE_TASK_ENDPOINT, json=payload, headers=headers) response.raise_for_status() return response.json() except Exception as e: logger.error(f"API Error: {str(e)}") return None def check_task_status(task_id): headers = {"Authorization": f"Bearer {generate_jwt_token()}"} try: response = requests.get(f"{API_BASE_URL}/v1/images/generations/{task_id}", headers=headers) response.raise_for_status() return response.json() except Exception as e: logger.error(f"Status Check Error: {str(e)}") return None # ===== MAIN FUNCTION ===== def transform_face(image_path, prompt): if not image_path: return None, "Please upload an image first" try: # Prepare image image_base64 = prepare_image_base64(image_path) # Create task task_data = create_face_transform_task(image_base64, prompt) if not task_data or task_data.get("code") != 0: return None, "Failed to start transformation" task_id = task_data["data"]["task_id"] # Poll for results (max 2 minutes) for _ in range(12): time.sleep(10) status_data = check_task_status(task_id) if not status_data: continue if status_data["data"]["task_status"] == "succeed": image_url = status_data["data"]["task_result"]["images"][0]["url"] img_data = requests.get(image_url).content output_path = f"/tmp/transformed_face_{task_id}.png" with open(output_path, "wb") as f: f.write(img_data) return output_path, None return None, "Processing timed out" except Exception as e: return None, f"Error: {str(e)}" # ===== GRADIO INTERFACE ===== with gr.Blocks() as app: gr.Markdown("# 🎭 Face Transformation") gr.Markdown("Upload a clear face photo and describe your transformation") with gr.Row(): with gr.Column(): image_input = gr.Image(type="filepath", label="Upload Face Photo", sources=["upload"]) prompt_input = gr.Textbox( label="Transformation Prompt", placeholder="e.g. 'cyberpunk character', 'renaissance painting'" ) generate_btn = gr.Button("Transform", variant="primary") gr.Markdown("### Requirements") gr.Markdown(""" - Clear frontal face photo - Single person only - Max 10MB size (JPG/PNG) - Min 300x300 resolution """) with gr.Column(): output_image = gr.Image(label="Transformed Result", interactive=False) output_file = gr.File(label="Download") status_output = gr.Textbox(label="Status", interactive=False) generate_btn.click( fn=lambda img, prompt: transform_face(img, prompt) + (None,), # Extra None for error placeholder inputs=[image_input, prompt_input], outputs=[output_image, output_file, status_output] ) if __name__ == "__main__": app.launch(server_name="0.0.0.0", server_port=7860)