Skydata001 commited on
Commit
1901f75
·
verified ·
1 Parent(s): 8b19f0a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -158
app.py CHANGED
@@ -7,10 +7,6 @@ import io
7
  import os
8
  import requests
9
  from io import BytesIO
10
- import firebase_admin
11
- from firebase_admin import credentials, db
12
- import datetime
13
- import json
14
 
15
  # --- 1. إعدادات الصفحة ---
16
  st.set_page_config(
@@ -88,31 +84,8 @@ CUSTOM_CSS = """
88
  st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
89
  # --- نهاية كود التصميم ---
90
 
91
- # --- <<< جديد: إعداد Firebase ---
92
- @st.cache_resource
93
- def init_firebase():
94
- try:
95
- # (انظر خطوة الإعداد) جلب المفتاح السري من Hugging Face Secrets
96
- creds_json_str = os.environ.get("FIREBASE_CREDS")
97
- if creds_json_str:
98
- creds_dict = json.loads(creds_json_str)
99
- cred = credentials.Certificate(creds_dict)
100
- # (هام) استبدل هذا برابط قاعدة بياناتك من ملف index.html
101
- db_url = "https://pixelrpg-5ebc3-default-rtdb.europe-west1.firebasedatabase.app"
102
-
103
- if not firebase_admin._apps:
104
- firebase_admin.initialize_app(cred, {'databaseURL': db_url})
105
- return True
106
- else:
107
- st.error("خطأ فادح: لم يتم العثور على مفتاح Firebase. يرجى الاتصال بمسؤول الموقع.")
108
- return False
109
- except Exception as e:
110
- st.error(f"خطأ في الاتصال بـ Firebase: {e}")
111
- return False
112
 
113
- firebase_initialized = init_firebase()
114
-
115
- # --- 2. قاموس الترجمة (معدل) ---
116
  localization = {
117
  'en': {
118
  'brand_title': "",
@@ -131,12 +104,7 @@ localization = {
131
  'generic_error': "An error occurred:",
132
  'page_title': "SkyData - Background Removal",
133
  'spinner_text': "Processing image, please wait...",
134
- # <<< جديد: رسائل نظام المفاتيح
135
- 'api_key_prompt': "Enter your API Key",
136
- 'api_key_missing': "Please enter your API Key to use the tool.",
137
- 'api_key_invalid': "Invalid API Key. Please get a key from our website.",
138
- 'limit_reached': "You have reached your daily limit (30 attempts).",
139
- 'attempts_left': "Attempts left today:"
140
  },
141
  'ar': {
142
  'brand_title': "",
@@ -155,16 +123,15 @@ localization = {
155
  'generic_error': "حدث خطأ:",
156
  'page_title': "SkyData - أداة إزالة الخلفية",
157
  'spinner_text': "جاري معالجة الصورة، يرجى الانتظار...",
158
- # <<< جديد: رسائل نظام المفاتيح
159
- 'api_key_prompt': "أدخل مفتاح الاستخدام (API Key)",
160
- 'api_key_missing': "الرجاء إدخال مفتاح الاستخدام لتشغيل الأداة.",
161
- 'api_key_invalid': "مفتاح استخدام غير صالح. الرجاء الحصول على مفتاح من موقعنا.",
162
- 'limit_reached': "لقد وصلت إلى الحد الأقصى (30 محاولة). يرجى المحاولة غداً.",
163
- 'attempts_left': "المحاولات المتبقية اليوم:"
164
  }
165
  }
166
 
167
- # --- 3. تحميل النموذج (كما هو) ---
 
 
 
 
168
  @st.cache_resource
169
  def load_model():
170
  model = AutoModelForImageSegmentation.from_pretrained("ZhengPeng7/BiRefNet", trust_remote_code=True)
@@ -173,61 +140,38 @@ def load_model():
173
  model = model.half()
174
  return model
175
 
176
- # --- 4. دالة المعالجة (كما هي) ---
 
 
 
 
 
 
 
 
177
  @st.cache_data
178
  def process(image):
179
- # (نفس دالة المعالجة السابقة)
180
  image_size = image.size
181
  input_images = transform_image(image).unsqueeze(0).to(device)
182
- if use_cuda: input_images = input_images.half()
 
 
183
  with torch.no_grad():
184
  preds = birefnet(input_images)[-1].sigmoid().cpu()
 
185
  pred = preds[0].squeeze()
186
  pred_pil = transforms.ToPILImage()(pred)
187
  mask = pred_pil.resize(image_size)
188
  image.putalpha(mask)
 
189
  img_bytes = io.BytesIO()
190
  image.save(img_bytes, format="PNG")
191
  img_bytes = img_bytes.getvalue()
 
192
  return image, img_bytes
193
 
194
- # --- <<< جديد: دالة التحقق من حد الاستخدام ---
195
- DAILY_LIMIT = 30
196
- @st.cache_data(ttl=60) # تخزين نتيجة التحقق لمدة 60 ثان��ة لتجنب الضغط على Firebase
197
- def check_and_update_limit(api_key, loc):
198
- if not firebase_initialized:
199
- return False, loc['generic_error']
200
-
201
- try:
202
- ref = db.reference(f'users/{api_key}')
203
- data = ref.get()
204
-
205
- if not data:
206
- return False, loc['api_key_invalid']
207
-
208
- today_date = datetime.date.today().isoformat()
209
- last_reset = data.get('lastResetDate', '1970-01-01')
210
- count = int(data.get('usageCount', 0))
211
-
212
- # تصفير العداد إذا كان يوماً جديداً
213
- if last_reset != today_date:
214
- count = 0
215
- ref.update({'lastResetDate': today_date, 'usageCount': 0})
216
-
217
- # التحقق من الحد
218
- if count >= DAILY_LIMIT:
219
- st.session_state.remaining_attempts = 0
220
- return False, loc['limit_reached']
221
-
222
- # (مهم) تحديث العداد في قاعدة البيانات
223
- ref.update({'usageCount': count + 1})
224
- st.session_state.remaining_attempts = DAILY_LIMIT - (count + 1)
225
- return True, "" # نجح
226
-
227
- except Exception as e:
228
- return False, f"Error checking key: {e}"
229
 
230
- # --- 5. واجهة التطبيق (Sidebar) (معدلة) ---
231
  lang_choice = st.sidebar.radio(
232
  label="Select Language / اختر اللغة",
233
  options=["English", "العربية"],
@@ -236,17 +180,8 @@ lang_choice = st.sidebar.radio(
236
  lang_code = 'ar' if lang_choice == 'العربية' else 'en'
237
  loc = localization[lang_code]
238
 
239
- # --- <<< جديد: إضافة خانة المفتاح ---
240
- st.sidebar.title(loc['brand_title'])
241
- api_key = st.sidebar.text_input(loc['api_key_prompt'], type="password")
242
-
243
- # --- <<< جديد: إظهار المحاولات المتبقية (إذا تم إدخال المفتاح)
244
- if 'remaining_attempts' in st.session_state:
245
- st.sidebar.info(f"{loc['attempts_left']} {st.session_state.remaining_attempts}")
246
-
247
- selected_tab = st.sidebar.radio(loc['input_method'], [loc['tab_upload'], loc['tab_url'], loc['tab_file']])
248
 
249
- # --- 6. واجهة التطبيق الرئيسية (Main App) (كما هي) ---
250
  LOGO_URL = "https://i.ibb.co/v4vwvcGq/skydatafull.webp"
251
  col1, col2 = st.columns([1, 6])
252
  with col1:
@@ -254,77 +189,106 @@ with col1:
254
  with col2:
255
  st.title(loc['page_title'])
256
 
257
- # --- منطق التبويبات (معدل بالكامل) ---
 
 
 
 
 
258
 
259
- # (ملاحظة: قمت بإنشاء دالة موحدة للمعالجة لتجنب تكرار الكود)
260
- def handle_processing(image, original_name):
261
- try:
262
- with st.spinner(loc['spinner_text']):
263
- processed_image, file_bytes = process(image)
264
 
265
- st.image(processed_image, caption=loc['processed_caption'])
266
- st.download_button(
267
- label=loc['download_button'],
268
- data=file_bytes,
269
- file_name=f"{original_name.rsplit('.', 1)[0]}.png",
270
- mime="image/png",
271
- )
272
- except Exception as e:
273
- st.error(f"{loc['generic_error']} {e}")
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
 
276
- # --- التحقق من المفتاح قبل أي شيء ---
277
- if not firebase_initialized:
278
- st.error("خطأ في الاتصال بقاعدة البيانات. لا يمكن تشغيل الأداة.")
279
- else:
280
- if selected_tab == loc['tab_upload']:
281
- uploaded_file = st.file_uploader(loc['upload_prompt'], type=["jpg", "jpeg", "png"])
282
- if uploaded_file is not None:
283
- if not api_key:
284
- st.info(loc['api_key_missing'])
285
- else:
286
- is_ok, message = check_and_update_limit(api_key, loc)
287
- if is_ok:
288
- image = Image.open(uploaded_file).convert("RGB")
289
- handle_processing(image, uploaded_file.name)
290
- else:
291
- st.warning(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
- elif selected_tab == loc['tab_url']:
294
- image_url = st.text_input(loc['url_prompt'])
295
- if image_url:
296
- if not api_key:
297
- st.info(loc['api_key_missing'])
298
- else:
299
- is_ok, message = check_and_update_limit(api_key, loc)
300
- if is_ok:
301
- try:
302
- response = requests.get(image_url, stream=True)
303
- response.raise_for_status()
304
- image = Image.open(BytesIO(response.content)).convert("RGB")
305
-
306
- try:
307
- file_name = image_url.split('/')[-1]
308
- except Exception:
309
- file_name = "processed_image.png"
310
-
311
- handle_processing(image, file_name)
312
- except requests.exceptions.RequestException as e:
313
- st.error(f"{loc['error_fetching']} {e}")
314
- except Exception as e:
315
- st.error(f"{loc['error_processing']} {e}")
316
- else:
317
- st.warning(message)
318
 
319
- elif selected_tab == loc['tab_file']:
320
- uploaded_file = st.file_uploader(loc['upload_prompt_file'], type=["jpg", "jpeg", "png"])
321
- if uploaded_file is not None:
322
- if not api_key:
323
- st.info(loc['api_key_missing'])
324
- else:
325
- is_ok, message = check_and_update_limit(api_key, loc)
326
- if is_ok:
327
- image = Image.open(uploaded_file).convert("RGB")
328
- handle_processing(image, uploaded_file.name)
329
- else:
330
- st.warning(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  import os
8
  import requests
9
  from io import BytesIO
 
 
 
 
10
 
11
  # --- 1. إعدادات الصفحة ---
12
  st.set_page_config(
 
84
  st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
85
  # --- نهاية كود التصميم ---
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ # --- 2. قاموس الترجمة (عربي / إنجليزي) ---
 
 
89
  localization = {
90
  'en': {
91
  'brand_title': "",
 
104
  'generic_error': "An error occurred:",
105
  'page_title': "SkyData - Background Removal",
106
  'spinner_text': "Processing image, please wait...",
107
+ 'limit_warning': "You have reached the limit (10 attempts). Please try again later.", # <<< جديد
 
 
 
 
 
108
  },
109
  'ar': {
110
  'brand_title': "",
 
123
  'generic_error': "حدث خطأ:",
124
  'page_title': "SkyData - أداة إزالة الخلفية",
125
  'spinner_text': "جاري معالجة الصورة، يرجى الانتظار...",
126
+ 'limit_warning': "لقد وصلت إلى الحد الأقصى (10 محاولات). يرجى المحاولة مرة أخرى لاحقاً.", # <<< جديد
 
 
 
 
 
127
  }
128
  }
129
 
130
+ # --- 3. تحميل النموذج ---
131
+ torch.set_float32_matmul_precision(["high", "highest"][0])
132
+ use_cuda = torch.cuda.is_available()
133
+ device = "cuda" if use_cuda else "cpu"
134
+
135
  @st.cache_resource
136
  def load_model():
137
  model = AutoModelForImageSegmentation.from_pretrained("ZhengPeng7/BiRefNet", trust_remote_code=True)
 
140
  model = model.half()
141
  return model
142
 
143
+ birefnet = load_model()
144
+
145
+ transform_image = transforms.Compose([
146
+ transforms.Resize((1024, 1024)), # الدقة العالية
147
+ transforms.ToTensor(),
148
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
149
+ ])
150
+
151
+ # --- 4. دالة المعالجة الرئيسية ---
152
  @st.cache_data
153
  def process(image):
 
154
  image_size = image.size
155
  input_images = transform_image(image).unsqueeze(0).to(device)
156
+ if use_cuda:
157
+ input_images = input_images.half()
158
+
159
  with torch.no_grad():
160
  preds = birefnet(input_images)[-1].sigmoid().cpu()
161
+
162
  pred = preds[0].squeeze()
163
  pred_pil = transforms.ToPILImage()(pred)
164
  mask = pred_pil.resize(image_size)
165
  image.putalpha(mask)
166
+
167
  img_bytes = io.BytesIO()
168
  image.save(img_bytes, format="PNG")
169
  img_bytes = img_bytes.getvalue()
170
+
171
  return image, img_bytes
172
 
173
+ # --- 5. واجهة التطبيق (Sidebar) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
 
175
  lang_choice = st.sidebar.radio(
176
  label="Select Language / اختر اللغة",
177
  options=["English", "العربية"],
 
180
  lang_code = 'ar' if lang_choice == 'العربية' else 'en'
181
  loc = localization[lang_code]
182
 
183
+ # --- 6. واجهة التطبيق الرئيسية (Main App) ---
 
 
 
 
 
 
 
 
184
 
 
185
  LOGO_URL = "https://i.ibb.co/v4vwvcGq/skydatafull.webp"
186
  col1, col2 = st.columns([1, 6])
187
  with col1:
 
189
  with col2:
190
  st.title(loc['page_title'])
191
 
192
+ st.sidebar.title(loc['brand_title'])
193
+ selected_tab = st.sidebar.radio(loc['input_method'], [loc['tab_upload'], loc['tab_url'], loc['tab_file']])
194
+
195
+ # --- <<< جديد: تهيئة عداد المحاولات ---
196
+ if 'attempt_count' not in st.session_state:
197
+ st.session_state.attempt_count = 0
198
 
199
+ # --- منطق التبويبات (معدل) ---
200
+
201
+ if selected_tab == loc['tab_upload']:
202
+ uploaded_file = st.file_uploader(loc['upload_prompt'], type=["jpg", "jpeg", "png"])
203
+ if uploaded_file is not None:
204
 
205
+ # <<< جديد: التحقق من الحد
206
+ if st.session_state.attempt_count >= 10:
207
+ st.warning(loc['limit_warning'])
208
+ else:
209
+ try:
210
+ image = Image.open(uploaded_file).convert("RGB")
211
+ with st.spinner(loc['spinner_text']):
212
+ processed_image, file_bytes = process(image)
213
+
214
+ # <<< جديد: زيادة العداد بعد النجاح
215
+ st.session_state.attempt_count += 1
216
+
217
+ st.image(processed_image, caption=loc['processed_caption'])
218
+ st.download_button(
219
+ label=loc['download_button'],
220
+ data=file_bytes,
221
+ file_name=f"{uploaded_file.name.rsplit('.', 1)[0]}.png",
222
+ mime="image/png",
223
+ )
224
+ except Exception as e:
225
+ st.error(f"{loc['generic_error']} {e}")
226
 
227
 
228
+ elif selected_tab == loc['tab_url']:
229
+ image_url = st.text_input(loc['url_prompt'])
230
+ if image_url:
231
+
232
+ # <<< جديد: التحقق من الحد
233
+ if st.session_state.attempt_count >= 10:
234
+ st.warning(loc['limit_warning'])
235
+ else:
236
+ try:
237
+ response = requests.get(image_url, stream=True)
238
+ response.raise_for_status()
239
+ image = Image.open(BytesIO(response.content)).convert("RGB")
240
+
241
+ with st.spinner(loc['spinner_text']):
242
+ processed_image, file_bytes = process(image)
243
+
244
+ # <<< جديد: زيادة العداد بعد النجاح
245
+ st.session_state.attempt_count += 1
246
+
247
+ st.image(processed_image, caption=loc['processed_caption'])
248
+
249
+ try:
250
+ file_name = image_url.split('/')[-1].rsplit('.', 1)[0] + ".png"
251
+ except Exception:
252
+ file_name = "processed_image.png"
253
+
254
+ st.download_button(
255
+ label=loc['download_button'],
256
+ data=file_bytes,
257
+ file_name=file_name,
258
+ mime="image/png",
259
+ )
260
+ except requests.exceptions.RequestException as e:
261
+ st.error(f"{loc['error_fetching']} {e}")
262
+ except Exception as e:
263
+ st.error(f"{loc['error_processing']} {e}")
264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
+ elif selected_tab == loc['tab_file']:
267
+ uploaded_file = st.file_uploader(loc['upload_prompt_file'], type=["jpg", "jpeg", "png"])
268
+ if uploaded_file is not None:
269
+
270
+ # <<< جديد: التحقق من الحد
271
+ if st.session_state.attempt_count >= 10:
272
+ st.warning(loc['limit_warning'])
273
+ else:
274
+ try:
275
+ image = Image.open(uploaded_file).convert("RGB")
276
+
277
+ with st.spinner(loc['spinner_text']):
278
+ processed_image, file_bytes = process(image)
279
+
280
+ # <<< جديد: زيادة العداد بعد النجاح
281
+ st.session_state.attempt_count += 1
282
+
283
+ st.image(processed_image, caption=loc['processed_caption'])
284
+ st.download_button(
285
+ label=loc['download_button'],
286
+ data=file_bytes,
287
+ file_name=f"{uploaded_file.name.rsplit('.', 1)[0]}.png",
288
+ mime="image/png",
289
+ )
290
+ except Exception as e:
291
+ st.error(f"{loc['generic_error']} {e}")
292
+
293
+ # <<< جديد: إظهار العداد للمستخدم (اختياري، يمكنك حذف هذا السطر)
294
+ st.sidebar.info(f"المحاولات المتبقية: {10 - st.session_state.attempt_count}")