ALIMHGFY commited on
Commit
cf116d7
·
verified ·
1 Parent(s): e3001ff

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -36
app.py CHANGED
@@ -1,42 +1,60 @@
1
  import gradio as gr
2
  import numpy as np
3
  from PIL import Image
 
4
 
5
  def get_mask_bbox(mask: Image.Image):
6
  mask_np = np.array(mask)
7
  if len(mask_np.shape) == 3:
8
- # إذا كان القناع ملون، استخدم قناة أخيرة (alpha إذا RGBA)
9
  mask_np = mask_np[..., -1] if mask_np.shape[2] == 4 else np.mean(mask_np, axis=-1)
10
  ys, xs = np.where(mask_np > 0)
11
  if ys.size == 0 or xs.size == 0:
12
  return None
13
  return int(xs.min()), int(ys.min()), int(xs.max()), int(ys.max())
14
 
15
- def mask_to_alpha(mask: Image.Image):
16
- # إذا كان القناع RGBA وفيه قناة ألفا واضحة
17
- if mask.mode == "RGBA":
18
- alpha = mask.split()[-1]
19
- return alpha
20
- # إذا كان القناع ملون (RGB)، حوله إلى رمادي واستخدمه كشفافية
21
- elif mask.mode == "RGB":
22
- gray = mask.convert("L")
23
- return gray
24
- # إذا كان القناع بالفعل رمادي أو ثنائي
25
- elif mask.mode == "L":
26
- return mask
27
- # إذا كان فيه وضع آخر، حوله لرمادي
28
- else:
29
- return mask.convert("L")
30
 
31
- def crop_masked_object(img: Image.Image, mask: Image.Image):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  bbox = get_mask_bbox(mask)
33
  if bbox is None:
34
  return None, None
35
  xmin, ymin, xmax, ymax = bbox
36
  cropped_img = img.crop((xmin, ymin, xmax + 1, ymax + 1)).convert("RGBA")
37
  cropped_mask = mask.crop((xmin, ymin, xmax + 1, ymax + 1))
38
- alpha_mask = mask_to_alpha(cropped_mask)
39
  cropped_img.putalpha(alpha_mask)
 
 
 
 
 
 
 
 
 
 
 
40
  return cropped_img, bbox
41
 
42
  def paste_object_on_mask(base_img, base_mask, obj_img, obj_bbox):
@@ -52,7 +70,8 @@ def paste_object_on_mask(base_img, base_mask, obj_img, obj_bbox):
52
  result.paste(obj_img, (x_paste, y_paste), obj_img)
53
  return result
54
 
55
- def process_transfer(image1_editor, mask1_upload, image2_editor, mask2_upload):
 
56
  if not image1_editor or not image2_editor:
57
  return None, None, None, "**❌ يرجى رفع الصورتين وتحديد الأقنعة."
58
  if image1_editor.get("background") is None or image2_editor.get("background") is None:
@@ -67,8 +86,7 @@ def process_transfer(image1_editor, mask1_upload, image2_editor, mask2_upload):
67
  return None, None, None, "**❌ يجب رسم أو رفع قناع على كلتا الصورتين."
68
  base_img = image1_editor["background"].convert("RGBA")
69
  obj_img = image2_editor["background"].convert("RGBA")
70
- # لاحظ: لا حاجة لتحويل القناع هنا، سيتم معالجته في crop_masked_object
71
- cropped_obj, obj_bbox = crop_masked_object(obj_img, obj_mask)
72
  if cropped_obj is None:
73
  return None, None, None, "**❌ لم يتم تحديد أي كائن في الصورة الثانية."
74
  result_img = paste_object_on_mask(base_img, base_mask, cropped_obj, obj_bbox)
@@ -91,20 +109,7 @@ def create_interface():
91
  """)
92
  gr.Markdown("""
93
  # 🖼️ نقل كائن من صورة إلى أخرى مع القناع والمحاذاة
94
-
95
- **يدعم جميع أنواع الأقنعة:**
96
- - قناع أبيض/أسود (ثنائي)
97
- - قناع متدرج (Soft Mask)
98
- - قناع ملون (RGB)
99
- - قناع فيه قناة ألفا شفافة (RGBA)
100
- - حتى أقنعة التقسيم (كل كائن بلون مختلف، سيتم نقل المناطق غير سوداء)
101
-
102
- **طريقة الاستخدام:**
103
- 1. ارفع الصورة الأولى (صورة الخلفية/مكان اللصق).
104
- 2. ارفع الصورة الثانية (الصورة التي بها الكائن).
105
- 3. ارسم أو ارفع أي نوع قناع للكائن في الصورة الثانية.
106
- 4. ارسم أو ارفع قناع في مكان اللصق في الصورة الأولى.
107
- 5. اضغط زر "نقل الكائن" وستظهر النتيجة!
108
  """)
109
  with gr.Row():
110
  with gr.Column():
@@ -133,6 +138,13 @@ def create_interface():
133
  height=200,
134
  elem_classes=["silver-border"]
135
  )
 
 
 
 
 
 
 
136
  with gr.Row():
137
  transfer_btn = gr.Button("🚀 نقل الكائن", variant="primary")
138
  with gr.Row():
@@ -142,7 +154,7 @@ def create_interface():
142
  status = gr.Markdown()
143
  transfer_btn.click(
144
  fn=process_transfer,
145
- inputs=[image1_editor, mask1_upload, image2_editor, mask2_upload],
146
  outputs=[base_mask, obj_mask, result_img, status]
147
  )
148
  return demo
 
1
  import gradio as gr
2
  import numpy as np
3
  from PIL import Image
4
+ import cv2
5
 
6
  def get_mask_bbox(mask: Image.Image):
7
  mask_np = np.array(mask)
8
  if len(mask_np.shape) == 3:
 
9
  mask_np = mask_np[..., -1] if mask_np.shape[2] == 4 else np.mean(mask_np, axis=-1)
10
  ys, xs = np.where(mask_np > 0)
11
  if ys.size == 0 or xs.size == 0:
12
  return None
13
  return int(xs.min()), int(ys.min()), int(xs.max()), int(ys.max())
14
 
15
+ def mask_to_alpha(mask: Image.Image, smooth_edges=True, morph_level=0):
16
+ # --- الحواف الناعمة ---
17
+ # إذا كان smooth_edges مفعّل، استخدم القناع كما هو (تدرج رمادي)
18
+ # إذا غير مفعّل، حول القناع إلى أبيض/أسود
19
+ mask_np = np.array(mask.convert("L"))
 
 
 
 
 
 
 
 
 
 
20
 
21
+ if not smooth_edges:
22
+ mask_np = np.where(mask_np > 127, 255, 0).astype(np.uint8)
23
+
24
+ # --- التنعيم المورفولوجي ---
25
+ if morph_level > 0:
26
+ # نستخدم GaussianBlur لتنعيم الحواف (يمكن استخدام غيره حسب رغبتك)
27
+ mask_np = cv2.GaussianBlur(mask_np, (0, 0), sigmaX=morph_level, sigmaY=morph_level, borderType=cv2.BORDER_DEFAULT)
28
+
29
+ # يمكن استخدام cv2.morphologyEx أيضاً
30
+ # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (morph_level, morph_level))
31
+ # mask_np = cv2.morphologyEx(mask_np, cv2.MORPH_CLOSE, kernel)
32
+
33
+ # بعد التنعيم، قد يتحول إلى float، فلنجعله uint8
34
+ mask_np = np.clip(mask_np, 0, 255).astype(np.uint8)
35
+
36
+ return Image.fromarray(mask_np)
37
+
38
+ def crop_masked_object(img: Image.Image, mask: Image.Image, smooth_edges=True, morph_level=0, scale=1.0, rotation=0, transparency=1.0):
39
  bbox = get_mask_bbox(mask)
40
  if bbox is None:
41
  return None, None
42
  xmin, ymin, xmax, ymax = bbox
43
  cropped_img = img.crop((xmin, ymin, xmax + 1, ymax + 1)).convert("RGBA")
44
  cropped_mask = mask.crop((xmin, ymin, xmax + 1, ymax + 1))
45
+ alpha_mask = mask_to_alpha(cropped_mask, smooth_edges, morph_level)
46
  cropped_img.putalpha(alpha_mask)
47
+ # تكبير/تصغير
48
+ if scale != 1.0:
49
+ new_size = (int(cropped_img.size[0] * scale), int(cropped_img.size[1] * scale))
50
+ cropped_img = cropped_img.resize(new_size, Image.LANCZOS)
51
+ # تدوير
52
+ if rotation != 0:
53
+ cropped_img = cropped_img.rotate(rotation, expand=True)
54
+ # شفافية
55
+ if transparency < 1.0:
56
+ alpha = cropped_img.split()[-1].point(lambda p: int(p * transparency))
57
+ cropped_img.putalpha(alpha)
58
  return cropped_img, bbox
59
 
60
  def paste_object_on_mask(base_img, base_mask, obj_img, obj_bbox):
 
70
  result.paste(obj_img, (x_paste, y_paste), obj_img)
71
  return result
72
 
73
+ def process_transfer(image1_editor, mask1_upload, image2_editor, mask2_upload,
74
+ scale, transparency, rotation, smooth_edges, morph_level):
75
  if not image1_editor or not image2_editor:
76
  return None, None, None, "**❌ يرجى رفع الصورتين وتحديد الأقنعة."
77
  if image1_editor.get("background") is None or image2_editor.get("background") is None:
 
86
  return None, None, None, "**❌ يجب رسم أو رفع قناع على كلتا الصورتين."
87
  base_img = image1_editor["background"].convert("RGBA")
88
  obj_img = image2_editor["background"].convert("RGBA")
89
+ cropped_obj, obj_bbox = crop_masked_object(obj_img, obj_mask, smooth_edges, morph_level, scale, rotation, transparency)
 
90
  if cropped_obj is None:
91
  return None, None, None, "**❌ لم يتم تحديد أي كائن في الصورة الثانية."
92
  result_img = paste_object_on_mask(base_img, base_mask, cropped_obj, obj_bbox)
 
109
  """)
110
  gr.Markdown("""
111
  # 🖼️ نقل كائن من صورة إلى أخرى مع القناع والمحاذاة
112
+ يمكنك تعديل إعدادات نقل الكائن من هنا، أو تركها على الإعدادات الافتراضية.
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  """)
114
  with gr.Row():
115
  with gr.Column():
 
138
  height=200,
139
  elem_classes=["silver-border"]
140
  )
141
+ with gr.Row():
142
+ scale = gr.Slider(0.5, 2.0, value=1.0, step=0.05, label="حجم الكائن (Scale)", elem_classes=["silver-border"])
143
+ transparency = gr.Slider(0.2, 1.0, value=1.0, step=0.05, label="شفافية الكائن (Transparency)", elem_classes=["silver-border"])
144
+ rotation = gr.Slider(-180, 180, value=0, step=1, label="تدوير الكائن (Rotation)", elem_classes=["silver-border"])
145
+ with gr.Row():
146
+ smooth_edges = gr.Checkbox(value=True, label="تفعيل الحواف الناعمة (Smooth Edges/Anti-aliasing)", elem_classes=["silver-border"])
147
+ morph_level = gr.Slider(0, 10, value=0, step=1, label="مستوى تنعيم القناع (Morphological Smoothing)", elem_classes=["silver-border"])
148
  with gr.Row():
149
  transfer_btn = gr.Button("🚀 نقل الكائن", variant="primary")
150
  with gr.Row():
 
154
  status = gr.Markdown()
155
  transfer_btn.click(
156
  fn=process_transfer,
157
+ inputs=[image1_editor, mask1_upload, image2_editor, mask2_upload, scale, transparency, rotation, smooth_edges, morph_level],
158
  outputs=[base_mask, obj_mask, result_img, status]
159
  )
160
  return demo