VietCat commited on
Commit
a0d96db
·
1 Parent(s): 625d803

Simplify zoom - use Image components with native scroll

Browse files

- Return to using gr.Image components for zoomed output (Gradio native)
- Gradio Image components handle scrolling automatically for large images
- Removed HTML base64 approach that didn't support horizontal scroll
- Much simpler implementation - just cv2.resize and display
- Gradio natively scrolls any image larger than container
- Zoom sliders (50-300%) adjust image size, Gradio handles the rest

Files changed (1) hide show
  1. app.py +48 -96
app.py CHANGED
@@ -63,9 +63,9 @@ def array_to_base64(image_array):
63
  return img_base64
64
 
65
  def apply_zoom_output(zoom_level):
66
- """Apply zoom to output image and display with scrolling"""
67
  if original_output_image is None:
68
- return "<p style='color: gray;'>Upload an image first</p>"
69
 
70
  zoom_factor = zoom_level / 100.0
71
  h, w = original_output_image.shape[:2]
@@ -77,42 +77,12 @@ def apply_zoom_output(zoom_level):
77
  else:
78
  zoomed = original_output_image
79
 
80
- # Convert to base64 for HTML display
81
- img_base64 = array_to_base64(zoomed)
82
-
83
- # Return HTML with scrollable container that allows both directions
84
- html = f"""
85
- <div style="
86
- border: 1px solid #ccc;
87
- border-radius: 4px;
88
- overflow: auto;
89
- max-height: 600px;
90
- width: 100%;
91
- padding: 10px;
92
- background-color: #f9f9f9;
93
- white-space: nowrap;
94
- box-sizing: border-box;
95
- ">
96
- <div style="display: inline-block;">
97
- <img src="data:image/png;base64,{img_base64}" style="
98
- width: {new_w}px;
99
- height: {new_h}px;
100
- display: block;
101
- image-rendering: crisp-edges;
102
- vertical-align: top;
103
- " />
104
- </div>
105
- </div>
106
- <p style="font-size: 12px; color: #666; margin-top: 5px;">
107
- Zoom: {zoom_level}% | Size: {new_w}×{new_h}px | Original: {w}×{h}px
108
- </p>
109
- """
110
- return html
111
 
112
  def apply_zoom_preprocessed(zoom_level):
113
- """Apply zoom to preprocessed image and display with scrolling"""
114
  if original_preprocessed_image is None:
115
- return "<p style='color: gray;'>Upload an image first</p>"
116
 
117
  zoom_factor = zoom_level / 100.0
118
  h, w = original_preprocessed_image.shape[:2]
@@ -124,40 +94,24 @@ def apply_zoom_preprocessed(zoom_level):
124
  else:
125
  zoomed = original_preprocessed_image
126
 
127
- # Convert to base64 for HTML display
128
- img_base64 = array_to_base64(zoomed)
129
-
130
- # Return HTML with scrollable container that allows both directions
131
- html = f"""
132
- <div style="
133
- border: 1px solid #ccc;
134
- border-radius: 4px;
135
- overflow: auto;
136
- max-height: 600px;
137
- width: 100%;
138
- padding: 10px;
139
- background-color: #f9f9f9;
140
- white-space: nowrap;
141
- box-sizing: border-box;
142
- ">
143
- <div style="display: inline-block;">
144
- <img src="data:image/png;base64,{img_base64}" style="
145
- width: {new_w}px;
146
- height: {new_h}px;
147
- display: block;
148
- image-rendering: crisp-edges;
149
- vertical-align: top;
150
- " />
151
- </div>
152
- </div>
153
- <p style="font-size: 12px; color: #666; margin-top: 5px;">
154
- Zoom: {zoom_level}% | Size: {new_w}×{new_h}px | Original: {w}×{h}px
155
- </p>
156
- """
157
- return html
158
 
159
  # Create Gradio interface
160
- with gr.Blocks(title="Traffic Sign Detector") as demo:
161
  gr.Markdown("# Traffic Sign Detector")
162
  gr.Markdown("Upload an image to detect traffic signs using YOLOv8. Detection runs automatically when you upload or adjust the threshold.")
163
 
@@ -182,33 +136,31 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
182
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
183
  reset_btn = gr.Button("Clear")
184
 
185
- gr.Markdown("### Zoom & Inspect (Scroll horizontally and vertically as needed)")
186
 
187
  with gr.Row():
188
- zoom_slider_output = gr.Slider(
189
- minimum=50,
190
- maximum=300,
191
- value=100,
192
- step=10,
193
- label="Zoom Detected Image (%)",
194
- info="Drag to zoom (50% to 300%)",
195
- scale=1
196
- )
197
-
198
- output_html = gr.HTML(label="Zoomed Detected Image")
199
 
200
  with gr.Row():
201
- zoom_slider_preprocessed = gr.Slider(
202
- minimum=50,
203
- maximum=300,
204
- value=100,
205
- step=10,
206
- label="Zoom Preprocessed Image (%)",
207
- info="Drag to zoom (50% to 300%)",
208
- scale=1
209
- )
210
-
211
- preprocessed_html = gr.HTML(label="Zoomed Preprocessed Image")
212
 
213
  # Auto-detect when image is uploaded
214
  input_image.change(
@@ -234,26 +186,26 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
234
  queue=True
235
  )
236
 
237
- # Zoom output image - display in HTML with actual size
238
  zoom_slider_output.change(
239
  fn=apply_zoom_output,
240
  inputs=[zoom_slider_output],
241
- outputs=[output_html],
242
  queue=False
243
  )
244
 
245
- # Zoom preprocessed image - display in HTML with actual size
246
  zoom_slider_preprocessed.change(
247
  fn=apply_zoom_preprocessed,
248
  inputs=[zoom_slider_preprocessed],
249
- outputs=[preprocessed_html],
250
  queue=False
251
  )
252
 
253
  # Clear button
254
  reset_btn.click(
255
- fn=lambda: (None, None, None, 100, 100, "", ""),
256
- outputs=[input_image, output_image, preprocessed_image, zoom_slider_output, zoom_slider_preprocessed, output_html, preprocessed_html]
257
  )
258
 
259
  if __name__ == "__main__":
 
63
  return img_base64
64
 
65
  def apply_zoom_output(zoom_level):
66
+ """Apply zoom to output image"""
67
  if original_output_image is None:
68
+ return None
69
 
70
  zoom_factor = zoom_level / 100.0
71
  h, w = original_output_image.shape[:2]
 
77
  else:
78
  zoomed = original_output_image
79
 
80
+ return zoomed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  def apply_zoom_preprocessed(zoom_level):
83
+ """Apply zoom to preprocessed image"""
84
  if original_preprocessed_image is None:
85
+ return None
86
 
87
  zoom_factor = zoom_level / 100.0
88
  h, w = original_preprocessed_image.shape[:2]
 
94
  else:
95
  zoomed = original_preprocessed_image
96
 
97
+ return zoomed
98
+
99
+ # Custom CSS for better scrolling
100
+ custom_css = """
101
+ .zoom-container {
102
+ overflow-x: auto !important;
103
+ overflow-y: auto !important;
104
+ max-height: 600px;
105
+ border: 1px solid #ccc;
106
+ border-radius: 4px;
107
+ padding: 10px;
108
+ background-color: #f9f9f9;
109
+ display: block !important;
110
+ }
111
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  # Create Gradio interface
114
+ with gr.Blocks(title="Traffic Sign Detector", css=custom_css) as demo:
115
  gr.Markdown("# Traffic Sign Detector")
116
  gr.Markdown("Upload an image to detect traffic signs using YOLOv8. Detection runs automatically when you upload or adjust the threshold.")
117
 
 
136
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
137
  reset_btn = gr.Button("Clear")
138
 
139
+ gr.Markdown("### Zoom & Inspect (Use scroll bars for inspection)")
140
 
141
  with gr.Row():
142
+ with gr.Column():
143
+ gr.Markdown("**Zoom Detected Image**")
144
+ zoom_slider_output = gr.Slider(
145
+ minimum=50,
146
+ maximum=300,
147
+ value=100,
148
+ step=10,
149
+ label="Zoom Level (%)"
150
+ )
151
+ output_zoomed = gr.Image(label="Zoomed Detected Image", interactive=False)
 
152
 
153
  with gr.Row():
154
+ with gr.Column():
155
+ gr.Markdown("**Zoom Preprocessed Image**")
156
+ zoom_slider_preprocessed = gr.Slider(
157
+ minimum=50,
158
+ maximum=300,
159
+ value=100,
160
+ step=10,
161
+ label="Zoom Level (%)"
162
+ )
163
+ preprocessed_zoomed = gr.Image(label="Zoomed Preprocessed Image", interactive=False)
 
164
 
165
  # Auto-detect when image is uploaded
166
  input_image.change(
 
186
  queue=True
187
  )
188
 
189
+ # Zoom output image
190
  zoom_slider_output.change(
191
  fn=apply_zoom_output,
192
  inputs=[zoom_slider_output],
193
+ outputs=[output_zoomed],
194
  queue=False
195
  )
196
 
197
+ # Zoom preprocessed image
198
  zoom_slider_preprocessed.change(
199
  fn=apply_zoom_preprocessed,
200
  inputs=[zoom_slider_preprocessed],
201
+ outputs=[preprocessed_zoomed],
202
  queue=False
203
  )
204
 
205
  # Clear button
206
  reset_btn.click(
207
+ fn=lambda: (None, None, None, 100, 100, None, None),
208
+ outputs=[input_image, output_image, preprocessed_image, zoom_slider_output, zoom_slider_preprocessed, output_zoomed, preprocessed_zoomed]
209
  )
210
 
211
  if __name__ == "__main__":