VietCat commited on
Commit
734fa17
·
1 Parent(s): 5cc1902

Replace zoom with scrollable HTML display - actual pixel-level zoom

Browse files

- Original images still display at standard size
- New zoom section uses HTML with base64 encoded images
- Zoomed images display at actual resized dimensions (not fixed container)
- Scrollable container for images larger than 600px height
- Shows zoom level and actual pixel dimensions
- Zoom range expanded to 50-300% for better inspection

Files changed (1) hide show
  1. app.py +108 -33
app.py CHANGED
@@ -5,6 +5,7 @@ from model import TrafficSignDetector
5
  import sys
6
  import io
7
  from PIL import Image
 
8
 
9
  # Load the detector
10
  detector = TrafficSignDetector('config.yaml')
@@ -46,10 +47,25 @@ def detect_traffic_signs(image, confidence_threshold):
46
 
47
  return result_image, preprocessed_image
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  def apply_zoom_output(zoom_level):
50
- """Apply zoom to output image"""
51
  if original_output_image is None:
52
- return None
53
 
54
  zoom_factor = zoom_level / 100.0
55
  h, w = original_output_image.shape[:2]
@@ -58,13 +74,39 @@ def apply_zoom_output(zoom_level):
58
 
59
  if new_w > 0 and new_h > 0:
60
  zoomed = cv2.resize(original_output_image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
61
- return zoomed
62
- return original_output_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  def apply_zoom_preprocessed(zoom_level):
65
- """Apply zoom to preprocessed image"""
66
  if original_preprocessed_image is None:
67
- return None
68
 
69
  zoom_factor = zoom_level / 100.0
70
  h, w = original_preprocessed_image.shape[:2]
@@ -73,8 +115,34 @@ def apply_zoom_preprocessed(zoom_level):
73
 
74
  if new_w > 0 and new_h > 0:
75
  zoomed = cv2.resize(original_preprocessed_image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
76
- return zoomed
77
- return original_preprocessed_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  # Create Gradio interface
80
  with gr.Blocks(title="Traffic Sign Detector") as demo:
@@ -83,28 +151,10 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
83
 
84
  with gr.Row():
85
  input_image = gr.Image(label="Upload Image", type="pil")
86
- with gr.Column():
87
- output_image = gr.Image(label="Detected Signs", interactive=False)
88
- zoom_slider_output = gr.Slider(
89
- minimum=50,
90
- maximum=200,
91
- value=100,
92
- step=10,
93
- label="Zoom Detected Image (%)",
94
- info="50% = 50% size, 100% = original, 200% = 2x size"
95
- )
96
 
97
  with gr.Row():
98
  preprocessed_image = gr.Image(label="Preprocessed Image (640x640, Letterboxed)", interactive=False)
99
- with gr.Column():
100
- zoom_slider_preprocessed = gr.Slider(
101
- minimum=50,
102
- maximum=200,
103
- value=100,
104
- step=10,
105
- label="Zoom Preprocessed Image (%)",
106
- info="50% = 50% size, 100% = original, 200% = 2x size"
107
- )
108
 
109
  with gr.Row():
110
  confidence_threshold = gr.Slider(
@@ -120,6 +170,31 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
120
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
121
  reset_btn = gr.Button("Clear")
122
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  # Auto-detect when image is uploaded
124
  input_image.change(
125
  fn=detect_traffic_signs,
@@ -144,26 +219,26 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
144
  queue=True
145
  )
146
 
147
- # Zoom output image
148
  zoom_slider_output.change(
149
  fn=apply_zoom_output,
150
  inputs=[zoom_slider_output],
151
- outputs=[output_image],
152
  queue=False
153
  )
154
 
155
- # Zoom preprocessed image
156
  zoom_slider_preprocessed.change(
157
  fn=apply_zoom_preprocessed,
158
  inputs=[zoom_slider_preprocessed],
159
- outputs=[preprocessed_image],
160
  queue=False
161
  )
162
 
163
  # Clear button
164
  reset_btn.click(
165
- fn=lambda: (None, None, None, 100, 100),
166
- outputs=[input_image, output_image, preprocessed_image, zoom_slider_output, zoom_slider_preprocessed]
167
  )
168
 
169
  if __name__ == "__main__":
 
5
  import sys
6
  import io
7
  from PIL import Image
8
+ import base64
9
 
10
  # Load the detector
11
  detector = TrafficSignDetector('config.yaml')
 
47
 
48
  return result_image, preprocessed_image
49
 
50
+ def array_to_base64(image_array):
51
+ """Convert numpy array to base64 PNG string"""
52
+ # Convert to PIL
53
+ pil_image = Image.fromarray(image_array.astype('uint8'))
54
+
55
+ # Save to bytes
56
+ import io as io_module
57
+ buffered = io_module.BytesIO()
58
+ pil_image.save(buffered, format="PNG")
59
+ buffered.seek(0)
60
+
61
+ # Convert to base64
62
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
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]
 
74
 
75
  if new_w > 0 and new_h > 0:
76
  zoomed = cv2.resize(original_output_image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
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
84
+ html = f"""
85
+ <div style="
86
+ border: 1px solid #ccc;
87
+ border-radius: 4px;
88
+ overflow: auto;
89
+ max-width: 100%;
90
+ max-height: 600px;
91
+ padding: 10px;
92
+ background-color: #f9f9f9;
93
+ ">
94
+ <img src="data:image/png;base64,{img_base64}" style="
95
+ width: {new_w}px;
96
+ height: {new_h}px;
97
+ display: block;
98
+ " />
99
+ </div>
100
+ <p style="font-size: 12px; color: #666; margin-top: 5px;">
101
+ Zoom: {zoom_level}% | Size: {new_w}×{new_h}px | Original: {w}×{h}px
102
+ </p>
103
+ """
104
+ return html
105
 
106
  def apply_zoom_preprocessed(zoom_level):
107
+ """Apply zoom to preprocessed image and display with scrolling"""
108
  if original_preprocessed_image is None:
109
+ return "<p style='color: gray;'>Upload an image first</p>"
110
 
111
  zoom_factor = zoom_level / 100.0
112
  h, w = original_preprocessed_image.shape[:2]
 
115
 
116
  if new_w > 0 and new_h > 0:
117
  zoomed = cv2.resize(original_preprocessed_image, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
118
+ else:
119
+ zoomed = original_preprocessed_image
120
+
121
+ # Convert to base64 for HTML display
122
+ img_base64 = array_to_base64(zoomed)
123
+
124
+ # Return HTML with scrollable container
125
+ html = f"""
126
+ <div style="
127
+ border: 1px solid #ccc;
128
+ border-radius: 4px;
129
+ overflow: auto;
130
+ max-width: 100%;
131
+ max-height: 600px;
132
+ padding: 10px;
133
+ background-color: #f9f9f9;
134
+ ">
135
+ <img src="data:image/png;base64,{img_base64}" style="
136
+ width: {new_w}px;
137
+ height: {new_h}px;
138
+ display: block;
139
+ " />
140
+ </div>
141
+ <p style="font-size: 12px; color: #666; margin-top: 5px;">
142
+ Zoom: {zoom_level}% | Size: {new_w}×{new_h}px | Original: {w}×{h}px
143
+ </p>
144
+ """
145
+ return html
146
 
147
  # Create Gradio interface
148
  with gr.Blocks(title="Traffic Sign Detector") as demo:
 
151
 
152
  with gr.Row():
153
  input_image = gr.Image(label="Upload Image", type="pil")
154
+ output_image = gr.Image(label="Detected Signs", interactive=False)
 
 
 
 
 
 
 
 
 
155
 
156
  with gr.Row():
157
  preprocessed_image = gr.Image(label="Preprocessed Image (640x640, Letterboxed)", interactive=False)
 
 
 
 
 
 
 
 
 
158
 
159
  with gr.Row():
160
  confidence_threshold = gr.Slider(
 
170
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
171
  reset_btn = gr.Button("Clear")
172
 
173
+ gr.Markdown("### Zoom & Inspect")
174
+
175
+ with gr.Row():
176
+ with gr.Column():
177
+ zoom_slider_output = gr.Slider(
178
+ minimum=50,
179
+ maximum=300,
180
+ value=100,
181
+ step=10,
182
+ label="Zoom Detected Image (%)",
183
+ info="Drag to zoom (50% to 300%)"
184
+ )
185
+ output_html = gr.HTML(label="Zoomed Detected Image")
186
+
187
+ with gr.Column():
188
+ zoom_slider_preprocessed = gr.Slider(
189
+ minimum=50,
190
+ maximum=300,
191
+ value=100,
192
+ step=10,
193
+ label="Zoom Preprocessed Image (%)",
194
+ info="Drag to zoom (50% to 300%)"
195
+ )
196
+ preprocessed_html = gr.HTML(label="Zoomed Preprocessed Image")
197
+
198
  # Auto-detect when image is uploaded
199
  input_image.change(
200
  fn=detect_traffic_signs,
 
219
  queue=True
220
  )
221
 
222
+ # Zoom output image - display in HTML with actual size
223
  zoom_slider_output.change(
224
  fn=apply_zoom_output,
225
  inputs=[zoom_slider_output],
226
+ outputs=[output_html],
227
  queue=False
228
  )
229
 
230
+ # Zoom preprocessed image - display in HTML with actual size
231
  zoom_slider_preprocessed.change(
232
  fn=apply_zoom_preprocessed,
233
  inputs=[zoom_slider_preprocessed],
234
+ outputs=[preprocessed_html],
235
  queue=False
236
  )
237
 
238
  # Clear button
239
  reset_btn.click(
240
+ fn=lambda: (None, None, None, 100, 100, "", ""),
241
+ outputs=[input_image, output_image, preprocessed_image, zoom_slider_output, zoom_slider_preprocessed, output_html, preprocessed_html]
242
  )
243
 
244
  if __name__ == "__main__":