AKA Math commited on
Commit
c7e9899
Β·
1 Parent(s): 2174bb6

updated UI

Browse files
Files changed (12) hide show
  1. .gitignore +38 -2
  2. .python-version +1 -0
  3. CONTRIBUTING.md +273 -0
  4. Makefile +82 -0
  5. QUICKSTART.md +99 -0
  6. README.md +105 -12
  7. app.py +325 -76
  8. deploy.sh +69 -0
  9. pyproject.toml +21 -0
  10. requirements.txt +1 -0
  11. run_simple.sh +30 -0
  12. setup.sh +73 -0
.gitignore CHANGED
@@ -1,2 +1,38 @@
1
- .venv/*
2
- /.streamlit/secrets.toml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+
8
+ # Virtual environments
9
+ .venv/
10
+ venv/
11
+ ENV/
12
+ env/
13
+
14
+ # IDEs
15
+ .vscode/
16
+ .idea/
17
+ *.swp
18
+ *.swo
19
+ *~
20
+ .DS_Store
21
+
22
+ # Streamlit
23
+ .streamlit/secrets.toml
24
+ .streamlit/config.toml
25
+
26
+ # uv
27
+ .uv/
28
+ uv.lock
29
+
30
+ # Testing
31
+ .pytest_cache/
32
+ .coverage
33
+ htmlcov/
34
+
35
+ # Distribution / packaging
36
+ dist/
37
+ build/
38
+ *.egg-info/
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.11.9
CONTRIBUTING.md ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CONTRIBUTING.md
2
+
3
+ ## πŸ› οΈ Development Setup
4
+
5
+ This project uses [uv](https://github.com/astral-sh/uv) for fast, reliable Python package management.
6
+
7
+ ### Prerequisites
8
+
9
+ - Python 3.11 or higher
10
+ - Git
11
+
12
+ ### Quick Start
13
+
14
+ 1. **Clone the repository**
15
+ ```bash
16
+ git clone https://github.com/amithjkamath/template-matching.git
17
+ cd template-matching
18
+ ```
19
+
20
+ 2. **Run setup**
21
+ ```bash
22
+ ./setup.sh
23
+ ```
24
+
25
+ Or if you prefer using Make:
26
+ ```bash
27
+ make setup
28
+ ```
29
+
30
+ 3. **Run the app locally**
31
+ ```bash
32
+ ./run_local.sh
33
+ # or
34
+ make run
35
+ ```
36
+
37
+ ### Manual Setup
38
+
39
+ If you prefer to set things up manually:
40
+
41
+ 1. **Install uv** (if not already installed)
42
+ ```bash
43
+ curl -LsSf https://astral.sh/uv/install.sh | sh
44
+ ```
45
+
46
+ 2. **Install dependencies**
47
+ ```bash
48
+ uv sync --no-build-isolation
49
+ ```
50
+
51
+ 3. **Run the app**
52
+ ```bash
53
+ uv run --no-build-isolation streamlit run app.py
54
+ ```
55
+
56
+ ### Alternative Simple Method
57
+
58
+ If you encounter build-related issues, you can use a simpler approach:
59
+
60
+ ```bash
61
+ # Install dependencies
62
+ uv pip install -r requirements.txt
63
+
64
+ # Run the app
65
+ uv run streamlit run app.py
66
+ ```
67
+
68
+ ## πŸ“‹ Available Commands
69
+
70
+ ### Using Shell Scripts
71
+
72
+ - `./setup.sh` - Initial setup (install uv and dependencies)
73
+ - `./run_local.sh` - Run the app locally
74
+ - `./deploy.sh` - Deploy to HuggingFace Spaces
75
+
76
+ ### Using Makefile
77
+
78
+ - `make help` - Show all available commands
79
+ - `make setup` - Initial setup
80
+ - `make install` - Install/update dependencies
81
+ - `make run` - Run the app locally
82
+ - `make deploy` - Deploy to HuggingFace
83
+ - `make clean` - Clean up cache and temporary files
84
+ - `make format` - Format code (requires black)
85
+ - `make lint` - Run linting checks (requires ruff)
86
+ - `make update` - Update all dependencies
87
+ - `make info` - Show project information
88
+
89
+ ## πŸ§ͺ Testing Locally
90
+
91
+ Before deploying, always test your changes locally:
92
+
93
+ 1. Run the app: `./run_local.sh`
94
+ 2. Open your browser to http://localhost:8501
95
+ 3. Test all interactive features
96
+ 4. Check console for any errors
97
+
98
+ ## πŸš€ Deployment Process
99
+
100
+ The app is deployed to HuggingFace Spaces. To deploy:
101
+
102
+ 1. **Ensure all changes are committed**
103
+ ```bash
104
+ git status
105
+ git add .
106
+ git commit -m "Your commit message"
107
+ ```
108
+
109
+ 2. **Deploy**
110
+ ```bash
111
+ ./deploy.sh
112
+ # or
113
+ make deploy
114
+ ```
115
+
116
+ The script will:
117
+ - Check for uncommitted changes
118
+ - Push to the remote repository
119
+ - HuggingFace Spaces will automatically rebuild
120
+
121
+ ## πŸ“¦ Dependency Management
122
+
123
+ ### Adding a new dependency
124
+
125
+ 1. **Add to pyproject.toml**
126
+ ```toml
127
+ dependencies = [
128
+ "new-package>=1.0.0",
129
+ ...
130
+ ]
131
+ ```
132
+
133
+ 2. **Update requirements.txt** (for HuggingFace Spaces)
134
+ ```
135
+ new-package>=1.0.0
136
+ ```
137
+
138
+ 3. **Sync dependencies**
139
+ ```bash
140
+ uv sync --no-build-isolation
141
+ # or
142
+ make install
143
+ ```
144
+
145
+ ### Updating dependencies
146
+
147
+ ```bash
148
+ uv sync --upgrade --no-build-isolation
149
+ # or
150
+ make update
151
+ ```
152
+
153
+ ## 🎨 Code Style
154
+
155
+ - Follow PEP 8 guidelines
156
+ - Use meaningful variable names
157
+ - Add docstrings to functions
158
+ - Keep functions focused and small
159
+
160
+ ### Formatting (optional)
161
+
162
+ Install formatting tools:
163
+ ```bash
164
+ uv add --dev black ruff
165
+ ```
166
+
167
+ Format code:
168
+ ```bash
169
+ make format
170
+ ```
171
+
172
+ Lint code:
173
+ ```bash
174
+ make lint
175
+ ```
176
+
177
+ ## πŸ“ Making Changes
178
+
179
+ 1. Create a new branch for your feature
180
+ ```bash
181
+ git checkout -b feature/your-feature-name
182
+ ```
183
+
184
+ 2. Make your changes
185
+
186
+ 3. Test locally
187
+ ```bash
188
+ make run
189
+ ```
190
+
191
+ 4. Commit your changes
192
+ ```bash
193
+ git add .
194
+ git commit -m "Description of changes"
195
+ ```
196
+
197
+ 5. Push and create a pull request (if working with others)
198
+ ```bash
199
+ git push origin feature/your-feature-name
200
+ ```
201
+
202
+ 6. Or merge to main and deploy
203
+ ```bash
204
+ git checkout main
205
+ git merge feature/your-feature-name
206
+ make deploy
207
+ ```
208
+
209
+ ## πŸ› Troubleshooting
210
+
211
+ ### Module not found error
212
+
213
+ If you see `ModuleNotFoundError`:
214
+ ```bash
215
+ uv sync --no-build-isolation # or make install
216
+ # Alternative:
217
+ uv pip install -r requirements.txt
218
+ ```
219
+
220
+ ### Build backend errors
221
+
222
+ The project is configured to skip build steps. If you still encounter hatchling/build errors:
223
+ ```bash
224
+ ./run_simple.sh
225
+ # or use pip directly:
226
+ uv pip install -r requirements.txt
227
+ uv run streamlit run app.py
228
+ ```
229
+
230
+ ### Port already in use
231
+
232
+ If port 8501 is already in use:
233
+ ```bash
234
+ uv run --no-build-isolation streamlit run app.py --server.port 8502
235
+ ```
236
+
237
+ ### uv not found
238
+
239
+ Make sure uv is in your PATH. After installation, restart your terminal or run:
240
+ ```bash
241
+ source ~/.cargo/env
242
+ ```
243
+
244
+ ## πŸ“š Project Structure
245
+
246
+ ```
247
+ template-matching/
248
+ β”œβ”€β”€ app.py # Main Streamlit application
249
+ β”œβ”€β”€ requirements.txt # Dependencies for HuggingFace Spaces
250
+ β”œβ”€β”€ pyproject.toml # Python project configuration (uv)
251
+ β”œβ”€β”€ Dockerfile # Docker configuration for HF Spaces
252
+ β”œβ”€β”€ packages.txt # System packages for HF Spaces
253
+ β”œβ”€β”€ README.md # User-facing documentation
254
+ β”œβ”€β”€ CONTRIBUTING.md # This file - developer documentation
255
+ β”œβ”€β”€ LICENSE # MIT License
256
+ β”œβ”€β”€ setup.sh # Setup script
257
+ β”œβ”€β”€ run_local.sh # Local run script
258
+ β”œβ”€β”€ deploy.sh # Deployment script
259
+ β”œβ”€β”€ Makefile # Make commands for convenience
260
+ └── .gitignore # Git ignore patterns
261
+ ```
262
+
263
+ ## 🀝 Contributing Guidelines
264
+
265
+ 1. Keep the educational focus in mind
266
+ 2. Maintain interactive and engaging features
267
+ 3. Test thoroughly before deploying
268
+ 4. Update documentation for any new features
269
+ 5. Keep dependencies minimal and up-to-date
270
+
271
+ ## πŸ“„ License
272
+
273
+ This project is licensed under the MIT License - see the LICENSE file for details.
Makefile ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Makefile for Template Matching Demo
2
+ .PHONY: help setup run deploy clean test format lint install
3
+
4
+ # Default target
5
+ .DEFAULT_GOAL := help
6
+
7
+ # Colors
8
+ BLUE := \033[0;34m
9
+ GREEN := \033[0;32m
10
+ YELLOW := \033[1;33m
11
+ NC := \033[0m
12
+
13
+ help: ## Show this help message
14
+ @echo "$(BLUE)Template Matching Demo - Available Commands$(NC)"
15
+ @echo ""
16
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(GREEN)%-15s$(NC) %s\n", $$1, $$2}'
17
+ @echo ""
18
+
19
+ setup: ## Initial setup - install uv and dependencies
20
+ @echo "$(BLUE)πŸ”§ Running setup...$(NC)"
21
+ @chmod +x setup.sh
22
+ @./setup.sh
23
+
24
+ install: ## Install/update dependencies using uv
25
+ @echo "$(BLUE)πŸ“¦ Installing dependencies...$(NC)"
26
+ @uv sync --no-build-isolation
27
+ @echo "$(GREEN)βœ… Dependencies installed$(NC)"
28
+
29
+ run: ## Run the app locally
30
+ @chmod +x run_local.sh
31
+ @./run_local.sh
32
+
33
+ deploy: ## Deploy to HuggingFace Spaces
34
+ @chmod +x deploy.sh
35
+ @./deploy.sh
36
+
37
+ clean: ## Clean up cache and temporary files
38
+ @echo "$(BLUE)🧹 Cleaning up...$(NC)"
39
+ @find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
40
+ @find . -type f -name "*.pyc" -delete 2>/dev/null || true
41
+ @find . -type f -name "*.pyo" -delete 2>/dev/null || true
42
+ @find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
43
+ @find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
44
+ @echo "$(GREEN)βœ… Cleanup complete$(NC)"
45
+
46
+ format: ## Format code with black and isort (if installed)
47
+ @echo "$(BLUE)🎨 Formatting code...$(NC)"
48
+ @if command -v black > /dev/null; then \
49
+ uv run black app.py; \
50
+ else \
51
+ echo "$(YELLOW)⚠️ black not installed, skipping$(NC)"; \
52
+ fi
53
+
54
+ lint: ## Run linting checks
55
+ @echo "$(BLUE)πŸ” Running linting...$(NC)"
56
+ @if command -v ruff > /dev/null; then \
57
+ uv run ruff check app.py; \
58
+ else \
59
+ echo "$(YELLOW)⚠️ ruff not installed, skipping$(NC)"; \
60
+ fi
61
+
62
+ test: ## Run tests (if any)
63
+ @echo "$(BLUE)πŸ§ͺ Running tests...$(NC)"
64
+ @echo "$(YELLOW)⚠️ No tests configured yet$(NC)"
65
+
66
+ check: format lint ## Run all checks (format + lint)
67
+ @echo "$(GREEN)βœ… All checks complete$(NC)"
68
+
69
+ update: ## Update all dependencies
70
+ @echo "$(BLUE)⬆️ Updating dependencies...$(NC)"
71
+ @uv sync --upgrade --no-build-isolation
72
+ @echo "$(GREEN)βœ… Dependencies updated$(NC)"
73
+
74
+ info: ## Show project information
75
+ @echo "$(BLUE)πŸ“Š Project Information$(NC)"
76
+ @echo ""
77
+ @echo " Name: Template Matching Demo"
78
+ @echo " Python: $$(python3 --version | awk '{print $$2}')"
79
+ @echo " UV: $$(uv --version 2>/dev/null || echo 'not installed')"
80
+ @echo " Repository: https://github.com/amithjkamath/template-matching"
81
+ @echo " HF Space: https://huggingface.co/spaces/amithjkamath/template-matching"
82
+ @echo ""
QUICKSTART.md ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # πŸš€ Quick Start Guide
2
+
3
+ ## First Time Setup
4
+
5
+ 1. **Run the setup script:**
6
+ ```bash
7
+ ./setup.sh
8
+ ```
9
+
10
+ This will:
11
+ - Check Python version (3.11+ required)
12
+ - Install uv package manager if needed
13
+ - Install all dependencies
14
+ - Set script permissions
15
+
16
+ ## Running the App
17
+
18
+ ### Simple Script
19
+ If you encounter build issues, use the simpler version:
20
+ ```bash
21
+ ./run_simple.sh
22
+ ```
23
+
24
+ ### Option 3: Make Command
25
+ ```bash
26
+ make run
27
+ ```
28
+
29
+ ### Option 4: Direct Command
30
+ ```bash
31
+ uv run --no-build-isolation streamlit run app.py
32
+ ```
33
+
34
+ The app will open in your browser at `http://localhost:8501`
35
+
36
+ ## Deploying to HuggingFace Spaces
37
+
38
+ ### Option 1: Shell Script
39
+ ```bash
40
+ ./deploy.sh
41
+ ```
42
+
43
+ ### Option 2: Make Command
44
+ ```bash
45
+ make deploy
46
+ ```
47
+
48
+ The script will:
49
+ - Check for uncommitted changes
50
+ - Prompt you to commit if needed
51
+ - Push to the repository
52
+ - HuggingFace Spaces will automatically rebuild
53
+
54
+ ## Troubleshooting
55
+
56
+ ### "Build backend error" or hatchling issues
57
+ The project is now configured to skip the build step. If you still see build errors:
58
+ ```bash
59
+ ./run_simple.sh
60
+ # or
61
+ uv pip install -r requirements.txt
62
+ uv run streamlit run app.py
63
+ ```
64
+
65
+ ### "Module not found" error
66
+ ```bash
67
+ uv sync --no-build-isolation
68
+ # or
69
+ uv pip install -r requirements.txt
70
+ ```
71
+
72
+ ### Port 8501 already in use
73
+ ```bash
74
+ uv run --no-build-isolation streamlit run app.py --server.port 8502
75
+ ```
76
+
77
+ ### Script permission denied
78
+ ```bash
79
+ chmod +x setup.sh run_local.sh run_simple.sh deploy.sh
80
+ ```
81
+
82
+ ## Available Commands
83
+
84
+ Run `make help` to see all available commands:
85
+
86
+ ```bash
87
+ make help # Show all commands
88
+ make setup # Initial setup
89
+ make install # Install dependencies
90
+ make run # Run locally
91
+ make deploy # Deploy to HuggingFace
92
+ make clean # Clean cache files
93
+ make update # Update dependencies
94
+ make info # Show project info
95
+ ```
96
+
97
+ ## Need Help?
98
+
99
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed documentation.
README.md CHANGED
@@ -6,27 +6,120 @@ colorTo: green
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
- short_description: Demonstration of template matching with waldo
10
  ---
11
 
12
- # template-matching
13
 
14
- This is a demonstration of how template matching works by computing correlation between the search space and the template.
15
 
16
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
17
 
18
- ## What is Template Matching?
19
 
20
- Template Matching is a simple method where the location of a template in a scene is computed by sliding it all across the scene and computing a similarity.
 
 
21
 
22
- ## What situations could this method be applied to?
23
 
24
- This method works best when the template is exactly replicated in the scene - at the same scale (size), not rotated or sheared.
25
 
26
- ## When would it not work?
27
 
28
- If the template is not exactly available in the scene (when there are size or rotation or other transformation changes), this method could fail catastrophically.
 
 
 
 
29
 
30
- ## Are there better similarity measures than correlation?
31
 
32
- Point based feature matching methods like SIFT, SURF, MSER and so on would work better in cases where the relative pose of the template with respect to the scene cannot be controlled.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  sdk: docker
7
  pinned: false
8
  license: mit
9
+ short_description: Interactive demonstration of template matching with Waldo
10
  ---
11
 
12
+ # πŸ” Template Matching Demo
13
 
14
+ An interactive educational tool that demonstrates how template matching works using computer vision. Learn by doing - click and drag to explore how computers find objects in images!
15
 
16
+ ## 🎯 What is Template Matching?
17
 
18
+ Template Matching is a simple yet powerful computer vision method that finds the location of a template image within a larger scene by:
19
 
20
+ 1. **Sliding** the template across all possible positions in the scene
21
+ 2. **Computing** a similarity score at each position
22
+ 3. **Identifying** the location with the highest similarity score
23
 
24
+ ## πŸš€ Try It Live
25
 
26
+ **[Launch the Interactive Demo](https://huggingface.co/spaces/amithjkamath/template-matching)**
27
 
28
+ ## ✨ Features
29
 
30
+ - πŸ–±οΈ **Interactive Template Placement**: Click anywhere on the image to test template matching
31
+ - πŸ“Š **Real-time Correlation Scores**: See quantitative match scores (0.0-1.0) with color coding
32
+ - πŸ”¬ **Zoomed Comparison View**: Side-by-side visualization of template vs. current patch
33
+ - 🎨 **Educational Heatmaps**: Understand how correlation works across the entire scene
34
+ - 🎯 **Instant Feedback**: Learn through exploration and immediate visual feedback
35
 
36
+ ## πŸ’» Run Locally
37
 
38
+ ### Quick Start
39
+
40
+ ```bash
41
+ # Clone the repository
42
+ git clone https://github.com/amithjkamath/template-matching.git
43
+ cd template-matching
44
+
45
+ # Run setup (installs uv and dependencies)
46
+ ./setup.sh
47
+
48
+ # Run the app
49
+ ./run_local.sh
50
+ ```
51
+
52
+ ### Using Make (Alternative)
53
+
54
+ ```bash
55
+ make setup # Initial setup
56
+ make run # Run the app
57
+ make deploy # Deploy to HuggingFace
58
+ make help # See all commands
59
+ ```
60
+
61
+ ### Manual Setup
62
+
63
+ ```bash
64
+ # Install uv (if not already installed)
65
+ curl -LsSf https://astral.sh/uv/install.sh | sh
66
+
67
+ # Install dependencies
68
+ uv sync
69
+
70
+ # Run the app
71
+ uv run streamlit run app.py
72
+ ```
73
+
74
+ ## πŸ“‹ Requirements
75
+
76
+ - Python 3.11 or higher
77
+ - [uv](https://github.com/astral-sh/uv) package manager (installed by setup.sh)
78
+
79
+ ## πŸ› οΈ Development
80
+
81
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development setup and guidelines.
82
+
83
+ ## πŸ“š What situations could this method be applied to?
84
+
85
+ Template matching works best when:
86
+ - βœ… The template is exactly replicated in the scene
87
+ - βœ… Same scale (size) and orientation
88
+ - βœ… No rotation or geometric transformations
89
+ - βœ… Similar lighting conditions
90
+
91
+ ## ⚠️ When would it not work?
92
+
93
+ Template matching struggles with:
94
+ - ❌ Scale changes (different sizes)
95
+ - ❌ Rotation or perspective changes
96
+ - ❌ Significant lighting/color differences
97
+ - ❌ Partial occlusions
98
+
99
+ ## πŸ”¬ Better Alternatives?
100
+
101
+ For scenarios with transformations, consider feature-based matching methods:
102
+ - **SIFT** (Scale-Invariant Feature Transform)
103
+ - **SURF** (Speeded-Up Robust Features)
104
+ - **ORB** (Oriented FAST and Rotated BRIEF)
105
+ - **AKAZE** (Accelerated-KAZE)
106
+
107
+ These methods are more robust to scale, rotation, and illumination changes.
108
+
109
+ ## 🀝 Contributing
110
+
111
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
112
+
113
+ ## πŸ“„ License
114
+
115
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
116
+
117
+ ## 🎨 Image Attribution
118
+
119
+ Images from "Where's Waldo?" are copyrighted by their original owners and are used here purely for educational purposes.
120
+
121
+ ## πŸ™ Acknowledgments
122
+
123
+ Inspired by:
124
+ - [OpenCV Web App with Streamlit](https://www.loginradius.com/blog/engineering/guest-post/opencv-web-app-with-streamlit/)
125
+ - [Finding Waldo: Feature Matching for OpenCV](https://medium.com/analytics-vidhya/finding-waldo-feature-matching-for-opencv-9bded7f5ab10)
app.py CHANGED
@@ -1,13 +1,16 @@
1
  """
2
  Inspired by https://www.loginradius.com/blog/engineering/guest-post/opencv-web-app-with-streamlit/
3
- and https://medium.com/analytics-vidhya/finding-waldo-feature-matching-for-opencv-9bded7f5ab10
4
  """
 
5
  import numpy as np
6
  import cv2 as cv
7
  import streamlit as st
8
  from huggingface_hub import hf_hub_download
 
9
 
10
 
 
11
  def compute_correlation(scene: np.array, template: np.array):
12
  """
13
  COMPUTE_CORRELATION computes the correlation between the pixels in scene and template
@@ -24,113 +27,359 @@ def compute_correlation(scene: np.array, template: np.array):
24
  return res
25
 
26
 
27
- def main_loop():
28
  """
29
- MAIN_LOOP is the main loop (duh) for this streamlit App.
 
30
  """
31
- st.set_page_config(layout="wide")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- st.title("Template Matching Demo")
34
- st.subheader(
35
- "This app demonstrates how a template matching algorithm works: by sliding the template across the scene!")
36
 
37
- # Load images from Hugging Face dataset
 
 
 
38
  template_path = hf_hub_download(
39
  repo_id="amithjkamath/exampleimages",
40
  filename="waldo-template.jpeg",
41
- repo_type="dataset"
42
  )
43
  template_image = cv.imread(template_path)
44
  template_image = cv.cvtColor(template_image, cv.COLOR_BGR2RGB)
45
 
46
- st.markdown(
47
- "To introduce this method, let's first get introduced to our protagonist - Waldo")
48
-
49
- st.text("Introducing Waldo!")
50
- st.image(template_image, width=100)
51
-
52
- st.markdown(
53
- "Now for the fun bit - can you find Waldo in the scene below? Most of us will take about 20 seconds, if not more!")
54
-
55
  scene_path = hf_hub_download(
56
  repo_id="amithjkamath/exampleimages",
57
  filename="waldo-scene.jpeg",
58
- repo_type="dataset"
59
  )
60
  scene_image = cv.imread(scene_path)
61
  scene_image = cv.cvtColor(scene_image, cv.COLOR_BGR2RGB)
62
 
63
- st.text("Can you find Waldo?")
64
- st.image(scene_image, width=1000)
65
 
 
 
 
 
 
 
 
 
66
  st.markdown(
67
- "Can a computer do better? Certainly. The template matching algorithm is conceptually really simple.")
68
- st.markdown(
69
- "The idea is to hold the template over all possible patches in the scene, and then compute a similarity.")
70
- st.markdown(
71
- "Naturally, the similarity will be the highest when the template matches what's in the patch underneath.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  st.markdown(
73
- "We could then record the location of the maximum similarity, and return it when we have scanned everywhere.")
74
-
75
- corr = compute_correlation(scene_image, template_image)
76
- norm_corr = (corr - corr.min()) / (corr.max() - corr.min())
77
- st.text("Here's the correlation image:")
78
- st.image(norm_corr, width=1000)
79
-
80
- result_image = scene_image.copy()
81
- threshold = 0.6
82
- # finding the values where it exceeds the threshold
83
- loc = np.where(corr >= threshold)
84
- template_shape = template_image.shape[::-1] # 3, W, H
85
- for pt in zip(*loc[::-1]):
86
- # draw rectangle on places where it exceeds threshold
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  cv.rectangle(
88
- result_image, pt, (pt[0] + template_shape[1], pt[1] + template_shape[2]), (0, 255, 0), 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- st.text("Here's the result:")
91
- st.image(result_image, width=1000)
 
 
 
92
 
93
- st.markdown("How does this work? The template slides across the scene, and \
94
- computes the correlation at each location. Use the slider below \
95
- to see how this works!")
 
 
96
 
97
- alpha = st.slider('Move to slide the template', 0.0,
98
- 1.0, value=0.00, step=0.0001)
99
- scene_image = scene_image * 0.25
100
- scene_image = scene_image.astype(np.uint8)
101
 
102
- norm_corr = cv.copyMakeBorder(norm_corr,
103
- template_shape[2]//2, template_shape[2]//2,
104
- template_shape[1]//2, template_shape[1]//2,
105
- cv.BORDER_CONSTANT)
106
- norm_corr = cv.multiply(255.0, norm_corr)
107
- norm_corr = np.dstack((norm_corr, norm_corr, norm_corr))
108
 
109
- out_image = scene_image.copy()
110
- out_shape = out_image.shape # H, W, 3
111
- range_movement = (out_shape[1] - template_shape[1]) * \
112
- (out_shape[0] - template_shape[2])
 
 
 
 
 
 
113
 
114
- absolute_loc = np.int32(alpha * range_movement)
115
- absolute_loc_y = absolute_loc // (out_shape[1] - template_shape[1])
116
- absolute_loc_x = absolute_loc % (out_shape[0] - template_shape[2])
117
- out_image[0:absolute_loc_y, :, :] = norm_corr[0:absolute_loc_y, :, :]
118
 
119
- out_image[absolute_loc_y:(absolute_loc_y + template_shape[2]),
120
- absolute_loc_x:(absolute_loc_x + template_shape[1]), :] = template_image
 
 
 
121
 
122
- if absolute_loc_y > (pt[1] + template_shape[2]):
123
- for pt in zip(*loc[::-1]):
124
- # draw rectangle on places where it exceeds threshold
125
- cv.rectangle(
126
- out_image, pt, (pt[0] + template_shape[1], pt[1] + template_shape[2]), (0, 255, 0), 2)
127
 
128
- st.text("Here's how the correlation is computed:")
129
- st.image(out_image, width=1000)
130
 
131
- st.markdown("Image copyrights for Where is Waldo - fully attributed to original owners. \
132
- It is used here purely for educational purposes.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
 
135
- if __name__ == '__main__':
136
  main_loop()
 
1
  """
2
  Inspired by https://www.loginradius.com/blog/engineering/guest-post/opencv-web-app-with-streamlit/
3
+ and https://medium.com/analytics-vidhya/finding-waldo-feature-matching-for-opencv-9bded7f5ab10
4
  """
5
+
6
  import numpy as np
7
  import cv2 as cv
8
  import streamlit as st
9
  from huggingface_hub import hf_hub_download
10
+ from streamlit_image_coordinates import streamlit_image_coordinates
11
 
12
 
13
+ @st.cache_data
14
  def compute_correlation(scene: np.array, template: np.array):
15
  """
16
  COMPUTE_CORRELATION computes the correlation between the pixels in scene and template
 
27
  return res
28
 
29
 
30
+ def compute_patch_correlation(scene_patch: np.array, template: np.array):
31
  """
32
+ Compute correlation score between a scene patch and template.
33
+ Returns a score between 0 and 1, where 1 is a perfect match.
34
  """
35
+ # Ensure patches are the same size
36
+ if scene_patch.shape != template.shape:
37
+ return 0.0
38
+
39
+ gray_patch = cv.cvtColor(scene_patch, cv.COLOR_BGR2GRAY)
40
+ cv.normalize(gray_patch, gray_patch, 0, 255, cv.NORM_MINMAX)
41
+
42
+ gray_template = cv.cvtColor(template, cv.COLOR_BGR2GRAY)
43
+ cv.normalize(gray_template, gray_template, 0, 255, cv.NORM_MINMAX)
44
+
45
+ # Use normalized cross-correlation
46
+ result = cv.matchTemplate(gray_patch, gray_template, cv.TM_CCOEFF_NORMED)
47
+ return float(result[0, 0]) if result.size > 0 else 0.0
48
+
49
+
50
+ def extract_patch(scene: np.array, x: int, y: int, template_shape):
51
+ """
52
+ Extract a patch from the scene centered at (x, y) with the same size as the template.
53
+ """
54
+ h, w = template_shape[0], template_shape[1]
55
+
56
+ # Calculate patch boundaries
57
+ y_start = max(0, y - h // 2)
58
+ y_end = min(scene.shape[0], y_start + h)
59
+ x_start = max(0, x - w // 2)
60
+ x_end = min(scene.shape[1], x_start + w)
61
+
62
+ # Adjust if we hit boundaries
63
+ if y_end - y_start < h:
64
+ y_start = max(0, y_end - h)
65
+ if x_end - x_start < w:
66
+ x_start = max(0, x_end - w)
67
+
68
+ patch = scene[y_start:y_end, x_start:x_end]
69
+
70
+ # Pad if necessary (edge cases)
71
+ if patch.shape[0] < h or patch.shape[1] < w:
72
+ patch = cv.copyMakeBorder(
73
+ patch,
74
+ 0,
75
+ h - patch.shape[0],
76
+ 0,
77
+ w - patch.shape[1],
78
+ cv.BORDER_CONSTANT,
79
+ value=[0, 0, 0],
80
+ )
81
+
82
+ return patch, (x_start, y_start, x_end, y_end)
83
 
 
 
 
84
 
85
+ @st.cache_resource
86
+ def load_images():
87
+ """Load and cache the template and scene images."""
88
+ # Load template
89
  template_path = hf_hub_download(
90
  repo_id="amithjkamath/exampleimages",
91
  filename="waldo-template.jpeg",
92
+ repo_type="dataset",
93
  )
94
  template_image = cv.imread(template_path)
95
  template_image = cv.cvtColor(template_image, cv.COLOR_BGR2RGB)
96
 
97
+ # Load scene
 
 
 
 
 
 
 
 
98
  scene_path = hf_hub_download(
99
  repo_id="amithjkamath/exampleimages",
100
  filename="waldo-scene.jpeg",
101
+ repo_type="dataset",
102
  )
103
  scene_image = cv.imread(scene_path)
104
  scene_image = cv.cvtColor(scene_image, cv.COLOR_BGR2RGB)
105
 
106
+ return template_image, scene_image
 
107
 
108
+
109
+ def main_loop():
110
+ """
111
+ MAIN_LOOP is the main loop (duh) for this streamlit App.
112
+ """
113
+ st.set_page_config(layout="wide")
114
+
115
+ st.title("πŸ” Interactive Template Matching Demo")
116
  st.markdown(
117
+ """
118
+ Welcome! This app teaches you how **template matching** works - a fundamental computer vision technique.
119
+ You'll learn by doing: click and drag the template around to see how computers "find" objects in images!
120
+ """
121
+ )
122
+
123
+ # Load images (cached)
124
+ template_image, scene_image = load_images()
125
+
126
+ # Introduction
127
+ col1, col2 = st.columns([1, 3])
128
+
129
+ with col1:
130
+ st.markdown("### Meet Waldo πŸ‘‹")
131
+ st.image(template_image, caption="Our template to find")
132
+ st.markdown(
133
+ "**Template size:** {}Γ—{}".format(
134
+ template_image.shape[1], template_image.shape[0]
135
+ )
136
+ )
137
+
138
+ with col2:
139
+ st.markdown("### The Challenge")
140
+ st.markdown(
141
+ """
142
+ Can you spot Waldo in this busy scene? Most people take 20+ seconds!
143
+
144
+ But computers can do it differently. Instead of using intuition, they use **template matching**:
145
+ - Slide the template over every possible position
146
+ - At each position, compute a **similarity score**
147
+ - The highest score reveals where Waldo is!
148
+ """
149
+ )
150
+
151
+ # Show the scene
152
+ st.markdown("---")
153
+ st.markdown("### 🎯 Try It Yourself: Interactive Template Matching")
154
  st.markdown(
155
+ """
156
+ **Instructions:** Click anywhere on the image below to place the template there.
157
+ Watch how the correlation score changes! Can you find where Waldo actually is?
158
+ """
159
+ )
160
+
161
+ # Initialize session state for template position
162
+ if "template_x" not in st.session_state:
163
+ st.session_state.template_x = scene_image.shape[1] // 4
164
+ st.session_state.template_y = scene_image.shape[0] // 4
165
+
166
+ if "last_computed_x" not in st.session_state:
167
+ st.session_state.last_computed_x = st.session_state.template_x
168
+ st.session_state.last_computed_y = st.session_state.template_y
169
+
170
+ if "computed_score" not in st.session_state:
171
+ st.session_state.computed_score = None
172
+
173
+ if "computed_patch" not in st.session_state:
174
+ st.session_state.computed_patch = None
175
+
176
+ # Create interactive image
177
+ col_left, col_right = st.columns([2, 1])
178
+
179
+ with col_left:
180
+ st.markdown("**Click on the image to move the template:**")
181
+
182
+ # Create overlay image with template
183
+ display_image = scene_image.copy()
184
+ t_h, t_w = template_image.shape[0], template_image.shape[1]
185
+
186
+ # Calculate template position (top-left corner)
187
+ x = st.session_state.template_x
188
+ y = st.session_state.template_y
189
+ x_start = max(0, x - t_w // 2)
190
+ y_start = max(0, y - t_h // 2)
191
+ x_end = min(scene_image.shape[1], x_start + t_w)
192
+ y_end = min(scene_image.shape[0], y_start + t_h)
193
+
194
+ # Draw rectangle around template position
195
  cv.rectangle(
196
+ display_image, (x_start, y_start), (x_end, y_end), (255, 255, 0), 3
197
+ )
198
+
199
+ # Overlay semi-transparent template
200
+ overlay = display_image.copy()
201
+ if y_end - y_start == t_h and x_end - x_start == t_w:
202
+ overlay[y_start:y_end, x_start:x_end] = cv.addWeighted(
203
+ overlay[y_start:y_end, x_start:x_end], 0.5, template_image, 0.5, 0
204
+ )
205
+ display_image = cv.addWeighted(display_image, 0.7, overlay, 0.3, 0)
206
+
207
+ # Get click coordinates
208
+ value = streamlit_image_coordinates(display_image, key="scene_image")
209
+
210
+ # Update position only if clicked (value changed)
211
+ if value is not None:
212
+ new_x = value["x"]
213
+ new_y = value["y"]
214
+ # Only update if position actually changed
215
+ if (
216
+ new_x != st.session_state.template_x
217
+ or new_y != st.session_state.template_y
218
+ ):
219
+ st.session_state.template_x = new_x
220
+ st.session_state.template_y = new_y
221
 
222
+ with col_right:
223
+ # Show current position
224
+ st.markdown("### πŸ“ Current Position")
225
+ st.markdown(f"**X:** {st.session_state.template_x}px")
226
+ st.markdown(f"**Y:** {st.session_state.template_y}px")
227
 
228
+ # Check if position has changed since last computation
229
+ position_changed = (
230
+ st.session_state.template_x != st.session_state.last_computed_x
231
+ or st.session_state.template_y != st.session_state.last_computed_y
232
+ )
233
 
234
+ # Button to compute match
235
+ if position_changed:
236
+ st.info("πŸ”„ Position changed! Click below to compute match score.")
 
237
 
238
+ compute_button = st.button(
239
+ "πŸ” Compute Match Score", type="primary", use_container_width=True
240
+ ) # Will be updated to width='stretch' in future
 
 
 
241
 
242
+ # Compute correlation if button clicked or initial load
243
+ if compute_button or st.session_state.computed_score is None:
244
+ with st.spinner("Computing correlation..."):
245
+ # Extract patch and compute correlation
246
+ patch, _ = extract_patch(
247
+ scene_image,
248
+ st.session_state.template_x,
249
+ st.session_state.template_y,
250
+ template_image.shape[:2],
251
+ )
252
 
253
+ score = compute_patch_correlation(patch, template_image)
 
 
 
254
 
255
+ # Store computed values
256
+ st.session_state.computed_score = score
257
+ st.session_state.computed_patch = patch
258
+ st.session_state.last_computed_x = st.session_state.template_x
259
+ st.session_state.last_computed_y = st.session_state.template_y
260
 
261
+ # Display match score with color coding
262
+ st.markdown("### πŸ“Š Match Score")
 
 
 
263
 
264
+ if st.session_state.computed_score is not None:
265
+ score = st.session_state.computed_score
266
 
267
+ # Determine match quality
268
+ if score >= 0.8:
269
+ quality = "πŸŽ‰ Excellent Match!"
270
+ color = "green"
271
+ explanation = "This is very likely the correct location!"
272
+ elif score >= 0.6:
273
+ quality = "βœ… Good Match"
274
+ color = "blue"
275
+ explanation = "Strong similarity, but maybe not perfect."
276
+ elif score >= 0.4:
277
+ quality = "⚠️ Moderate Match"
278
+ color = "orange"
279
+ explanation = "Some similarity, but probably not the right spot."
280
+ else:
281
+ quality = "❌ Poor Match"
282
+ color = "red"
283
+ explanation = "Very low similarity - keep searching!"
284
+
285
+ # Display score with highlighting
286
+ st.markdown(
287
+ f"""
288
+ <div style="background-color: {color}; padding: 20px; border-radius: 10px; text-align: center;">
289
+ <h2 style="color: white; margin: 0;">{score:.3f}</h2>
290
+ <p style="color: white; margin: 5px 0 0 0; font-size: 18px;"><b>{quality}</b></p>
291
+ </div>
292
+ """,
293
+ unsafe_allow_html=True,
294
+ )
295
+
296
+ st.markdown(f"*{explanation}*")
297
+ else:
298
+ st.warning("Click 'Compute Match Score' to analyze this position.")
299
+
300
+ # Show zoomed comparison
301
+ st.markdown("### πŸ”¬ Close-up Comparison")
302
+
303
+ if st.session_state.computed_patch is not None:
304
+ st.markdown("**Template vs Current Patch:**")
305
+
306
+ # Create side-by-side comparison
307
+ comparison = np.hstack([template_image, st.session_state.computed_patch])
308
+ st.image(
309
+ comparison,
310
+ caption="Left: Template | Right: Current patch",
311
+ width="stretch",
312
+ )
313
+ else:
314
+ st.info("Compute match score to see the comparison.")
315
+
316
+ # Educational section: Show the full correlation heatmap
317
+ st.markdown("---")
318
+ st.markdown("### 🧠 How Does the Computer Find Waldo?")
319
+
320
+ with st.expander("Click here to see the full solution!", expanded=False):
321
+ st.markdown(
322
+ """
323
+ The computer doesn't guess - it's systematic! It computes the correlation score at **every possible position**.
324
+ Here's the resulting **correlation heatmap** where brighter areas indicate better matches:
325
+ """
326
+ )
327
+
328
+ corr = compute_correlation(scene_image, template_image)
329
+ norm_corr = (corr - corr.min()) / (corr.max() - corr.min())
330
+
331
+ col1, col2 = st.columns(2)
332
+
333
+ with col1:
334
+ st.image(
335
+ norm_corr,
336
+ caption="Correlation Heatmap (bright = high match)",
337
+ width="stretch",
338
+ )
339
+ st.markdown("Notice the bright spot? That's where Waldo is! 🎯")
340
+
341
+ with col2:
342
+ # Show result with bounding boxes
343
+ result_image = scene_image.copy()
344
+ threshold = 0.6
345
+ loc = np.where(corr >= threshold)
346
+ template_shape = template_image.shape
347
+
348
+ for pt in zip(*loc[::-1]):
349
+ cv.rectangle(
350
+ result_image,
351
+ pt,
352
+ (pt[0] + template_shape[1], pt[1] + template_shape[0]),
353
+ (0, 255, 0),
354
+ 3,
355
+ )
356
+
357
+ st.image(
358
+ result_image,
359
+ caption="Detected locations (green boxes)",
360
+ width="stretch",
361
+ )
362
+ st.markdown("Green boxes show all locations with correlation > 0.6")
363
+
364
+ st.markdown(
365
+ """
366
+ **Key Insight:** Template matching is a brute-force approach that checks every possible location.
367
+ While simple, it's very effective for finding exact or near-exact matches!
368
+ """
369
+ )
370
+
371
+ # Footer
372
+ st.markdown("---")
373
+ st.markdown(
374
+ """
375
+ <small>
376
+ 🎨 Image copyrights for "Where's Waldo?" are fully attributed to original owners.
377
+ Used here purely for educational purposes.
378
+ </small>
379
+ """,
380
+ unsafe_allow_html=True,
381
+ )
382
 
383
 
384
+ if __name__ == "__main__":
385
  main_loop()
deploy.sh ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Script to deploy the app to HuggingFace Spaces
3
+
4
+ # Colors for output
5
+ GREEN='\033[0;32m'
6
+ BLUE='\033[0;34m'
7
+ YELLOW='\033[1;33m'
8
+ RED='\033[0;31m'
9
+ NC='\033[0m' # No Color
10
+
11
+ echo -e "${BLUE}πŸš€ Deploying Template Matching Demo to HuggingFace Spaces...${NC}"
12
+
13
+ # Check if we're in a git repository
14
+ if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
15
+ echo -e "${RED}❌ Not a git repository${NC}"
16
+ exit 1
17
+ fi
18
+
19
+ # Check if there are uncommitted changes
20
+ if ! git diff-index --quiet HEAD --; then
21
+ echo -e "${YELLOW}⚠️ You have uncommitted changes. Please commit them first.${NC}"
22
+ echo ""
23
+ echo "Uncommitted files:"
24
+ git status --short
25
+ echo ""
26
+ read -p "Do you want to commit all changes now? (y/n) " -n 1 -r
27
+ echo
28
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
29
+ read -p "Enter commit message: " commit_msg
30
+ git add .
31
+ git commit -m "$commit_msg"
32
+ echo -e "${GREEN}βœ… Changes committed${NC}"
33
+ else
34
+ echo -e "${RED}❌ Deployment cancelled. Please commit your changes first.${NC}"
35
+ exit 1
36
+ fi
37
+ fi
38
+
39
+ # Get current branch
40
+ current_branch=$(git branch --show-current)
41
+ echo -e "${BLUE}πŸ“ Current branch: ${current_branch}${NC}"
42
+
43
+ # Check if we're on main/master branch
44
+ if [[ "$current_branch" != "main" && "$current_branch" != "master" ]]; then
45
+ echo -e "${YELLOW}⚠️ Warning: You're not on the main/master branch${NC}"
46
+ read -p "Continue anyway? (y/n) " -n 1 -r
47
+ echo
48
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
49
+ echo -e "${RED}❌ Deployment cancelled${NC}"
50
+ exit 1
51
+ fi
52
+ fi
53
+
54
+ # Push to origin
55
+ echo -e "${BLUE}πŸ“€ Pushing to remote repository...${NC}"
56
+ git push origin $current_branch
57
+
58
+ if [ $? -eq 0 ]; then
59
+ echo -e "${GREEN}βœ… Successfully deployed to HuggingFace Spaces!${NC}"
60
+ echo ""
61
+ echo -e "${BLUE}🌐 Your space should update automatically at:${NC}"
62
+ echo -e "${GREEN} https://huggingface.co/spaces/amithjkamath/template-matching${NC}"
63
+ echo ""
64
+ echo -e "${YELLOW}πŸ’‘ Note: It may take a few minutes for the changes to appear.${NC}"
65
+ else
66
+ echo -e "${RED}❌ Failed to push to remote repository${NC}"
67
+ echo "Please check your git configuration and remote settings."
68
+ exit 1
69
+ fi
pyproject.toml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "template-matching"
3
+ version = "0.1.0"
4
+ description = "Interactive template matching demo using OpenCV and Streamlit"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11.9"
7
+ dependencies = [
8
+ "opencv-python-headless>=4.8.0",
9
+ "streamlit>=1.28.0",
10
+ "streamlit-image-coordinates>=0.1.6",
11
+ "pillow>=10.0.0",
12
+ "numpy>=1.24.0",
13
+ "huggingface-hub>=0.17.0",
14
+ ]
15
+
16
+ [tool.pylint."MESSAGES CONTROL"]
17
+ disable = ["c-extension-no-member"]
18
+
19
+ [tool.pyright]
20
+ reportGeneralTypeIssues = false
21
+ reportOptionalMemberAccess = false
requirements.txt CHANGED
@@ -1,5 +1,6 @@
1
  opencv-python-headless
2
  streamlit
 
3
  Pillow
4
  numpy
5
  huggingface_hub
 
1
  opencv-python-headless
2
  streamlit
3
+ streamlit-image-coordinates
4
  Pillow
5
  numpy
6
  huggingface_hub
run_simple.sh ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Alternative simple run script using uv pip directly
3
+
4
+ # Colors for output
5
+ GREEN='\033[0;32m'
6
+ BLUE='\033[0;34m'
7
+ NC='\033[0m' # No Color
8
+
9
+ echo -e "${BLUE}πŸš€ Starting Template Matching Demo locally...${NC}"
10
+
11
+ # Check if uv is installed
12
+ if ! command -v uv &> /dev/null; then
13
+ echo "❌ uv is not installed. Please install it first:"
14
+ echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
15
+ exit 1
16
+ fi
17
+
18
+ # Install dependencies if needed
19
+ echo -e "${BLUE}πŸ“¦ Checking dependencies...${NC}"
20
+ uv pip install -q -r requirements.txt
21
+
22
+ # Run the Streamlit app
23
+ echo -e "${GREEN}βœ… Starting Streamlit app...${NC}"
24
+ echo -e "${BLUE}🌐 The app will open at http://localhost:8501${NC}"
25
+ echo ""
26
+
27
+ uv run streamlit run app.py
28
+
29
+ # If the app exits
30
+ echo -e "${BLUE}πŸ‘‹ App stopped.${NC}"
setup.sh ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Setup script for Template Matching Demo
3
+
4
+ # Colors for output
5
+ GREEN='\033[0;32m'
6
+ BLUE='\033[0;34m'
7
+ YELLOW='\033[1;33m'
8
+ RED='\033[0;31m'
9
+ NC='\033[0m' # No Color
10
+
11
+ echo -e "${BLUE}πŸ”§ Setting up Template Matching Demo...${NC}"
12
+ echo ""
13
+
14
+ # Check Python version
15
+ echo -e "${BLUE}🐍 Checking Python version...${NC}"
16
+ python_version=$(python3 --version 2>&1 | awk '{print $2}')
17
+ required_version="3.11.9"
18
+
19
+ if python3 -c "import sys; exit(0 if sys.version_info >= (3, 11) else 1)"; then
20
+ echo -e "${GREEN}βœ… Python ${python_version} is installed${NC}"
21
+ else
22
+ echo -e "${RED}❌ Python 3.11+ is required${NC}"
23
+ exit 1
24
+ fi
25
+
26
+ # Check if uv is installed
27
+ echo -e "${BLUE}πŸ“¦ Checking for uv package manager...${NC}"
28
+ if ! command -v uv &> /dev/null; then
29
+ echo -e "${YELLOW}⚠️ uv is not installed${NC}"
30
+ echo -e "${BLUE}Installing uv...${NC}"
31
+ curl -LsSf https://astral.sh/uv/install.sh | sh
32
+
33
+ # Source the shell configuration to make uv available
34
+ if [ -f "$HOME/.cargo/env" ]; then
35
+ source "$HOME/.cargo/env"
36
+ fi
37
+
38
+ if command -v uv &> /dev/null; then
39
+ echo -e "${GREEN}βœ… uv installed successfully${NC}"
40
+ else
41
+ echo -e "${RED}❌ Failed to install uv. Please install manually:${NC}"
42
+ echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
43
+ exit 1
44
+ fi
45
+ else
46
+ echo -e "${GREEN}βœ… uv is already installed${NC}"
47
+ fi
48
+
49
+ # Sync dependencies
50
+ echo ""
51
+ echo -e "${BLUE}πŸ“₯ Installing dependencies...${NC}"
52
+ uv sync --no-build-isolation
53
+
54
+ if [ $? -eq 0 ]; then
55
+ echo -e "${GREEN}βœ… Dependencies installed successfully${NC}"
56
+ else
57
+ echo -e "${RED}❌ Failed to install dependencies${NC}"
58
+ exit 1
59
+ fi
60
+
61
+ # Make scripts executable
62
+ echo ""
63
+ echo -e "${BLUE}πŸ” Setting script permissions...${NC}"
64
+ chmod +x run_local.sh deploy.sh setup.sh
65
+
66
+ echo ""
67
+ echo -e "${GREEN}βœ… Setup complete!${NC}"
68
+ echo ""
69
+ echo -e "${BLUE}Next steps:${NC}"
70
+ echo " 1. Run locally: ${GREEN}./run_local.sh${NC}"
71
+ echo " 2. Deploy: ${GREEN}./deploy.sh${NC}"
72
+ echo ""
73
+ echo -e "${YELLOW}πŸ’‘ Tip: You can also use 'make run' or 'make deploy' if you prefer${NC}"