AKA Math commited on
Commit
dead0ef
Β·
0 Parent(s):

Update demo

Browse files
Files changed (18) hide show
  1. .gitignore +37 -0
  2. .python-version +1 -0
  3. CONTRIBUTING.md +134 -0
  4. Dockerfile +30 -0
  5. GETTING_STARTED.md +353 -0
  6. LICENSE +21 -0
  7. Makefile +55 -0
  8. PROJECT_SUMMARY.md +333 -0
  9. QUICKSTART.md +123 -0
  10. README.md +174 -0
  11. app.py +997 -0
  12. deploy.sh +67 -0
  13. packages.txt +2 -0
  14. pyproject.toml +35 -0
  15. requirements.txt +5 -0
  16. run_simple.sh +32 -0
  17. setup.sh +60 -0
  18. test_setup.py +159 -0
.gitignore ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environments
7
+ venv/
8
+ env/
9
+ ENV/
10
+
11
+ # Streamlit
12
+ .streamlit/
13
+
14
+ # Distribution / packaging
15
+ *.egg-info/
16
+ dist/
17
+ build/
18
+
19
+ # IDE
20
+ .vscode/
21
+ .idea/
22
+ *.swp
23
+ *.swo
24
+ *~
25
+
26
+ # OS files
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # Jupyter Notebook
31
+ .ipynb_checkpoints
32
+
33
+ # pytest
34
+ .pytest_cache/
35
+
36
+ # mypy
37
+ .mypy_cache/
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.11
CONTRIBUTING.md ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to Edge Detection Demo
2
+
3
+ Thank you for your interest in contributing! This educational tool is designed for teaching edge detection concepts.
4
+
5
+ ## How to Contribute
6
+
7
+ ### Reporting Issues
8
+
9
+ If you find bugs or have suggestions:
10
+ 1. Check if the issue already exists in the GitHub Issues
11
+ 2. Create a new issue with:
12
+ - Clear description of the problem
13
+ - Steps to reproduce (if it's a bug)
14
+ - Expected vs actual behavior
15
+ - Screenshots if applicable
16
+
17
+ ### Suggesting Enhancements
18
+
19
+ We welcome ideas for educational improvements:
20
+ - Additional edge detection algorithms (e.g., Deriche, Shen-Castan)
21
+ - New visualization methods (e.g., 3D surface plots)
22
+ - Better explanations of existing concepts
23
+ - Interactive exercises or quizzes
24
+ - Multi-scale edge detection demonstrations
25
+
26
+ ### Code Contributions
27
+
28
+ 1. **Fork the repository**
29
+
30
+ 2. **Create a feature branch:**
31
+ ```bash
32
+ git checkout -b feature/your-feature-name
33
+ ```
34
+
35
+ 3. **Make your changes:**
36
+ - Follow the existing code style
37
+ - Add comments for complex logic
38
+ - Update documentation if needed
39
+
40
+ 4. **Test your changes:**
41
+ ```bash
42
+ streamlit run app.py
43
+ ```
44
+ - Test all interactive features
45
+ - Verify algorithms are correct
46
+ - Check edge cases
47
+
48
+ 5. **Commit your changes:**
49
+ ```bash
50
+ git add .
51
+ git commit -m "Add: Brief description of your changes"
52
+ ```
53
+
54
+ 6. **Push and create a Pull Request:**
55
+ ```bash
56
+ git push origin feature/your-feature-name
57
+ ```
58
+ Then create a PR on GitHub with a clear description.
59
+
60
+ ## Code Style Guidelines
61
+
62
+ ### Python Code
63
+
64
+ - Follow PEP 8 style guide
65
+ - Use meaningful variable names
66
+ - Add docstrings to functions:
67
+ ```python
68
+ def function_name(param):
69
+ """
70
+ Brief description.
71
+
72
+ Args:
73
+ param: Description
74
+
75
+ Returns:
76
+ Description of return value
77
+ """
78
+ ```
79
+
80
+ ### Streamlit UI
81
+
82
+ - Keep UI simple and intuitive
83
+ - Use consistent markdown formatting
84
+ - Add helpful tooltips (help parameter in widgets)
85
+ - Organize content in logical sections
86
+
87
+ ### Documentation
88
+
89
+ - Update README.md for major features
90
+ - Keep QUICKSTART.md up to date
91
+ - Add inline comments for complex algorithms
92
+ - Include references to academic sources
93
+
94
+ ## Educational Content Guidelines
95
+
96
+ This is an educational tool, so clarity is paramount:
97
+
98
+ 1. **Explanations should be:**
99
+ - Accurate and technically correct
100
+ - Easy to understand for graduate students
101
+ - Progressive (simple concepts first)
102
+ - Include visual examples
103
+
104
+ 2. **Interactive elements should:**
105
+ - Provide immediate feedback
106
+ - Show clear cause-and-effect
107
+ - Include reasonable default values
108
+ - Have helpful tooltips
109
+
110
+ 3. **Mathematical content should:**
111
+ - Use proper LaTeX notation
112
+ - Define all symbols
113
+ - Provide intuitive interpretations
114
+ - Include both equations and words
115
+
116
+ ## Testing
117
+
118
+ Before submitting a PR, please verify:
119
+
120
+ - [ ] App runs without errors
121
+ - [ ] All sliders and controls work correctly
122
+ - [ ] Edge detection algorithms produce correct results
123
+ - [ ] Images display properly
124
+ - [ ] Gradient visualizations are accurate
125
+ - [ ] Educational content is clear and accurate
126
+ - [ ] No typos in text
127
+
128
+ ## Questions?
129
+
130
+ Feel free to open an issue for discussion before starting major work.
131
+
132
+ ## License
133
+
134
+ By contributing, you agree that your contributions will be licensed under the MIT License.
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ libgl1 \
9
+ libglib2.0-0 \
10
+ curl \
11
+ && rm -rf /var/lib/apt/lists/*
12
+
13
+ # Copy requirements first for better caching
14
+ COPY requirements.txt .
15
+
16
+ # Install Python dependencies
17
+ RUN pip install --no-cache-dir -r requirements.txt
18
+
19
+ # Copy application files
20
+ COPY app.py .
21
+ COPY README.md .
22
+
23
+ # Expose Streamlit port
24
+ EXPOSE 8501
25
+
26
+ # Health check
27
+ HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health || exit 1
28
+
29
+ # Run the application
30
+ ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
GETTING_STARTED.md ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # πŸ“– Getting Started with the Edge Detection Demo
2
+
3
+ ## Prerequisites
4
+
5
+ - **Python 3.11+** (Python 3.9+ should also work)
6
+ - **Git** (for cloning the repository)
7
+ - **pip** (usually comes with Python)
8
+ - **5-10 minutes** of your time
9
+
10
+ Optional:
11
+ - **Make** (for convenient commands)
12
+ - **Docker** (for containerized deployment)
13
+
14
+ ## ⚑️ Quick Start (30 seconds)
15
+
16
+ The absolute fastest way to get started:
17
+
18
+ ```bash
19
+ # 1. Navigate to the project directory
20
+ cd edge-detection
21
+
22
+ # 2. Run the setup script
23
+ ./setup.sh
24
+
25
+ # 3. Run the app
26
+ ./run_simple.sh
27
+ ```
28
+
29
+ That's it! Your browser should open to `http://localhost:8501`
30
+
31
+ ---
32
+
33
+ ## πŸ“š Detailed Installation
34
+
35
+ ### Step 1: Clone the Repository (if needed)
36
+
37
+ ```bash
38
+ git clone <repository-url>
39
+ cd edge-detection
40
+ ```
41
+
42
+ ### Step 2: Choose Your Setup Method
43
+
44
+ #### Option A: Automated Setup Script (Recommended)
45
+
46
+ ```bash
47
+ chmod +x setup.sh
48
+ ./setup.sh
49
+ ```
50
+
51
+ This will:
52
+ - Check Python version
53
+ - Create a virtual environment
54
+ - Install all dependencies
55
+
56
+ #### Option B: Manual Setup
57
+
58
+ ```bash
59
+ # Create virtual environment
60
+ python3 -m venv venv
61
+
62
+ # Activate virtual environment
63
+ source venv/bin/activate # On macOS/Linux
64
+ # OR
65
+ venv\Scripts\activate # On Windows
66
+
67
+ # Upgrade pip
68
+ pip install --upgrade pip
69
+
70
+ # Install dependencies
71
+ pip install -r requirements.txt
72
+ ```
73
+
74
+ #### Option C: Using Make
75
+
76
+ ```bash
77
+ make setup
78
+ ```
79
+
80
+ ### Step 3: Run the Application
81
+
82
+ #### Option A: Quick Run Script
83
+
84
+ ```bash
85
+ chmod +x run_simple.sh
86
+ ./run_simple.sh
87
+ ```
88
+
89
+ #### Option B: Manual Run
90
+
91
+ ```bash
92
+ source venv/bin/activate # Activate virtual environment
93
+ streamlit run app.py
94
+ ```
95
+
96
+ #### Option C: Using Make
97
+
98
+ ```bash
99
+ make run
100
+ ```
101
+
102
+ ### Step 4: Open in Browser
103
+
104
+ The app should automatically open in your default browser at:
105
+ ```
106
+ http://localhost:8501
107
+ ```
108
+
109
+ If it doesn't open automatically, manually navigate to that URL.
110
+
111
+ ---
112
+
113
+ ## πŸŽ“ First Steps in the Demo
114
+
115
+ Once the app is running:
116
+
117
+ 1. **Upload an Image** (optional)
118
+ - Look in the sidebar
119
+ - Click "Upload your own image"
120
+ - Or use the default generated image
121
+
122
+ 2. **Try Different Methods**
123
+ - Select "Sobel" from the dropdown
124
+ - Observe the edge detection results
125
+ - Try adjusting the kernel size
126
+ - Enable "Show Gradient Components"
127
+
128
+ 3. **Explore Prewitt and Roberts**
129
+ - Switch methods and compare results
130
+ - Notice differences in edge quality and noise sensitivity
131
+
132
+ 4. **Experiment with Laplacian**
133
+ - Observe the isotropic response
134
+ - See why pre-smoothing is important
135
+ - Try different kernel sizes
136
+
137
+ 5. **Master Canny**
138
+ - Adjust low and high thresholds
139
+ - Observe edge connectivity
140
+ - Compare with simpler methods using comparison mode
141
+
142
+ 6. **Read Educational Content**
143
+ - Click on the four tabs at the bottom
144
+ - Read the explanations
145
+ - Understand the mathematical foundations
146
+
147
+ ---
148
+
149
+ ## πŸ”§ Troubleshooting
150
+
151
+ ### Problem: "Python not found"
152
+
153
+ **Solution:**
154
+ ```bash
155
+ # Check if Python is installed
156
+ python3 --version
157
+
158
+ # If not, install Python 3.11+ from python.org
159
+ ```
160
+
161
+ ### Problem: "Permission denied" when running scripts
162
+
163
+ **Solution:**
164
+ ```bash
165
+ chmod +x *.sh
166
+ ```
167
+
168
+ ### Problem: Import errors when running
169
+
170
+ **Solution:**
171
+ ```bash
172
+ # Make sure you're in the virtual environment
173
+ source venv/bin/activate
174
+
175
+ # Reinstall requirements
176
+ pip install --force-reinstall -r requirements.txt
177
+ ```
178
+
179
+ ### Problem: "Port 8501 already in use"
180
+
181
+ **Solution:**
182
+ ```bash
183
+ # Find and kill the process using the port
184
+ lsof -ti:8501 | xargs kill
185
+
186
+ # Or run on a different port
187
+ streamlit run app.py --server.port 8502
188
+ ```
189
+
190
+ ### Problem: Images not displaying
191
+
192
+ **Solution:**
193
+ - Check internet connection (for HuggingFace download)
194
+ - The app will automatically fall back to a generated image
195
+ - Try uploading your own image via the sidebar
196
+
197
+ ### Problem: Slow performance
198
+
199
+ **Solution:**
200
+ - Use smaller images (app auto-resizes to 512px)
201
+ - Close other applications
202
+ - Try simpler methods (Roberts, Sobel) instead of Canny
203
+
204
+ ---
205
+
206
+ ## 🎯 Next Steps
207
+
208
+ ### For Students
209
+ - Work through the educational tabs systematically
210
+ - Try uploading different types of images (portraits, landscapes, text)
211
+ - Compare all five methods on the same image
212
+ - Answer the discussion questions in PROJECT_SUMMARY.md
213
+
214
+ ### For Instructors
215
+ - Review QUICKSTART.md for teaching tips
216
+ - Customize the app (see CONTRIBUTING.md)
217
+ - Deploy to Hugging Face Spaces (see below)
218
+ - Share with your class
219
+
220
+ ### For Developers
221
+ - Read CONTRIBUTING.md for development guidelines
222
+ - Check out PROJECT_SUMMARY.md for technical details
223
+ - Consider adding new features (multi-scale, learning-based methods)
224
+
225
+ ---
226
+
227
+ ## πŸš€ Deploying to Hugging Face Spaces
228
+
229
+ Want to share this with others online?
230
+
231
+ ### Step 1: Create a Hugging Face Account
232
+ - Go to https://huggingface.co/join
233
+ - Sign up for free
234
+
235
+ ### Step 2: Create a New Space
236
+ - Go to https://huggingface.co/new-space
237
+ - Choose "Streamlit" as the SDK
238
+ - Make it public (for educational use)
239
+
240
+ ### Step 3: Deploy
241
+
242
+ ```bash
243
+ chmod +x deploy.sh
244
+ ./deploy.sh
245
+ ```
246
+
247
+ Follow the prompts and enter your Space name (e.g., "username/edge-detection-demo").
248
+
249
+ Your demo will be live at: `https://huggingface.co/spaces/username/edge-detection-demo`
250
+
251
+ ---
252
+
253
+ ## 🐳 Docker Deployment (Advanced)
254
+
255
+ If you prefer Docker:
256
+
257
+ ```bash
258
+ # Build the image
259
+ docker build -t edge-detection .
260
+
261
+ # Run the container
262
+ docker run -p 8501:8501 edge-detection
263
+
264
+ # Access at http://localhost:8501
265
+ ```
266
+
267
+ ---
268
+
269
+ ## πŸ“ Common Commands Reference
270
+
271
+ ```bash
272
+ # Setup and installation
273
+ ./setup.sh # Initial setup
274
+ make setup # Alternative using make
275
+
276
+ # Running the app
277
+ ./run_simple.sh # Quick run
278
+ make run # Alternative using make
279
+ streamlit run app.py # Direct run
280
+
281
+ # Testing
282
+ python test_setup.py # Verify installation
283
+ make test # Alternative using make
284
+
285
+ # Deployment
286
+ ./deploy.sh # Deploy to HuggingFace
287
+ make deploy # Alternative using make
288
+
289
+ # Cleanup
290
+ make clean # Remove virtual environment
291
+ ```
292
+
293
+ ---
294
+
295
+ ## πŸ’‘ Tips for Best Experience
296
+
297
+ 1. **Image Selection**: Use images with clear edges (buildings, objects, text)
298
+ 2. **Pre-processing**: Enable Gaussian blur for noisy images
299
+ 3. **Parameter Tuning**: Start with default values, then experiment
300
+ 4. **Comparison**: Use comparison mode to understand differences
301
+ 5. **Educational Content**: Read tabs in order (What Are Edges? β†’ Gradient Methods β†’ Canny β†’ Practical)
302
+
303
+ ---
304
+
305
+ ## πŸ“– Learning Path
306
+
307
+ **Beginner** (1-2 hours):
308
+ 1. Read "What Are Edges?" tab
309
+ 2. Try Roberts and Sobel
310
+ 3. Compare results on sample image
311
+ 4. Upload your own image
312
+
313
+ **Intermediate** (2-4 hours):
314
+ 1. Study gradient-based methods tab
315
+ 2. Experiment with all five methods
316
+ 3. Enable gradient visualizations
317
+ 4. Try different kernel sizes
318
+ 5. Read Canny algorithm tab
319
+
320
+ **Advanced** (4+ hours):
321
+ 1. Study practical considerations tab
322
+ 2. Tune Canny parameters systematically
323
+ 3. Compare methods quantitatively
324
+ 4. Test on domain-specific images
325
+ 5. Consider implementing improvements
326
+
327
+ ---
328
+
329
+ ## πŸ†˜ Getting Help
330
+
331
+ - **Documentation**: Check README.md, QUICKSTART.md, PROJECT_SUMMARY.md
332
+ - **GitHub Issues**: Report bugs or request features
333
+ - **Discussion Board**: Ask questions about edge detection concepts
334
+
335
+ ---
336
+
337
+ ## βœ… Verification
338
+
339
+ To verify everything is working correctly:
340
+
341
+ ```bash
342
+ python test_setup.py
343
+ ```
344
+
345
+ This will check:
346
+ - All dependencies are installed
347
+ - App structure is correct
348
+ - Edge detection functions work
349
+ - Visualizations render properly
350
+
351
+ ---
352
+
353
+ **Ready to explore edge detection?** Run `./run_simple.sh` and start learning! πŸš€
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Uni Bern Intro to Image Analysis Course
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Makefile ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .PHONY: help setup install run clean deploy test
2
+
3
+ help:
4
+ @echo "πŸ” Edge Detection Demo - Available Commands:"
5
+ @echo ""
6
+ @echo " make setup - Set up virtual environment and install dependencies"
7
+ @echo " make install - Install/update dependencies only"
8
+ @echo " make run - Run the Streamlit app locally"
9
+ @echo " make clean - Remove virtual environment and cache files"
10
+ @echo " make deploy - Deploy to Hugging Face Spaces"
11
+ @echo " make test - Run basic tests"
12
+ @echo ""
13
+
14
+ setup:
15
+ @echo "πŸ”§ Setting up environment..."
16
+ @./setup.sh
17
+
18
+ install:
19
+ @echo "πŸ“₯ Installing dependencies..."
20
+ @if [ ! -d "venv" ]; then \
21
+ echo "❌ Virtual environment not found. Run 'make setup' first."; \
22
+ exit 1; \
23
+ fi
24
+ @. venv/bin/activate && pip install -r requirements.txt
25
+ @echo "βœ… Dependencies installed"
26
+
27
+ run:
28
+ @echo "πŸš€ Starting Streamlit app..."
29
+ @if [ ! -d "venv" ]; then \
30
+ echo "❌ Virtual environment not found. Run 'make setup' first."; \
31
+ exit 1; \
32
+ fi
33
+ @. venv/bin/activate && streamlit run app.py
34
+
35
+ clean:
36
+ @echo "🧹 Cleaning up..."
37
+ @rm -rf venv
38
+ @rm -rf __pycache__
39
+ @rm -rf .streamlit
40
+ @find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
41
+ @find . -type f -name "*.pyc" -delete
42
+ @echo "βœ… Cleanup complete"
43
+
44
+ deploy:
45
+ @echo "πŸš€ Deploying to Hugging Face Spaces..."
46
+ @./deploy.sh
47
+
48
+ test:
49
+ @echo "πŸ§ͺ Running basic import tests..."
50
+ @if [ ! -d "venv" ]; then \
51
+ echo "❌ Virtual environment not found. Run 'make setup' first."; \
52
+ exit 1; \
53
+ fi
54
+ @. venv/bin/activate && python -c "import app; print('βœ… App imports successfully')"
55
+ @echo "βœ… Basic tests passed"
PROJECT_SUMMARY.md ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # πŸ” Edge Detection Demo - Project Summary
2
+
3
+ ## Overview
4
+
5
+ This interactive educational demo teaches computational approaches to edge detection in digital images, a fundamental operation in computer vision. The application is designed for graduate-level image analysis courses and demonstrates:
6
+
7
+ - **Classical Gradient-Based Methods**: Roberts Cross, Prewitt, and Sobel operators
8
+ - **Second-Order Methods**: Laplacian edge detection
9
+ - **Optimal Edge Detection**: The Canny multi-stage algorithm
10
+ - **Comparative Analysis**: Side-by-side method comparisons
11
+ - **Educational Theory**: Comprehensive explanations with mathematical foundations
12
+
13
+ ## Key Features
14
+
15
+ ### Interactive Edge Detection
16
+ - **Five Detection Methods**: Switch between Sobel, Prewitt, Roberts, Laplacian, and Canny
17
+ - **Real-Time Processing**: See edge detection results as you adjust parameters
18
+ - **Method-Specific Controls**: Kernel sizes, thresholds, and algorithm parameters
19
+ - **Pre-processing Options**: Gaussian blur for noise reduction
20
+
21
+ ### Advanced Visualizations
22
+ - **Gradient Components**: View Gx and Gy separately (horizontal and vertical gradients)
23
+ - **Gradient Direction**: Color-coded visualization showing edge orientation and strength
24
+ - **Edge Statistics**: Quantitative metrics (edge pixel count, percentages)
25
+ - **Comparison Mode**: Side-by-side comparison with Canny edge detection
26
+
27
+ ### Comprehensive Educational Content
28
+ - **Four Educational Tabs**:
29
+ 1. **What Are Edges?**: Foundational concepts and significance
30
+ 2. **Gradient-Based Methods**: Detailed explanations of Roberts, Prewitt, Sobel, Laplacian
31
+ 3. **Canny Algorithm**: Complete breakdown of the five-stage optimal approach
32
+ 4. **Practical Considerations**: Real-world application guidance
33
+ - **Mathematical Formulations**: Kernel definitions, gradient equations (LaTeX formatted)
34
+ - **"Aha!" Moments**: Key insights that crystallize understanding
35
+ - **Historical Context**: Evolution of edge detection from 1963 to present
36
+
37
+ ## Educational Objectives
38
+
39
+ Students will learn:
40
+
41
+ 1. **Mathematical Foundations**: How convolution kernels approximate derivatives
42
+ 2. **First-Order vs. Second-Order**: Differences between gradient and Laplacian operators
43
+ 3. **Optimal Edge Detection**: Canny's three criteria and their implementation
44
+ 4. **Parameter Sensitivity**: How thresholds and kernel sizes affect results
45
+ 5. **Comparative Analysis**: Strengths and weaknesses of different methods
46
+ 6. **Practical Skills**: When to use each method in real applications
47
+
48
+ ## Project Structure
49
+
50
+ ```
51
+ edge-detection/
52
+ β”œβ”€β”€ app.py # Main Streamlit application (1000+ lines)
53
+ β”œβ”€β”€ requirements.txt # Python dependencies
54
+ β”œβ”€β”€ README.md # Main documentation
55
+ β”œβ”€β”€ QUICKSTART.md # Getting started guide
56
+ β”œβ”€β”€ CONTRIBUTING.md # Contribution guidelines
57
+ β”œβ”€β”€ LICENSE # MIT License
58
+ β”‚
59
+ β”œβ”€β”€ Configuration Files:
60
+ β”œβ”€β”€ packages.txt # System dependencies for HF Spaces
61
+ β”œβ”€β”€ .python-version # Python 3.11
62
+ β”œβ”€β”€ .gitignore # Git ignore rules
63
+ β”œβ”€β”€ pyproject.toml # Project metadata
64
+ β”œβ”€β”€ Dockerfile # Docker deployment
65
+ β”œβ”€β”€ Makefile # Convenient make commands
66
+ β”‚
67
+ └── Scripts:
68
+ β”œβ”€β”€ setup.sh # Initial setup with venv
69
+ β”œβ”€β”€ run_simple.sh # Quick local run
70
+ └── deploy.sh # Deploy to HuggingFace
71
+ ```
72
+
73
+ ## Technical Implementation
74
+
75
+ ### Algorithms Implemented
76
+
77
+ #### Roberts Cross Operator (1963)
78
+ - 2Γ—2 diagonal gradient kernels
79
+ - Simplest gradient operator
80
+ - Computational complexity: O(NΒ²)
81
+ - Best for: Sharp diagonal edges, computational constraints
82
+
83
+ #### Prewitt Operator (1970)
84
+ - 3Γ—3 gradient kernels with uniform weighting
85
+ - Incorporates smoothing perpendicular to gradient
86
+ - Separable: can be computed as two 1D convolutions
87
+ - Best for: General-purpose edge detection, educational demonstrations
88
+
89
+ #### Sobel Operator (1968)
90
+ - 3Γ—3 gradient kernels with center weighting (2:1)
91
+ - Better approximation of true gradient than Prewitt
92
+ - Industry-standard first-order operator
93
+ - Variable kernel sizes: 1Γ—1, 3Γ—3, 5Γ—5, 7Γ—7
94
+ - Best for: Most applications requiring gradient information
95
+
96
+ #### Laplacian Operator
97
+ - Second-order isotropic operator
98
+ - Detects zero-crossings (rapid intensity changes)
99
+ - Highly sensitive to noise (requires pre-smoothing)
100
+ - Often combined with Gaussian β†’ Laplacian of Gaussian (LoG)
101
+ - Best for: Precise edge localization, blob detection
102
+
103
+ #### Canny Edge Detector (1986)
104
+ - Multi-stage optimal algorithm:
105
+ 1. Gaussian smoothing (noise reduction)
106
+ 2. Gradient calculation (Sobel)
107
+ 3. Non-maximum suppression (edge thinning)
108
+ 4. Double thresholding (strong/weak edges)
109
+ 5. Hysteresis edge tracking (connectivity)
110
+ - Parameters: low/high thresholds, aperture size, L2 gradient
111
+ - Best for: High-quality edge detection, when computational cost is acceptable
112
+
113
+ ### Features Highlights
114
+
115
+ **Gradient Visualization:**
116
+ - Displays Gx and Gy components separately
117
+ - Shows which edges each component detects (vertical vs. horizontal)
118
+ - Helps students understand gradient decomposition
119
+
120
+ **Direction Visualization:**
121
+ - HSV color mapping: Hue = direction, Value = magnitude
122
+ - Intuitive understanding of edge orientation
123
+ - Beautiful visualization of gradient fields
124
+
125
+ **Comparison Mode:**
126
+ - Compare any method with Canny
127
+ - Adjustable Canny parameters for fair comparison
128
+ - Quantitative metrics for both methods
129
+
130
+ ## Quick Start
131
+
132
+ ### For Users (Simplest)
133
+ ```bash
134
+ chmod +x run_simple.sh
135
+ ./run_simple.sh
136
+ ```
137
+
138
+ ### Using Make
139
+ ```bash
140
+ make setup # First time only
141
+ make run # Start the app
142
+ ```
143
+
144
+ ### Manual Setup
145
+ ```bash
146
+ python3 -m venv venv
147
+ source venv/bin/activate
148
+ pip install -r requirements.txt
149
+ streamlit run app.py
150
+ ```
151
+
152
+ ## Deployment Options
153
+
154
+ ### 1. Hugging Face Spaces (Recommended)
155
+ ```bash
156
+ chmod +x deploy.sh
157
+ ./deploy.sh
158
+ ```
159
+ - Free hosting
160
+ - Automatic builds
161
+ - Share with students via URL
162
+ - No server maintenance
163
+
164
+ ### 2. Docker
165
+ ```bash
166
+ docker build -t edge-detection .
167
+ docker run -p 8501:8501 edge-detection
168
+ ```
169
+
170
+ ### 3. Local Server
171
+ ```bash
172
+ streamlit run app.py --server.port 8501
173
+ ```
174
+
175
+ ## Teaching with This Demo
176
+
177
+ ### Suggested Lesson Plan
178
+
179
+ **Part 1: Introduction to Edges (15 minutes)**
180
+ 1. Start with the "What Are Edges?" tab
181
+ 2. Discuss why edges matter in computer vision
182
+ 3. Show original image and various edge detection results
183
+ 4. Emphasize: edges = local intensity changes
184
+
185
+ **Part 2: Gradient-Based Methods (30 minutes)**
186
+ 1. **Roberts Cross**: Introduce the gradient concept
187
+ - Show 2Γ—2 kernels
188
+ - Demonstrate on sample image
189
+ - Discuss limitations (noise sensitivity)
190
+
191
+ 2. **Prewitt**: Introduce smoothing
192
+ - Compare with Roberts
193
+ - Show how 3Γ—3 kernel reduces noise
194
+
195
+ 3. **Sobel**: Weighted averaging
196
+ - Compare with Prewitt
197
+ - Show gradient components (Gx, Gy)
198
+ - Demonstrate gradient direction visualization
199
+ - Let students experiment with kernel sizes
200
+
201
+ 4. **Laplacian**: Second-order derivatives
202
+ - Explain zero-crossings
203
+ - Show double-edge effect
204
+ - Demonstrate noise sensitivity
205
+ - Discuss why pre-smoothing is essential
206
+
207
+ **Part 3: Canny Algorithm (25 minutes)**
208
+ 1. Introduce Canny's three criteria
209
+ 2. Walk through each stage:
210
+ - Gaussian smoothing
211
+ - Gradient calculation
212
+ - Non-maximum suppression (show edge thinning)
213
+ - Double thresholding (show strong/weak classification)
214
+ - Hysteresis (explain edge connectivity)
215
+ 3. Demonstrate parameter effects:
216
+ - Vary low/high thresholds
217
+ - Show how ratio affects edge continuity
218
+ 4. Compare Canny with Sobel using comparison mode
219
+
220
+ **Part 4: Practical Considerations (20 minutes)**
221
+ 1. Discuss when to use each method
222
+ 2. Show effect of pre-processing (Gaussian blur)
223
+ 3. Demonstrate parameter tuning strategies
224
+ 4. Discuss computational trade-offs
225
+ 5. Show real-world application examples
226
+
227
+ **Part 5: Interactive Exploration (10 minutes)**
228
+ 1. Let students upload their own images
229
+ 2. Challenge: Find optimal parameters for different image types
230
+ 3. Compare results across methods
231
+
232
+ ### Discussion Questions
233
+
234
+ 1. **Conceptual:**
235
+ - Why are edges important in computer vision?
236
+ - How do derivatives relate to edges?
237
+ - What makes an edge detector "optimal"?
238
+
239
+ 2. **Analytical:**
240
+ - Why does Sobel weight the center row/column more heavily?
241
+ - Why is the Laplacian isotropic while Sobel is directional?
242
+ - How does non-maximum suppression achieve single-pixel-wide edges?
243
+
244
+ 3. **Practical:**
245
+ - When would you prefer Sobel over Canny?
246
+ - How do you choose Canny's threshold values?
247
+ - Why pre-smooth before applying Laplacian?
248
+
249
+ ### Assignments
250
+
251
+ 1. **Comparative Analysis**: Compare all five methods on the same image. Discuss strengths/weaknesses.
252
+ 2. **Parameter Exploration**: Find optimal Canny thresholds for three different image types.
253
+ 3. **Gradient Analysis**: Analyze gradient direction patterns in natural vs. man-made scenes.
254
+ 4. **Noise Robustness**: Compare methods on images with different noise levels.
255
+ 5. **Computational Efficiency**: Measure and compare execution times of different methods.
256
+
257
+ ## Performance
258
+
259
+ - **Load Time**: < 2 seconds on first load (with caching)
260
+ - **Interactive Response**: Real-time (< 100ms per parameter change)
261
+ - **Memory Usage**: ~150-250 MB (depends on image size)
262
+ - **Supported Image Sizes**: Up to 4K (auto-resized to 512px for demo)
263
+
264
+ ## Known Limitations
265
+
266
+ 1. Very large images (>10MB) may be slow - auto-resized to 512px
267
+ 2. Gradient direction visualization requires some color vision
268
+ 3. Mathematical content assumes calculus background
269
+ 4. Advanced topics (multi-scale, learning-based) not covered
270
+
271
+ ## Educational Resources
272
+
273
+ ### Foundational Papers
274
+ - Canny, J. (1986). "A Computational Approach to Edge Detection." IEEE TPAMI.
275
+ - Marr, D., & Hildreth, E. (1980). "Theory of Edge Detection." Proc. Royal Society of London B.
276
+
277
+ ### Textbooks
278
+ - Gonzalez & Woods. "Digital Image Processing" (4th Edition).
279
+ - Szeliski, R. "Computer Vision: Algorithms and Applications."
280
+ - Forsyth & Ponce. "Computer Vision: A Modern Approach."
281
+
282
+ ### Online Resources
283
+ - [OpenCV Edge Detection Tutorial](https://docs.opencv.org/master/da/d22/tutorial_py_canny.html)
284
+ - [Stanford CS231n: Convolutional Neural Networks](http://cs231n.stanford.edu/)
285
+
286
+ ## Future Enhancements
287
+
288
+ Potential additions for future versions:
289
+ - **Multi-Scale Detection**: Edge detection at multiple scales
290
+ - **Learning-Based Methods**: Structured forests, HED (CNN-based)
291
+ - **Oriented Filters**: Gabor filters, steerable pyramids
292
+ - **Performance Benchmarks**: Quantitative evaluation with ground truth
293
+ - **3D Visualization**: Surface plots of gradient fields
294
+ - **Video Processing**: Edge detection on video streams
295
+
296
+ ## Contributing
297
+
298
+ We welcome contributions! See `CONTRIBUTING.md` for:
299
+ - Bug reports
300
+ - Feature requests
301
+ - Code contributions
302
+ - Documentation improvements
303
+ - Educational content enhancements
304
+
305
+ ## License
306
+
307
+ MIT License - Free for educational and commercial use
308
+
309
+ ## Acknowledgments
310
+
311
+ - Inspired by classical computer vision courses at leading universities
312
+ - Built with Streamlit for rapid prototyping and deployment
313
+ - OpenCV for efficient image processing
314
+ - HuggingFace for free hosting
315
+
316
+ ## Support
317
+
318
+ - **Issues**: GitHub Issues for bugs and features
319
+ - **Questions**: Discussion board for educational questions
320
+ - **Documentation**: See README.md, QUICKSTART.md, CONTRIBUTING.md
321
+
322
+ ## Success Stories
323
+
324
+ Perfect for:
325
+ - Graduate image analysis courses
326
+ - Computer vision fundamentals
327
+ - Self-paced online learning
328
+ - Workshop demonstrations
329
+ - Research group tutorials
330
+
331
+ ---
332
+
333
+ **Ready to start?** Run `./run_simple.sh` and explore computational edge detection!
QUICKSTART.md ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Quick Start Guide
2
+
3
+ ## For Students/Users
4
+
5
+ ### Online Usage
6
+ Simply visit the Hugging Face Space URL (provided by your instructor) to use the demo directly in your browser. No installation needed!
7
+
8
+ ### Local Usage
9
+
10
+ 1. **Clone the repository:**
11
+ ```bash
12
+ git clone <repository-url>
13
+ cd edge-detection
14
+ ```
15
+
16
+ 2. **Run the setup script:**
17
+ ```bash
18
+ chmod +x setup.sh
19
+ ./setup.sh
20
+ ```
21
+
22
+ 3. **Start the demo:**
23
+ ```bash
24
+ chmod +x run_simple.sh
25
+ ./run_simple.sh
26
+ ```
27
+
28
+ 4. **Open your browser** to `http://localhost:8501`
29
+
30
+ ---
31
+
32
+ ## For Instructors/Developers
33
+
34
+ ### Customizing the Demo
35
+
36
+ The main application is in `app.py`. Key functions you can modify:
37
+
38
+ - `generate_sample_image()`: Customize the default sample image
39
+ - `apply_sobel_filter()`, `apply_prewitt_filter()`, etc.: Modify filter implementations
40
+ - `main_loop()`: Change the UI layout and educational content
41
+
42
+ ### Using Your Own Images
43
+
44
+ You have two options:
45
+
46
+ 1. **Upload at runtime**: Users can upload images via the sidebar
47
+ 2. **Default image**: Modify the `load_sample_image()` function to load from a URL or local path
48
+
49
+ To use images from your own HuggingFace dataset:
50
+
51
+ ```python
52
+ image_path = hf_hub_download(
53
+ repo_id="your-username/your-dataset",
54
+ filename="your-image.jpg",
55
+ repo_type="dataset",
56
+ )
57
+ ```
58
+
59
+ ### Deploying to Hugging Face Spaces
60
+
61
+ 1. **Create a Space:**
62
+ - Go to https://huggingface.co/new-space
63
+ - Choose a name (e.g., "edge-detection-demo")
64
+ - Select "Streamlit" as the SDK
65
+ - Choose "Public" for educational use
66
+
67
+ 2. **Deploy:**
68
+ ```bash
69
+ chmod +x deploy.sh
70
+ ./deploy.sh
71
+ ```
72
+
73
+ 3. **Enter your Space name** when prompted (e.g., "username/edge-detection-demo")
74
+
75
+ ### Troubleshooting
76
+
77
+ **"Import errors" when running locally:**
78
+ - Make sure you ran `setup.sh` first
79
+ - Activate the virtual environment: `source venv/bin/activate`
80
+ - Reinstall requirements: `pip install -r requirements.txt`
81
+
82
+ **App not loading on HuggingFace:**
83
+ - Check the "Logs" tab in your Space
84
+ - Verify `packages.txt` includes all system dependencies
85
+ - Ensure Python version in `.python-version` is supported
86
+
87
+ **Images not displaying:**
88
+ - Check if the HuggingFace dataset is public
89
+ - Verify the repo_id and filename in `load_sample_image()`
90
+ - The app will fall back to a generated image if download fails
91
+
92
+ ---
93
+
94
+ ## Educational Tips
95
+
96
+ ### For Teaching
97
+
98
+ 1. **Start Simple**: Begin with Roberts Cross to introduce the gradient concept
99
+ 2. **Progress Gradually**: Move to Sobel/Prewitt, then Laplacian
100
+ 3. **Culminate with Canny**: Show how optimal edge detection builds on simpler methods
101
+ 4. **Use Comparisons**: Enable side-by-side comparisons to highlight differences
102
+
103
+ ### Discussion Points
104
+
105
+ - **Gradient Operators**: Why do we need 2D convolution kernels?
106
+ - **First vs. Second Order**: When is Laplacian better than Sobel?
107
+ - **Canny's Criteria**: What makes an edge detector "optimal"?
108
+ - **Trade-offs**: Speed vs. quality, simplicity vs. robustness
109
+
110
+ ### Suggested Exercises
111
+
112
+ 1. Compare edge detection on images with different noise levels
113
+ 2. Find optimal Canny thresholds for different types of images
114
+ 3. Analyze how kernel size affects edge localization
115
+ 4. Examine gradient direction patterns in natural images
116
+ 5. Compare computational complexity of different methods
117
+
118
+ ## Resources
119
+
120
+ - [OpenCV Edge Detection Tutorial](https://docs.opencv.org/master/da/d22/tutorial_py_canny.html)
121
+ - [Canny's Original Paper (1986)](https://ieeexplore.ieee.org/document/4767851)
122
+ - [Digital Image Processing (Gonzalez & Woods)](http://www.imageprocessingplace.com/)
123
+ - [Computer Vision: Algorithms and Applications (Szeliski)](http://szeliski.org/Book/)
README.md ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Interactive Edge Detection Demo
3
+ colorFrom: red
4
+ colorTo: orange
5
+ sdk: streamlit
6
+ sdk_version: 1.39.0
7
+ app_file: app.py
8
+ pinned: false
9
+ license: mit
10
+ ---
11
+
12
+ # πŸ” Interactive Edge Detection Demo
13
+
14
+ An educational demonstration of classical and advanced edge detection algorithms for graduate-level image analysis courses.
15
+
16
+ ## What You'll Learn
17
+
18
+ This interactive demo explores the computational foundations of edge detection:
19
+
20
+ - **Classical Gradient-Based Methods**: Sobel, Prewitt, Roberts Cross operators
21
+ - **Second-Order Methods**: Laplacian edge detection
22
+ - **Optimal Edge Detection**: The Canny algorithm and its multi-stage pipeline
23
+ - **Comparative Analysis**: Understanding trade-offs between different approaches
24
+ - **Mathematical Foundations**: Gradients, derivatives, and convolution kernels
25
+ - **Practical Considerations**: Pre-processing, parameter tuning, and method selection
26
+
27
+ ## Features
28
+
29
+ ### Interactive Edge Detection
30
+ - **Five Detection Methods**: Sobel, Prewitt, Roberts, Laplacian, and Canny
31
+ - **Real-Time Visualization**: See edges detected as you adjust parameters
32
+ - **Gradient Analysis**: Visualize gradient components (Gx, Gy) and directions
33
+ - **Side-by-Side Comparison**: Compare any method with Canny edge detection
34
+
35
+ ### Educational Content
36
+ - **Four Educational Tabs**: Comprehensive explanations of theory and practice
37
+ - **Mathematical Formulations**: Kernel definitions and gradient equations
38
+ - **"Aha!" Moments**: Key insights that make concepts click
39
+ - **Practical Guidance**: When to use each method and how to tune parameters
40
+
41
+ ### Customization
42
+ - **Upload Your Images**: Test algorithms on your own data
43
+ - **Pre-processing Options**: Apply Gaussian blur to reduce noise
44
+ - **Method-Specific Parameters**: Kernel sizes, thresholds, and more
45
+ - **Gradient Visualizations**: Color-coded direction and magnitude maps
46
+
47
+ ## Quick Start
48
+
49
+ ### Online Usage
50
+ Visit the Hugging Face Space (URL provided by your instructor) to use the demo directly in your browser.
51
+
52
+ ### Local Development
53
+
54
+ #### Setup
55
+
56
+ ```bash
57
+ # Clone the repository
58
+ git clone <your-repo-url>
59
+ cd edge-detection
60
+
61
+ # Install dependencies
62
+ pip install -r requirements.txt
63
+ ```
64
+
65
+ #### Run Locally
66
+
67
+ ```bash
68
+ # Simple run
69
+ streamlit run app.py
70
+
71
+ # Or use the provided script
72
+ chmod +x run_simple.sh
73
+ ./run_simple.sh
74
+ ```
75
+
76
+ The app will be available at `http://localhost:8501`
77
+
78
+ ## Deployment to Hugging Face Spaces
79
+
80
+ This app is designed to be deployed to Hugging Face Spaces:
81
+
82
+ 1. Create a new Space on [Hugging Face](https://huggingface.co/spaces)
83
+ 2. Choose "Streamlit" as the SDK
84
+ 3. Push this repository to your Space
85
+
86
+ Or use the deployment script:
87
+
88
+ ```bash
89
+ chmod +x deploy.sh
90
+ ./deploy.sh
91
+ ```
92
+
93
+ ## Educational Use
94
+
95
+ This demo is designed for:
96
+ - Graduate image analysis courses
97
+ - Computer vision fundamentals
98
+ - Digital image processing tutorials
99
+ - Self-paced learning about edge detection
100
+
101
+ ### Key Concepts Covered
102
+
103
+ - **Image Gradients**: First-order derivatives and directional information
104
+ - **Convolution Kernels**: How discrete filters approximate derivatives
105
+ - **Multi-Scale Analysis**: The role of smoothing in edge detection
106
+ - **Optimal Edge Detection**: Canny's three criteria and their implementation
107
+ - **Practical Trade-offs**: Speed vs. quality, simplicity vs. robustness
108
+
109
+ ### Suggested Learning Path
110
+
111
+ 1. **Start with Roberts**: Understand the basic concept of gradient operators
112
+ 2. **Progress to Sobel/Prewitt**: See how larger kernels improve robustness
113
+ 3. **Explore Laplacian**: Learn about second-order derivatives
114
+ 4. **Master Canny**: Understand the multi-stage optimal approach
115
+ 5. **Compare Methods**: Use the comparison feature to see differences
116
+
117
+ ## Project Structure
118
+
119
+ ```
120
+ edge-detection/
121
+ β”œβ”€β”€ app.py # Main Streamlit application
122
+ β”œβ”€β”€ requirements.txt # Python dependencies
123
+ β”œβ”€β”€ README.md # This file
124
+ β”œβ”€β”€ packages.txt # System dependencies for HF Spaces
125
+ β”œβ”€β”€ .python-version # Python version specification
126
+ β”œβ”€β”€ Dockerfile # Docker deployment
127
+ β”œβ”€β”€ setup.sh # Setup script
128
+ β”œβ”€β”€ run_simple.sh # Local run script
129
+ └── deploy.sh # Deployment helper script
130
+ ```
131
+
132
+ ## Technical Details
133
+
134
+ ### Implemented Algorithms
135
+
136
+ **Roberts Cross (1963)**
137
+ - 2Γ—2 diagonal gradient kernels
138
+ - Fastest, simplest method
139
+ - Good for sharp, diagonal edges
140
+
141
+ **Prewitt (1970)**
142
+ - 3Γ—3 gradient kernels with equal weighting
143
+ - Incorporates smoothing perpendicular to gradient
144
+ - Good balance of simplicity and robustness
145
+
146
+ **Sobel (1968)**
147
+ - 3Γ—3 gradient kernels with center weighting
148
+ - Most widely used first-order operator
149
+ - Better approximation of true gradient
150
+
151
+ **Laplacian**
152
+ - Second-order isotropic operator
153
+ - Detects zero-crossings
154
+ - Requires careful noise handling
155
+
156
+ **Canny (1986)**
157
+ - Five-stage optimal edge detector
158
+ - Combines smoothing, gradients, NMS, and hysteresis
159
+ - Industry standard for quality edge detection
160
+
161
+ ## License
162
+
163
+ MIT License - Free for educational and commercial use
164
+
165
+ ## Credits
166
+
167
+ Created for the Intro to Image Analysis course at the University of Bern.
168
+ Inspired by interactive teaching tools for computer vision education.
169
+
170
+ ## References
171
+
172
+ - Canny, J. (1986). "A Computational Approach to Edge Detection." IEEE TPAMI.
173
+ - Gonzalez & Woods. "Digital Image Processing" (4th Edition).
174
+ - Szeliski, R. "Computer Vision: Algorithms and Applications."
app.py ADDED
@@ -0,0 +1,997 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Interactive Edge Detection Demo
3
+ An educational tool for graduate-level image analysis courses
4
+
5
+ This demo explores computational approaches to edge detection, focusing on:
6
+ - Classical gradient-based filters (Sobel, Prewitt, Roberts, Laplacian)
7
+ - Advanced Canny edge detection
8
+ - Comparative analysis of different methods
9
+ """
10
+
11
+ import io
12
+ import numpy as np
13
+ import cv2 as cv
14
+ import streamlit as st
15
+ from PIL import Image
16
+ from huggingface_hub import hf_hub_download
17
+
18
+
19
+ @st.cache_resource
20
+ def load_sample_image():
21
+ """Load a sample image for the demo. Falls back to generated image if download fails."""
22
+ try:
23
+ # Try to download from HuggingFace
24
+ image_path = hf_hub_download(
25
+ repo_id="amithjkamath/exampleimages",
26
+ filename="sample-image.jpg",
27
+ repo_type="dataset",
28
+ )
29
+ img = cv.imread(image_path)
30
+ img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
31
+ except Exception:
32
+ # Generate a sample image with edges
33
+ img = generate_sample_image()
34
+
35
+ return img
36
+
37
+
38
+ def generate_sample_image(size=512):
39
+ """Generate a sample image with interesting edge features for demonstration."""
40
+ img = np.zeros((size, size, 3), dtype=np.uint8)
41
+
42
+ # Create a gradient background (soft edges)
43
+ for i in range(size):
44
+ img[i, :, :] = int(100 * i / size)
45
+
46
+ # Add geometric shapes with different edge characteristics
47
+ # Circle with smooth edges
48
+ cv.circle(img, (size//4, size//4), size//8, (255, 255, 255), -1)
49
+
50
+ # Rectangle with sharp corners
51
+ cv.rectangle(img, (size//2, size//2), (3*size//4, 3*size//4), (200, 50, 50), -1)
52
+
53
+ # Triangle (sharp edges at different orientations)
54
+ pts = np.array([[3*size//4, size//4], [size-50, size//4 + size//8],
55
+ [3*size//4 + size//16, 50]], np.int32)
56
+ cv.fillPoly(img, [pts], (50, 200, 200))
57
+
58
+ # Add some thin lines (challenging for edge detection)
59
+ cv.line(img, (50, 3*size//4), (size//3, 3*size//4), (255, 255, 255), 2)
60
+
61
+ # Add text (multiple edge orientations)
62
+ cv.putText(img, "EDGES", (size//8, size-80),
63
+ cv.FONT_HERSHEY_SIMPLEX, 1.5, (255, 255, 255), 3)
64
+
65
+ return img
66
+
67
+
68
+ def apply_sobel_filter(img, ksize=3):
69
+ """
70
+ Apply Sobel edge detection filter.
71
+
72
+ Sobel computes the gradient using separable kernels:
73
+ - Gx: horizontal gradient (vertical edges)
74
+ - Gy: vertical gradient (horizontal edges)
75
+ - Magnitude: sqrt(GxΒ² + GyΒ²)
76
+
77
+ Args:
78
+ img: Input grayscale image
79
+ ksize: Kernel size (1, 3, 5, 7)
80
+
81
+ Returns:
82
+ edges: Edge magnitude image
83
+ gx, gy: Gradient components
84
+ """
85
+ # Compute gradients in both directions
86
+ gx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=ksize)
87
+ gy = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=ksize)
88
+
89
+ # Compute magnitude
90
+ magnitude = np.sqrt(gx**2 + gy**2)
91
+
92
+ # Normalize to 0-255 range
93
+ magnitude = np.uint8(255 * magnitude / np.max(magnitude)) if np.max(magnitude) > 0 else magnitude.astype(np.uint8)
94
+
95
+ return magnitude, gx, gy
96
+
97
+
98
+ def apply_prewitt_filter(img):
99
+ """
100
+ Apply Prewitt edge detection filter.
101
+
102
+ Prewitt is similar to Sobel but uses simpler kernels:
103
+ Gx = [[-1, 0, 1], Gy = [[-1, -1, -1],
104
+ [-1, 0, 1], [ 0, 0, 0],
105
+ [-1, 0, 1]] [ 1, 1, 1]]
106
+
107
+ Args:
108
+ img: Input grayscale image
109
+
110
+ Returns:
111
+ edges: Edge magnitude image
112
+ gx, gy: Gradient components
113
+ """
114
+ # Define Prewitt kernels
115
+ kernel_x = np.array([[-1, 0, 1],
116
+ [-1, 0, 1],
117
+ [-1, 0, 1]], dtype=np.float32)
118
+
119
+ kernel_y = np.array([[-1, -1, -1],
120
+ [ 0, 0, 0],
121
+ [ 1, 1, 1]], dtype=np.float32)
122
+
123
+ # Apply convolution
124
+ gx = cv.filter2D(img, cv.CV_64F, kernel_x)
125
+ gy = cv.filter2D(img, cv.CV_64F, kernel_y)
126
+
127
+ # Compute magnitude
128
+ magnitude = np.sqrt(gx**2 + gy**2)
129
+
130
+ # Normalize to 0-255 range
131
+ magnitude = np.uint8(255 * magnitude / np.max(magnitude)) if np.max(magnitude) > 0 else magnitude.astype(np.uint8)
132
+
133
+ return magnitude, gx, gy
134
+
135
+
136
+ def apply_roberts_filter(img):
137
+ """
138
+ Apply Roberts Cross edge detection filter.
139
+
140
+ Roberts uses 2x2 diagonal gradient kernels:
141
+ Gx = [[ 1, 0], Gy = [[ 0, 1],
142
+ [ 0, -1]] [-1, 0]]
143
+
144
+ This is the simplest gradient operator, sensitive to diagonal edges.
145
+
146
+ Args:
147
+ img: Input grayscale image
148
+
149
+ Returns:
150
+ edges: Edge magnitude image
151
+ gx, gy: Gradient components
152
+ """
153
+ # Define Roberts Cross kernels
154
+ kernel_x = np.array([[ 1, 0],
155
+ [ 0, -1]], dtype=np.float32)
156
+
157
+ kernel_y = np.array([[ 0, 1],
158
+ [-1, 0]], dtype=np.float32)
159
+
160
+ # Apply convolution
161
+ gx = cv.filter2D(img, cv.CV_64F, kernel_x)
162
+ gy = cv.filter2D(img, cv.CV_64F, kernel_y)
163
+
164
+ # Compute magnitude
165
+ magnitude = np.sqrt(gx**2 + gy**2)
166
+
167
+ # Normalize to 0-255 range
168
+ magnitude = np.uint8(255 * magnitude / np.max(magnitude)) if np.max(magnitude) > 0 else magnitude.astype(np.uint8)
169
+
170
+ return magnitude, gx, gy
171
+
172
+
173
+ def apply_laplacian_filter(img, ksize=3):
174
+ """
175
+ Apply Laplacian edge detection filter.
176
+
177
+ The Laplacian is a second-order derivative operator that detects
178
+ regions of rapid intensity change. Unlike gradient-based methods,
179
+ it's isotropic (rotation-invariant) but more sensitive to noise.
180
+
181
+ Common kernel (ksize=3):
182
+ [[ 0, 1, 0],
183
+ [ 1, -4, 1],
184
+ [ 0, 1, 0]]
185
+
186
+ Args:
187
+ img: Input grayscale image
188
+ ksize: Kernel size (1, 3, 5, 7)
189
+
190
+ Returns:
191
+ edges: Edge response image
192
+ """
193
+ # Apply Laplacian
194
+ laplacian = cv.Laplacian(img, cv.CV_64F, ksize=ksize)
195
+
196
+ # Take absolute value (edges can be positive or negative)
197
+ laplacian = np.abs(laplacian)
198
+
199
+ # Normalize to 0-255 range
200
+ laplacian = np.uint8(255 * laplacian / np.max(laplacian)) if np.max(laplacian) > 0 else laplacian.astype(np.uint8)
201
+
202
+ return laplacian
203
+
204
+
205
+ def apply_canny_edge_detector(img, low_threshold=50, high_threshold=150, aperture_size=3, use_l2=True):
206
+ """
207
+ Apply Canny edge detection algorithm.
208
+
209
+ The Canny edge detector is a multi-stage algorithm:
210
+ 1. Noise reduction (Gaussian blur)
211
+ 2. Gradient calculation (Sobel)
212
+ 3. Non-maximum suppression (thin edges)
213
+ 4. Double thresholding (strong and weak edges)
214
+ 5. Edge tracking by hysteresis (connect weak edges to strong ones)
215
+
216
+ Args:
217
+ img: Input grayscale image
218
+ low_threshold: Lower threshold for hysteresis
219
+ high_threshold: Upper threshold for hysteresis
220
+ aperture_size: Sobel kernel size (3, 5, 7)
221
+ use_l2: Use L2 norm for gradient magnitude (more accurate but slower)
222
+
223
+ Returns:
224
+ edges: Binary edge map
225
+ """
226
+ edges = cv.Canny(img, low_threshold, high_threshold,
227
+ apertureSize=aperture_size, L2gradient=use_l2)
228
+
229
+ return edges
230
+
231
+
232
+ def compute_gradient_direction(gx, gy):
233
+ """
234
+ Compute gradient direction from gradient components.
235
+
236
+ Args:
237
+ gx, gy: Gradient components in x and y directions
238
+
239
+ Returns:
240
+ direction: Gradient direction in degrees (0-360)
241
+ """
242
+ direction = np.arctan2(gy, gx) * 180 / np.pi
243
+ direction = (direction + 360) % 360 # Ensure positive angles
244
+
245
+ return direction
246
+
247
+
248
+ def create_gradient_visualization(gx, gy):
249
+ """
250
+ Create a color-coded visualization of gradient direction.
251
+
252
+ Uses HSV color space where:
253
+ - Hue represents direction
254
+ - Saturation is constant
255
+ - Value represents magnitude
256
+
257
+ Args:
258
+ gx, gy: Gradient components
259
+
260
+ Returns:
261
+ RGB image showing gradient direction and magnitude
262
+ """
263
+ # Compute magnitude and direction
264
+ magnitude = np.sqrt(gx**2 + gy**2)
265
+ direction = np.arctan2(gy, gx)
266
+
267
+ # Normalize magnitude
268
+ magnitude_norm = magnitude / np.max(magnitude) if np.max(magnitude) > 0 else magnitude
269
+
270
+ # Create HSV image
271
+ hsv = np.zeros((*gx.shape, 3), dtype=np.uint8)
272
+ hsv[..., 0] = ((direction + np.pi) / (2 * np.pi) * 180).astype(np.uint8) # Hue: 0-180
273
+ hsv[..., 1] = 255 # Saturation: full
274
+ hsv[..., 2] = (magnitude_norm * 255).astype(np.uint8) # Value: magnitude
275
+
276
+ # Convert to RGB
277
+ rgb = cv.cvtColor(hsv, cv.COLOR_HSV2RGB)
278
+
279
+ return rgb
280
+
281
+
282
+ def main_loop():
283
+ """Main application loop."""
284
+ st.set_page_config(layout="wide", page_title="Edge Detection Demo")
285
+
286
+ st.title("πŸ” Interactive Edge Detection Demo")
287
+ st.markdown("""
288
+ Welcome to an educational exploration of edge detection in digital image processing.
289
+ This demo demonstrates how computational methods identify boundaries and discontinuities in imagesβ€”
290
+ a fundamental task in computer vision with applications from medical imaging to autonomous systems.
291
+ """)
292
+
293
+ # Load sample image
294
+ sample_img = load_sample_image()
295
+
296
+ # Sidebar: Image input
297
+ st.sidebar.header("Image Input")
298
+ uploaded_file = st.sidebar.file_uploader("Upload your own image (optional)",
299
+ type=['png', 'jpg', 'jpeg'])
300
+
301
+ if uploaded_file is not None:
302
+ # Use uploaded image
303
+ file_bytes = np.asarray(bytearray(uploaded_file.read()), dtype=np.uint8)
304
+ img = cv.imdecode(file_bytes, cv.IMREAD_COLOR)
305
+ img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
306
+
307
+ # Resize if too large
308
+ max_size = 512
309
+ h, w = img.shape[:2]
310
+ if max(h, w) > max_size:
311
+ scale = max_size / max(h, w)
312
+ new_w, new_h = int(w * scale), int(h * scale)
313
+ img = cv.resize(img, (new_w, new_h), interpolation=cv.INTER_AREA)
314
+ else:
315
+ img = sample_img
316
+
317
+ # Convert to grayscale for edge detection
318
+ gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
319
+
320
+ # Sidebar: Pre-processing options
321
+ st.sidebar.header("Pre-processing")
322
+
323
+ apply_blur = st.sidebar.checkbox("Apply Gaussian Blur", value=False,
324
+ help="Reduces noise before edge detection")
325
+
326
+ if apply_blur:
327
+ blur_ksize = st.sidebar.slider("Blur Kernel Size",
328
+ min_value=3, max_value=15, value=5, step=2,
329
+ help="Larger kernels = more smoothing")
330
+ gray = cv.GaussianBlur(gray, (blur_ksize, blur_ksize), 0)
331
+
332
+ # Sidebar: Select edge detection method
333
+ st.sidebar.header("Edge Detection Method")
334
+
335
+ method = st.sidebar.selectbox(
336
+ "Select Method",
337
+ ["Sobel", "Prewitt", "Roberts", "Laplacian", "Canny"],
338
+ help="Choose which edge detection algorithm to apply"
339
+ )
340
+
341
+ # Method-specific parameters
342
+ st.sidebar.subheader("Method Parameters")
343
+
344
+ if method == "Sobel":
345
+ sobel_ksize = st.sidebar.select_slider(
346
+ "Sobel Kernel Size",
347
+ options=[1, 3, 5, 7],
348
+ value=3,
349
+ help="Larger kernels detect coarser edges"
350
+ )
351
+ edges, gx, gy = apply_sobel_filter(gray, ksize=sobel_ksize)
352
+ show_gradients = st.sidebar.checkbox("Show Gradient Components", value=False)
353
+ show_direction = st.sidebar.checkbox("Show Gradient Direction", value=False)
354
+
355
+ elif method == "Prewitt":
356
+ edges, gx, gy = apply_prewitt_filter(gray)
357
+ show_gradients = st.sidebar.checkbox("Show Gradient Components", value=False)
358
+ show_direction = st.sidebar.checkbox("Show Gradient Direction", value=False)
359
+
360
+ elif method == "Roberts":
361
+ edges, gx, gy = apply_roberts_filter(gray)
362
+ show_gradients = st.sidebar.checkbox("Show Gradient Components", value=False)
363
+ show_direction = st.sidebar.checkbox("Show Gradient Direction", value=False)
364
+
365
+ elif method == "Laplacian":
366
+ laplacian_ksize = st.sidebar.select_slider(
367
+ "Laplacian Kernel Size",
368
+ options=[1, 3, 5, 7],
369
+ value=3,
370
+ help="Larger kernels = smoother response"
371
+ )
372
+ edges = apply_laplacian_filter(gray, ksize=laplacian_ksize)
373
+ show_gradients = False
374
+ show_direction = False
375
+
376
+ elif method == "Canny":
377
+ st.sidebar.markdown("**Threshold Values**")
378
+ low_threshold = st.sidebar.slider(
379
+ "Low Threshold",
380
+ min_value=0, max_value=255, value=50,
381
+ help="Pixels below this are definitely not edges"
382
+ )
383
+ high_threshold = st.sidebar.slider(
384
+ "High Threshold",
385
+ min_value=0, max_value=255, value=150,
386
+ help="Pixels above this are definitely edges"
387
+ )
388
+ aperture_size = st.sidebar.select_slider(
389
+ "Sobel Kernel Size",
390
+ options=[3, 5, 7],
391
+ value=3,
392
+ help="Kernel size for internal gradient computation"
393
+ )
394
+ use_l2 = st.sidebar.checkbox("Use L2 Gradient", value=True,
395
+ help="More accurate but slower magnitude calculation")
396
+
397
+ edges = apply_canny_edge_detector(gray, low_threshold, high_threshold,
398
+ aperture_size, use_l2)
399
+ show_gradients = False
400
+ show_direction = False
401
+
402
+ # Display: Original and detected edges
403
+ st.markdown("---")
404
+ st.markdown("## Visual Results")
405
+
406
+ col1, col2 = st.columns(2)
407
+
408
+ with col1:
409
+ st.markdown("### Original Image")
410
+ st.image(img, use_column_width=True, caption="Input image")
411
+ st.caption(f"Size: {img.shape[1]}Γ—{img.shape[0]} pixels")
412
+
413
+ with col2:
414
+ st.markdown(f"### {method} Edge Detection")
415
+ if method == "Canny":
416
+ # Canny produces binary edges, display with inverted colormap for visibility
417
+ st.image(edges, use_column_width=True, caption=f"{method} edges", clamp=True)
418
+ else:
419
+ st.image(edges, use_column_width=True, caption=f"{method} edge magnitude", clamp=True)
420
+
421
+ # Show edge statistics
422
+ edge_pixels = np.sum(edges > 0)
423
+ total_pixels = edges.shape[0] * edges.shape[1]
424
+ edge_percentage = (edge_pixels / total_pixels) * 100
425
+ st.caption(f"Edge pixels: {edge_pixels:,} ({edge_percentage:.2f}%)")
426
+
427
+ # Display gradient components if requested
428
+ if show_gradients and method in ["Sobel", "Prewitt", "Roberts"]:
429
+ st.markdown("---")
430
+ st.markdown("### Gradient Components")
431
+ st.markdown("""
432
+ The gradient has two components:
433
+ - **Gx** (horizontal gradient): responds to vertical edges
434
+ - **Gy** (vertical gradient): responds to horizontal edges
435
+ """)
436
+
437
+ col1, col2 = st.columns(2)
438
+
439
+ with col1:
440
+ st.markdown("**Gx: Horizontal Gradient**")
441
+ gx_display = np.abs(gx)
442
+ gx_display = np.uint8(255 * gx_display / np.max(gx_display)) if np.max(gx_display) > 0 else gx_display.astype(np.uint8)
443
+ st.image(gx_display, use_column_width=True, caption="Vertical edges", clamp=True)
444
+
445
+ with col2:
446
+ st.markdown("**Gy: Vertical Gradient**")
447
+ gy_display = np.abs(gy)
448
+ gy_display = np.uint8(255 * gy_display / np.max(gy_display)) if np.max(gy_display) > 0 else gy_display.astype(np.uint8)
449
+ st.image(gy_display, use_column_width=True, caption="Horizontal edges", clamp=True)
450
+
451
+ # Display gradient direction if requested
452
+ if show_direction and method in ["Sobel", "Prewitt", "Roberts"]:
453
+ st.markdown("---")
454
+ st.markdown("### Gradient Direction Visualization")
455
+ st.markdown("""
456
+ This color-coded visualization shows:
457
+ - **Hue (color)**: Direction of the gradient (edge orientation)
458
+ - **Brightness**: Magnitude of the gradient (edge strength)
459
+ """)
460
+
461
+ gradient_viz = create_gradient_visualization(gx, gy)
462
+ st.image(gradient_viz, use_column_width=True, caption="Gradient direction and magnitude")
463
+
464
+ # Add color legend
465
+ st.markdown("""
466
+ **Color Legend:**
467
+ - πŸ”΄ Red: Rightward gradient
468
+ - 🟑 Yellow: Upward-right gradient
469
+ - 🟒 Green: Upward gradient
470
+ - πŸ”΅ Cyan: Upward-left gradient
471
+ - 🟣 Blue: Leftward gradient
472
+ - 🟣 Magenta: Downward-left gradient
473
+ """)
474
+
475
+ # Comparison with Canny
476
+ if method != "Canny":
477
+ st.sidebar.markdown("---")
478
+ st.sidebar.header("Compare with Canny")
479
+ show_comparison = st.sidebar.checkbox("Show Canny Comparison", value=False)
480
+
481
+ if show_comparison:
482
+ st.markdown("---")
483
+ st.markdown(f"## Comparison: {method} vs. Canny")
484
+
485
+ col1, col2 = st.columns(2)
486
+
487
+ # Default Canny parameters for comparison
488
+ canny_low = st.sidebar.slider("Canny Low Threshold (Comparison)",
489
+ min_value=0, max_value=255, value=50)
490
+ canny_high = st.sidebar.slider("Canny High Threshold (Comparison)",
491
+ min_value=0, max_value=255, value=150)
492
+
493
+ canny_edges = apply_canny_edge_detector(gray, canny_low, canny_high)
494
+
495
+ with col1:
496
+ st.markdown(f"### {method}")
497
+ st.image(edges, use_column_width=True, clamp=True)
498
+ edge_pixels_method = np.sum(edges > 0)
499
+ st.caption(f"Edge pixels: {edge_pixels_method:,}")
500
+
501
+ with col2:
502
+ st.markdown("### Canny")
503
+ st.image(canny_edges, use_column_width=True, clamp=True)
504
+ edge_pixels_canny = np.sum(canny_edges > 0)
505
+ st.caption(f"Edge pixels: {edge_pixels_canny:,}")
506
+
507
+ # Educational content
508
+ st.markdown("---")
509
+ st.markdown("## πŸ“š Educational Insights")
510
+
511
+ tab1, tab2, tab3, tab4 = st.tabs([
512
+ "What Are Edges?",
513
+ "Gradient-Based Methods",
514
+ "Canny Algorithm",
515
+ "Practical Considerations"
516
+ ])
517
+
518
+ with tab1:
519
+ st.markdown("""
520
+ ### What Are Edges in Digital Images?
521
+
522
+ **Definition:**
523
+ Edges correspond to significant local changes in image intensity. They typically occur at:
524
+ - **Object boundaries**: Where one object ends and another begins
525
+ - **Surface orientation changes**: Corners, creases, and ridges
526
+ - **Material property changes**: Reflectance, color, or texture discontinuities
527
+ - **Illumination boundaries**: Shadows and highlights
528
+
529
+ **Why Edge Detection Matters:**
530
+ Edge detection is foundational to computer vision because edges:
531
+ - Reduce data dimensionality while preserving structural information
532
+ - Are relatively invariant to illumination changes
533
+ - Enable higher-level tasks: object recognition, segmentation, tracking
534
+ - Form the basis for feature extraction in many applications
535
+
536
+ **The Computational Challenge:**
537
+ Distinguishing true edges from noise requires balancing:
538
+ - **Sensitivity**: Detecting all significant edges
539
+ - **Specificity**: Avoiding false positives from noise
540
+ - **Localization**: Accurately positioning detected edges
541
+
542
+ **The "Aha!" Moment:**
543
+ Edges are fundamentally about **derivatives**. Just as derivatives in calculus identify rates of change,
544
+ image gradients identify spatial rates of intensity change. This mathematical insight transforms
545
+ a perceptual concept (edges) into a computational operation (differentiation).
546
+ """)
547
+
548
+ with tab2:
549
+ st.markdown("""
550
+ ### Gradient-Based Edge Detection Methods
551
+
552
+ **Fundamental Principle:**
553
+ All gradient-based methods approximate the first derivative of image intensity.
554
+ The gradient is a vector pointing in the direction of greatest intensity increase:
555
+
556
+ $$\\nabla I = \\begin{bmatrix} \\frac{\\partial I}{\\partial x} \\\\ \\frac{\\partial I}{\\partial y} \\end{bmatrix} = \\begin{bmatrix} G_x \\\\ G_y \\end{bmatrix}$$
557
+
558
+ The **edge magnitude** is: $|\\nabla I| = \\sqrt{G_x^2 + G_y^2}$
559
+
560
+ The **edge direction** is: $\\theta = \\arctan\\left(\\frac{G_y}{G_x}\\right)$
561
+
562
+ ---
563
+
564
+ #### Roberts Cross Operator (1963)
565
+
566
+ **Kernels (2Γ—2):**
567
+ $$G_x = \\begin{bmatrix} +1 & 0 \\\\ 0 & -1 \\end{bmatrix}, \\quad
568
+ G_y = \\begin{bmatrix} 0 & +1 \\\\ -1 & 0 \\end{bmatrix}$$
569
+
570
+ **Characteristics:**
571
+ - Simplest gradient operator, computationally efficient
572
+ - Computes diagonal differences (45Β° rotated gradients)
573
+ - Sensitive to noise due to small kernel
574
+ - Good for images with sharp, diagonal features
575
+ - Historical significance: one of the earliest edge detectors
576
+
577
+ **The "Aha!" Moment:**
578
+ Roberts showed that a simple 2Γ—2 difference operation could capture edgesβ€”
579
+ proving that edge detection doesn't require complex computation.
580
+
581
+ ---
582
+
583
+ #### Prewitt Operator (1970)
584
+
585
+ **Kernels (3Γ—3):**
586
+ $$G_x = \\begin{bmatrix} -1 & 0 & +1 \\\\ -1 & 0 & +1 \\\\ -1 & 0 & +1 \\end{bmatrix}, \\quad
587
+ G_y = \\begin{bmatrix} -1 & -1 & -1 \\\\ 0 & 0 & 0 \\\\ +1 & +1 & +1 \\end{bmatrix}$$
588
+
589
+ **Characteristics:**
590
+ - Uses 3Γ—3 neighborhood for gradient estimation
591
+ - Incorporates implicit averaging (smoothing) perpendicular to gradient direction
592
+ - More robust to noise than Roberts
593
+ - Equal weighting of all pixels in each row/column
594
+ - Separable: can be computed as two 1D convolutions
595
+
596
+ **Key Insight:**
597
+ Prewitt demonstrates the value of **spatial averaging**β€”smoothing in one direction
598
+ while differentiating in the other reduces noise sensitivity.
599
+
600
+ ---
601
+
602
+ #### Sobel Operator (1968)
603
+
604
+ **Kernels (3Γ—3):**
605
+ $$G_x = \\begin{bmatrix} -1 & 0 & +1 \\\\ -2 & 0 & +2 \\\\ -1 & 0 & +1 \\end{bmatrix}, \\quad
606
+ G_y = \\begin{bmatrix} -1 & -2 & -1 \\\\ 0 & 0 & 0 \\\\ +1 & +2 & +1 \\end{bmatrix}$$
607
+
608
+ **Characteristics:**
609
+ - Similar to Prewitt but with **weighted averaging** (2:1 center weight)
610
+ - Better approximation of the true gradient
611
+ - More isotropic response (similar sensitivity to all edge orientations)
612
+ - Most widely used first-order edge detector
613
+ - Can extend to larger kernel sizes (5Γ—5, 7Γ—7) for coarser features
614
+
615
+ **The "Aha!" Moment:**
616
+ Sobel's weighting scheme approximates a Gaussian smoothing perpendicular to the gradient.
617
+ This is an early recognition that optimal edge detection combines **smoothing and differentiation**β€”
618
+ a principle later formalized by Canny.
619
+
620
+ ---
621
+
622
+ #### Laplacian Operator (Second-Order)
623
+
624
+ **Mathematical Form:**
625
+ $$\\nabla^2 I = \\frac{\\partial^2 I}{\\partial x^2} + \\frac{\\partial^2 I}{\\partial y^2}$$
626
+
627
+ **Common Kernel (3Γ—3):**
628
+ $$\\nabla^2 = \\begin{bmatrix} 0 & 1 & 0 \\\\ 1 & -4 & 1 \\\\ 0 & 1 & 0 \\end{bmatrix}$$
629
+
630
+ **Characteristics:**
631
+ - Second-order derivative: detects zero-crossings (rapid intensity changes)
632
+ - **Isotropic**: rotationally invariant, no directional bias
633
+ - Produces double edges (both sides of intensity transitions)
634
+ - Very sensitive to noise (second derivative amplifies high frequencies)
635
+ - Often combined with Gaussian smoothing β†’ **Laplacian of Gaussian (LoG)**
636
+
637
+ **Key Insight:**
638
+ The Laplacian identifies edge locations as zero-crossingsβ€”where the second derivative
639
+ changes sign. This gives **precise localization** but at the cost of noise sensitivity.
640
+
641
+ ---
642
+
643
+ #### Comparison Summary
644
+
645
+ | Method | Kernel Size | Order | Noise Sensitivity | Directionality | Computational Cost |
646
+ |--------|-------------|-------|-------------------|----------------|-------------------|
647
+ | Roberts | 2Γ—2 | 1st | High | Diagonal | Very Low |
648
+ | Prewitt | 3Γ—3 | 1st | Medium | Separable (H/V) | Low |
649
+ | Sobel | 3Γ—3+ | 1st | Medium-Low | Separable (H/V) | Low |
650
+ | Laplacian | 3Γ—3+ | 2nd | Very High | Isotropic | Low |
651
+
652
+ **Practical Recommendation:**
653
+ - **Sobel**: Best general-purpose gradient operator
654
+ - **Roberts**: When speed is critical and images are low-noise
655
+ - **Prewitt**: Similar to Sobel, historical interest
656
+ - **Laplacian**: When precise localization matters, always pre-smooth
657
+ """)
658
+
659
+ if method in ["Sobel", "Prewitt", "Roberts"]:
660
+ st.info(f"""
661
+ **Current Method: {method}**
662
+
663
+ You're currently using the {method} operator. Notice how it responds to different edge orientations
664
+ in your image. Try toggling "Show Gradient Components" to see how Gx and Gy separately capture
665
+ vertical and horizontal edges.
666
+ """)
667
+
668
+ with tab3:
669
+ st.markdown("""
670
+ ### The Canny Edge Detector: Optimal Edge Detection
671
+
672
+ **Historical Context:**
673
+ In 1986, John Canny published a landmark paper deriving an "optimal" edge detector
674
+ from first principles. He defined three criteria for good edge detection:
675
+
676
+ 1. **Good Detection**: Minimize false positives and false negatives
677
+ 2. **Good Localization**: Detected edges should be close to true edges
678
+ 3. **Single Response**: One detector response per edge (no double edges)
679
+
680
+ **The Canny Algorithm: A Multi-Stage Pipeline**
681
+
682
+ ---
683
+
684
+ #### Stage 1: Noise Reduction (Gaussian Smoothing)
685
+
686
+ $$G(x, y) = \\frac{1}{2\\pi\\sigma^2} e^{-\\frac{x^2 + y^2}{2\\sigma^2}}$$
687
+
688
+ - Convolve image with Gaussian filter to reduce noise
689
+ - $\\sigma$ controls smoothing scale
690
+ - Trade-off: larger $\\sigma$ removes more noise but also blurs edges
691
+
692
+ **Insight:** Canny recognized that edge detection and noise suppression are inherently coupled.
693
+ The Gaussian is optimal for smoothing while preserving edge localization.
694
+
695
+ ---
696
+
697
+ #### Stage 2: Gradient Calculation
698
+
699
+ - Apply Sobel operator (or similar) to compute $G_x$ and $G_y$
700
+ - Calculate magnitude: $M = \\sqrt{G_x^2 + G_y^2}$
701
+ - Calculate direction: $\\theta = \\arctan(G_y / G_x)$
702
+
703
+ ---
704
+
705
+ #### Stage 3: Non-Maximum Suppression (Edge Thinning)
706
+
707
+ **The Problem:** Gradient operators produce thick edges (multiple pixels respond to a single edge).
708
+
709
+ **The Solution:**
710
+ - For each pixel, examine magnitude along gradient direction
711
+ - Suppress pixel if either neighbor along gradient direction has higher magnitude
712
+ - Result: edges thinned to single-pixel width
713
+
714
+ **The "Aha!" Moment:**
715
+ This stage enforces the "single response" criterion. By suppressing non-maxima perpendicular
716
+ to the edge direction, Canny achieves sub-pixel localization accuracy.
717
+
718
+ ---
719
+
720
+ #### Stage 4: Double Thresholding
721
+
722
+ Use two thresholds to classify edge pixels:
723
+ - **Strong edges**: $M > T_{high}$ β€” definitely edges
724
+ - **Weak edges**: $T_{low} < M < T_{high}$ β€” potential edges
725
+ - **Non-edges**: $M < T_{low}$ β€” definitely not edges
726
+
727
+ **Rationale:**
728
+ - Single threshold is too binary: either misses edges or includes noise
729
+ - Double threshold creates a "confidence gradient"
730
+
731
+ ---
732
+
733
+ #### Stage 5: Edge Tracking by Hysteresis
734
+
735
+ **The Final Step:**
736
+ - Keep all strong edges
737
+ - Keep weak edges only if connected to strong edges
738
+ - Discard isolated weak edges
739
+
740
+ **The "Aha!" Moment:**
741
+ Hysteresis exploits **edge continuity**. Real edges are extended structures; noise is isolated.
742
+ By requiring weak edges to connect to strong edges, Canny achieves robustness without
743
+ sacrificing sensitivity.
744
+
745
+ ---
746
+
747
+ ### Why Canny Is "Optimal"
748
+
749
+ Canny proved that, under certain assumptions (Gaussian noise, linear filters), his algorithm
750
+ achieves optimal trade-offs among his three criteria. Specifically:
751
+
752
+ - The **optimal filter** for edge detection is approximately the derivative of a Gaussian
753
+ - The multi-scale nature (controlled by $\\sigma$) allows detection of edges at different scales
754
+ - Non-maximum suppression and hysteresis address fundamental ambiguities in edge detection
755
+
756
+ ---
757
+
758
+ ### Limitations and Practical Considerations
759
+
760
+ **When Canny Struggles:**
761
+ - **Texture**: Dense texture can trigger many weak edges
762
+ - **Low contrast**: Subtle edges may fall below threshold
763
+ - **Computational cost**: More expensive than simple gradient operators
764
+
765
+ **Parameter Tuning:**
766
+ - **$T_{low}$ and $T_{high}$**: Typical ratio is 1:2 or 1:3
767
+ - Too high: miss edges
768
+ - Too low: include noise
769
+ - **$\\sigma$ (implicit in aperture size)**: Larger for coarse edges, smaller for fine details
770
+
771
+ **Modern Context:**
772
+ Despite being nearly 40 years old, Canny remains widely used. Its principles influenced:
773
+ - Multi-scale edge detection (e.g., Canny-Deriche)
774
+ - Learning-based edge detection (e.g., structured forests, CNNs)
775
+ - Active contours and level sets
776
+
777
+ ---
778
+
779
+ ### Interactive Exploration
780
+
781
+ **Try This:**
782
+ 1. Set low_threshold = 50, high_threshold = 150 (default)
783
+ 2. Gradually increase both thresholds β†’ edges disappear
784
+ 3. Gradually decrease both β†’ more edges, including noise
785
+ 4. Compare Canny to Sobel with the comparison feature
786
+
787
+ **Question to Ponder:**
788
+ Can you find threshold values where Canny detects edges that Sobel misses,
789
+ and vice versa? What does this reveal about their different approaches?
790
+ """)
791
+
792
+ if method == "Canny":
793
+ st.info(f"""
794
+ **Current Parameters:**
795
+ - Low Threshold: {low_threshold}
796
+ - High Threshold: {high_threshold}
797
+ - Ratio: 1:{high_threshold/low_threshold:.2f}
798
+
799
+ **Interpretation:**
800
+ - Pixels with magnitude > {high_threshold} are **strong edges** (white)
801
+ - Pixels with magnitude between {low_threshold} and {high_threshold} are kept only if connected to strong edges
802
+ - All other pixels are **non-edges** (black)
803
+ """)
804
+
805
+ with tab4:
806
+ st.markdown("""
807
+ ### Practical Considerations in Edge Detection
808
+
809
+ ---
810
+
811
+ #### 1. Pre-processing: The Critical First Step
812
+
813
+ **Gaussian Smoothing:**
814
+ - Almost always beneficial, especially for Laplacian
815
+ - Suppresses noise while preserving edge structure
816
+ - $\\sigma$ controls scale: larger $\\sigma$ for coarser edges
817
+
818
+ **Histogram Equalization:**
819
+ - Enhances contrast in low-contrast images
820
+ - Can make subtle edges detectable
821
+ - May amplify noise in uniform regions
822
+
823
+ **Morphological Operations:**
824
+ - Closing: Connect nearby edge segments
825
+ - Opening: Remove isolated noise pixels
826
+ - Applied after edge detection, not before
827
+
828
+ ---
829
+
830
+ #### 2. Choosing the Right Method
831
+
832
+ **Sobel** when:
833
+ - You need directional gradient information
834
+ - Computational efficiency matters
835
+ - Image is reasonably clean
836
+ - You're building a pipeline for further processing (e.g., Hough transform)
837
+
838
+ **Canny** when:
839
+ - You need thin, connected edges
840
+ - You can afford the computational cost
841
+ - You need robustness to noise
842
+ - Output will be used for segmentation or shape analysis
843
+
844
+ **Laplacian** when:
845
+ - You need isotropic response
846
+ - You're detecting blob-like features (LoG)
847
+ - Combined with Gaussian (LoG): excellent for multi-scale analysis
848
+
849
+ **Roberts** when:
850
+ - Extreme computational constraints
851
+ - Diagonal edges are prominent
852
+ - Historical comparison or educational purposes
853
+
854
+ ---
855
+
856
+ #### 3. Common Pitfalls and Solutions
857
+
858
+ **Problem: Too many edges detected**
859
+ - **Cause:** Low threshold, high noise, texture
860
+ - **Solution:**
861
+ - Increase threshold (Canny)
862
+ - Pre-smooth more aggressively
863
+ - Use morphological opening to remove small responses
864
+
865
+ **Problem: Missing important edges**
866
+ - **Cause:** High threshold, low contrast, blurred image
867
+ - **Solution:**
868
+ - Decrease threshold
869
+ - Apply histogram equalization
870
+ - Try multi-scale detection (multiple $\\sigma$ values)
871
+
872
+ **Problem: Thick, messy edges**
873
+ - **Cause:** Not using non-maximum suppression
874
+ - **Solution:**
875
+ - Use Canny instead of raw gradient magnitude
876
+ - Implement custom NMS if using Sobel/Prewitt
877
+
878
+ **Problem: Disconnected edge segments**
879
+ - **Cause:** Threshold too high, gaps in actual edges
880
+ - **Solution:**
881
+ - Lower threshold (especially Canny's low threshold)
882
+ - Apply morphological closing
883
+ - Use probabilistic Hough transform to connect segments
884
+
885
+ ---
886
+
887
+ #### 4. Domain-Specific Considerations
888
+
889
+ **Medical Imaging:**
890
+ - Often low contrast, high noise
891
+ - Canny with careful threshold tuning
892
+ - Multi-scale approaches (LoG pyramids)
893
+ - Consider anisotropic diffusion for pre-processing
894
+
895
+ **Natural Images (Photography):**
896
+ - Complex scenes, varied lighting
897
+ - Canny generally works well
898
+ - May need semantic segmentation to identify "important" edges
899
+
900
+ **Document Analysis:**
901
+ - High contrast, sharp edges
902
+ - Simple methods (Sobel, Roberts) often sufficient
903
+ - Adaptive thresholding for varying illumination
904
+
905
+ **Industrial Inspection:**
906
+ - Controlled environment, consistent lighting
907
+ - Fast methods preferred (Sobel)
908
+ - Template matching often combined with edges
909
+
910
+ **Autonomous Driving:**
911
+ - Real-time requirements
912
+ - Lane detection: focused edge detection in ROI
913
+ - Modern systems use CNN-based edge detection
914
+
915
+ ---
916
+
917
+ #### 5. Computational Performance
918
+
919
+ **Runtime Complexity (for NΓ—N image):**
920
+ - Roberts, Prewitt, Sobel: O(NΒ²) β€” single-pass convolution
921
+ - Laplacian: O(NΒ²) β€” single-pass convolution
922
+ - Canny: O(NΒ²) but with higher constant factor due to multi-stage pipeline
923
+
924
+ **Memory Considerations:**
925
+ - Gradient methods: 2-3Γ— image memory (for Gx, Gy, magnitude)
926
+ - Canny: 3-4Γ— image memory (gradients + intermediate stages)
927
+
928
+ **Optimization Strategies:**
929
+ - Use separable filters when possible (Sobel, Prewitt)
930
+ - Implement on GPU for real-time applications
931
+ - Consider approximate methods for very large images
932
+ - Use integral images for multi-scale LoG
933
+
934
+ ---
935
+
936
+ #### 6. Beyond Classical Methods
937
+
938
+ **Learning-Based Edge Detection:**
939
+ - Structured Edge Detection (SED): Random forests on local patches
940
+ - Holistically-Nested Edge Detection (HED): Deep CNN
941
+ - Advantages: Context-aware, learns from data
942
+ - Disadvantages: Requires training data, computationally intensive
943
+
944
+ **Multi-Scale Approaches:**
945
+ - Canny at multiple scales, combine results
946
+ - Laplacian of Gaussian pyramid
947
+ - Scale-space theory (Lindeberg)
948
+
949
+ **Oriented Edge Detection:**
950
+ - Steerable filters: efficient computation of gradients at multiple orientations
951
+ - Gabor filters: detect edges at specific scales and orientations
952
+ - Useful for texture analysis and orientation-dependent tasks
953
+
954
+ ---
955
+
956
+ #### 7. Evaluation Metrics
957
+
958
+ **Quantitative Evaluation (when ground truth available):**
959
+ - **Precision**: fraction of detected edges that are true edges
960
+ - **Recall**: fraction of true edges that are detected
961
+ - **F-measure**: harmonic mean of precision and recall
962
+ - **Localization error**: distance between detected and true edge pixels
963
+
964
+ **Qualitative Evaluation:**
965
+ - Visual inspection: Are important edges detected?
966
+ - Downstream task performance: Does edge quality improve final result?
967
+
968
+ ---
969
+
970
+ ### Take-Home Messages
971
+
972
+ 1. **No Universal Best Method**: Choice depends on application requirements
973
+ 2. **Pre-processing Matters**: Clean input β†’ clean edges
974
+ 3. **Thresholds Are Critical**: Tune based on your specific images
975
+ 4. **Computational Complexity**: Simple doesn't mean inferior
976
+ 5. **Edge Detection Is a Means, Not an End**: Always consider the downstream task
977
+
978
+ **The Ultimate "Aha!" Moment:**
979
+ Edge detection transforms high-dimensional pixel data into sparse, interpretable structures.
980
+ This dimensionality reductionβ€”from millions of pixels to thousands of edge pointsβ€”is what makes
981
+ computer vision computationally tractable. Understanding edges is understanding how machines
982
+ "see" structure in the visual world.
983
+ """)
984
+
985
+ # Footer
986
+ st.markdown("---")
987
+ st.markdown("""
988
+ <small>
989
+ Educational Demo for Image Analysis Courses |
990
+ Built with Streamlit and OpenCV |
991
+ <a href="https://github.com/ubern-image-analysis/edge-detection" target="_blank">View Source</a>
992
+ </small>
993
+ """, unsafe_allow_html=True)
994
+
995
+
996
+ if __name__ == "__main__":
997
+ main_loop()
deploy.sh ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Deployment script for Hugging Face Spaces
4
+
5
+ set -e
6
+
7
+ echo "πŸš€ Deploying Edge Detection Demo to Hugging Face Spaces"
8
+ echo "========================================================"
9
+ echo ""
10
+
11
+ # Check if git is initialized
12
+ if [ ! -d ".git" ]; then
13
+ echo "πŸ“¦ Initializing git repository..."
14
+ git init
15
+ git add .
16
+ git commit -m "Initial commit: Edge Detection Demo"
17
+ fi
18
+
19
+ # Get Space information
20
+ echo "Please provide your Hugging Face Space details:"
21
+ echo ""
22
+ read -p "πŸ“ Space name (e.g., username/edge-detection-demo): " SPACE_NAME
23
+
24
+ if [ -z "$SPACE_NAME" ]; then
25
+ echo "❌ Space name cannot be empty"
26
+ exit 1
27
+ fi
28
+
29
+ SPACE_URL="https://huggingface.co/spaces/$SPACE_NAME"
30
+
31
+ echo ""
32
+ echo "πŸ”— Space URL: $SPACE_URL"
33
+ echo ""
34
+
35
+ # Check if remote already exists
36
+ if git remote | grep -q "hf"; then
37
+ echo "πŸ“ Remote 'hf' already exists, updating URL..."
38
+ git remote set-url hf "$SPACE_URL"
39
+ else
40
+ echo "πŸ“ Adding Hugging Face remote..."
41
+ git remote add hf "$SPACE_URL"
42
+ fi
43
+
44
+ echo ""
45
+ echo "πŸ“¦ Committing changes..."
46
+
47
+ # Add all files
48
+ git add .
49
+
50
+ # Check if there are changes to commit
51
+ if git diff --staged --quiet; then
52
+ echo "ℹ️ No changes to commit."
53
+ else
54
+ read -p "πŸ’¬ Enter commit message (default: 'Update demo'): " COMMIT_MSG
55
+ COMMIT_MSG=${COMMIT_MSG:-"Update demo"}
56
+ git commit -m "$COMMIT_MSG"
57
+ fi
58
+
59
+ echo ""
60
+ echo "πŸš€ Pushing to Hugging Face Spaces..."
61
+ git push hf main
62
+
63
+ echo ""
64
+ echo "βœ… Deployment complete!"
65
+ echo "🌐 Your Space will be available at: $SPACE_URL"
66
+ echo ""
67
+ echo "⏱️ Note: It may take a few minutes for the Space to build and start."
packages.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ libgl1
2
+ libglib2.0-0
pyproject.toml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=45", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "edge-detection-demo"
7
+ version = "1.0.0"
8
+ description = "Interactive educational demo for edge detection algorithms"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Uni Bern Intro to Image Analysis Course"}
14
+ ]
15
+ keywords = ["edge-detection", "computer-vision", "image-processing", "education", "streamlit"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Education",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Topic :: Scientific/Engineering :: Image Processing",
23
+ ]
24
+
25
+ dependencies = [
26
+ "numpy>=1.24.0",
27
+ "opencv-python-headless>=4.8.0",
28
+ "streamlit>=1.39.0",
29
+ "Pillow>=10.0.0",
30
+ "huggingface-hub>=0.19.0",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/ubern-image-analysis/edge-detection"
35
+ Repository = "https://github.com/ubern-image-analysis/edge-detection"
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ numpy>=1.24.0
2
+ opencv-python-headless>=4.8.0
3
+ streamlit>=1.39.0
4
+ Pillow>=10.0.0
5
+ huggingface-hub>=0.19.0
run_simple.sh ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Simple script to run the Streamlit app locally
4
+
5
+ echo "πŸ” Starting Edge Detection Demo..."
6
+ echo ""
7
+
8
+ # Check if virtual environment exists
9
+ if [ ! -d "venv" ]; then
10
+ echo "πŸ“¦ No virtual environment found. Creating one..."
11
+ python3 -m venv venv
12
+ fi
13
+
14
+ # Activate virtual environment
15
+ echo "πŸ”§ Activating virtual environment..."
16
+ source venv/bin/activate
17
+
18
+ # Install requirements if needed
19
+ if [ ! -f "venv/.requirements_installed" ]; then
20
+ echo "πŸ“₯ Installing dependencies..."
21
+ pip install -r requirements.txt
22
+ touch venv/.requirements_installed
23
+ else
24
+ echo "βœ… Dependencies already installed"
25
+ fi
26
+
27
+ # Run the app
28
+ echo ""
29
+ echo "πŸš€ Launching Streamlit app..."
30
+ echo " Open your browser to http://localhost:8501"
31
+ echo ""
32
+ streamlit run app.py
setup.sh ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Setup script for local development
4
+
5
+ set -e
6
+
7
+ echo "πŸ”§ Setting up Edge Detection Demo"
8
+ echo "=================================="
9
+ echo ""
10
+
11
+ # Check Python version
12
+ echo "🐍 Checking Python version..."
13
+ PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
14
+ echo " Found Python $PYTHON_VERSION"
15
+
16
+ REQUIRED_VERSION="3.11"
17
+ if [ "$(printf '%s\n' "$REQUIRED_VERSION" "$PYTHON_VERSION" | sort -V | head -n1)" != "$REQUIRED_VERSION" ]; then
18
+ echo "⚠️ Warning: Python $REQUIRED_VERSION or higher is recommended."
19
+ fi
20
+
21
+ echo ""
22
+
23
+ # Create virtual environment
24
+ if [ -d "venv" ]; then
25
+ echo "πŸ“¦ Virtual environment already exists."
26
+ read -p " Do you want to recreate it? (y/N): " RECREATE
27
+ if [ "$RECREATE" = "y" ] || [ "$RECREATE" = "Y" ]; then
28
+ echo " Removing old virtual environment..."
29
+ rm -rf venv
30
+ echo " Creating new virtual environment..."
31
+ python3 -m venv venv
32
+ fi
33
+ else
34
+ echo "πŸ“¦ Creating virtual environment..."
35
+ python3 -m venv venv
36
+ fi
37
+
38
+ # Activate virtual environment
39
+ echo "πŸ”§ Activating virtual environment..."
40
+ source venv/bin/activate
41
+
42
+ # Upgrade pip
43
+ echo "⬆️ Upgrading pip..."
44
+ pip install --upgrade pip > /dev/null 2>&1
45
+
46
+ # Install requirements
47
+ echo "πŸ“₯ Installing requirements..."
48
+ pip install -r requirements.txt
49
+
50
+ echo ""
51
+ echo "βœ… Setup complete!"
52
+ echo ""
53
+ echo "πŸ“š Next steps:"
54
+ echo " 1. Run './run_simple.sh' to start the demo locally"
55
+ echo " 2. Or activate the environment: 'source venv/bin/activate'"
56
+ echo " 3. Then run: 'streamlit run app.py'"
57
+ echo ""
58
+ echo "πŸš€ To deploy to Hugging Face Spaces:"
59
+ echo " 1. Create a Space at https://huggingface.co/new-space"
60
+ echo " 2. Run './deploy.sh' and follow the prompts"
test_setup.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Quick test script to verify the edge detection demo setup
4
+ """
5
+
6
+ import sys
7
+
8
+ def test_imports():
9
+ """Test that all required packages can be imported."""
10
+ print("πŸ§ͺ Testing package imports...")
11
+
12
+ packages = [
13
+ ('numpy', 'NumPy'),
14
+ ('cv2', 'OpenCV'),
15
+ ('streamlit', 'Streamlit'),
16
+ ('PIL', 'Pillow'),
17
+ ('huggingface_hub', 'HuggingFace Hub'),
18
+ ]
19
+
20
+ failed = []
21
+ for module, name in packages:
22
+ try:
23
+ __import__(module)
24
+ print(f" βœ… {name}")
25
+ except ImportError as e:
26
+ print(f" ❌ {name}: {e}")
27
+ failed.append(name)
28
+
29
+ return len(failed) == 0, failed
30
+
31
+
32
+ def test_app_structure():
33
+ """Test that app.py has the expected structure."""
34
+ print("\nπŸ“ Testing app structure...")
35
+
36
+ try:
37
+ import app
38
+
39
+ # Check for key functions
40
+ functions = [
41
+ 'load_sample_image',
42
+ 'generate_sample_image',
43
+ 'apply_sobel_filter',
44
+ 'apply_prewitt_filter',
45
+ 'apply_roberts_filter',
46
+ 'apply_laplacian_filter',
47
+ 'apply_canny_edge_detector',
48
+ 'compute_gradient_direction',
49
+ 'create_gradient_visualization',
50
+ 'main_loop',
51
+ ]
52
+
53
+ failed = []
54
+ for func in functions:
55
+ if hasattr(app, func):
56
+ print(f" βœ… Function '{func}' found")
57
+ else:
58
+ print(f" ❌ Function '{func}' missing")
59
+ failed.append(func)
60
+
61
+ return len(failed) == 0, failed
62
+
63
+ except Exception as e:
64
+ print(f" ❌ Error loading app: {e}")
65
+ return False, [str(e)]
66
+
67
+
68
+ def test_edge_detection():
69
+ """Test basic edge detection functions."""
70
+ print("\nβš™οΈ Testing edge detection...")
71
+
72
+ try:
73
+ import app
74
+ import numpy as np
75
+
76
+ # Create a simple test image
77
+ test_img = np.random.randint(0, 256, (128, 128), dtype=np.uint8)
78
+
79
+ # Test Sobel
80
+ edges, gx, gy = app.apply_sobel_filter(test_img, ksize=3)
81
+ assert edges.shape == test_img.shape, "Sobel output shape mismatch"
82
+ print(f" βœ… Sobel filter works")
83
+
84
+ # Test Prewitt
85
+ edges, gx, gy = app.apply_prewitt_filter(test_img)
86
+ assert edges.shape == test_img.shape, "Prewitt output shape mismatch"
87
+ print(f" βœ… Prewitt filter works")
88
+
89
+ # Test Roberts
90
+ edges, gx, gy = app.apply_roberts_filter(test_img)
91
+ print(f" βœ… Roberts filter works")
92
+
93
+ # Test Laplacian
94
+ edges = app.apply_laplacian_filter(test_img, ksize=3)
95
+ assert edges.shape == test_img.shape, "Laplacian output shape mismatch"
96
+ print(f" βœ… Laplacian filter works")
97
+
98
+ # Test Canny
99
+ edges = app.apply_canny_edge_detector(test_img, 50, 150)
100
+ assert edges.shape == test_img.shape, "Canny output shape mismatch"
101
+ print(f" βœ… Canny edge detector works")
102
+
103
+ # Test gradient visualization
104
+ viz = app.create_gradient_visualization(gx, gy)
105
+ print(f" βœ… Gradient visualization works")
106
+
107
+ return True, []
108
+
109
+ except Exception as e:
110
+ print(f" ❌ Error: {e}")
111
+ import traceback
112
+ traceback.print_exc()
113
+ return False, [str(e)]
114
+
115
+
116
+ def main():
117
+ """Run all tests."""
118
+ print("=" * 60)
119
+ print("πŸ” Edge Detection Demo - Test Suite")
120
+ print("=" * 60)
121
+
122
+ all_passed = True
123
+
124
+ # Test 1: Imports
125
+ passed, failed = test_imports()
126
+ if not passed:
127
+ print(f"\n❌ Import test failed. Missing packages: {', '.join(failed)}")
128
+ print("\nπŸ’‘ Run: pip install -r requirements.txt")
129
+ all_passed = False
130
+
131
+ # Test 2: App structure
132
+ if passed: # Only run if imports work
133
+ passed, failed = test_app_structure()
134
+ if not passed:
135
+ print(f"\n❌ App structure test failed.")
136
+ all_passed = False
137
+
138
+ # Test 3: Edge detection
139
+ if passed:
140
+ passed, failed = test_edge_detection()
141
+ if not passed:
142
+ print(f"\n❌ Edge detection test failed.")
143
+ all_passed = False
144
+
145
+ # Summary
146
+ print("\n" + "=" * 60)
147
+ if all_passed:
148
+ print("βœ… All tests passed! Ready to run the demo.")
149
+ print("\nπŸš€ Start with: ./run_simple.sh")
150
+ print(" Or: make run")
151
+ print(" Or: streamlit run app.py")
152
+ return 0
153
+ else:
154
+ print("❌ Some tests failed. Please fix the issues above.")
155
+ return 1
156
+
157
+
158
+ if __name__ == "__main__":
159
+ sys.exit(main())