selfit-camera commited on
Commit
6cda385
·
1 Parent(s): 4422126
Files changed (2) hide show
  1. app.py +34 -1
  2. util.py +13 -329
app.py CHANGED
@@ -660,7 +660,40 @@ def edit_image_interface(input_image, prompt, lang, request: gr.Request, progres
660
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, priority: {task_priority}, prompt: {prompt.strip()}", flush=True)
661
 
662
  # Call image editing processing function with priority
663
- input_image_url, result_url, message, task_uuid = process_image_edit(input_image, prompt.strip(), None, progress_callback, priority=task_priority)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
 
665
  if result_url:
666
  print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
 
660
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, priority: {task_priority}, prompt: {prompt.strip()}", flush=True)
661
 
662
  # Call image editing processing function with priority
663
+ input_image_url, result_url, message, task_uuid = process_image_edit(input_image, prompt.strip(), None, progress_callback, priority=task_priority, client_ip=client_ip)
664
+
665
+ # Check if HF user limit exceeded
666
+ if message and message.startswith("HF_LIMIT_EXCEEDED:"):
667
+ error_message = message.replace("HF_LIMIT_EXCEEDED:", "")
668
+ # Generate HF limit exceeded button (similar to blocked status)
669
+ hf_limit_url = 'https://omnicreator.net/#generator'
670
+
671
+ hf_limit_button_html = f"""
672
+ <div style='display: flex; justify-content: center; gap: 15px; margin: 10px 0 5px 0; padding: 0px;'>
673
+ <a href='{hf_limit_url}' target='_blank' style='
674
+ display: inline-flex;
675
+ align-items: center;
676
+ justify-content: center;
677
+ padding: 16px 32px;
678
+ background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
679
+ color: white;
680
+ text-decoration: none;
681
+ border-radius: 12px;
682
+ font-weight: 600;
683
+ font-size: 16px;
684
+ text-align: center;
685
+ min-width: 200px;
686
+ box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
687
+ transition: all 0.3s ease;
688
+ border: none;
689
+ '>&#128640; Unlimited Generation</a>
690
+ </div>
691
+ """
692
+
693
+ # Use translated message or default
694
+ limit_message = error_message if error_message else t("error_free_limit_reached", lang)
695
+
696
+ return None, limit_message, gr.update(value=hf_limit_button_html, visible=True)
697
 
698
  if result_url:
699
  print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
util.py CHANGED
@@ -18,10 +18,6 @@ from botocore.client import Config
18
  from PIL import Image
19
 
20
 
21
- # TOKEN = os.environ['TOKEN']
22
- # APIKEY = os.environ['APIKEY']
23
- # UKAPIURL = os.environ['UKAPIURL']
24
-
25
  OneKey = os.environ['OneKey'].strip()
26
  OneKey = OneKey.split("#")
27
  TOKEN = OneKey[0]
@@ -33,46 +29,8 @@ R2_SECRET_KEY = OneKey[5]
33
  R2_ENDPOINT = OneKey[6]
34
  GoodWebsiteUrl = OneKey[7]
35
 
36
-
37
- # tmpFolder is no longer needed since we upload directly from memory
38
- # tmpFolder = "tmp"
39
- # os.makedirs(tmpFolder, exist_ok=True)
40
-
41
-
42
- # Legacy function - no longer used since we upload directly from memory
43
- # def upload_user_img(clientIp, timeId, img):
44
- # fileName = clientIp.replace(".", "")+str(timeId)+".jpg"
45
- # local_path = os.path.join(tmpFolder, fileName)
46
- # img = cv2.imread(img)
47
- # cv2.imwrite(os.path.join(tmpFolder, fileName), img)
48
- #
49
- # json_data = {
50
- # "token": TOKEN,
51
- # "input1": fileName,
52
- # "input2": "",
53
- # "protocol": "",
54
- # "cloud": "ali"
55
- # }
56
- #
57
- # session = requests.session()
58
- # ret = requests.post(
59
- # f"{UKAPIURL}/upload",
60
- # headers={'Content-Type': 'application/json'},
61
- # json=json_data
62
- # )
63
- #
64
- # res = ""
65
- # if ret.status_code==200:
66
- # if 'upload1' in ret.json():
67
- # upload_url = ret.json()['upload1']
68
- # headers = {'Content-Type': 'image/jpeg'}
69
- # response = session.put(upload_url, data=open(local_path, 'rb').read(), headers=headers)
70
- # # print(response.status_code)
71
- # if response.status_code == 200:
72
- # res = upload_url
73
- # if os.path.exists(local_path):
74
- # os.remove(local_path)
75
- # return res
76
 
77
 
78
  class R2Api:
@@ -164,100 +122,7 @@ def upload_user_img_r2(clientIp, timeId, pil_image):
164
  return res
165
 
166
 
167
-
168
- def create_mask_from_layers(base_image, layers):
169
- """
170
- Create mask image from ImageEditor layers
171
-
172
- Args:
173
- base_image (PIL.Image): Original image
174
- layers (list): ImageEditor layer data
175
-
176
- Returns:
177
- PIL.Image: Black and white mask image
178
- """
179
- from PIL import Image, ImageDraw
180
- import numpy as np
181
-
182
- # Create blank mask with same size as original image
183
- mask = Image.new('L', base_image.size, 0) # 'L' mode is grayscale, 0 is black
184
-
185
- if not layers:
186
- return mask
187
-
188
- # Iterate through all layers, set drawn areas to white
189
- for layer in layers:
190
- if layer is not None:
191
- # Convert layer to numpy array
192
- layer_array = np.array(layer)
193
-
194
- # Check layer format
195
- if len(layer_array.shape) == 3: # RGB/RGBA format
196
- # If RGBA, check alpha channel
197
- if layer_array.shape[2] == 4:
198
- # Use alpha channel as mask
199
- alpha_channel = layer_array[:, :, 3]
200
- # Set non-transparent areas (alpha > 0) to white
201
- mask_array = np.where(alpha_channel > 0, 255, 0).astype(np.uint8)
202
- else:
203
- # RGB format, check if not pure black (0,0,0)
204
- # Assume drawn areas are non-black
205
- non_black = np.any(layer_array > 0, axis=2)
206
- mask_array = np.where(non_black, 255, 0).astype(np.uint8)
207
- elif len(layer_array.shape) == 2: # Grayscale
208
- # Use grayscale values directly, set non-zero areas to white
209
- mask_array = np.where(layer_array > 0, 255, 0).astype(np.uint8)
210
- else:
211
- continue
212
-
213
- # Convert mask_array to PIL image and merge into total mask
214
- layer_mask = Image.fromarray(mask_array, mode='L')
215
- # Resize to match original image
216
- if layer_mask.size != base_image.size:
217
- layer_mask = layer_mask.resize(base_image.size, Image.LANCZOS)
218
-
219
- # Merge masks (use maximum value to ensure all drawn areas are included)
220
- mask_array_current = np.array(mask)
221
- layer_mask_array = np.array(layer_mask)
222
- combined_mask_array = np.maximum(mask_array_current, layer_mask_array)
223
- mask = Image.fromarray(combined_mask_array, mode='L')
224
-
225
- return mask
226
-
227
-
228
- def upload_mask_image_r2(client_ip, time_id, mask_image):
229
- """
230
- Upload mask image to R2 directly from memory
231
-
232
- Args:
233
- client_ip (str): Client IP
234
- time_id (int): Timestamp
235
- mask_image (PIL.Image): Mask image
236
-
237
- Returns:
238
- str: Uploaded URL
239
- """
240
- # Generate unique filename using UUID to prevent file conflicts in concurrent environment
241
- unique_id = str(uuid.uuid4())
242
- file_name = f"mask_img_{unique_id}_{time_id}.png"
243
-
244
- try:
245
- # Convert mask image to bytes
246
- img_buffer = io.BytesIO()
247
- mask_image.save(img_buffer, format='PNG')
248
- img_data = img_buffer.getvalue()
249
-
250
- # Upload directly from memory
251
- res = R2Api().upload_from_memory(img_data, file_name, 'image/png')
252
-
253
- return res
254
- except Exception as e:
255
- print(f"Failed to upload mask image: {e}")
256
- return None
257
-
258
-
259
-
260
- def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_url="", reference_image_url="", priority=0):
261
  """
262
  Submit image editing task with improved error handling using API v2
263
 
@@ -270,13 +135,15 @@ def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_ur
270
  }
271
 
272
  data = {
 
273
  "user_image": user_image_url,
274
  "user_mask": mask_image_url,
275
  "type": task_type,
276
  "text": prompt,
277
  "user_uuid": APIKEY,
278
  "priority": priority,
279
- "secret_key": "219ngu"
 
280
  }
281
 
282
  if reference_image_url:
@@ -288,7 +155,7 @@ def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_ur
288
  while retry_count < max_retries:
289
  try:
290
  response = requests.post(
291
- f'{UKAPIURL}/public_image_edit_v2',
292
  headers=headers,
293
  json=data,
294
  timeout=30 # 增加超时时间
@@ -344,7 +211,7 @@ def check_task_status(task_id):
344
  while retry_count < max_retries:
345
  try:
346
  response = requests.post(
347
- f'{UKAPIURL}/status_image_edit_v2',
348
  headers=headers,
349
  json=data,
350
  timeout=15 # 状态查询超时时间短一些
@@ -393,7 +260,7 @@ def check_task_status(task_id):
393
  return 'error', None, f"Failed after {max_retries} retries"
394
 
395
 
396
- def process_image_edit(img_input, prompt, reference_image=None, progress_callback=None, priority=0):
397
  """
398
  Complete process for image editing
399
 
@@ -405,7 +272,6 @@ def process_image_edit(img_input, prompt, reference_image=None, progress_callbac
405
  """
406
  try:
407
  # Generate client IP and timestamp
408
- client_ip = "127.0.0.1" # Default IP
409
  time_id = int(time.time())
410
 
411
  # Process input image - supports PIL Image and file path
@@ -453,8 +319,11 @@ def process_image_edit(img_input, prompt, reference_image=None, progress_callbac
453
  return None, None, f"reference image processing failed: {str(e)}", None
454
 
455
  # Submit image editing task
456
- task_id, error = submit_image_edit_task(uploaded_url, prompt, reference_image_url=reference_url, priority=priority)
457
  if error:
 
 
 
458
  return None, None, error, None
459
 
460
  if progress_callback:
@@ -509,191 +378,6 @@ def process_image_edit(img_input, prompt, reference_image=None, progress_callbac
509
  return None, None, f"error occurred during processing: {str(e)}", None
510
 
511
 
512
- def process_local_image_edit(base_image, layers, prompt, reference_image=None, progress_callback=None, use_example_mask=None, priority=0):
513
- """
514
- 处理局部图片编辑的完整流程
515
-
516
- Args:
517
- base_image (PIL.Image): 原始图片
518
- layers (list): ImageEditor的层数据
519
- prompt (str): 编辑指令
520
- progress_callback: 进度回调函数
521
- priority (int): Task priority, 1 for high priority, 0 for normal priority
522
- """
523
- try:
524
- # Generate client IP and timestamp
525
- client_ip = "127.0.0.1" # Default IP
526
- time_id = int(time.time())
527
-
528
- if progress_callback:
529
- progress_callback("creating mask image...")
530
-
531
- # Check if we should use example mask (backdoor for example case)
532
- if use_example_mask:
533
- # Load local mask file for example
534
- try:
535
- from PIL import Image
536
- import os
537
-
538
- # Check if base_image is valid
539
- if base_image is None:
540
- return None, None, "Base image is None, cannot process example mask", None
541
-
542
- if os.path.exists(use_example_mask):
543
- mask_image = Image.open(use_example_mask)
544
-
545
- # Ensure mask has same size as base image
546
- if hasattr(base_image, 'size') and mask_image.size != base_image.size:
547
- mask_image = mask_image.resize(base_image.size)
548
-
549
- # Ensure mask is in L mode (grayscale)
550
- if mask_image.mode != 'L':
551
- mask_image = mask_image.convert('L')
552
-
553
- print(f"🎭 Using example mask from: {use_example_mask}, size: {mask_image.size}")
554
- else:
555
- return None, None, f"Example mask file not found: {use_example_mask}", None
556
- except Exception as e:
557
- import traceback
558
- traceback.print_exc()
559
- return None, None, f"Failed to load example mask: {str(e)}", None
560
- else:
561
- # Normal case: create mask from layers
562
- mask_image = create_mask_from_layers(base_image, layers)
563
-
564
- # 检查mask是否有内容
565
- mask_array = np.array(mask_image)
566
- if np.max(mask_array) == 0:
567
- return None, None, "please draw mask", None
568
-
569
- # Print mask statistics
570
- if not use_example_mask:
571
- print(f"📝 创建mask图片成功,绘制区域像素数: {np.sum(mask_array > 0)}")
572
- else:
573
- mask_array = np.array(mask_image)
574
- print(f"🎭 Example mask loaded successfully, mask pixels: {np.sum(mask_array > 0)}")
575
-
576
- if progress_callback:
577
- progress_callback("uploading original image...")
578
-
579
- # 直接从内存上传原始图片
580
- uploaded_url = upload_user_img_r2(client_ip, time_id, base_image)
581
- if not uploaded_url:
582
- return None, None, "original image upload failed", None
583
-
584
- # 从上传 URL 中提取实际的图片 URL
585
- if "?" in uploaded_url:
586
- uploaded_url = uploaded_url.split("?")[0]
587
-
588
- if progress_callback:
589
- progress_callback("uploading mask image...")
590
-
591
- # 直接从内存上传mask图片
592
- mask_url = upload_mask_image_r2(client_ip, time_id, mask_image)
593
- if not mask_url:
594
- return None, None, "mask image upload failed", None
595
-
596
- # 从上传 URL 中提取实际的图片 URL
597
- if "?" in mask_url:
598
- mask_url = mask_url.split("?")[0]
599
-
600
- reference_url = ""
601
- if reference_image is not None:
602
- try:
603
- if progress_callback:
604
- progress_callback("uploading reference image...")
605
-
606
- if hasattr(reference_image, 'save'):
607
- reference_pil = reference_image
608
- else:
609
- reference_pil = Image.open(reference_image)
610
-
611
- reference_url = upload_user_img_r2(client_ip, time_id, reference_pil)
612
- if not reference_url:
613
- return None, None, "reference image upload failed", None
614
-
615
- if "?" in reference_url:
616
- reference_url = reference_url.split("?")[0]
617
- except Exception as e:
618
- return None, None, f"reference image processing failed: {str(e)}", None
619
-
620
- print(f"📤 图片上传成功:")
621
- print(f" 原始图片: {uploaded_url}")
622
- print(f" Mask图片: {mask_url}")
623
- if reference_url:
624
- print(f" 参考图片: {reference_url}")
625
-
626
- if progress_callback:
627
- progress_callback("submitting local edit task...")
628
-
629
- # 提交局部图片编辑任务 (task_type=81)
630
- task_id, error = submit_image_edit_task(
631
- uploaded_url,
632
- prompt,
633
- task_type="81",
634
- mask_image_url=mask_url,
635
- reference_image_url=reference_url,
636
- priority=priority
637
- )
638
- if error:
639
- return None, None, error, None
640
-
641
- if progress_callback:
642
- progress_callback(f"task submitted, ID: {task_id}, processing...")
643
-
644
- print(f"🚀 局部编辑任务已提交,任务ID: {task_id}")
645
-
646
- # Wait for task completion
647
- max_attempts = 60 # Wait up to 10 minutes
648
- task_uuid = None
649
- for attempt in range(max_attempts):
650
- status, output_url, task_data = check_task_status(task_id)
651
-
652
- # Extract task_uuid from task_data
653
- if task_data and isinstance(task_data, dict):
654
- task_uuid = task_data.get('uuid', None)
655
-
656
- if status == 'completed':
657
- if output_url:
658
- print(f"✅ 局部编辑任务完成,结果: {output_url}")
659
- return uploaded_url, output_url, "local image edit completed", task_uuid
660
- else:
661
- return None, None, "task completed but no result image returned", task_uuid
662
- elif status == 'error' or status == 'failed':
663
- return None, None, f"task processing failed: {task_data}", task_uuid
664
- elif status in ['queued', 'processing', 'running', 'created', 'working']:
665
- # Enhanced progress message with queue info and website promotion
666
- if progress_callback and task_data and isinstance(task_data, dict):
667
- queue_info = task_data.get('queue_info', {})
668
- if queue_info and status in ['queued', 'created']:
669
- tasks_ahead = queue_info.get('tasks_ahead', 0)
670
- current_priority = queue_info.get('current_priority', 0)
671
- if tasks_ahead > 0:
672
- progress_callback(f"⏳ Queue: {tasks_ahead} tasks ahead | Low priority | Visit website for instant processing → https://omnicreator.net/#generator")
673
- else:
674
- progress_callback(f"🚀 Processing your local editing request...")
675
- elif status == 'processing':
676
- progress_callback(f"🎨 AI is processing... Please wait")
677
- elif status in ['running', 'working']:
678
- progress_callback(f"⚡ Generating... Almost done")
679
- else:
680
- progress_callback(f"📋 Task status: {status}")
681
- else:
682
- if progress_callback:
683
- progress_callback(f"processing... (status: {status})")
684
- time.sleep(1) # Wait 1 second before retry
685
- else:
686
- if progress_callback:
687
- progress_callback(f"unknown status: {status}")
688
- time.sleep(1)
689
-
690
- return None, None, "task processing timeout", task_uuid
691
-
692
- except Exception as e:
693
- print(f"❌ 局部编辑处理异常: {str(e)}")
694
- return None, None, f"error occurred during processing: {str(e)}", None
695
-
696
-
697
  def download_and_check_result_nsfw(image_url, nsfw_detector=None):
698
  """
699
  下载结果图片并进行NSFW检测
 
18
  from PIL import Image
19
 
20
 
 
 
 
 
21
  OneKey = os.environ['OneKey'].strip()
22
  OneKey = OneKey.split("#")
23
  TOKEN = OneKey[0]
 
29
  R2_ENDPOINT = OneKey[6]
30
  GoodWebsiteUrl = OneKey[7]
31
 
32
+ # HuggingFace用户任务限制配置
33
+ HF_TASK_LIMIT = 2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
 
36
  class R2Api:
 
122
  return res
123
 
124
 
125
+ def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_url="", reference_image_url="", priority=0, client_ip=""):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  """
127
  Submit image editing task with improved error handling using API v2
128
 
 
135
  }
136
 
137
  data = {
138
+ "client_ip":client_ip,
139
  "user_image": user_image_url,
140
  "user_mask": mask_image_url,
141
  "type": task_type,
142
  "text": prompt,
143
  "user_uuid": APIKEY,
144
  "priority": priority,
145
+ "secret_key": "219ngu",
146
+ "hf_task_limit": HF_TASK_LIMIT # 传递HF用户任务限制配置
147
  }
148
 
149
  if reference_image_url:
 
155
  while retry_count < max_retries:
156
  try:
157
  response = requests.post(
158
+ f'{UKAPIURL}/public_image_edit_v3',
159
  headers=headers,
160
  json=data,
161
  timeout=30 # 增加超时时间
 
211
  while retry_count < max_retries:
212
  try:
213
  response = requests.post(
214
+ f'{UKAPIURL}/status_image_edit_v3',
215
  headers=headers,
216
  json=data,
217
  timeout=15 # 状态查询超时时间短一些
 
260
  return 'error', None, f"Failed after {max_retries} retries"
261
 
262
 
263
+ def process_image_edit(img_input, prompt, reference_image=None, progress_callback=None, priority=0, client_ip=""):
264
  """
265
  Complete process for image editing
266
 
 
272
  """
273
  try:
274
  # Generate client IP and timestamp
 
275
  time_id = int(time.time())
276
 
277
  # Process input image - supports PIL Image and file path
 
319
  return None, None, f"reference image processing failed: {str(e)}", None
320
 
321
  # Submit image editing task
322
+ task_id, error = submit_image_edit_task(uploaded_url, prompt, reference_image_url=reference_url, priority=priority, client_ip=client_ip)
323
  if error:
324
+ # Check if error is HF user limit exceeded
325
+ if "Task creation failed: HuggingFace user has reached the task limit" in error or ("HuggingFace" in error and "task limit" in error):
326
+ return None, None, "HF_LIMIT_EXCEEDED:" + error, None
327
  return None, None, error, None
328
 
329
  if progress_callback:
 
378
  return None, None, f"error occurred during processing: {str(e)}", None
379
 
380
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  def download_and_check_result_nsfw(image_url, nsfw_detector=None):
382
  """
383
  下载结果图片并进行NSFW检测