File size: 13,944 Bytes
8b624b1
00af269
 
8b624b1
 
 
 
 
 
 
 
 
a1c9be1
8b624b1
 
 
 
 
00af269
5ba0cb4
a1c9be1
 
00af269
 
5ba0cb4
00af269
d10b790
00af269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ba0cb4
00af269
 
 
 
 
 
 
 
a1c9be1
00af269
 
 
 
 
 
 
 
 
d10b790
 
00af269
 
d10b790
00af269
d10b790
00af269
 
 
 
 
 
 
 
 
d10b790
00af269
 
 
5ba0cb4
00af269
 
1901f75
00af269
 
 
 
1901f75
 
a1c9be1
00af269
a1c9be1
1901f75
a1c9be1
 
 
 
 
5dcbdcb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a1c9be1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1901f75
a1c9be1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b19f0a
a1c9be1
 
 
 
 
 
 
 
 
 
 
 
d10b790
a1c9be1
 
 
 
 
 
 
 
 
 
 
 
 
1901f75
 
 
 
 
 
a1c9be1
 
1901f75
 
 
8b624b1
a1c9be1
 
 
1901f75
 
 
 
 
 
 
 
 
 
 
 
a1c9be1
 
1901f75
 
 
 
 
8b624b1
a1c9be1
 
 
1901f75
 
 
 
 
 
a1c9be1
 
1901f75
 
 
 
06a33e4
5dcbdcb
00af269
a1c9be1
00af269
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import streamlit as st
# تمت إزالة استيراد components
# import streamlit.components.v1 as components # لاستقبال الرسائل
from PIL import Image
import torch
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
import io
import os
import requests
from io import BytesIO

# --- 1. إعدادات الصفحة (تبقى في الأعلى) ---
st.set_page_config(
    page_title="SkyData - Background Removal",
    layout="wide"
)

# --- <<< إزالة كاملة لخطوة المصادقة والتحقق من النطاق ---

# تهيئة متغير "المصادقة" في حالة الجلسة
if 'authenticated' not in st.session_state:
    # **التعديل هنا:** تعيين القيمة إلى True بشكل افتراضي لتجاوز التحقق
    st.session_state.authenticated = True 

# --- <<< هذا هو الجزء الذي تم تعديله (تمت إزالته) ---
# كود HTML/JavaScript الذي سيتم حقنه في Streamlit
# auth_listener_html = """
# <script>
# // --- الجزء الأول: الاستماع لرسالة المصادقة من الأب ---

# // النطاقات المسموحة (للرسالة القادمة)
# const allowedOrigins = [
#     "https://www.skydata.kozow.com",
#     "https://skydata.kozow.com"
# ];
# const allowedDomains = [
#     "www.skydata.kozow.com",
#     "skydata.kozow.com"
# ];

# // الاستماع للرسائل القادمة من النافذة "الأب"
# window.addEventListener('message', (event) => {
    
#     // التحقق إذا كان مصدر الرسالة ضمن القائمة المسموحة
#     if (!allowedOrigins.includes(event.origin)) {
#         console.warn("تم رفض رسالة من نطاق غير مصرح به:", event.origin);
#         return;
#     }

#     try {
#         const data = JSON.parse(event.data);
        
#         // التحقق من محتوى الرسالة (لزيادة الأمان)
#         if (data.source === 'skydata-auth' && allowedDomains.includes(data.domain)) {
#             // إذا نجحت المصادقة، أرسل "True" إلى Streamlit
#             Streamlit.setComponentValue({ "authenticated": true });
#         }
#     } catch (e) {
#         console.error("خطأ في معالجة الرسالة:", e);
#     }
# }, false);


# // --- الجزء الثاني (الجديد): إرسال رسالة "أنا جاهز" إلى الأب ---
# // (هذا الكود سيعمل فوراً عند تحميل هذا المكوّن)

# const readyMessage = JSON.stringify({ source: 'streamlit-ready' });

# // يجب أن نرسل الرسالة إلى النطاقات المسموحة للأب
# allowedOrigins.forEach(origin => {
#     try {
#         // إرسال الرسالة إلى النافذة "الأب"
#         window.parent.postMessage(readyMessage, origin);
#     } catch (e) {
#         console.error("فشل إرسال رسالة الاستعداد إلى الأب:", origin, e);
#     }
# });

# </script>
# """
# --- <<< نهاية الجزء الذي تم تعديله (تمت إزالته) ---

# تشغيل "المستمع" كعنصر HTML غير مرئي (تمت إزالته)
# auth_result = components.html(auth_listener_html, height=0, width=0)

# (الكود الذي أصلحناه سابقاً - تمت إزالته لعدم الحاجة)
# if auth_result and isinstance(auth_result, dict) and auth_result.get("authenticated") == True:
#     st.session_state.authenticated = True
# --- نهاية خطوة المصادقة (تمت إزالة الأجزاء غير الضرورية) ---


# --- التحقق الرئيسي: لا تقم بتشغيل أي شيء إلا بعد المصادقة ---
# **تم ترك هذا التحقق، لكن بما أن القيمة هي True افتراضياً، سيتم تشغيل التطبيق**
if st.session_state.authenticated:
    
    # --- 2. كود التصميم (CSS) ---
    CUSTOM_CSS = """
    <style>
        /* ... (نفس كود CSS بالكامل) ... */
        body, [data-testid="stAppViewContainer"], [data-testid="stHeader"] {
            background-color: #121212 !important; color: #e0e0e0 !important;
        }
        [data-testid="stHeader"] {
            background-color: #1e1e1e !important; border-bottom: 1px solid #333;
        }
        h1, h2, h3, h4, h5, h6 { color: #ffffff !important; }
        p, label, [data-testid="stMarkdownContainer"] { color: #e0e0e0 !important; }

        /* ... (باقي كود CSS الخاص بك) ... */
        [data-testid="stSidebar"][aria-expanded="true"] {
            background-color: #1e1e1e !important; border-right: 1px solid #333 !important;
        }
        [data-testid="stSidebar"][aria-expanded="true"] [data-testid="stRadio"] label { 
            background-color: #2a2a2a; padding: 10px; border-radius: 5px;
            margin: 5px 0; transition: all 0.3s ease;
        }
        [data-testid="stSidebar"][aria-expanded="true"] [data-testid="stRadio"] label:hover {
            background-color: #333; color: #007bff;
        }
        [data-testid="stSidebar"][aria-expanded="true"] [data-testid="stRadio"] input:checked + div {
            color: #007bff; 
        }
        [data-testid="stDownloadButton"] > button {
            background-color: #007bff !important; color: #ffffff !important;
            border: none !important; border-radius: 5px !important;
            font-weight: bold !important; padding: 12px 20px !important;
            transition: all 0.3s ease !important; width: 100%; 
        }
        [data-testid="stDownloadButton"] > button:hover {
            background-color: #0056b3 !important; transform: scale(1.03) !important;
        }
        [data-testid="stFileUploader"] section {
            background-color: #1e1e1e !important; border: 1px dashed #333 !important;
            border-radius: 8px !important;
        }
        [data-testid="stFileUploader"] section > button { 
            background-color: #007bff !important; color: #ffffff !important;
            border: none !important; border-radius: 5px !important;
            font-weight: bold !important; transition: all 0.3s ease !important;
        }
        [data-testid="stFileUploader"] section > button:hover {
            background-color: #0056b3 !important; transform: scale(1.03) !important;
        }
        [data-testid="stFileUploader"] label { color: #ffffff !important; }
        [data-testid="stFileUploader"] section [data-testid="stMarkdownContainer"] p {
            color: #aaa !important;
        }
        [data-testid="stImage"] figcaption { color: #aaa !important; }
        [data-testid="stSpinner"] > div { color: #e0e0e0 !important; }
        [data-testid="stTextInput"] input { 
            background-color: #1e1e1e !important; color: #e0e0e0 !important;
            border: 1px solid #333 !important; border-radius: 5px !important;
        }
    </style>
    """
    st.markdown(CUSTOM_CSS, unsafe_allow_html=True)

    # --- 3. قاموس الترجمة ---
    localization = {
        'en': {
            'brand_title': "", 'lang_select': "Select Language", 'input_method': "Select Input Method",
            'tab_upload': "Image Upload", 'tab_url': "URL Input", 'tab_file': "File Output",
            'upload_prompt': "Upload an image", 'upload_prompt_file': "Upload an image for file output",
            'url_prompt': "Paste an image URL", 'processed_caption': "Processed Image",
            'download_button': "Download PNG", 'error_fetching': "Error fetching image from URL:",
            'error_processing': "Error processing image:", 'generic_error': "An error occurred:",
            'page_title': "SkyData - Background Removal", 'spinner_text': "Processing image, please wait...",
        },
        'ar': {
            'brand_title': "", 'lang_select': "اختر اللغة", 'input_method': "اختر طريقة الإدخال",
            'tab_upload': "رفع صورة", 'tab_url': "رابط صورة", 'tab_file': "تصدير ملف",
            'upload_prompt': "ارفع صورة", 'upload_prompt_file': "ارفع صورة لتصدير الملف",
            'url_prompt': "الصق رابط الصورة", 'processed_caption': "الصورة المعالجة",
            'download_button': "تحميل بصيغة PNG", 'error_fetching': "خطأ في جلب الصورة من الرابط:",
            'error_processing': "خطأ في معالجة الصورة:", 'generic_error': "حدث خطأ:",
            'page_title': "SkyData - أداة إزالة الخلفية", 'spinner_text': "جاري معالجة الصورة، يرجى الانتظار...",
        }
    }

    # --- 4. تحميل النموذج ---
    torch.set_float32_matmul_precision(["high", "highest"][0])
    use_cuda = torch.cuda.is_available()
    device = "cuda" if use_cuda else "cpu"

    @st.cache_resource
    def load_model():
        model = AutoModelForImageSegmentation.from_pretrained("ZhengPeng7/BiRefNet", trust_remote_code=True)
        model.to(device)
        if use_cuda:
            model = model.half()
        return model

    birefnet = load_model()

    transform_image = transforms.Compose([
        transforms.Resize((1024, 1024)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
    ])

    # --- 5. دالة المعالجة الرئيسية ---
    @st.cache_data
    def process(image):
        image_size = image.size
        input_images = transform_image(image).unsqueeze(0).to(device)
        if use_cuda:
            input_images = input_images.half()

        with torch.no_grad():
            preds = birefnet(input_images)[-1].sigmoid().cpu()
        
        pred = preds[0].squeeze()
        pred_pil = transforms.ToPILImage()(pred)
        mask = pred_pil.resize(image_size)
        image.putalpha(mask)
        
        img_bytes = io.BytesIO()
        image.save(img_bytes, format="PNG")
        img_bytes = img_bytes.getvalue()
        
        return image, img_bytes

    # --- 6. واجهة التطبيق (Sidebar) ---
    lang_choice = st.sidebar.radio(
        label="Select Language / اختر اللغة", 
        options=["English", "العربية"], 
        horizontal=True
    )
    lang_code = 'ar' if lang_choice == 'العربية' else 'en'
    loc = localization[lang_code]

    # --- 7. واجهة التطبيق الرئيسية (Main App) ---
    LOGO_URL = "https://i.ibb.co/v4vwvcGq/skydatafull.webp"
    col1, col2 = st.columns([1, 6]) 
    with col1:
        st.image(LOGO_URL, width=80) 
    with col2:
        st.title(loc['page_title'])

    st.sidebar.title(loc['brand_title'])
    selected_tab = st.sidebar.radio(loc['input_method'], [loc['tab_upload'], loc['tab_url'], loc['tab_file']])

    # --- 8. منطق التبويبات ---
    if selected_tab == loc['tab_upload']:
        uploaded_file = st.file_uploader(loc['upload_prompt'], type=["jpg", "jpeg", "png"])
        if uploaded_file is not None:
            try:
                image = Image.open(uploaded_file).convert("RGB")
                with st.spinner(loc['spinner_text']):
                    processed_image, file_bytes = process(image)
                st.image(processed_image, caption=loc['processed_caption'])
                st.download_button(
                    label=loc['download_button'], data=file_bytes,
                    file_name=f"{uploaded_file.name.rsplit('.', 1)[0]}.png", mime="image/png",
                )
            except Exception as e:
                st.error(f"{loc['generic_error']} {e}")

    elif selected_tab == loc['tab_url']:
        image_url = st.text_input(loc['url_prompt'])
        if image_url:
            try:
                response = requests.get(image_url, stream=True)
                response.raise_for_status()
                image = Image.open(BytesIO(response.content)).convert("RGB")
                with st.spinner(loc['spinner_text']):
                    processed_image, file_bytes = process(image)
                st.image(processed_image, caption=loc['processed_caption'])
                try:
                    file_name = image_url.split('/')[-1].rsplit('.', 1)[0] + ".png"
                except Exception:
                    file_name = "processed_image.png"
                st.download_button(
                    label=loc['download_button'], data=file_bytes,
                    file_name=file_name, mime="image/png",
                )
            except requests.exceptions.RequestException as e:
                st.error(f"{loc['error_fetching']} {e}")
            except Exception as e:
                st.error(f"{loc['error_processing']} {e}")

    elif selected_tab == loc['tab_file']:
        uploaded_file = st.file_uploader(loc['upload_prompt_file'], type=["jpg", "jpeg", "png"])
        if uploaded_file is not None:
            try:
                image = Image.open(uploaded_file).convert("RGB")
                with st.spinner(loc['spinner_text']):
                    processed_image, file_bytes = process(image)
                st.image(processed_image, caption=loc['processed_caption'])
                st.download_button(
                    label=loc['download_button'], data=file_bytes,
                    file_name=f"{uploaded_file.name.rsplit('.', 1)[0]}.png", mime="image/png",
                )
            except Exception as e:
                st.error(f"{loc['generic_error']} {e}")


# --- إذا فشلت المصادقة ---
# **تمت إزالة الكود الذي يعرض رسالة "الوصول مرفوض"**
else:
    pass 
    # st.error("🔒 الوصول مرفوض.")
    # st.error("لا يمكن تشغيل هذه الأداة إلا من خلال النطاق المعتمد: https://www.skydata.kozow.com/")
    # st.warning("Access Denied. This tool can only be run when embedded on https://www.skydata.kozow.com/")