soccer-ball-detection / scripts /push_to_huggingface.py
eeeeeeeeeeeeee3's picture
Upload scripts/push_to_huggingface.py with huggingface_hub
58bebae verified
#!/usr/bin/env python3
"""
Push soccer ball detection project to Hugging Face Hub.
Includes code, configs, and trained model checkpoints.
"""
import argparse
import os
import shutil
import sys
from pathlib import Path
from typing import List, Optional
try:
from huggingface_hub import HfApi, create_repo, login
from huggingface_hub.utils import HfHubHTTPError
HF_AVAILABLE = True
except ImportError:
HF_AVAILABLE = False
print("Error: huggingface_hub not installed. Install with: pip install huggingface_hub")
sys.exit(1)
def get_files_to_upload(project_root: Path, exclude_patterns: List[str] = None) -> List[Path]:
"""Get list of files to upload, excluding patterns."""
if exclude_patterns is None:
exclude_patterns = [
'__pycache__',
'.git',
'.gitignore',
'venv',
'env',
'.venv',
'.env',
'.env.local',
'mlruns',
'logs',
'*.log',
'.DS_Store',
'Thumbs.db',
'data/raw',
'data/output',
'datasets',
'*.tar.gz',
'*.tar',
'*.zip',
]
files_to_upload = []
# Files/directories to include
include_patterns = [
'*.py',
'*.yaml',
'*.yml',
'*.md',
'*.txt',
'*.json',
'*.html',
'*.sh',
'*.dockerfile',
'Dockerfile',
'docker-compose*.yml',
'configs/',
'src/',
'scripts/',
'models/checkpoints/*.pth', # Include model checkpoints
]
def should_include(file_path: Path) -> bool:
"""Check if file should be included."""
rel_path = file_path.relative_to(project_root)
rel_str = str(rel_path)
# Check exclude patterns
for pattern in exclude_patterns:
if pattern in rel_str or file_path.name.startswith('.'):
# Special handling for .gitkeep files
if file_path.name == '.gitkeep':
return True
return False
# Check include patterns
for pattern in include_patterns:
if pattern.endswith('/'):
# Directory pattern
if rel_str.startswith(pattern[:-1]):
return True
elif '*' in pattern:
# Wildcard pattern
import fnmatch
if fnmatch.fnmatch(file_path.name, pattern):
return True
else:
# Exact match
if file_path.name == pattern or rel_str == pattern:
return True
return False
# Walk through project directory
for root, dirs, files in os.walk(project_root):
root_path = Path(root)
# Filter directories to skip
dirs[:] = [d for d in dirs if not any(pattern in d for pattern in exclude_patterns)]
# Check files
for file in files:
file_path = root_path / file
if should_include(file_path):
files_to_upload.append(file_path)
return sorted(files_to_upload)
def create_hf_readme(project_root: Path, repo_id: str) -> str:
"""Create README content for Hugging Face."""
readme_content = f"""---
license: mit
tags:
- computer-vision
- object-detection
- soccer
- ball-detection
- detr
- rf-detr
- pytorch
datasets:
- custom
metrics:
- mAP
- precision
- recall
---
# Soccer Ball Detection with RF-DETR
Automated soccer ball detection pipeline using RF-DETR (Roboflow DETR) optimized for tiny object detection (<15 pixels).
## Model Details
### Architecture
- **Model**: RF-DETR Base
- **Backbone**: ResNet-50
- **Classes**: Ball (single class detection)
- **Input Resolution**: 1120x1120 (optimized for memory)
- **Precision**: Mixed Precision (FP16/FP32) training, FP16 inference
### Performance
Based on training evaluation report (Epoch 39):
- **mAP@0.5:0.95**: 0.682 (68.2%)
- **mAP@0.5**: 0.990 (99.0%)
- **Small Objects mAP**: 0.598 (59.8%)
- **Training Loss**: 3.073
- **Validation Loss**: 3.658
## Quick Start
### Installation
```bash
pip install -r requirements.txt
```
### Usage
```python
from src.perception.local_detector import LocalDetector
from pathlib import Path
# Initialize detector
detector = LocalDetector(
model_path="models/checkpoints/latest_checkpoint.pth",
config_path="configs/default.yaml"
)
# Detect ball in image
results = detector.detect(image_path)
```
### Process Video
```bash
python main.py --video path/to/video.mp4 --config configs/default.yaml --output data/output
```
## Training
### Train from Scratch
```bash
python scripts/train_ball.py \\
--config configs/training.yaml \\
--dataset-dir datasets/combined \\
--output-dir models \\
--epochs 50
```
### Resume Training
```bash
python scripts/train_ball.py \\
--config configs/resume_20_epochs.yaml \\
--dataset-dir datasets/combined \\
--output-dir models \\
--resume models/checkpoints/latest_checkpoint.pth \\
--epochs 50
```
## Project Structure
```
soccer_cv_ball/
├── main.py # Main orchestrator
├── configs/ # Configuration files
├── src/
│ ├── perception/ # Detection, tracking
│ ├── analysis/ # Event detection
│ ├── visualization/ # Dashboard
│ └── training/ # Training utilities
├── scripts/ # Training and utility scripts
├── models/ # Model checkpoints
└── data/ # Dataset (not included)
```
## Configuration
Key configuration files:
- `configs/training.yaml` - Main training configuration
- `configs/default.yaml` - Inference configuration
- `configs/resume_*.yaml` - Resume training configurations
## Datasets
This model was trained on:
- SoccerSynth-Detection (synthetic data)
- Open Soccer Ball Dataset
- Custom validation sets
## Precision Strategy
- **Training**: Mixed Precision (FP16/FP32) - RF-DETR default
- **Inference**: FP16 (half precision) for ~3x speedup
- **Future**: INT8 via QAT (Quantization-Aware Training) for edge devices
## Citation
If you use this model, please cite:
```bibtex
@software{{soccer_ball_detection,
title={{Soccer Ball Detection with RF-DETR}},
author={{Your Name}},
year={{2026}},
url={{https://huggingface.co/{repo_id}}}
}}
```
## License
MIT License - See LICENSE file for details.
## Acknowledgments
- RF-DETR by Roboflow
- SoccerSynth-Detection dataset
- Open Soccer Ball Dataset
"""
return readme_content
def push_to_huggingface(
repo_id: str,
project_root: Path,
token: Optional[str] = None,
private: bool = False,
include_models: bool = True
):
"""Push project to Hugging Face Hub using HTTP API."""
project_root = Path(project_root).resolve()
print(f"📦 Preparing to push to Hugging Face: {repo_id}")
print(f" Project root: {project_root}")
# Initialize API
api = HfApi(token=token)
# Verify login
try:
user_info = api.whoami()
print(f"🔐 Logged in as: {user_info.get('name', 'unknown')}")
except Exception as e:
print(f"❌ Authentication failed: {e}")
print(" Please provide a valid token with --token argument")
sys.exit(1)
# Create repository
print(f"📁 Creating repository: {repo_id}")
try:
api.create_repo(repo_id, private=private, exist_ok=True, repo_type="model")
print("✅ Repository created/exists")
except HfHubHTTPError as e:
if "already exists" in str(e).lower():
print("✅ Repository already exists")
else:
print(f"⚠️ Repository creation warning: {e}")
# Get files to upload
print("🔍 Finding files to upload...")
files_to_upload = get_files_to_upload(project_root)
print(f" Found {len(files_to_upload)} files")
# Filter out model files if not including models
if not include_models:
files_to_upload = [f for f in files_to_upload if not str(f).endswith('.pth')]
print(f" Excluding model files: {len(files_to_upload)} files remaining")
# Create README
print("📝 Creating README.md...")
readme_path = project_root / "README.md"
readme_content = create_hf_readme(project_root, repo_id)
with open(readme_path, 'w') as f:
f.write(readme_content)
print("✅ README.md created")
# Upload files
print("📤 Uploading files to Hugging Face Hub...")
uploaded = 0
failed = 0
for file_path in files_to_upload:
try:
rel_path = file_path.relative_to(project_root)
# Convert to forward slashes for HF
path_in_repo = str(rel_path).replace('\\', '/')
# Skip if it's the original README (we'll upload our generated one)
if path_in_repo == "README.md" and file_path != readme_path:
continue
# Upload file
api.upload_file(
path_or_fileobj=str(file_path),
path_in_repo=path_in_repo,
repo_id=repo_id,
repo_type="model",
token=token
)
uploaded += 1
if uploaded % 10 == 0:
print(f" Uploaded {uploaded}/{len(files_to_upload)} files...")
except Exception as e:
failed += 1
print(f" ⚠️ Failed to upload {file_path.name}: {e}")
# Upload the generated README
try:
api.upload_file(
path_or_fileobj=str(readme_path),
path_in_repo="README.md",
repo_id=repo_id,
repo_type="model",
token=token
)
print("✅ README.md uploaded")
except Exception as e:
print(f"⚠️ Failed to upload README.md: {e}")
print(f"\n✅ Upload complete!")
print(f" Uploaded: {uploaded} files")
if failed > 0:
print(f" Failed: {failed} files")
print(f" View at: https://huggingface.co/{repo_id}")
def main():
parser = argparse.ArgumentParser(
description="Push soccer ball detection project to Hugging Face Hub"
)
parser.add_argument(
"--repo-id",
type=str,
required=True,
help="Hugging Face repository ID (e.g., 'username/soccer-ball-detection')"
)
parser.add_argument(
"--project-root",
type=str,
default=".",
help="Project root directory (default: current directory)"
)
parser.add_argument(
"--token",
type=str,
default=None,
help="Hugging Face API token (or use huggingface-cli login)"
)
parser.add_argument(
"--private",
action="store_true",
help="Create private repository"
)
parser.add_argument(
"--no-models",
action="store_true",
help="Exclude model checkpoint files (.pth)"
)
args = parser.parse_args()
push_to_huggingface(
repo_id=args.repo_id,
project_root=Path(args.project_root),
token=args.token,
private=args.private,
include_models=not args.no_models
)
if __name__ == "__main__":
main()