sukrukirman commited on
Commit
4905bdc
·
1 Parent(s): 245b72d
Files changed (2) hide show
  1. app.py +322 -167
  2. requirements.txt +4 -1
app.py CHANGED
@@ -1,53 +1,67 @@
1
- import sys
2
  import os
3
- current_dir = os.path.dirname(os.path.abspath(__file__))
4
- src_path = os.path.join(current_dir, 'src')
5
- if src_path not in sys.path:
6
- sys.path.insert(0, src_path)
7
-
8
  import gradio as gr
9
  import json
10
- from typing import Any, Dict, Generator
11
  from dotenv import load_dotenv
12
  import gradio.themes as gr_themes
13
- import io
14
- from contextlib import redirect_stdout, redirect_stderr
 
 
15
 
16
- # Load environment variables from a .env file for local development
17
  load_dotenv()
18
 
19
  # --- Secure Token Management ---
 
20
  VIDDEXA_TOKEN = os.getenv("HF_TOKEN")
21
 
22
  # A simple cache to store loaded model instances
23
  _MODEL_CACHE: Dict[str, Any] = {}
24
 
25
-
26
- def _load_model(model_id: str, user_hf_token: str | None = None):
27
- """
28
- Loads a model, caches it, and handles token management.
29
- All print outputs from this function will be captured.
30
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  if model_id in _MODEL_CACHE:
32
- print(f"Model '{model_id}' found in cache.")
33
  return _MODEL_CACHE[model_id]
34
-
35
- print(f"Loading model '{model_id}'...")
36
- active_token = None
37
- if model_id == "viddexa/mobilenet_v2_1.0_224":
38
- if not VIDDEXA_TOKEN:
39
- raise gr.Error(
40
- "The featured model 'viddexa/mobilenet_v2_1.0_224' requires an 'HF_TOKEN' to be set in the Space Secrets or a local .env file."
41
- )
42
- active_token = VIDDEXA_TOKEN
43
- elif user_hf_token:
44
- active_token = user_hf_token
45
-
46
  try:
47
  from moderators.auto_model import AutoModerator
48
- model = AutoModerator.from_pretrained(model_id, token=active_token, use_fast=True)
 
49
  _MODEL_CACHE[model_id] = model
50
- print("Model loaded successfully.")
51
  return model
52
  except Exception as e:
53
  error_msg = f"Failed to load model: {model_id}. Error: {e}"
@@ -56,160 +70,301 @@ def _load_model(model_id: str, user_hf_token: str | None = None):
56
  raise gr.Error(error_msg)
57
 
58
 
59
- def _to_jsonable(results: Any) -> Any:
60
- """Helper function to make model outputs JSON-serializable."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  try:
62
- return [getattr(r, "classifications", r) for r in results]
63
- except TypeError:
64
- return results
65
  except Exception:
66
- return results
67
-
68
-
69
- # --- NEW: Rewritten 'infer' function as a generator to stream logs ---
70
- def infer(image_path: str, model_choice: str, custom_model_id: str, user_hf_token: str) -> Generator[
71
- tuple[str, Any], None, None]:
72
- """
73
- The main inference function that now yields updates to stream logs to the UI.
74
- """
75
- # 1. Clear previous outputs and show an initial message
76
- yield "Starting analysis...", None
77
-
78
- if not image_path:
79
- raise gr.Error("Please upload an image first.")
80
-
81
- log_stream = io.StringIO()
82
- try:
83
- # 2. Capture all printed output from the loading and inference process
84
- with redirect_stdout(log_stream), redirect_stderr(log_stream):
85
- if model_choice == "Custom Model":
86
- model_id = (custom_model_id or "").strip()
87
- if not model_id:
88
- raise gr.Error("Please enter the Hugging Face ID for your custom model.")
89
- else:
90
- model_id = model_choice
91
- user_hf_token = ""
92
-
93
- # Load model and yield logs generated during loading
94
- model = _load_model(model_id, user_hf_token)
95
- yield log_stream.getvalue(), None
96
-
97
- # Run inference and yield any new logs
98
- print("\nRunning inference on the image...")
99
- results = model(image_path)
100
- print("Inference complete.")
101
- yield log_stream.getvalue(), None
102
-
103
- # 3. Process the final result and yield it with the complete log
104
- final_json = json.loads(json.dumps(_to_jsonable(results), ensure_ascii=False, indent=2))
105
- yield log_stream.getvalue(), final_json
106
-
107
- except gr.Error as e:
108
- # If a Gradio error happens, show it in the logs
109
- yield str(e), None
110
- except Exception as e:
111
- # For other exceptions, capture the error message and show it
112
- yield f"An unexpected error occurred:\n{e}", None
113
-
114
-
115
- def on_model_choice_change(choice: str):
116
- """Shows or hides the custom model input fields based on the dropdown selection."""
117
- return gr.update(visible=(choice == "Custom Model"))
118
-
119
-
120
- # --- Enhanced Gradio Interface with a Log Viewer ---
121
- with gr.Blocks(
122
- theme=gr_themes.Default(
123
- primary_hue="blue",
124
- secondary_hue="neutral",
125
- font=gr_themes.GoogleFont("Inter")
126
- ),
127
- title="Moderators - Visual Content Moderation"
128
- ) as demo:
129
- gr.Markdown("# 🖼️ Moderators: Visual Content Moderation")
130
- gr.Markdown(
131
- "Analyze an image using the featured `viddexa/mobilenet_v2_1.0_224` model, "
132
- "or select another model from the list. You can also use your own private or public model from the Hub."
133
  )
134
 
135
- with gr.Row(variant="panel"):
136
- # Column 1: Controls and Inputs
137
- with gr.Column(scale=1):
138
- gr.Markdown("### ⚙️ Controls")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  model_choice = gr.Dropdown(
140
- choices=[
141
- "viddexa/mobilenet_v2_1.0_224",
142
- "Falconsai/nsfw_image_detection",
143
- "Custom Model",
144
- ],
145
- value="viddexa/mobilenet_v2_1.0_224",
146
- label="Select Model",
147
- info="Choose a model for the analysis.",
148
  )
149
 
150
- with gr.Group(visible=False) as custom_model_group:
151
- custom_model_id = gr.Textbox(
152
- label="Custom Hugging Face Model ID",
153
- placeholder="username/model-name",
154
- info="Enter the ID of the model you want to use."
155
- )
156
- user_hf_token = gr.Textbox(
157
- label='HF Token (if your model is private)',
158
- type="password",
159
- placeholder="hf_...",
160
- info="An access token is required for private models."
161
- )
162
-
163
- gr.Markdown("### 🖼️ Upload Image")
164
- image_input = gr.Image(type="filepath", label="Image to analyze")
165
-
166
- run_btn = gr.Button("Analyze", variant="primary")
167
-
168
- gr.Examples(
169
- examples=[
170
- ["https://assets.clevelandclinic.org/transform/LargeFeatureImage/cd71f4bd-81d4-45d8-a450-74df78e4477a/Apples-184940975-770x533-1_jpg",
171
- "viddexa/mobilenet_v2_1.0_224"],
172
- ["https://img.freepik.com/free-photo/breast-screening-is-very-important-every-woman_329181-14953.jpg",
173
- "Falconsai/nsfw_image_detection"],
174
- ["https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSbRwt56NYsiHwrT8oS-igzgeEzp7p3Jbe2dw&s",
175
- "viddexa/mobilenet_v2_1.0_224"],
176
- ["https://img.freepik.com/premium-photo/portrait-beautiful-young-woman_1048944-5548042.jpg",
177
- "Falconsai/nsfw_image_detection"]
178
- ],
179
- inputs=[image_input, model_choice],
180
- label="Click an example to run",
181
- )
182
 
183
  # Column 2: Outputs
184
- with gr.Column(scale=2):
185
- gr.Markdown("### 📊 Results")
186
- # --- NEW: Status Log Textbox ---
187
- status_log = gr.Textbox(
188
- label="Status Logs",
189
- info="Shows model loading progress and other technical details.",
190
- interactive=False,
191
- lines=8, # Give it some height
192
- )
193
- output_json = gr.JSON(label="Model Output (JSON)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
- # Define the interactive events
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  run_btn.click(
197
- fn=infer,
198
- inputs=[image_input, model_choice, custom_model_id, user_hf_token],
199
- # --- NEW: The click event now updates both the log and the JSON output ---
200
- outputs=[status_log, output_json],
201
  )
202
 
203
- model_choice.change(
204
- fn=on_model_choice_change,
205
- inputs=model_choice,
206
- outputs=custom_model_group,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  )
208
 
 
 
 
 
 
 
209
  if __name__ == "__main__":
 
210
  if not os.path.exists("examples"):
211
  os.makedirs("examples")
212
  print("Created 'examples' directory.")
213
- print("Please add some images like 'safe_image.jpg' to it for the examples to work.")
 
 
 
 
 
 
 
214
 
215
  demo.launch()
 
 
1
  import os
 
 
 
 
 
2
  import gradio as gr
3
  import json
4
+ from typing import Any, Dict, Tuple
5
  from dotenv import load_dotenv
6
  import gradio.themes as gr_themes
7
+ from urllib.request import urlopen, Request, urlretrieve
8
+ from io import BytesIO
9
+ from PIL import Image
10
+ from functools import lru_cache
11
 
12
+ # Load environment variables from a .env file
13
  load_dotenv()
14
 
15
  # --- Secure Token Management ---
16
+ # Get the Hugging Face token from environment variables
17
  VIDDEXA_TOKEN = os.getenv("HF_TOKEN")
18
 
19
  # A simple cache to store loaded model instances
20
  _MODEL_CACHE: Dict[str, Any] = {}
21
 
22
+ # --- CSS Styles ---
23
+ # Custom CSS for the verdict card
24
+ CUSTOM_CSS = """
25
+ .verdict-safe { background-color: #D5F5E3; border: 2px solid #2ECC71; color: #1D8348; }
26
+ .verdict-sensitive { background-color: #FCF3CF; border: 2px solid #F1C40F; color: #B7950B; }
27
+ .verdict-nsfw { background-color: #FADBD8; border: 2px solid #E74C3C; color: #B03A2E; }
28
+ .verdict-card { padding: 20px; border-radius: 10px; text-align: center; font-size: 24px; font-weight: bold; }
29
+
30
+ /* Responsive Galleries: show mobile (2 cols) on small screens, desktop (4 cols) on larger */
31
+ #examples-gallery-mobile { display: block; }
32
+ #examples-gallery-desktop { display: none; }
33
+ @media (min-width: 900px) {
34
+ #examples-gallery-mobile { display: none; }
35
+ #examples-gallery-desktop { display: block; }
36
+ }
37
+
38
+ /* Ensure the Viddexa logo is readable on dark backgrounds */
39
+ .viddexa-logo {
40
+ background-color: #FFFFFF; /* white background behind the SVG */
41
+ padding: 8px; /* a little breathing room */
42
+ border-radius: 8px; /* soften the corners */
43
+ }
44
+ """
45
+
46
+
47
+ # --- Helper Functions ---
48
+
49
+ @lru_cache(maxsize=32)
50
+ def _download_image_bytes(image_url: str) -> bytes:
51
+ req = Request(image_url, headers={"User-Agent": "viddexa-gradio-demo/1.0"})
52
+ with urlopen(req, timeout=20) as resp:
53
+ return resp.read()
54
+
55
+
56
+ def _load_model(model_id: str) -> Any:
57
+ """Loads a model and caches it."""
58
  if model_id in _MODEL_CACHE:
 
59
  return _MODEL_CACHE[model_id]
 
 
 
 
 
 
 
 
 
 
 
 
60
  try:
61
  from moderators.auto_model import AutoModerator
62
+ # Download the model from Hugging Face
63
+ model = AutoModerator.from_pretrained(model_id, token=VIDDEXA_TOKEN, use_fast=True)
64
  _MODEL_CACHE[model_id] = model
 
65
  return model
66
  except Exception as e:
67
  error_msg = f"Failed to load model: {model_id}. Error: {e}"
 
70
  raise gr.Error(error_msg)
71
 
72
 
73
+ def _get_image_input(image_path: str | None, image_url: str | None) -> Image.Image:
74
+ """Gets image data from either an uploaded file path or a URL."""
75
+ if image_url:
76
+ try:
77
+ data = _download_image_bytes(image_url)
78
+ img = Image.open(BytesIO(data))
79
+ return img.convert("RGB")
80
+ except Exception as fetch_err:
81
+ raise gr.Error(f"Could not download or open the image from the URL: {fetch_err}")
82
+ elif image_path:
83
+ # Open the image from a local file
84
+ img = Image.open(image_path)
85
+ return img.convert("RGB")
86
+ else:
87
+ raise gr.Error("Please upload an image or provide an image URL.")
88
+
89
+
90
+ def _format_results(results: list) -> Tuple[str, Dict[str, float], str, Dict]:
91
+ """Formats the model output for the Gradio interface."""
92
+ if not results or "classifications" not in results[0]:
93
+ return "<div class='verdict-card'>No classifications found.</div>", {}, "No classifications found.", {}
94
+
95
+ classifications = results[0]["classifications"]
96
+
97
+ # Normalize to a dict[str, float]
98
+ label_output: Dict[str, float]
99
+ if isinstance(classifications, dict):
100
+ label_output = {str(k): float(v) for k, v in classifications.items()}
101
+ else:
102
+ # Assume list[{'label': str, 'score': float}] shape
103
+ try:
104
+ label_output = {str(item['label']): float(item['score']) for item in classifications}
105
+ except Exception:
106
+ # Fallback to empty if unexpected
107
+ label_output = {}
108
+
109
+ # Determine the final verdict
110
+ scores = {label.lower(): score for label, score in label_output.items()}
111
+ nsfw_score = scores.get("nsfw", 0.0)
112
+
113
+ if nsfw_score > 0.7:
114
+ verdict_text = "HIGH RISK: NSFW"
115
+ verdict_class = "verdict-nsfw"
116
+ elif nsfw_score > 0.2:
117
+ verdict_text = "MEDIUM RISK: SENSITIVE"
118
+ verdict_class = "verdict-sensitive"
119
+ else:
120
+ verdict_text = "LOW RISK: SAFE"
121
+ verdict_class = "verdict-safe"
122
+
123
+ verdict_html = f"<div class='verdict-card {verdict_class}'>{verdict_text}</div>"
124
+
125
+ # Prepare scores for Markdown list
126
+ markdown_output = "### All Scores\n---\n"
127
+ for label, score in sorted(label_output.items(), key=lambda kv: kv[1], reverse=True):
128
+ markdown_output += f"- **{label.capitalize()}**: {score:.4f}\n"
129
+
130
+ return verdict_html, label_output, markdown_output, results[0]
131
+
132
+
133
+ # --- Main Analysis Function ---
134
+
135
+ def analyze_image(image_path: str | None, image_url: str | None, model_choice: str,
136
+ progress=gr.Progress(track_tqdm=True)):
137
+ """The main inference function for the Gradio interface."""
138
+ progress(0, desc="Initializing Analysis...")
139
+
140
+ # 1. Get Image Input
141
+ progress(0.2, desc="Processing Image...")
142
+ input_image = _get_image_input(image_path, image_url)
143
+
144
+ # 2. Load Model
145
+ progress(0.5, desc=f"Loading Model: {os.path.basename(model_choice)}...")
146
+ model = _load_model(model_choice)
147
+
148
+ # 3. Run Inference
149
+ progress(0.8, desc="Running Inference...")
150
+ results = model(input_image)
151
+
152
+ # Helper to make model outputs JSON-serializable and in expected shape
153
+ json_results = [
154
+ {"classifications": getattr(r, "classifications", r)}
155
+ for r in results
156
+ ]
157
+ json_results = json.loads(json.dumps(json_results, ensure_ascii=False))
158
+
159
+ # 4. Format and Return Results
160
+ progress(1, desc="Complete!")
161
+ return _format_results(json_results)
162
+
163
+
164
+ def analyze_image_from_url(image_url: str, model_choice: str, progress=gr.Progress(track_tqdm=True)):
165
+ """Wrapper to run examples with just URL + model."""
166
+ return analyze_image(None, image_url, model_choice, progress)
167
+
168
+
169
+ def analyze_image_with_status(image_path: str | None, image_url: str | None, model_choice: str,
170
+ progress=gr.Progress(track_tqdm=True)):
171
+ """Run analysis and also return a user-friendly status string."""
172
+ verdict_html, label_scores, md_scores, json_obj = analyze_image(image_path, image_url, model_choice, progress)
173
+ if image_url:
174
+ status = f"Last analysed URL: {image_url}"
175
+ elif image_path:
176
+ status = "Last analysed uploaded image."
177
+ else:
178
+ status = "Last analysed: —"
179
+ return verdict_html, label_scores, md_scores, json_obj, status
180
+
181
+
182
+ # Example mapping for gallery selections
183
+ EXAMPLE_ITEMS = [
184
+ (
185
+ "https://assets.clevelandclinic.org/transform/LargeFeatureImage/cd71f4bd-81d4-45d8-a450-74df78e4477a/Apples-184940975-770x533-1_jpg",
186
+ "viddexa/nsfw-mini",
187
+ "Apples (mini)",
188
+ ),
189
+ (
190
+ "https://img.freepik.com/free-photo/breast-screening-is-very-important-every-woman_329181-14953.jpg",
191
+ "viddexa/nsfw-nano",
192
+ "Breast screening (nano)",
193
+ ),
194
+ (
195
+ "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSbRwt56NYsiHwrT8oS-igzgeEzp7p3Jbe2dw&s",
196
+ "viddexa/nsfw-mini",
197
+ "Thumbnail (mini)",
198
+ ),
199
+ (
200
+ "https://img.freepik.com/premium-photo/portrait-beautiful-young-woman_1048944-5548042.jpg",
201
+ "viddexa/nsfw-nano",
202
+ "Portrait (nano)",
203
+ ),
204
+ ]
205
+
206
+
207
+ def run_example_by_index(evt: gr.SelectData):
208
+ """Handle gallery selection: run analysis for the selected example and update inputs."""
209
  try:
210
+ idx = int(getattr(evt, "index", 0))
 
 
211
  except Exception:
212
+ idx = 0
213
+ idx = max(0, min(idx, len(EXAMPLE_ITEMS) - 1))
214
+ url, model, caption = EXAMPLE_ITEMS[idx]
215
+ verdict_html, label_scores, md_scores, json_obj = analyze_image(None, url, model)
216
+ status = f"Last analysed example: {caption}"
217
+ # Also update the dropdown and URL textbox for transparency
218
+ return (
219
+ verdict_html,
220
+ label_scores,
221
+ md_scores,
222
+ json_obj,
223
+ gr.update(value=model),
224
+ gr.update(value=url),
225
+ status,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  )
227
 
228
+
229
+ # --- Gradio Interface ---
230
+
231
+ with gr.Blocks(theme=gr_themes.Soft(), title="Viddexa - Visual Content Moderation", css=CUSTOM_CSS) as demo:
232
+ # Header and Introduction
233
+ gr.HTML("""
234
+ <div class=\"viddexa-header\" style=\"text-align: center; max-width: 800px; margin: 0 auto;\">
235
+ <img class=\"viddexa-logo\" src=\"https://aky-tech.com/images/viddexa-logo.svg\" alt=\"Viddexa logo\" style=\"max-width: 320px; width: 80%; height: auto; display: block; margin: 10px auto 6px;\" />
236
+ <p style=\"font-size: 1.2em; color: #888;\">
237
+ This demo showcases the open-source <code>moderators</code> Python package and NSFW (Not Safe For Work) detection models developed by <b>Viddexa</b>.
238
+ Upload an image or provide a URL to get an instant content analysis.
239
+ </p>
240
+ <p>
241
+ 🔗 <b>Project Links:</b>
242
+ <a href=\"https://huggingface.co/viddexa/nsfw-mini\" target=\"_blank\">[Model: nsfw-mini]</a> |
243
+ <a href=\"https://huggingface.co/viddexa/nsfw-nano\" target=\"_blank\">[Model: nsfw-nano]</a> |
244
+ <a href=\"https://github.com/viddexa/moderators\" target=\"_blank\">[GitHub]</a> |
245
+ <a href=\"https://pypi.org/project/moderators/\" target=\"_blank\">[PyPI]</a>
246
+ </p>
247
+ </div>
248
+ """)
249
+
250
+ with gr.Row(variant="panel") as main_row:
251
+ # Capture both columns so we can add widgets in any order
252
+ with gr.Column(scale=1, min_width=350) as left_col:
253
+ gr.Markdown("## ⚙️ Step 1: Configure Settings")
254
  model_choice = gr.Dropdown(
255
+ choices=["viddexa/nsfw-mini", "viddexa/nsfw-nano"],
256
+ value="viddexa/nsfw-mini",
257
+ label="Analysis Model",
258
+ info="Choose the faster 'nano' or the more comprehensive 'mini' model.",
 
 
 
 
259
  )
260
 
261
+ gr.Markdown("## 🖼️ Step 2: Provide an Image")
262
+ with gr.Tabs() as input_tabs:
263
+ with gr.TabItem("Upload Image", id="upload_tab"):
264
+ image_input = gr.Image(type="filepath", label="Drag & drop a file or click to upload")
265
+ with gr.TabItem("From URL", id="url_tab"):
266
+ image_url_input = gr.Textbox(
267
+ label="Image URL",
268
+ placeholder="https://example.com/image.jpg",
269
+ )
270
+
271
+ run_btn = gr.Button("Start Analysis", variant="primary", scale=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
273
  # Column 2: Outputs
274
+ with gr.Column(scale=2, min_width=500) as right_col:
275
+ gr.Markdown("## 📊 Step 3: Review Results")
276
+ verdict_output = gr.HTML(label="Final Verdict")
277
+ label_output = gr.Label(label="Classification Scores", num_top_classes=4, show_label=True)
278
+ markdown_output = gr.Markdown(label="All Scores")
279
+
280
+ with gr.Accordion("Show Raw JSON Output", open=False):
281
+ json_output = gr.JSON(label="Model Output (JSON)")
282
+
283
+ # Full-width Examples Gallery section
284
+ gr.Markdown("## 🎯 Try an Example (click an image)")
285
+ gallery_items = [[url, caption] for (url, _model, caption) in EXAMPLE_ITEMS]
286
+ examples_gallery_mobile = gr.Gallery(
287
+ label="Try an Example",
288
+ value=gallery_items,
289
+ columns=2,
290
+ height="auto",
291
+ allow_preview=False,
292
+ elem_id="examples-gallery-mobile",
293
+ )
294
+ examples_gallery_desktop = gr.Gallery(
295
+ label=None,
296
+ value=gallery_items,
297
+ columns=4,
298
+ height="auto",
299
+ allow_preview=False,
300
+ elem_id="examples-gallery-desktop",
301
+ )
302
 
303
+ # Status label under galleries
304
+ status_md = gr.Markdown("Last analyse: —", elem_id="last-example-status")
305
+
306
+ # When a gallery image is selected, run analysis and update outputs + inputs + status
307
+ for gallery in (examples_gallery_mobile, examples_gallery_desktop):
308
+ gallery.select(
309
+ fn=run_example_by_index,
310
+ outputs=[
311
+ verdict_output,
312
+ label_output,
313
+ markdown_output,
314
+ json_output,
315
+ model_choice,
316
+ image_url_input,
317
+ status_md,
318
+ ],
319
+ )
320
+
321
+ # Interactive Event Listeners
322
+
323
+ # When the analysis button is clicked
324
  run_btn.click(
325
+ fn=analyze_image_with_status,
326
+ inputs=[image_input, image_url_input, model_choice],
327
+ outputs=[verdict_output, label_output, markdown_output, json_output, status_md],
 
328
  )
329
 
330
+ # Clear the other input when one changes (avoid relying on Tabs.select on some Gradio versions)
331
+ def _clear_url_on_image_change(_img):
332
+ # Keep image as-is, clear URL
333
+ return gr.update(), gr.update(value="")
334
+
335
+ def _clear_image_on_url_change(_url):
336
+ # Clear uploaded image, keep URL as-is
337
+ return gr.update(value=None), gr.update()
338
+
339
+ image_input.change(
340
+ fn=_clear_url_on_image_change,
341
+ inputs=[image_input],
342
+ outputs=[image_input, image_url_input],
343
+ )
344
+ image_url_input.change(
345
+ fn=_clear_image_on_url_change,
346
+ inputs=[image_url_input],
347
+ outputs=[image_input, image_url_input],
348
  )
349
 
350
+ gr.HTML("""
351
+ <div style=\"text-align: center; margin-top: 20px; color: #888;\">
352
+ <p>Developed by Viddexa.</p>
353
+ </div>
354
+ """)
355
+
356
  if __name__ == "__main__":
357
+ # Create an 'examples' directory and download a sample image if it doesn't exist
358
  if not os.path.exists("examples"):
359
  os.makedirs("examples")
360
  print("Created 'examples' directory.")
361
+ try:
362
+ urlretrieve(
363
+ "https://images.pexels.com/photos/36717/amazing-animal-beautiful-beautifull.jpg",
364
+ "examples/safe_nature.jpg"
365
+ )
366
+ print("Downloaded an example image to 'examples/safe_nature.jpg'")
367
+ except Exception as e:
368
+ print(f"Could not download example image: {e}")
369
 
370
  demo.launch()
requirements.txt CHANGED
@@ -6,4 +6,7 @@ transformers>=4.36
6
 
7
  # Transformers'ın çalışması için gereken backend ve yardımcı kütüphaneler
8
  torch
9
- Pillow
 
 
 
 
6
 
7
  # Transformers'ın çalışması için gereken backend ve yardımcı kütüphaneler
8
  torch
9
+ Pillow
10
+
11
+ # Moderators artık paket olarak kullanılıyor
12
+ moderators