selfit-camera commited on
Commit
159a427
·
1 Parent(s): b5bb9d3
Files changed (5) hide show
  1. app.py +343 -90
  2. datas/cat01.webp +3 -0
  3. datas/panda01.jpeg +3 -0
  4. datas/panda01m.jpeg +3 -0
  5. util.py +100 -51
app.py CHANGED
@@ -8,6 +8,8 @@ from util import process_image_edit, process_local_image_edit, download_and_chec
8
  from nfsw import NSFWDetector
9
 
10
  # Configuration parameters
 
 
11
  FREE_TRY_N = 20 # Free phase: first 15 tries without restrictions
12
  SLOW_TRY_N = 25 # Slow phase start: 25 tries
13
  SLOW2_TRY_N = 32 # Slow phase start: 32 tries
@@ -210,6 +212,9 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
210
 
211
  print(f"📊 User phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}")
212
 
 
 
 
213
  # Check if completely blocked
214
  if current_phase == 'blocked':
215
  # Generate blocked limit button
@@ -316,7 +321,7 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
316
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
317
 
318
  # Call image editing processing function
319
- result_url, message, task_uuid = process_image_edit(input_image, prompt.strip(), progress_callback)
320
 
321
  if result_url:
322
  print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
@@ -437,6 +442,28 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
437
  </div>
438
  """
439
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
441
  else:
442
  print(f"❌ Processing failed - IP: {client_ip}, error: {message}", flush=True)
@@ -446,7 +473,7 @@ def edit_image_interface(input_image, prompt, request: gr.Request, progress=gr.P
446
  print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
447
  return None, f"❌ Error occurred during processing: {str(e)}", gr.update(visible=False)
448
 
449
- def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Progress()):
450
  """
451
  Handle local editing requests (with phase-based limitations)
452
  """
@@ -463,14 +490,55 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
463
  if image_dict is None:
464
  return None, "Please upload an image and draw the area to edit", gr.update(visible=False)
465
 
466
- # Check if background and layers exist
467
- if "background" not in image_dict or "layers" not in image_dict:
468
- return None, "Please draw the area to edit on the image", gr.update(visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
 
470
- base_image = image_dict["background"]
471
- layers = image_dict["layers"]
 
472
 
473
- if not layers:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  return None, "Please draw the area to edit on the image", gr.update(visible=False)
475
 
476
  if not prompt or prompt.strip() == "":
@@ -489,6 +557,9 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
489
 
490
  print(f"📊 Local edit user phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}")
491
 
 
 
 
492
  # Check if completely blocked
493
  if current_phase == 'blocked':
494
  # Generate blocked limit button
@@ -594,8 +665,18 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
594
 
595
  print(f"✅ Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
596
 
 
 
 
 
 
597
  # Call local image editing processing function
598
- result_url, message, task_uuid = process_local_image_edit(base_image, layers, prompt.strip(), progress_callback)
 
 
 
 
 
599
 
600
  if result_url:
601
  print(f"✅ Local editing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
@@ -716,6 +797,28 @@ def local_edit_interface(image_dict, prompt, request: gr.Request, progress=gr.Pr
716
  </div>
717
  """
718
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
720
  else:
721
  print(f"❌ Local editing processing failed - IP: {client_ip}, error: {message}", flush=True)
@@ -807,100 +910,250 @@ def create_app():
807
  </div>
808
  """, padding=False)
809
 
810
- # Direct interface - no tabs needed
811
- with gr.Row():
812
- with gr.Column(scale=1):
813
- gr.Markdown("### 📸 Upload Image")
814
- input_image = gr.Image(
815
- label="Select image to edit",
816
- type="pil",
817
- height=512,
818
- elem_classes=["upload-area"]
819
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
820
 
821
- gr.Markdown("### ✍️ Editing Instructions")
822
- prompt_input = gr.Textbox(
823
- label="Enter editing prompt",
824
- placeholder="For example: change background to beach, add rainbow, remove background, etc...",
825
- lines=3,
826
- max_lines=5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
827
  )
828
 
829
- edit_button = gr.Button(
830
- "🚀 Start Editing",
831
- variant="primary",
832
- size="lg"
 
 
 
 
 
833
  )
834
-
835
- with gr.Column(scale=1):
836
- gr.Markdown("### 🎯 Editing Result")
837
- output_image = gr.Image(
838
- label="Edited image",
839
- height=320,
840
- elem_classes=["result-area"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
841
  )
842
 
843
- # Add "Use as Input" button
844
- use_as_input_btn = gr.Button(
845
- "🔄 Use as Input",
846
- variant="secondary",
847
- size="sm",
848
- elem_classes=["use-as-input-btn"]
849
- )
 
850
 
851
- status_output = gr.Textbox(
852
- label="Processing status",
853
- lines=2,
854
- max_lines=3,
855
- interactive=False
856
  )
857
 
858
- # Action buttons that will show after task completion
859
- action_buttons = gr.HTML(visible=False)
860
-
861
- # Example area
862
- gr.Markdown("### 💡 Prompt Examples")
863
- with gr.Row():
864
- example_prompts = [
865
- "Set the background to a grand opera stage with red curtains",
866
- "Change the outfit into a traditional Chinese hanfu with flowing sleeves",
867
- "Give the character blue dragon-like eyes with glowing pupils",
868
- "Change lighting to soft dreamy pastel glow",
869
- "Change pose to sitting cross-legged on the ground"
870
- ]
871
-
872
- for prompt in example_prompts:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  gr.Button(
874
- prompt,
875
- size="sm"
 
876
  ).click(
877
- lambda p=prompt: p,
878
- outputs=prompt_input
879
  )
880
 
881
- # Bind button click events - simplified, remove state management
882
- edit_button.click(
883
- fn=edit_image_interface,
884
- inputs=[input_image, prompt_input],
885
- outputs=[output_image, status_output, action_buttons],
886
- show_progress=True,
887
- # Increase concurrency settings
888
- concurrency_limit=10, # Limit concurrent requests
889
- api_name="global_edit"
890
- )
891
-
892
- # Simplify "Use as Input" button, directly copy image
893
- def simple_use_as_input(output_img):
894
- if output_img is not None:
895
- return output_img
896
- return None
897
-
898
- use_as_input_btn.click(
899
- fn=simple_use_as_input,
900
- inputs=[output_image],
901
- outputs=[input_image]
902
- )
903
-
904
  # SEO Content Section
905
  gr.HTML("""
906
  <div style="width: 100%; margin: 50px 0; padding: 0 20px;">
 
8
  from nfsw import NSFWDetector
9
 
10
  # Configuration parameters
11
+
12
+ TIP_TRY_N = 8 # Show like button tip after 12 tries
13
  FREE_TRY_N = 20 # Free phase: first 15 tries without restrictions
14
  SLOW_TRY_N = 25 # Slow phase start: 25 tries
15
  SLOW2_TRY_N = 32 # Slow phase start: 32 tries
 
212
 
213
  print(f"📊 User phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}")
214
 
215
+ # Check if user reached the like button tip threshold
216
+ show_like_tip = (current_count >= TIP_TRY_N)
217
+
218
  # Check if completely blocked
219
  if current_phase == 'blocked':
220
  # Generate blocked limit button
 
321
  print(f"✅ Processing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
322
 
323
  # Call image editing processing function
324
+ result_url, message, task_uuid = process_image_edit(input_image, prompt.strip(), None, progress_callback)
325
 
326
  if result_url:
327
  print(f"✅ Processing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
 
442
  </div>
443
  """
444
 
445
+ # Add popup script if needed (using different approach)
446
+ if show_like_tip:
447
+ action_buttons_html += """
448
+ <div style='display: flex; justify-content: center; margin: 15px 0 5px 0; padding: 0px;'>
449
+ <div style='
450
+ display: inline-flex;
451
+ align-items: center;
452
+ justify-content: center;
453
+ padding: 12px 24px;
454
+ background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
455
+ color: white;
456
+ border-radius: 10px;
457
+ font-weight: 600;
458
+ font-size: 14px;
459
+ text-align: center;
460
+ max-width: 400px;
461
+ box-shadow: 0 3px 12px rgba(255, 107, 107, 0.3);
462
+ border: none;
463
+ '>👉 Click the ❤️ Like button to unlock more free trial attempts!</div>
464
+ </div>
465
+ """
466
+
467
  return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
468
  else:
469
  print(f"❌ Processing failed - IP: {client_ip}, error: {message}", flush=True)
 
473
  print(f"❌ Processing exception - IP: {client_ip}, error: {str(e)}")
474
  return None, f"❌ Error occurred during processing: {str(e)}", gr.update(visible=False)
475
 
476
+ def local_edit_interface(image_dict, prompt, reference_image, request: gr.Request, progress=gr.Progress()):
477
  """
478
  Handle local editing requests (with phase-based limitations)
479
  """
 
490
  if image_dict is None:
491
  return None, "Please upload an image and draw the area to edit", gr.update(visible=False)
492
 
493
+ # Handle different input formats for ImageEditor
494
+ if isinstance(image_dict, dict):
495
+ # ImageEditor dict format
496
+ if "background" not in image_dict or "layers" not in image_dict:
497
+ return None, "Please draw the area to edit on the image", gr.update(visible=False)
498
+
499
+ base_image = image_dict["background"]
500
+ layers = image_dict["layers"]
501
+
502
+ # Special handling: if background is None but composite exists, use composite
503
+ if base_image is None and "composite" in image_dict and image_dict["composite"] is not None:
504
+ print("🔧 Background is None, using composite instead")
505
+ base_image = image_dict["composite"]
506
+ else:
507
+ # Simple case: Direct PIL Image (from example)
508
+ base_image = image_dict
509
+ layers = []
510
+
511
+ # Check for special example case - bypass mask requirement
512
+ is_example_case = prompt and prompt.startswith("EXAMPLE_PANDA_CAT_")
513
 
514
+ # Debug: check current state
515
+ if is_example_case:
516
+ print(f"🔍 Example case detected - base_image is None: {base_image is None}")
517
 
518
+ # Special handling for example case: load image directly from file
519
+ if is_example_case and base_image is None:
520
+ try:
521
+ from PIL import Image
522
+ import os
523
+
524
+ main_path = "datas/panda01.jpeg"
525
+ print(f"🔍 Trying to load: {main_path}, exists: {os.path.exists(main_path)}")
526
+
527
+ if os.path.exists(main_path):
528
+ base_image = Image.open(main_path)
529
+ print(f"✅ Successfully loaded example image: {base_image.size}")
530
+ else:
531
+ return None, f"❌ Example image not found: {main_path}", gr.update(visible=False)
532
+ except Exception as e:
533
+ return None, f"❌ Failed to load example image: {str(e)}", gr.update(visible=False)
534
+
535
+ # Additional check for base_image
536
+ if base_image is None:
537
+ if is_example_case:
538
+ print(f"❌ Example case but base_image still None!")
539
+ return None, "❌ No image found. Please upload an image first.", gr.update(visible=False)
540
+
541
+ if not layers and not is_example_case:
542
  return None, "Please draw the area to edit on the image", gr.update(visible=False)
543
 
544
  if not prompt or prompt.strip() == "":
 
557
 
558
  print(f"📊 Local edit user phase info - IP: {client_ip}, current phase: {current_phase}, generation count: {current_count}")
559
 
560
+ # Check if user reached the like button tip threshold
561
+ show_like_tip = (current_count >= TIP_TRY_N)
562
+
563
  # Check if completely blocked
564
  if current_phase == 'blocked':
565
  # Generate blocked limit button
 
665
 
666
  print(f"✅ Local editing started - IP: {client_ip}, phase: {current_phase}, total count: {updated_count}, prompt: {prompt.strip()}", flush=True)
667
 
668
+ # Clean prompt for API call
669
+ clean_prompt = prompt.strip()
670
+ if clean_prompt.startswith("EXAMPLE_PANDA_CAT_"):
671
+ clean_prompt = clean_prompt[18:] # Remove the prefix
672
+
673
  # Call local image editing processing function
674
+ if is_example_case:
675
+ # For example case, pass special flag to use local mask file
676
+ result_url, message, task_uuid = process_local_image_edit(base_image, layers, clean_prompt, reference_image, progress_callback, use_example_mask="datas/panda01m.jpeg")
677
+ else:
678
+ # Normal case
679
+ result_url, message, task_uuid = process_local_image_edit(base_image, layers, clean_prompt, reference_image, progress_callback)
680
 
681
  if result_url:
682
  print(f"✅ Local editing completed successfully - IP: {client_ip}, result_url: {result_url}, task_uuid: {task_uuid}", flush=True)
 
797
  </div>
798
  """
799
 
800
+ # Add popup script if needed (using different approach)
801
+ if show_like_tip:
802
+ action_buttons_html += """
803
+ <div style='display: flex; justify-content: center; margin: 15px 0 5px 0; padding: 0px;'>
804
+ <div style='
805
+ display: inline-flex;
806
+ align-items: center;
807
+ justify-content: center;
808
+ padding: 12px 24px;
809
+ background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
810
+ color: white;
811
+ border-radius: 10px;
812
+ font-weight: 600;
813
+ font-size: 14px;
814
+ text-align: center;
815
+ max-width: 400px;
816
+ box-shadow: 0 3px 12px rgba(255, 107, 107, 0.3);
817
+ border: none;
818
+ '>👉 Please consider clicking the ❤️ Like button to support this space!</div>
819
+ </div>
820
+ """
821
+
822
  return final_result, final_message, gr.update(value=action_buttons_html, visible=True)
823
  else:
824
  print(f"❌ Local editing processing failed - IP: {client_ip}, error: {message}", flush=True)
 
910
  </div>
911
  """, padding=False)
912
 
913
+ with gr.Tabs():
914
+ with gr.Tab("🌍 Global Editor"):
915
+ with gr.Row():
916
+ with gr.Column(scale=1):
917
+ gr.Markdown("### 📸 Upload Image")
918
+ input_image = gr.Image(
919
+ label="Select image to edit",
920
+ type="pil",
921
+ height=512,
922
+ elem_classes=["upload-area"]
923
+ )
924
+
925
+ gr.Markdown("### ✍️ Editing Instructions")
926
+ prompt_input = gr.Textbox(
927
+ label="Enter editing prompt",
928
+ placeholder="For example: change background to beach, add rainbow, remove background, etc...",
929
+ lines=3,
930
+ max_lines=5
931
+ )
932
+
933
+ edit_button = gr.Button(
934
+ "🚀 Start Editing",
935
+ variant="primary",
936
+ size="lg"
937
+ )
938
+
939
+ with gr.Column(scale=1):
940
+ gr.Markdown("### 🎯 Editing Result")
941
+ output_image = gr.Image(
942
+ label="Edited image",
943
+ height=320,
944
+ elem_classes=["result-area"]
945
+ )
946
+
947
+ use_as_input_btn = gr.Button(
948
+ "🔄 Use as Input",
949
+ variant="secondary",
950
+ size="sm",
951
+ elem_classes=["use-as-input-btn"]
952
+ )
953
+
954
+ status_output = gr.Textbox(
955
+ label="Processing status",
956
+ lines=2,
957
+ max_lines=3,
958
+ interactive=False
959
+ )
960
+
961
+ action_buttons = gr.HTML(visible=False)
962
 
963
+ gr.Markdown("### 💡 Prompt Examples")
964
+ with gr.Row():
965
+ example_prompts = [
966
+ "Set the background to a grand opera stage with red curtains",
967
+ "Change the outfit into a traditional Chinese hanfu with flowing sleeves",
968
+ "Give the character blue dragon-like eyes with glowing pupils",
969
+ "Change lighting to soft dreamy pastel glow",
970
+ "Change pose to sitting cross-legged on the ground"
971
+ ]
972
+
973
+ for prompt in example_prompts:
974
+ gr.Button(
975
+ prompt,
976
+ size="sm"
977
+ ).click(
978
+ lambda p=prompt: p,
979
+ outputs=prompt_input
980
+ )
981
+
982
+ edit_button.click(
983
+ fn=edit_image_interface,
984
+ inputs=[input_image, prompt_input],
985
+ outputs=[output_image, status_output, action_buttons],
986
+ show_progress=True,
987
+ concurrency_limit=10,
988
+ api_name="global_edit"
989
  )
990
 
991
+ def simple_use_as_input(output_img):
992
+ if output_img is not None:
993
+ return output_img
994
+ return None
995
+
996
+ use_as_input_btn.click(
997
+ fn=simple_use_as_input,
998
+ inputs=[output_image],
999
+ outputs=[input_image]
1000
  )
1001
+
1002
+ with gr.Tab("🖌️ Local Inpaint"):
1003
+ with gr.Row():
1004
+ with gr.Column(scale=1):
1005
+ gr.Markdown("### 📸 Upload Image and Draw Mask")
1006
+ local_input_image = gr.ImageEditor(
1007
+ label="Upload image and draw mask",
1008
+ type="pil",
1009
+ height=512,
1010
+ brush=gr.Brush(colors=["#ff0000"], default_size=180),
1011
+ elem_classes=["upload-area"]
1012
+ )
1013
+
1014
+ gr.Markdown("### 🖼️ Reference Image(Optional)")
1015
+ local_reference_image = gr.Image(
1016
+ label="Upload reference image (optional)",
1017
+ type="pil",
1018
+ height=256
1019
+ )
1020
+
1021
+ gr.Markdown("### ✍️ Editing Instructions")
1022
+ local_prompt_input = gr.Textbox(
1023
+ label="Enter local editing prompt",
1024
+ placeholder="For example: change selected area hair to golden, add patterns to selected object, change selected area color, etc...",
1025
+ lines=3,
1026
+ max_lines=5
1027
+ )
1028
+
1029
+ local_edit_button = gr.Button(
1030
+ "🎯 Start Local Editing",
1031
+ variant="primary",
1032
+ size="lg"
1033
+ )
1034
+
1035
+ with gr.Column(scale=1):
1036
+ gr.Markdown("### 🎯 Editing Result")
1037
+ local_output_image = gr.Image(
1038
+ label="Local edited image",
1039
+ height=320,
1040
+ elem_classes=["result-area"]
1041
+ )
1042
+
1043
+ local_use_as_input_btn = gr.Button(
1044
+ "🔄 Use as Input",
1045
+ variant="secondary",
1046
+ size="sm",
1047
+ elem_classes=["use-as-input-btn"]
1048
+ )
1049
+
1050
+ local_status_output = gr.Textbox(
1051
+ label="Processing status",
1052
+ lines=2,
1053
+ max_lines=3,
1054
+ interactive=False
1055
+ )
1056
+
1057
+ local_action_buttons = gr.HTML(visible=False)
1058
+
1059
+ local_edit_button.click(
1060
+ fn=local_edit_interface,
1061
+ inputs=[local_input_image, local_prompt_input, local_reference_image],
1062
+ outputs=[local_output_image, local_status_output, local_action_buttons],
1063
+ show_progress=True,
1064
+ concurrency_limit=8,
1065
+ api_name="local_edit"
1066
  )
1067
 
1068
+ def simple_local_use_as_input(output_img):
1069
+ if output_img is not None:
1070
+ return {
1071
+ "background": output_img,
1072
+ "layers": [],
1073
+ "composite": output_img
1074
+ }
1075
+ return None
1076
 
1077
+ local_use_as_input_btn.click(
1078
+ fn=simple_local_use_as_input,
1079
+ inputs=[local_output_image],
1080
+ outputs=[local_input_image]
 
1081
  )
1082
 
1083
+ # Local inpaint example
1084
+ gr.Markdown("### 💡 Local Inpaint Example")
1085
+
1086
+ def load_local_example():
1087
+ """Load panda to cat transformation example - simplified, mask handled in backend"""
1088
+ try:
1089
+ from PIL import Image
1090
+ import os
1091
+
1092
+ # Check file paths
1093
+ main_path = "datas/panda01.jpeg"
1094
+ ref_path = "datas/cat01.webp"
1095
+
1096
+ # Load main image
1097
+ if not os.path.exists(main_path):
1098
+ return None, None, "EXAMPLE_PANDA_CAT_let the cat ride on the panda"
1099
+
1100
+ main_img = Image.open(main_path)
1101
+
1102
+ # Load reference image
1103
+ if not os.path.exists(ref_path):
1104
+ ref_img = None
1105
+ else:
1106
+ ref_img = Image.open(ref_path)
1107
+
1108
+ # ImageEditor format
1109
+ editor_data = {
1110
+ "background": main_img,
1111
+ "layers": [],
1112
+ "composite": main_img
1113
+ }
1114
+
1115
+ # Special prompt to indicate this is the example case
1116
+ prompt = "EXAMPLE_PANDA_CAT_let the cat ride on the panda"
1117
+
1118
+ # Return just the PIL image instead of dict format to avoid UI state issues
1119
+ return main_img, ref_img, prompt
1120
+
1121
+ except Exception as e:
1122
+ return None, None, "EXAMPLE_PANDA_CAT_Transform the panda head into a cute cat head, keeping the body"
1123
+
1124
+ # Example display
1125
+ gr.Markdown("#### 🐼➡️🐱 Example: Panda to Cat Transformation")
1126
+ with gr.Row():
1127
+ with gr.Column(scale=2):
1128
+ # Preview images for local example
1129
+ with gr.Row():
1130
+ try:
1131
+ gr.Image("datas/panda01.jpeg", label="Main Image", height=120, width=120, show_label=True, interactive=False)
1132
+ gr.Image("datas/panda01m.jpeg", label="Mask", height=120, width=120, show_label=True, interactive=False)
1133
+ gr.Image("datas/cat01.webp", label="Reference", height=120, width=120, show_label=True, interactive=False)
1134
+ except:
1135
+ gr.Markdown("*Preview images not available*")
1136
+ gr.Markdown("**Prompt**: let the cat ride on the panda \n**Note**: Mask will be automatically applied when you submit this example")
1137
+ with gr.Column(scale=1):
1138
+ gr.Button(
1139
+ "🎨 Load Panda Example",
1140
+ size="lg",
1141
+ variant="secondary"
1142
+ ).click(
1143
+ fn=load_local_example,
1144
+ outputs=[local_input_image, local_reference_image, local_prompt_input]
1145
+ )
1146
+
1147
+ # Add a refresh button to fix UI state issues
1148
  gr.Button(
1149
+ "🔄 Refresh Image Editor",
1150
+ size="sm",
1151
+ variant="secondary"
1152
  ).click(
1153
+ fn=lambda: gr.update(),
1154
+ outputs=[local_input_image]
1155
  )
1156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1157
  # SEO Content Section
1158
  gr.HTML("""
1159
  <div style="width: 100%; margin: 50px 0; padding: 0 20px;">
datas/cat01.webp ADDED

Git LFS Details

  • SHA256: 1bf3d4cb894df32d7175b6ba091b21f7efe28178561ef68b8d0cb90f84adcb83
  • Pointer size: 130 Bytes
  • Size of remote file: 56.1 kB
datas/panda01.jpeg ADDED

Git LFS Details

  • SHA256: ce5abe529cdd94a469fc1f86515e8f45a4d7b7594f9eadb318b8a597fd769a81
  • Pointer size: 131 Bytes
  • Size of remote file: 112 kB
datas/panda01m.jpeg ADDED

Git LFS Details

  • SHA256: 659dfb517584cf3bc4ab3f5b444db325917a2f6badc97ec8f277d500e75c8d40
  • Pointer size: 130 Bytes
  • Size of remote file: 21.4 kB
util.py CHANGED
@@ -134,44 +134,7 @@ class R2Api:
134
  continue
135
  print("upload_from_memory time is ====>", time.time() - t1)
136
  return f"{self.domain}{cloud_path}"
137
-
138
- def upload_file(self, local_path, cloud_path):
139
- t1 = time.time()
140
- head_dict = {
141
- 'jpg': 'image/jpeg',
142
- 'jpeg': 'image/jpeg',
143
- 'png': 'image/png',
144
- 'gif': 'image/gif',
145
- 'bmp': 'image/bmp',
146
- 'webp': 'image/webp',
147
- 'ico': 'image/x-icon'
148
- }
149
- ftype = os.path.basename(local_path).split(".")[-1].lower()
150
- ctype = head_dict.get(ftype, 'application/octet-stream')
151
- headers = {"Content-Type": ctype}
152
-
153
-
154
- cloud_path = f"ImageEdit/Uploads/{str(datetime.date.today())}/{os.path.basename(local_path)}"
155
- url = self.client.generate_presigned_url(
156
- "put_object",
157
- Params={"Bucket": self.R2_BUCKET, "Key": cloud_path, "ContentType": ctype},
158
- ExpiresIn=604800
159
- )
160
 
161
- retry_count = 0
162
- while retry_count < 3:
163
- try:
164
- with open(local_path, 'rb') as f:
165
- self.session.put(url, data=f.read(), headers=headers, timeout=8)
166
- break
167
- except (requests.exceptions.Timeout, requests.exceptions.RequestException):
168
- retry_count += 1
169
- if retry_count == 3:
170
- raise Exception('Failed to upload file to R2 after 3 retries!')
171
- continue
172
- print("upload_file time is ====>", time.time() - t1)
173
- return f"{self.domain}{cloud_path}"
174
-
175
  def upload_user_img_r2(clientIp, timeId, pil_image):
176
  """
177
  Upload PIL Image directly to R2 without saving to local file
@@ -293,7 +256,7 @@ def upload_mask_image_r2(client_ip, time_id, mask_image):
293
 
294
 
295
 
296
- def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_url=""):
297
  """
298
  Submit image editing task with improved error handling using API v2
299
  """
@@ -311,6 +274,9 @@ def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_ur
311
  "priority": 0,
312
  "secret_key": "219ngu"
313
  }
 
 
 
314
 
315
  retry_count = 0
316
  max_retries = 3
@@ -423,7 +389,7 @@ def check_task_status(task_id):
423
  return 'error', None, f"Failed after {max_retries} retries"
424
 
425
 
426
- def process_image_edit(img_input, prompt, progress_callback=None):
427
  """
428
  Complete process for image editing
429
 
@@ -461,8 +427,28 @@ def process_image_edit(img_input, prompt, progress_callback=None):
461
  if progress_callback:
462
  progress_callback("submitting edit task...")
463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  # Submit image editing task
465
- task_id, error = submit_image_edit_task(uploaded_url, prompt)
466
  if error:
467
  return None, error, None
468
 
@@ -518,7 +504,7 @@ def process_image_edit(img_input, prompt, progress_callback=None):
518
  return None, f"error occurred during processing: {str(e)}", None
519
 
520
 
521
- def process_local_image_edit(base_image, layers, prompt, progress_callback=None):
522
  """
523
  处理局部图片编辑的完整流程
524
 
@@ -536,15 +522,50 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
536
  if progress_callback:
537
  progress_callback("creating mask image...")
538
 
539
- # 从layers创建mask图片
540
- mask_image = create_mask_from_layers(base_image, layers)
541
-
542
- # 检查mask是否有内容
543
- mask_array = np.array(mask_image)
544
- if np.max(mask_array) == 0:
545
- return None, "please draw mask", None
546
-
547
- print(f"📝 创建mask图片成功,绘制区域像素数: {np.sum(mask_array > 0)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
  if progress_callback:
550
  progress_callback("uploading original image...")
@@ -570,15 +591,43 @@ def process_local_image_edit(base_image, layers, prompt, progress_callback=None)
570
  if "?" in mask_url:
571
  mask_url = mask_url.split("?")[0]
572
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  print(f"📤 图片上传成功:")
574
  print(f" 原始图片: {uploaded_url}")
575
  print(f" Mask图片: {mask_url}")
 
 
576
 
577
  if progress_callback:
578
  progress_callback("submitting local edit task...")
579
 
580
  # 提交局部图片编辑任务 (task_type=81)
581
- task_id, error = submit_image_edit_task(uploaded_url, prompt, task_type="81", mask_image_url=mask_url)
 
 
 
 
 
 
582
  if error:
583
  return None, error, None
584
 
 
134
  continue
135
  print("upload_from_memory time is ====>", time.time() - t1)
136
  return f"{self.domain}{cloud_path}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  def upload_user_img_r2(clientIp, timeId, pil_image):
139
  """
140
  Upload PIL Image directly to R2 without saving to local file
 
256
 
257
 
258
 
259
+ def submit_image_edit_task(user_image_url, prompt, task_type="80", mask_image_url="", reference_image_url=""):
260
  """
261
  Submit image editing task with improved error handling using API v2
262
  """
 
274
  "priority": 0,
275
  "secret_key": "219ngu"
276
  }
277
+
278
+ if reference_image_url:
279
+ data["user_image2"] = reference_image_url
280
 
281
  retry_count = 0
282
  max_retries = 3
 
389
  return 'error', None, f"Failed after {max_retries} retries"
390
 
391
 
392
+ def process_image_edit(img_input, prompt, reference_image=None, progress_callback=None):
393
  """
394
  Complete process for image editing
395
 
 
427
  if progress_callback:
428
  progress_callback("submitting edit task...")
429
 
430
+ reference_url = ""
431
+ if reference_image is not None:
432
+ try:
433
+ if progress_callback:
434
+ progress_callback("uploading reference image...")
435
+
436
+ if hasattr(reference_image, 'save'):
437
+ reference_pil = reference_image
438
+ else:
439
+ reference_pil = Image.open(reference_image)
440
+
441
+ reference_url = upload_user_img_r2(client_ip, time_id, reference_pil)
442
+ if not reference_url:
443
+ return None, "reference image upload failed", None
444
+
445
+ if "?" in reference_url:
446
+ reference_url = reference_url.split("?")[0]
447
+ except Exception as e:
448
+ return None, f"reference image processing failed: {str(e)}", None
449
+
450
  # Submit image editing task
451
+ task_id, error = submit_image_edit_task(uploaded_url, prompt, reference_image_url=reference_url)
452
  if error:
453
  return None, error, None
454
 
 
504
  return None, f"error occurred during processing: {str(e)}", None
505
 
506
 
507
+ def process_local_image_edit(base_image, layers, prompt, reference_image=None, progress_callback=None, use_example_mask=None):
508
  """
509
  处理局部图片编辑的完整流程
510
 
 
522
  if progress_callback:
523
  progress_callback("creating mask image...")
524
 
525
+ # Check if we should use example mask (backdoor for example case)
526
+ if use_example_mask:
527
+ # Load local mask file for example
528
+ try:
529
+ from PIL import Image
530
+ import os
531
+
532
+ # Check if base_image is valid
533
+ if base_image is None:
534
+ return None, "Base image is None, cannot process example mask", None
535
+
536
+ if os.path.exists(use_example_mask):
537
+ mask_image = Image.open(use_example_mask)
538
+
539
+ # Ensure mask has same size as base image
540
+ if hasattr(base_image, 'size') and mask_image.size != base_image.size:
541
+ mask_image = mask_image.resize(base_image.size)
542
+
543
+ # Ensure mask is in L mode (grayscale)
544
+ if mask_image.mode != 'L':
545
+ mask_image = mask_image.convert('L')
546
+
547
+ print(f"🎭 Using example mask from: {use_example_mask}, size: {mask_image.size}")
548
+ else:
549
+ return None, f"Example mask file not found: {use_example_mask}", None
550
+ except Exception as e:
551
+ import traceback
552
+ traceback.print_exc()
553
+ return None, f"Failed to load example mask: {str(e)}", None
554
+ else:
555
+ # Normal case: create mask from layers
556
+ mask_image = create_mask_from_layers(base_image, layers)
557
+
558
+ # 检查mask是否有内容
559
+ mask_array = np.array(mask_image)
560
+ if np.max(mask_array) == 0:
561
+ return None, "please draw mask", None
562
+
563
+ # Print mask statistics
564
+ if not use_example_mask:
565
+ print(f"📝 创建mask图片成功,绘制区域像素数: {np.sum(mask_array > 0)}")
566
+ else:
567
+ mask_array = np.array(mask_image)
568
+ print(f"🎭 Example mask loaded successfully, mask pixels: {np.sum(mask_array > 0)}")
569
 
570
  if progress_callback:
571
  progress_callback("uploading original image...")
 
591
  if "?" in mask_url:
592
  mask_url = mask_url.split("?")[0]
593
 
594
+ reference_url = ""
595
+ if reference_image is not None:
596
+ try:
597
+ if progress_callback:
598
+ progress_callback("uploading reference image...")
599
+
600
+ if hasattr(reference_image, 'save'):
601
+ reference_pil = reference_image
602
+ else:
603
+ reference_pil = Image.open(reference_image)
604
+
605
+ reference_url = upload_user_img_r2(client_ip, time_id, reference_pil)
606
+ if not reference_url:
607
+ return None, "reference image upload failed", None
608
+
609
+ if "?" in reference_url:
610
+ reference_url = reference_url.split("?")[0]
611
+ except Exception as e:
612
+ return None, f"reference image processing failed: {str(e)}", None
613
+
614
  print(f"📤 图片上传成功:")
615
  print(f" 原始图片: {uploaded_url}")
616
  print(f" Mask图片: {mask_url}")
617
+ if reference_url:
618
+ print(f" 参考图片: {reference_url}")
619
 
620
  if progress_callback:
621
  progress_callback("submitting local edit task...")
622
 
623
  # 提交局部图片编辑任务 (task_type=81)
624
+ task_id, error = submit_image_edit_task(
625
+ uploaded_url,
626
+ prompt,
627
+ task_type="81",
628
+ mask_image_url=mask_url,
629
+ reference_image_url=reference_url
630
+ )
631
  if error:
632
  return None, error, None
633