kenfoo commited on
Commit
bc0e444
·
verified ·
1 Parent(s): 30175c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -85
app.py CHANGED
@@ -2,13 +2,15 @@ import gradio as gr
2
  import random
3
  import os
4
  import requests
 
 
 
5
 
6
  HF_TOKEN = os.environ.get("girlToken")
7
 
8
  API_BASE = "https://prithivmlmods-qwen-image-edit-2511-loras-fast.hf.space"
9
- UPLOAD_URL = f"{API_BASE}/gradio_api/upload"
10
- CALL_URL = f"{API_BASE}/gradio_api/call/v2/infer"
11
- POLL_URL = f"{API_BASE}/gradio_api/call/infer" # need event_id
12
 
13
  LORA_STYLES = [
14
  'Multiple-Angles', 'Photo-to-Anime', 'Anime-V2', 'Light-Migration',
@@ -20,54 +22,66 @@ LORA_STYLES = [
20
  ]
21
  MAX_SEED = 2**31 - 1
22
 
23
- def upload_file(image_path):
24
- files = {'files': (os.path.basename(image_path), open(image_path, "rb"), "application/octet-stream")}
25
- headers = {'Authorization': f'Bearer {HF_TOKEN}'}
26
- resp = requests.post(UPLOAD_URL, files=files, headers=headers)
27
- resp.raise_for_status()
28
- # 新增: 先检查返回内容类型,如果非JSON直接返回字符串(文件路径?)包为dict以兼容API异常返回
29
  try:
30
- res = resp.json()
31
- except Exception as e:
32
- # 检查不是直接给了一个路径字符串
33
  try:
34
- raw_text = resp.text
35
- # 可能是图片路径?直接包装为dict
36
- if raw_text and raw_text.strip().startswith("/"):
37
- return {"path": raw_text.strip()}
38
- else:
39
- raise RuntimeError("解析API响应的JSON失败且内容非预期: %s" % raw_text)
40
- except Exception as e2:
41
- raise RuntimeError("解析API响应的JSON失败: %s / 文本: %s" % (e, resp.text))
42
-
43
- if isinstance(res, list) and len(res) > 0:
44
- if isinstance(res[0], dict):
45
- return res[0]
46
- # 允许直接为字符串则强制包装
47
- elif isinstance(res[0], str) and res[0].startswith("/"):
48
- return {"path": res[0]}
49
- else:
50
- raise RuntimeError("返回结果不是字典类型: %s" % str(res[0]))
51
- elif isinstance(res, dict):
52
- return res
53
- elif isinstance(res, str) and res.startswith("/"):
54
- return {"path": res}
55
- else:
56
- # 返回格式不明, 直接异常
57
- raise RuntimeError("API响应格式异常,期望是dict或list: %s" % str(res))
58
-
59
- def call_infer(payload):
60
- headers = {'Authorization': f'Bearer {HF_TOKEN}'}
61
- resp = requests.post(CALL_URL, json=payload, headers=headers)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  resp.raise_for_status()
 
63
  job = resp.json()
64
  event_id = job.get("event_id")
65
  return event_id
66
 
67
  def poll_infer(event_id):
68
- url = f"{POLL_URL}/{event_id}"
 
69
  headers = {'Authorization': f'Bearer {HF_TOKEN}'}
70
- # 简单轮询直到拿到结果(可优化为更好策略)
71
  import time
72
  for _ in range(60):
73
  resp = requests.get(url, headers=headers)
@@ -77,7 +91,7 @@ def poll_infer(event_id):
77
  return result.get("data"), result.get("outputs")
78
  elif result.get("status") == "error":
79
  raise Exception(result.get("error"))
80
- time.sleep(2) # 2秒再次轮询
81
  raise TimeoutError("等候API返回超时")
82
 
83
  def infer(
@@ -101,59 +115,45 @@ def infer(
101
  if randomize_seed:
102
  seed = random.randint(0, MAX_SEED)
103
 
104
- # 1. 上传文件
105
  try:
106
- uploaded_info = upload_file(image)
107
- # 检查uploaded_info类型
108
- if not isinstance(uploaded_info, dict) or "path" not in uploaded_info:
109
- print(f"[图片上传] 失败: 返回结果无'path'字段: {uploaded_info}")
110
- return None, seed
111
- img_obj = {
112
- "path": uploaded_info["path"],
113
- "meta": {"_type": "gradio.FileData"},
114
- "orig_name": os.path.basename(image)
115
- }
116
  except Exception as e:
117
- print(f"[图片上传] 失败: {e}")
118
  return None, seed
119
 
120
- # 2. 准备参数并调用推理
121
- payload = {
122
- "images": [{"image": img_obj, "caption": None}],
123
- "prompt": prompt,
124
- "lora_adapter": lora_adapter,
125
- "seed": int(seed),
126
- "randomize_seed": bool(randomize_seed),
127
- "guidance_scale": int(guidance_scale),
128
- "steps": int(steps),
129
- }
130
-
131
- print("准备调用远端API:", payload)
132
  try:
133
- event_id = call_infer(payload)
134
- print("API返回event_id: ", event_id)
135
- # 3. 结果轮询
 
 
 
 
 
 
 
136
  data, outputs = poll_infer(event_id)
137
  print("[API 完成] data:", data, "outputs:", outputs)
138
- # 解析输出(通常outputs可能就结果图片的路径
139
- # 兼容dict或list
140
- image_info = None
141
  seed_used = seed
142
  if outputs:
143
- if isinstance(outputs, list) and len(outputs) == 2:
144
- image_info, seed_used = outputs
145
- elif isinstance(outputs, dict):
146
- image_info = outputs.get("path") or outputs.get("url")
147
  seed_used = outputs.get("seed", seed)
 
 
148
  else:
149
- image_info = outputs
150
  elif data:
151
- image_info = data
152
- if isinstance(image_info, dict):
153
- img_out = image_info.get("path") or image_info.get("url")
154
- else:
155
- img_out = image_info
156
- # 补成绝对地址
 
 
157
  if img_out and isinstance(img_out, str) and not img_out.startswith("http"):
158
  img_out = API_BASE + img_out
159
  return img_out, int(seed_used)
@@ -206,7 +206,7 @@ with gr.Blocks(css=css) as demo:
206
  randomize_seed = gr.Checkbox(label="随机化种子", value=True)
207
  guidance_scale = gr.Slider(
208
  label="引导强度 (Guidance Scale)",
209
- minimum=0.1,
210
  maximum=10.0,
211
  step=0.1,
212
  value=1.0,
 
2
  import random
3
  import os
4
  import requests
5
+ import base64
6
+ from PIL import Image
7
+ from io import BytesIO
8
 
9
  HF_TOKEN = os.environ.get("girlToken")
10
 
11
  API_BASE = "https://prithivmlmods-qwen-image-edit-2511-loras-fast.hf.space"
12
+ INFER_URL = f"{API_BASE}/gradio/infer" # Not used, for clarity
13
+ NAMED_API_URL = f"{API_BASE}/gradio_api/call/v2/infer"
 
14
 
15
  LORA_STYLES = [
16
  'Multiple-Angles', 'Photo-to-Anime', 'Anime-V2', 'Light-Migration',
 
22
  ]
23
  MAX_SEED = 2**31 - 1
24
 
25
+ def encode_image_file_to_b64_json(image_path):
 
 
 
 
 
26
  try:
27
+ with open(image_path, "rb") as f:
28
+ image_bytes = f.read()
29
+ # 检查格式,如果不是 jpeg/png 尝试转码
30
  try:
31
+ img = Image.open(BytesIO(image_bytes))
32
+ buffered = BytesIO()
33
+ img.save(buffered, format="JPEG")
34
+ image_bytes = buffered.getvalue()
35
+ except Exception:
36
+ pass
37
+ im_b64 = base64.b64encode(image_bytes).decode("utf-8")
38
+ # gradio_client 一致,图片用 json 数组包裹并带类型
39
+ payload = [
40
+ {
41
+ "data": im_b64,
42
+ "mime_type": "image/jpeg",
43
+ "orig_name": os.path.basename(image_path),
44
+ }
45
+ ]
46
+ import json
47
+ return json.dumps(payload)
48
+ except Exception as e:
49
+ raise RuntimeError(f"图片编码失败: {e}")
50
+
51
+ def call_named_infer(
52
+ images_b64_json,
53
+ prompt,
54
+ lora_adapter,
55
+ seed,
56
+ randomize_seed,
57
+ guidance_scale,
58
+ steps
59
+ ):
60
+ headers = {
61
+ 'Authorization': f'Bearer {HF_TOKEN}',
62
+ 'Content-Type': 'application/json'
63
+ }
64
+ payload = {
65
+ "images_b64_json": images_b64_json,
66
+ "prompt": prompt,
67
+ "lora_adapter": lora_adapter,
68
+ "seed": int(seed),
69
+ "randomize_seed": bool(randomize_seed),
70
+ "guidance_scale": float(guidance_scale), # 注意类型
71
+ "steps": int(steps),
72
+ }
73
+ print("准备调用/infer:", payload)
74
+ resp = requests.post(NAMED_API_URL, json=payload, headers=headers)
75
  resp.raise_for_status()
76
+ # gradio api新版返回 event_id,然后/POLL 获取最终结果
77
  job = resp.json()
78
  event_id = job.get("event_id")
79
  return event_id
80
 
81
  def poll_infer(event_id):
82
+ # 必须用 gradio_api/call/infer/{event_id}
83
+ url = f"{API_BASE}/gradio_api/call/infer/{event_id}"
84
  headers = {'Authorization': f'Bearer {HF_TOKEN}'}
 
85
  import time
86
  for _ in range(60):
87
  resp = requests.get(url, headers=headers)
 
91
  return result.get("data"), result.get("outputs")
92
  elif result.get("status") == "error":
93
  raise Exception(result.get("error"))
94
+ time.sleep(2)
95
  raise TimeoutError("等候API返回超时")
96
 
97
  def infer(
 
115
  if randomize_seed:
116
  seed = random.randint(0, MAX_SEED)
117
 
 
118
  try:
119
+ images_b64_json = encode_image_file_to_b64_json(image)
 
 
 
 
 
 
 
 
 
120
  except Exception as e:
121
+ print(f"[图片 base64编码失败] {e}")
122
  return None, seed
123
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  try:
125
+ event_id = call_named_infer(
126
+ images_b64_json,
127
+ prompt,
128
+ lora_adapter,
129
+ seed,
130
+ randomize_seed,
131
+ guidance_scale,
132
+ steps
133
+ )
134
+ print("API返回event_id:", event_id)
135
  data, outputs = poll_infer(event_id)
136
  print("[API 完成] data:", data, "outputs:", outputs)
137
+ # 适配返回结构:outputs dict(含 path/url/seed 等,或者直接 path
138
+ img_out = None
 
139
  seed_used = seed
140
  if outputs:
141
+ if isinstance(outputs, dict):
142
+ img_out = outputs.get("url") or outputs.get("path")
 
 
143
  seed_used = outputs.get("seed", seed)
144
+ elif isinstance(outputs, str) and outputs.startswith("/"):
145
+ img_out = API_BASE + outputs
146
  else:
147
+ img_out = outputs
148
  elif data:
149
+ if isinstance(data, dict):
150
+ img_out = data.get("url") or data.get("path")
151
+ seed_used = data.get("seed", seed)
152
+ elif isinstance(data, str) and data.startswith("/"):
153
+ img_out = API_BASE + data
154
+ else:
155
+ img_out = data
156
+ # url 补全为完整远端URL(部分 gradio 返回 path 不是http开头)
157
  if img_out and isinstance(img_out, str) and not img_out.startswith("http"):
158
  img_out = API_BASE + img_out
159
  return img_out, int(seed_used)
 
206
  randomize_seed = gr.Checkbox(label="随机化种子", value=True)
207
  guidance_scale = gr.Slider(
208
  label="引导强度 (Guidance Scale)",
209
+ minimum=1.0,
210
  maximum=10.0,
211
  step=0.1,
212
  value=1.0,