VietCat commited on
Commit
d17b1c4
·
1 Parent(s): de45b47

Fix zoom slider functionality - now updates image display directly

Browse files

- Use Gradio State to store original images after detection
- Zoom sliders now update main image display (not hidden components)
- Queue=False for immediate zoom response (no delay)
- Cleaner UI with working zoom on all browsers/OS
- Reset button also resets zoom sliders to 100%

Files changed (1) hide show
  1. app.py +66 -33
app.py CHANGED
@@ -36,48 +36,66 @@ def detect_traffic_signs(image, confidence_threshold):
36
 
37
  return result_image, preprocessed_image
38
 
39
- def zoom_image(image, zoom_level):
40
  """
41
- Zoom image by resizing it.
42
- :param image: PIL Image
43
- :param zoom_level: zoom percentage (50-200)
44
- :return: zoomed image
45
  """
46
  if image is None:
47
  return None
48
 
49
  # Convert to PIL if needed
50
  if isinstance(image, np.ndarray):
51
- image = Image.fromarray(image)
 
 
52
 
53
  # Calculate new size
54
  zoom_factor = zoom_level / 100.0
55
- new_width = int(image.width * zoom_factor)
56
- new_height = int(image.height * zoom_factor)
57
 
58
  # Resize image
59
- zoomed = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
60
- return zoomed
 
 
61
 
62
  # Create Gradio interface
63
- with gr.Blocks(title="Traffic Sign Detector") as demo:
64
  gr.Markdown("# Traffic Sign Detector")
65
  gr.Markdown("Upload an image to detect traffic signs using YOLOv8. Detection runs automatically when you upload or adjust the threshold.")
66
 
 
 
 
 
67
  with gr.Row():
68
  input_image = gr.Image(label="Upload Image", type="pil")
69
  with gr.Column():
70
  output_image = gr.Image(label="Detected Signs", interactive=False)
71
- with gr.Row():
72
- zoom_slider_output = gr.Slider(minimum=50, maximum=200, value=100, step=10, label="Zoom Output (%)")
73
- output_image_zoomed = gr.Image(label="Zoomed Output", interactive=False, visible=False)
 
 
 
 
 
74
 
75
  with gr.Row():
76
  preprocessed_image = gr.Image(label="Preprocessed Image (640x640, Letterboxed)", interactive=False)
77
  with gr.Column():
78
- gr.Markdown("#### Zoom Preprocessed (%)")
79
- zoom_slider_preprocessed = gr.Slider(minimum=50, maximum=200, value=100, step=10, label="Zoom Preprocessed")
80
- preprocessed_image_zoomed = gr.Image(label="Zoomed Preprocessed", interactive=False, visible=False)
 
 
 
 
 
81
 
82
  with gr.Row():
83
  confidence_threshold = gr.Slider(
@@ -93,48 +111,63 @@ with gr.Blocks(title="Traffic Sign Detector") as demo:
93
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
94
  reset_btn = gr.Button("Clear")
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  # Auto-detect when image is uploaded
97
  input_image.change(
98
- fn=detect_traffic_signs,
99
  inputs=[input_image, confidence_threshold],
100
- outputs=[output_image, preprocessed_image],
101
  queue=True
102
  )
103
 
104
  # Auto-detect when threshold is changed
105
  confidence_threshold.change(
106
- fn=detect_traffic_signs,
107
  inputs=[input_image, confidence_threshold],
108
- outputs=[output_image, preprocessed_image],
109
  queue=True
110
  )
111
 
112
  # Manual detection button
113
  detect_btn.click(
114
- fn=detect_traffic_signs,
115
  inputs=[input_image, confidence_threshold],
116
- outputs=[output_image, preprocessed_image],
117
  queue=True
118
  )
119
 
120
- # Zoom output image
121
  zoom_slider_output.change(
122
- fn=zoom_image,
123
- inputs=[output_image, zoom_slider_output],
124
- outputs=[output_image_zoomed]
 
125
  )
126
 
127
- # Zoom preprocessed image
128
  zoom_slider_preprocessed.change(
129
- fn=zoom_image,
130
- inputs=[preprocessed_image, zoom_slider_preprocessed],
131
- outputs=[preprocessed_image_zoomed]
 
132
  )
133
 
134
  # Clear button
135
  reset_btn.click(
136
- fn=lambda: (None, None, None, None, None),
137
- outputs=[input_image, output_image, preprocessed_image, output_image_zoomed, preprocessed_image_zoomed]
138
  )
139
 
140
  if __name__ == "__main__":
 
36
 
37
  return result_image, preprocessed_image
38
 
39
+ def apply_zoom(image, zoom_level):
40
  """
41
+ Apply zoom to image by resizing it.
42
+ :param image: numpy array or PIL Image
43
+ :param zoom_level: zoom percentage (50-200, where 100 = 100%)
44
+ :return: zoomed image as numpy array
45
  """
46
  if image is None:
47
  return None
48
 
49
  # Convert to PIL if needed
50
  if isinstance(image, np.ndarray):
51
+ pil_image = Image.fromarray(image.astype('uint8'))
52
+ else:
53
+ pil_image = image
54
 
55
  # Calculate new size
56
  zoom_factor = zoom_level / 100.0
57
+ new_width = int(pil_image.width * zoom_factor)
58
+ new_height = int(pil_image.height * zoom_factor)
59
 
60
  # Resize image
61
+ zoomed = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
62
+
63
+ # Convert back to numpy
64
+ return np.array(zoomed)
65
 
66
  # Create Gradio interface
67
+ with gr.Blocks(title="Traffic Sign Detector", css=".zoom-info { font-size: 12px; color: #666; }") as demo:
68
  gr.Markdown("# Traffic Sign Detector")
69
  gr.Markdown("Upload an image to detect traffic signs using YOLOv8. Detection runs automatically when you upload or adjust the threshold.")
70
 
71
+ # Store original images for zooming
72
+ output_image_state = gr.State(None)
73
+ preprocessed_image_state = gr.State(None)
74
+
75
  with gr.Row():
76
  input_image = gr.Image(label="Upload Image", type="pil")
77
  with gr.Column():
78
  output_image = gr.Image(label="Detected Signs", interactive=False)
79
+ zoom_slider_output = gr.Slider(
80
+ minimum=50,
81
+ maximum=200,
82
+ value=100,
83
+ step=10,
84
+ label="Zoom Detected Image (%)",
85
+ info="50% = 50% size, 100% = original, 200% = 2x size"
86
+ )
87
 
88
  with gr.Row():
89
  preprocessed_image = gr.Image(label="Preprocessed Image (640x640, Letterboxed)", interactive=False)
90
  with gr.Column():
91
+ zoom_slider_preprocessed = gr.Slider(
92
+ minimum=50,
93
+ maximum=200,
94
+ value=100,
95
+ step=10,
96
+ label="Zoom Preprocessed Image (%)",
97
+ info="50% = 50% size, 100% = original, 200% = 2x size"
98
+ )
99
 
100
  with gr.Row():
101
  confidence_threshold = gr.Slider(
 
111
  detect_btn = gr.Button("Detect Traffic Signs", variant="primary")
112
  reset_btn = gr.Button("Clear")
113
 
114
+ def detect_and_store(image, confidence_threshold):
115
+ """Detect and store original images for zooming"""
116
+ result_image, preprocessed_image = detect_traffic_signs(image, confidence_threshold)
117
+ return result_image, preprocessed_image, result_image, preprocessed_image
118
+
119
+ def apply_zoom_output(original_image, zoom_level):
120
+ """Apply zoom to output image"""
121
+ return apply_zoom(original_image, zoom_level)
122
+
123
+ def apply_zoom_preprocessed(original_image, zoom_level):
124
+ """Apply zoom to preprocessed image"""
125
+ return apply_zoom(original_image, zoom_level)
126
+
127
  # Auto-detect when image is uploaded
128
  input_image.change(
129
+ fn=detect_and_store,
130
  inputs=[input_image, confidence_threshold],
131
+ outputs=[output_image, preprocessed_image, output_image_state, preprocessed_image_state],
132
  queue=True
133
  )
134
 
135
  # Auto-detect when threshold is changed
136
  confidence_threshold.change(
137
+ fn=detect_and_store,
138
  inputs=[input_image, confidence_threshold],
139
+ outputs=[output_image, preprocessed_image, output_image_state, preprocessed_image_state],
140
  queue=True
141
  )
142
 
143
  # Manual detection button
144
  detect_btn.click(
145
+ fn=detect_and_store,
146
  inputs=[input_image, confidence_threshold],
147
+ outputs=[output_image, preprocessed_image, output_image_state, preprocessed_image_state],
148
  queue=True
149
  )
150
 
151
+ # Zoom output image - update the main display
152
  zoom_slider_output.change(
153
+ fn=apply_zoom_output,
154
+ inputs=[output_image_state, zoom_slider_output],
155
+ outputs=[output_image],
156
+ queue=False
157
  )
158
 
159
+ # Zoom preprocessed image - update the main display
160
  zoom_slider_preprocessed.change(
161
+ fn=apply_zoom_preprocessed,
162
+ inputs=[preprocessed_image_state, zoom_slider_preprocessed],
163
+ outputs=[preprocessed_image],
164
+ queue=False
165
  )
166
 
167
  # Clear button
168
  reset_btn.click(
169
+ fn=lambda: (None, None, None, None, 100, 100),
170
+ outputs=[input_image, output_image, preprocessed_image, output_image_state, zoom_slider_output, zoom_slider_preprocessed]
171
  )
172
 
173
  if __name__ == "__main__":