KrishDev15 commited on
Commit
1f4e4ae
·
verified ·
1 Parent(s): acb7e06

Upload 30 files

Browse files
.gitignore ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ ENV/
26
+
27
+ # IDE
28
+ .idea/
29
+ .vscode/
30
+ *.swp
31
+ *.swo
32
+
33
+ # Streamlit
34
+ .streamlit/
35
+
36
+ # Model files
37
+ *.h5
38
+ *.tflite
39
+ *.pb
40
+
41
+ # Misc
42
+ .DS_Store
43
+ .env
44
+ .env.local
45
+ .env.development.local
46
+ .env.test.local
47
+ .env.production.local
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.10.13
Dockerfile CHANGED
@@ -1,21 +1,17 @@
1
- FROM python:3.9-slim
2
 
3
- WORKDIR /app
4
 
5
- RUN apt-get update && apt-get install -y \
6
- build-essential \
7
- curl \
8
- software-properties-common \
9
- git \
10
- && rm -rf /var/lib/apt/lists/*
11
 
12
- COPY requirements.txt ./
13
- COPY src/ ./src/
14
 
15
- RUN pip3 install -r requirements.txt
 
 
16
 
17
- EXPOSE 8501
18
 
19
- HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
20
-
21
- ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
1
+ FROM python:3.10-slim
2
 
3
+ WORKDIR /code
4
 
5
+ # Install system dependencies for OpenCV and MediaPipe
6
+ RUN apt-get update && apt-get install -y libgl1 ffmpeg
 
 
 
 
7
 
8
+ # Copy all project files into the container
9
+ COPY . .
10
 
11
+ # Install Python dependencies
12
+ RUN pip install --upgrade pip
13
+ RUN pip install -r requirements.txt
14
 
15
+ EXPOSE 7860
16
 
17
+ CMD ["streamlit", "run", "simple_sign_interpreter.py", "--server.port=7860", "--server.headless=true", "--server.enableCORS=false"]
 
 
INSTALLATION.md ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📦 Installation Guide - Sign Language Interpreter
2
+
3
+ This guide provides detailed installation instructions for the Sign Language Interpreter system.
4
+
5
+ ## 🔧 System Requirements
6
+
7
+ - **Python**: 3.8 - 3.11 (recommended: 3.9 or 3.10)
8
+ - **Operating System**: Windows 10/11, macOS 10.14+, or Linux
9
+ - **Hardware**:
10
+ - Webcam (built-in or external)
11
+ - 4GB RAM minimum (8GB recommended)
12
+ - 2GB free disk space
13
+ - **Internet**: Required for initial setup and package installation
14
+
15
+ ## 🚀 Quick Installation
16
+
17
+ ### Option 1: Automated Setup (Recommended)
18
+
19
+ 1. **Download and Extract**
20
+ ```bash
21
+ # If using git
22
+ git clone https://github.com/yourusername/sign-language-interpreter.git
23
+ cd sign-language-interpreter
24
+
25
+ # Or download and extract ZIP file
26
+ ```
27
+
28
+ 2. **Run Setup Script**
29
+ ```bash
30
+ python setup.py
31
+ ```
32
+
33
+ This will:
34
+ - Create necessary directories
35
+ - Install required packages
36
+ - Create a sample model
37
+ - Verify installation
38
+
39
+ ### Option 2: Manual Installation
40
+
41
+ 1. **Create Virtual Environment**
42
+ ```bash
43
+ python -m venv venv
44
+
45
+ # Activate virtual environment
46
+ # Windows:
47
+ venv\Scripts\activate
48
+ # macOS/Linux:
49
+ source venv/bin/activate
50
+ ```
51
+
52
+ 2. **Install Dependencies**
53
+ ```bash
54
+ pip install -r requirements.txt
55
+ ```
56
+
57
+ 3. **Create Directories**
58
+ ```bash
59
+ mkdir models dataset logs examples
60
+ ```
61
+
62
+ 4. **Create Sample Model**
63
+ ```bash
64
+ python create_simple_model.py
65
+ ```
66
+
67
+ ## 🔍 Verification
68
+
69
+ Run the system test to verify everything is working:
70
+
71
+ ```bash
72
+ python test_system.py
73
+ ```
74
+
75
+ Expected output:
76
+ ```
77
+ 🤟 Sign Language Interpreter - System Test
78
+ ==================================================
79
+
80
+ Dependencies:
81
+ ✅ All dependencies installed
82
+
83
+ Camera:
84
+ ✅ Camera working
85
+
86
+ MediaPipe:
87
+ ✅ MediaPipe working
88
+
89
+ Model Files:
90
+ ✅ Model files found
91
+
92
+ Predictor:
93
+ ✅ Predictor working - Sample prediction: hello (0.85)
94
+
95
+ ==================================================
96
+ Test Results: 5/5 tests passed
97
+ 🎉 All tests passed! System is ready to use.
98
+ ```
99
+
100
+ ## 🚀 Running the Application
101
+
102
+ ### Web Interface (Recommended)
103
+ ```bash
104
+ streamlit run app.py
105
+ ```
106
+
107
+ ### Command Line Interface
108
+ ```bash
109
+ python examples/basic_usage.py
110
+ ```
111
+
112
+ ## 🛠️ Troubleshooting
113
+
114
+ ### Common Issues
115
+
116
+ 1. **Python Version Error**
117
+ ```
118
+ Error: This package requires Python 3.8-3.11
119
+ ```
120
+ **Solution**: Install a compatible Python version
121
+
122
+ 2. **Camera Access Error**
123
+ ```
124
+ Error: Camera not accessible
125
+ ```
126
+ **Solutions**:
127
+ - Close other applications using the camera
128
+ - Check camera permissions
129
+ - Try a different camera index
130
+
131
+ 3. **Package Installation Error**
132
+ ```
133
+ Error: Could not find a version that satisfies the requirement
134
+ ```
135
+ **Solutions**:
136
+ - Update pip: `python -m pip install --upgrade pip`
137
+ - Try installing packages individually
138
+ - Check Python version compatibility
139
+
140
+ 4. **TensorFlow Installation Issues**
141
+ ```
142
+ Error: No matching distribution found for tensorflow
143
+ ```
144
+ **Solutions**:
145
+ - Use Python 3.8-3.11
146
+ - Install TensorFlow separately: `pip install tensorflow`
147
+ - Use CPU-only version: `pip install tensorflow-cpu`
148
+
149
+ ### Platform-Specific Issues
150
+
151
+ #### Windows
152
+ - **Visual C++ Error**: Install Microsoft Visual C++ Redistributable
153
+ - **Long Path Error**: Enable long path support in Windows settings
154
+
155
+ #### macOS
156
+ - **Permission Error**: Grant camera access in System Preferences
157
+ - **Homebrew Issues**: Update Homebrew and Python
158
+
159
+ #### Linux
160
+ - **Camera Error**: Install v4l-utils: `sudo apt install v4l-utils`
161
+ - **Permission Error**: Add user to video group: `sudo usermod -a -G video $USER`
162
+
163
+ ## 🔄 Updates
164
+
165
+ To update the system:
166
+
167
+ 1. **Update Code**
168
+ ```bash
169
+ git pull # If using git
170
+ ```
171
+
172
+ 2. **Update Dependencies**
173
+ ```bash
174
+ pip install -r requirements.txt --upgrade
175
+ ```
176
+
177
+ 3. **Test System**
178
+ ```bash
179
+ python test_system.py
180
+ ```
181
+
182
+ ## 🆘 Getting Help
183
+
184
+ If you encounter issues:
185
+
186
+ 1. **Check Documentation**
187
+ - Read `README.md` for overview
188
+ - Check `QUICKSTART.md` for basic usage
189
+ - Review this installation guide
190
+
191
+ 2. **Run Diagnostics**
192
+ ```bash
193
+ python test_system.py --verbose
194
+ ```
195
+
196
+ 3. **Community Support**
197
+ - GitHub Issues: Report bugs and feature requests
198
+ - Discord: Join our community chat
199
+ - Email: support@signlanguage.ai
200
+
201
+ ## 📋 Next Steps
202
+
203
+ After successful installation:
204
+
205
+ 1. **Try the Demo**: `streamlit run app.py`
206
+ 2. **Collect Data**: `python data_collection.py`
207
+ 3. **Train Model**: `python train_model.py`
208
+ 4. **Read Documentation**: Check `docs/` folder for guides
209
+
210
+ ## 🔐 Security Notes
211
+
212
+ - The application only accesses your camera locally
213
+ - No data is sent to external servers
214
+ - All processing happens on your device
215
+ - You can review the source code for transparency
QUICKSTART.md ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 Quick Start Guide - Sign Language Interpreter
2
+
3
+ This guide will help you get started with the Sign Language Interpreter system quickly.
4
+
5
+ ## 📋 Prerequisites
6
+
7
+ - Python 3.8 or higher
8
+ - Webcam
9
+ - Git (optional)
10
+
11
+ ## 🛠️ Installation
12
+
13
+ 1. **Setup Environment**
14
+ ```bash
15
+ # Clone repository (if using git)
16
+ git clone https://github.com/yourusername/sign-language-interpreter.git
17
+ cd sign-language-interpreter
18
+
19
+ # Create virtual environment
20
+ python -m venv venv
21
+
22
+ # Activate virtual environment
23
+ # On Windows:
24
+ venv\Scripts\activate
25
+ # On macOS/Linux:
26
+ source venv/bin/activate
27
+
28
+ # Run setup script
29
+ python setup.py
30
+ ```
31
+
32
+ ## 🎯 Using the Application
33
+
34
+ ### 1. Quick Demo
35
+ To try the system with a pre-trained model:
36
+ ```bash
37
+ streamlit run app.py
38
+ ```
39
+ This will launch the web interface where you can:
40
+ - Adjust confidence threshold
41
+ - Toggle hand landmark visualization
42
+ - See real-time sign language interpretation
43
+
44
+ ### 2. Collect Your Own Data
45
+ To create a custom dataset:
46
+ ```bash
47
+ python data_collection.py
48
+ ```
49
+ Follow the on-screen instructions to:
50
+ - Choose signs to collect
51
+ - Record sequences of hand gestures
52
+ - Save data for training
53
+
54
+ ### 3. Train Custom Model
55
+ To train a model on your collected data:
56
+ ```bash
57
+ python train_model.py
58
+ ```
59
+ This will:
60
+ - Load your collected dataset
61
+ - Train a new model
62
+ - Save the trained model
63
+ - Generate performance metrics
64
+
65
+ ## 🔍 Troubleshooting
66
+
67
+ 1. **Camera not working?**
68
+ - Check if another application is using the camera
69
+ - Ensure camera permissions are granted
70
+ - Try closing and reopening the application
71
+
72
+ 2. **Poor recognition?**
73
+ - Ensure good lighting
74
+ - Keep hands within camera frame
75
+ - Try adjusting the confidence threshold
76
+ - Collect more training data
77
+
78
+ 3. **Installation issues?**
79
+ - Check Python version: `python --version`
80
+ - Update pip: `python -m pip install --upgrade pip`
81
+ - Install requirements manually: `pip install -r requirements.txt`
82
+
83
+ ## 📚 Additional Resources
84
+
85
+ - Check `README.md` for detailed documentation
86
+ - View example signs in `examples/` directory
87
+ - See training tips in `docs/training_guide.md`
88
+
89
+ ## 🆘 Need Help?
90
+
91
+ - Check the issues section on GitHub
92
+ - Join our Discord community
93
+ - Email support at: support@signlanguage.ai
94
+
95
+ ## 🔄 Updates
96
+
97
+ To update the application:
98
+ ```bash
99
+ git pull # If using git
100
+ pip install -r requirements.txt # Update dependencies
101
+ ```
102
+
103
+ ## 📝 Notes
104
+
105
+ - The system works best in well-lit environments
106
+ - Position yourself 2-3 feet from the camera
107
+ - Keep your hand gestures clear and deliberate
108
+ - Practice with the sample signs first
README.md CHANGED
@@ -1,20 +1,126 @@
1
- ---
2
- title: Sign Intrepreter
3
- emoji: 🚀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
- pinned: false
11
- short_description: Streamlit template space
12
- license: apache-2.0
13
- ---
14
-
15
- # Welcome to Streamlit!
16
-
17
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
18
-
19
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
20
- forums](https://discuss.streamlit.io).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🤟 Sign Language Interpreter
2
+
3
+ A real-time sign language recognition system built with Python, MediaPipe, TensorFlow, and Streamlit. This application can detect and interpret sign language gestures in real-time using computer vision and machine learning.
4
+
5
+ ## 🌟 Features
6
+
7
+ - **Real-time Hand Detection**: Uses MediaPipe for accurate hand landmark detection
8
+ - **Sign Language Recognition**: Supports 29 gestures including:
9
+ - Numbers: 1, 2, 4, 5, 6, 7, 8
10
+ - Letters: A-X (excluding E)
11
+ - **Live Webcam Processing**: Real-time gesture recognition through webcam
12
+ - **Interactive Web Interface**: Built with Streamlit for easy use
13
+ - **Adjustable Confidence**: Configurable detection confidence threshold
14
+ - **Optimized Performance**: Fast response with temporal prediction smoothing
15
+
16
+ ## 🚀 Quick Start
17
+
18
+ ### Prerequisites
19
+ - Python 3.8+
20
+ - Webcam
21
+ - Git
22
+
23
+ ### Installation
24
+
25
+ 1. **Clone the repository**
26
+ ```bash
27
+ git clone https://github.com/yourusername/ai-sign-language-interpreter.git
28
+ cd ai-sign-language-interpreter
29
+ ```
30
+
31
+ 2. **Install dependencies**
32
+ ```bash
33
+ pip install -r requirements.txt
34
+ ```
35
+
36
+ 3. **Run the application**
37
+ ```bash
38
+ streamlit run simple_sign_interpreter.py
39
+ ```
40
+
41
+ 4. **Open your browser** and navigate to `http://localhost:8501`
42
+
43
+ ## 🎯 Usage
44
+
45
+ 1. **Start the application** using the command above
46
+ 2. **Allow camera access** when prompted by your browser
47
+ 3. **Click "START"** to begin webcam capture
48
+ 4. **Show hand gestures** to the camera
49
+ 5. **Adjust confidence threshold** using the sidebar slider for better detection
50
+ 6. **View real-time predictions** in the "Current Sign" panel
51
+
52
+ ## 📁 Project Structure
53
+
54
+ ```
55
+ ai-sign-language-interpreter/
56
+ ├── simple_sign_interpreter.py # Main Streamlit application
57
+ ├── sign_utils.py # Core prediction and landmark detection
58
+ ├── models/
59
+ │ ├── sign_model.tflite # Trained TensorFlow Lite model
60
+ │ └── labels.txt # Class labels for gestures
61
+ ├── requirements.txt # Python dependencies
62
+ ├── .gitignore # Git ignore file
63
+ └── README.md # Project documentation
64
+ ```
65
+
66
+ ## 🔧 Technical Details
67
+
68
+ ### Architecture
69
+ - **Frontend**: Streamlit web interface
70
+ - **Computer Vision**: MediaPipe for hand landmark detection
71
+ - **Machine Learning**: TensorFlow Lite for gesture classification
72
+ - **Real-time Processing**: Optimized prediction pipeline with temporal smoothing
73
+
74
+ ### Model Information
75
+ - **Input**: 63-dimensional hand landmark features (21 landmarks × 3 coordinates)
76
+ - **Output**: 29 sign language gesture classes
77
+ - **Format**: TensorFlow Lite (.tflite) for optimized inference
78
+ - **Performance**: Real-time processing at 30+ FPS
79
+
80
+ ### Key Components
81
+
82
+ 1. **MediaPipeLandmarksModel**: Handles hand detection and landmark extraction
83
+ 2. **SignLanguagePredictor**: Manages model inference and prediction smoothing
84
+ 3. **Streamlit Interface**: Provides user-friendly web interface
85
+
86
+ ## 🎛️ Configuration
87
+
88
+ ### Adjustable Parameters
89
+ - **Detection Confidence**: Minimum confidence for hand detection (default: 0.5)
90
+ - **Tracking Confidence**: Minimum confidence for hand tracking (default: 0.5)
91
+ - **Prediction Threshold**: Minimum confidence for gesture prediction (default: 0.5)
92
+ - **Buffer Size**: Number of frames for prediction smoothing (default: 8)
93
+
94
+ ## 🤝 Contributing
95
+
96
+ 1. Fork the repository
97
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
98
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
99
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
100
+ 5. Open a Pull Request
101
+
102
+ ## 📝 License
103
+
104
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
105
+
106
+ ## 🙏 Acknowledgments
107
+
108
+ - **MediaPipe** for hand landmark detection
109
+ - **TensorFlow** for machine learning framework
110
+ - **Streamlit** for the web interface
111
+ - **OpenCV** for computer vision utilities
112
+
113
+ ## 📞 Support
114
+
115
+ If you encounter any issues or have questions:
116
+ 1. Check the [Issues](https://github.com/kris07hna/SIGN-LANG-INTREPRETOR/issues) page
117
+ 2. Create a new issue with detailed description
118
+ 3. Include system information and error messages
119
+
120
+ ## 🔮 Future Enhancements
121
+
122
+ - [ ] Support for more sign language gestures
123
+ - [ ] Multi-hand gesture recognition
124
+ - [ ] Sign language sentence formation
125
+ - [ ] Mobile app version
126
+ - [ ] Real-time translation to multiple languages
README_NEW.md ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🤟 AI Sign Language Interpreter
2
+
3
+ A comprehensive, real-time sign language interpretation system powered by AI. This application uses computer vision and machine learning to recognize and interpret sign language gestures, providing instant translations with natural language understanding through Google's Gemini AI.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🎯 Real-time sign language recognition
8
+ - 🤖 AI-powered gesture interpretation
9
+ - 📹 Live webcam processing
10
+ - 🔤 Support for letters (A-X) and numbers (1-8)
11
+ - 💡 Natural language interpretation using Gemini AI
12
+ - 🎨 Beautiful, responsive user interface
13
+ - 📱 Mobile-friendly design
14
+
15
+ ## 🚀 Quick Start
16
+
17
+ 1. **Install Dependencies**
18
+ ```bash
19
+ pip install -r requirements_new.txt
20
+ ```
21
+
22
+ 2. **Set Up Gemini API**
23
+ - Create `.streamlit/secrets.toml` if not exists
24
+ - Add your Gemini API key:
25
+ ```toml
26
+ GEMINI_API_KEY = "your-api-key-here"
27
+ ```
28
+
29
+ 3. **Run the Application**
30
+ ```bash
31
+ python run_app.py
32
+ ```
33
+ This will start both the landing page and the Streamlit application.
34
+ - Landing page: http://localhost:5000
35
+ - Streamlit app: http://localhost:8501
36
+
37
+ ## 📖 Usage
38
+
39
+ 1. Visit the landing page at http://localhost:5000
40
+ 2. Click "Try it Out" to launch the interpreter
41
+ 3. Allow camera access when prompted
42
+ 4. Position your hand clearly in the camera view
43
+ 5. Make sign language gestures
44
+ 6. View real-time interpretations and AI-enhanced understanding
45
+
46
+ ## 🎯 Supported Gestures
47
+
48
+ ### Numbers
49
+ - 1, 2, 4, 5, 6, 7, 8
50
+
51
+ ### Letters
52
+ - A through X (except E)
53
+
54
+ ## 💡 Tips for Best Results
55
+
56
+ - Ensure good lighting conditions
57
+ - Keep your hand centered in the frame
58
+ - Make clear, distinct gestures
59
+ - Hold gestures steady for 1-2 seconds
60
+ - Maintain consistent hand orientation
61
+ - Avoid cluttered backgrounds
62
+
63
+ ## 🛠️ Technical Details
64
+
65
+ ### Components
66
+
67
+ - **Frontend**
68
+ - Landing page: HTML/CSS with Tailwind CSS
69
+ - Application UI: Streamlit
70
+ - Real-time video: streamlit-webrtc
71
+
72
+ - **Backend**
73
+ - Web Server: Flask
74
+ - ML Framework: TensorFlow Lite
75
+ - Hand Tracking: MediaPipe
76
+ - AI Interpretation: Google Gemini
77
+
78
+ ### Model Architecture
79
+
80
+ - Custom TensorFlow model trained on hand gesture dataset
81
+ - MediaPipe hand landmark detection
82
+ - 63-dimensional feature vector for gesture recognition
83
+ - Real-time processing optimizations
84
+
85
+ ## 📊 Performance
86
+
87
+ - Real-time processing at 30+ FPS
88
+ - Low latency gesture recognition
89
+ - High accuracy on supported gestures
90
+ - Efficient memory usage
91
+
92
+ ## 🤝 Contributing
93
+
94
+ Contributions are welcome! Please feel free to submit pull requests.
95
+
96
+ ## 📝 License
97
+
98
+ This project is licensed under the MIT License - see the LICENSE file for details.
99
+
100
+ ## 🙏 Acknowledgments
101
+
102
+ - MediaPipe team for hand tracking technology
103
+ - Google for Gemini AI API
104
+ - Streamlit team for the amazing framework
105
+ - Open source community for various tools and libraries
106
+
107
+ ## 📞 Support
108
+
109
+ For issues, questions, or contributions, please open an issue on GitHub.
110
+
111
+ ---
112
+
113
+ Made with ❤️ for breaking communication barriers
app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase
3
+ import cv2
4
+ import numpy as np
5
+ from av import VideoFrame
6
+ import os
7
+ from sign_utils import SignLanguagePredictor, MediaPipeLandmarksModel
8
+
9
+ # Page config
10
+ st.set_page_config(
11
+ page_title="Sign Language Digits Interpreter",
12
+ page_icon="🔢",
13
+ layout="wide"
14
+ )
15
+
16
+ # Sidebar
17
+ st.sidebar.title("🔢 Controls")
18
+ confidence_threshold = st.sidebar.slider("Confidence Threshold", 0.0, 1.0, 0.5)
19
+ show_landmarks = st.sidebar.checkbox("Show Hand Landmarks", True)
20
+
21
+ # Main content
22
+ st.title("Sign Language Digits Interpreter")
23
+ st.markdown("""
24
+ This app recognizes hand signs for digits (0-9) in real-time.
25
+ Show your hand signs to the camera and see the interpretations!
26
+
27
+ Instructions:
28
+ 1. Allow camera access when prompted
29
+ 2. Show your hand sign for a digit (0-9)
30
+ 3. Hold the sign steady for best results
31
+ 4. Adjust the confidence threshold if needed
32
+ """)
33
+
34
+ # Initialize models
35
+ @st.cache_resource
36
+ def load_models():
37
+ try:
38
+ predictor = SignLanguagePredictor(
39
+ model_path="models/model.tflite",
40
+ labels_path="models/labels.txt"
41
+ )
42
+ return predictor
43
+ except Exception as e:
44
+ st.error(f"Error loading models: {str(e)}")
45
+ return None
46
+
47
+ predictor = load_models()
48
+
49
+ class DigitInterpreter(VideoTransformerBase):
50
+ def __init__(self):
51
+ self.predictor = predictor
52
+ self.landmark_model = MediaPipeLandmarksModel()
53
+ self.result_text = "Show your hand sign"
54
+ self.confidence = 0.0
55
+ self.landmarks = None
56
+ self.frame_count = 0
57
+ self.last_prediction = None
58
+ self.prediction_counter = {}
59
+
60
+ def recv(self, frame: VideoFrame) -> VideoFrame:
61
+ image = frame.to_ndarray(format="bgr24")
62
+ self.frame_count += 1
63
+
64
+ if self.predictor:
65
+ # Get landmarks
66
+ self.landmarks = self.landmark_model.predict_landmarks(image)
67
+
68
+ # Get prediction
69
+ if self.landmarks:
70
+ try:
71
+ # Use static prediction mode
72
+ prediction, self.confidence = self.predictor.predict_static(image)
73
+
74
+ # Only update prediction if confidence is above threshold
75
+ if self.confidence > confidence_threshold:
76
+ # Count predictions to reduce flickering
77
+ if prediction not in self.prediction_counter:
78
+ self.prediction_counter = {prediction: 1}
79
+ else:
80
+ self.prediction_counter[prediction] += 1
81
+
82
+ # Update result if we have consistent predictions
83
+ max_pred = max(self.prediction_counter.items(), key=lambda x: x[1])
84
+ if max_pred[1] >= 3: # Require 3 consistent predictions
85
+ self.result_text = f"Digit: {prediction}"
86
+ self.last_prediction = prediction
87
+ else:
88
+ self.result_text = "Show your hand sign"
89
+ self.prediction_counter = {}
90
+ except Exception as e:
91
+ self.result_text = "Processing..."
92
+ self.confidence = 0.0
93
+ self.prediction_counter = {}
94
+ else:
95
+ self.result_text = "No hand detected"
96
+ self.confidence = 0.0
97
+ self.prediction_counter = {}
98
+
99
+ # Draw landmarks if enabled
100
+ if show_landmarks and self.landmarks:
101
+ image = self.landmark_model.draw_landmarks(image, self.landmarks)
102
+
103
+ # Add text overlay
104
+ cv2.putText(
105
+ image,
106
+ f"{self.result_text} ({self.confidence:.2f})",
107
+ (20, 40),
108
+ cv2.FONT_HERSHEY_SIMPLEX,
109
+ 1.0,
110
+ (0, 255, 0) if self.confidence > confidence_threshold else (0, 0, 255),
111
+ 2
112
+ )
113
+
114
+ return VideoFrame.from_ndarray(image, format="bgr24")
115
+
116
+ # Create two columns
117
+ col1, col2 = st.columns([2, 1])
118
+
119
+ with col1:
120
+ # Webcam component
121
+ ctx = webrtc_streamer(
122
+ key="digit_recognition",
123
+ video_processor_factory=DigitInterpreter,
124
+ media_stream_constraints={"video": True, "audio": False},
125
+ async_processing=True,
126
+ )
127
+
128
+ with col2:
129
+ # Interpretation display
130
+ st.markdown("### 🔢 Current Digit")
131
+ if ctx.video_transformer:
132
+ # Large digit display
133
+ digit_display = st.empty()
134
+ confidence_meter = st.progress(0)
135
+
136
+ # Update interpretation and confidence
137
+ if ctx.video_transformer.last_prediction is not None and ctx.video_transformer.confidence > confidence_threshold:
138
+ digit_display.markdown(f"# {ctx.video_transformer.last_prediction}")
139
+ else:
140
+ digit_display.markdown("# -")
141
+ confidence_meter.progress(int(ctx.video_transformer.confidence * 100))
142
+
143
+ # Show confidence value
144
+ st.text(f"Confidence: {ctx.video_transformer.confidence:.2f}")
145
+ else:
146
+ st.warning("📷 Turn on your webcam to start!")
147
+
148
+ # Instructions
149
+ st.markdown("---")
150
+ st.markdown("""
151
+ ### How to Use
152
+ 1. Make sure you're in a well-lit environment
153
+ 2. Position your hand clearly in the camera view
154
+ 3. Make hand signs for digits 0-9
155
+ 4. Hold each sign steady for accurate recognition
156
+ 5. Adjust the confidence threshold if needed:
157
+ - Higher threshold = more accurate but less sensitive
158
+ - Lower threshold = more sensitive but may have false positives
159
+ """)
160
+
161
+ # Footer
162
+ st.markdown("---")
163
+ st.markdown("Made with ❤️ using MediaPipe, TensorFlow, and Streamlit")
app_enhanced.py ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import cv2
3
+ import numpy as np
4
+ import tensorflow as tf
5
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase, RTCConfiguration
6
+ import av
7
+ import mediapipe as mp
8
+ import google.generativeai as genai
9
+ from collections import deque
10
+ import time
11
+ import json
12
+
13
+ # Configure page
14
+ st.set_page_config(
15
+ page_title="🤟 AI Sign Language Interpreter",
16
+ page_icon="🤟",
17
+ layout="wide",
18
+ initial_sidebar_state="expanded"
19
+ )
20
+
21
+ # Load custom CSS
22
+ st.markdown("""
23
+ <style>
24
+ .main-header {
25
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
26
+ padding: 2rem;
27
+ border-radius: 10px;
28
+ color: white;
29
+ text-align: center;
30
+ margin-bottom: 2rem;
31
+ }
32
+ .feature-card {
33
+ background: #ffffff;
34
+ border: 1px solid #e0e0e0;
35
+ padding: 1.5rem;
36
+ border-radius: 10px;
37
+ border-left: 4px solid #667eea;
38
+ margin: 1rem 0;
39
+ color: #1a1a1a;
40
+ font-weight: 600;
41
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
42
+ }
43
+ .prediction-box {
44
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
45
+ color: white;
46
+ padding: 1.5rem;
47
+ border-radius: 10px;
48
+ text-align: center;
49
+ font-size: 1.2rem;
50
+ margin: 1rem 0;
51
+ }
52
+ .confidence-meter {
53
+ background: #e9ecef;
54
+ border-radius: 10px;
55
+ padding: 0.5rem;
56
+ margin: 0.5rem 0;
57
+ }
58
+ .sentence-builder {
59
+ background: #ffffff;
60
+ border: 2px solid #e0e0e0;
61
+ border-radius: 10px;
62
+ padding: 1rem;
63
+ margin: 1rem 0;
64
+ min-height: 100px;
65
+ color: #1a1a1a;
66
+ font-weight: 600;
67
+ font-size: 1.1rem;
68
+ }
69
+ .control-panel {
70
+ background: #ffffff;
71
+ border: 2px solid #dee2e6;
72
+ border-radius: 10px;
73
+ padding: 1rem;
74
+ margin: 1rem 0;
75
+ color: #1a1a1a;
76
+ font-weight: 600;
77
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
78
+ }
79
+ </style>
80
+ """, unsafe_allow_html=True)
81
+
82
+ # Initialize Gemini AI
83
+ @st.cache_resource
84
+ def initialize_gemini():
85
+ try:
86
+ genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
87
+ model = genai.GenerativeModel('gemini-pro')
88
+ return model
89
+ except Exception as e:
90
+ st.error(f"Failed to initialize Gemini AI: {e}")
91
+ return None
92
+
93
+ # Load model and labels
94
+ @st.cache_resource
95
+ def load_model_and_labels():
96
+ try:
97
+ interpreter = tf.lite.Interpreter(model_path="models/model.tflite")
98
+ interpreter.allocate_tensors()
99
+
100
+ with open("models/labels.txt", "r") as f:
101
+ labels = [line.strip() for line in f.readlines()]
102
+
103
+ return interpreter, labels
104
+ except Exception as e:
105
+ st.error(f"Error loading model: {e}")
106
+ return None, []
107
+
108
+ # Initialize MediaPipe
109
+ @st.cache_resource
110
+ def initialize_mediapipe():
111
+ mp_hands = mp.solutions.hands
112
+ hands = mp_hands.Hands(
113
+ static_image_mode=False,
114
+ max_num_hands=2,
115
+ min_detection_confidence=0.7,
116
+ min_tracking_confidence=0.5
117
+ )
118
+ mp_drawing = mp.solutions.drawing_utils
119
+ return hands, mp_drawing, mp_hands
120
+
121
+ class EnhancedSignInterpreter(VideoTransformerBase):
122
+ def __init__(self, confidence_threshold=0.7, sequence_length=10):
123
+ self.interpreter, self.labels = load_model_and_labels()
124
+ self.hands, self.mp_drawing, self.mp_hands = initialize_mediapipe()
125
+ self.gemini_model = initialize_gemini()
126
+
127
+ # Enhanced features
128
+ self.confidence_threshold = confidence_threshold
129
+ self.sequence_length = sequence_length
130
+ self.gesture_sequence = deque(maxlen=sequence_length)
131
+ self.last_prediction = ""
132
+ self.last_confidence = 0.0
133
+ self.sentence_builder = []
134
+ self.last_gesture_time = time.time()
135
+ self.gesture_hold_time = 1.5 # seconds to hold gesture for confirmation
136
+
137
+ # Prediction smoothing
138
+ self.prediction_buffer = deque(maxlen=5)
139
+
140
+ def extract_landmarks(self, image):
141
+ """Extract hand landmarks using MediaPipe"""
142
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
143
+ results = self.hands.process(image_rgb)
144
+
145
+ if results.multi_hand_landmarks:
146
+ landmarks = []
147
+ for hand_landmarks in results.multi_hand_landmarks:
148
+ for landmark in hand_landmarks.landmark:
149
+ landmarks.extend([landmark.x, landmark.y, landmark.z])
150
+
151
+ # Pad or truncate to 63 features (21 landmarks * 3 coordinates)
152
+ if len(landmarks) < 63:
153
+ landmarks.extend([0.0] * (63 - len(landmarks)))
154
+ else:
155
+ landmarks = landmarks[:63]
156
+
157
+ return np.array(landmarks, dtype=np.float32)
158
+ return None
159
+
160
+ def predict_gesture(self, landmarks):
161
+ """Predict gesture from landmarks"""
162
+ if self.interpreter is None or landmarks is None:
163
+ return "No gesture", 0.0
164
+
165
+ try:
166
+ input_details = self.interpreter.get_input_details()
167
+ output_details = self.interpreter.get_output_details()
168
+
169
+ # Reshape landmarks for model input
170
+ input_data = landmarks.reshape(1, -1)
171
+
172
+ self.interpreter.set_tensor(input_details[0]['index'], input_data)
173
+ self.interpreter.invoke()
174
+
175
+ output_data = self.interpreter.get_tensor(output_details[0]['index'])
176
+ predicted_class = np.argmax(output_data)
177
+ confidence = float(np.max(output_data))
178
+
179
+ if predicted_class < len(self.labels):
180
+ return self.labels[predicted_class], confidence
181
+ else:
182
+ return "Unknown", confidence
183
+
184
+ except Exception as e:
185
+ return f"Error: {str(e)}", 0.0
186
+
187
+ def smooth_prediction(self, prediction, confidence):
188
+ """Smooth predictions using buffer"""
189
+ self.prediction_buffer.append((prediction, confidence))
190
+
191
+ # Get most common prediction in buffer
192
+ predictions = [p[0] for p in self.prediction_buffer]
193
+ confidences = [p[1] for p in self.prediction_buffer]
194
+
195
+ # Find most frequent prediction
196
+ unique_predictions = list(set(predictions))
197
+ if unique_predictions:
198
+ most_common = max(unique_predictions, key=predictions.count)
199
+ avg_confidence = np.mean([c for p, c in self.prediction_buffer if p == most_common])
200
+ return most_common, avg_confidence
201
+
202
+ return prediction, confidence
203
+
204
+ def update_sentence_builder(self, gesture, confidence):
205
+ """Update sentence builder with confirmed gestures"""
206
+ current_time = time.time()
207
+ cooldown_period = 0.5 # Time to wait before accepting new gestures
208
+
209
+ # Check if the gesture is valid and meets confidence threshold
210
+ is_valid_gesture = (gesture not in ["No gesture", "Unknown"] and
211
+ confidence > self.confidence_threshold)
212
+
213
+ # Time elapsed since last gesture
214
+ time_elapsed = current_time - self.last_gesture_time
215
+
216
+ if is_valid_gesture:
217
+ # Case 1: New gesture detected
218
+ if gesture != self.last_prediction:
219
+ self.last_prediction = gesture
220
+ self.last_confidence = confidence
221
+ self.last_gesture_time = current_time
222
+
223
+ # Case 2: Gesture held long enough and cooldown period passed
224
+ elif (time_elapsed > self.gesture_hold_time and
225
+ (not self.sentence_builder or
226
+ time_elapsed > self.gesture_hold_time + cooldown_period)):
227
+ if not self.sentence_builder or self.sentence_builder[-1] != gesture:
228
+ self.sentence_builder.append(gesture)
229
+ self.last_gesture_time = current_time
230
+ self.last_prediction = "" # Reset prediction to allow new gestures
231
+ else:
232
+ # Reset prediction if gesture is invalid or confidence is low
233
+ if time_elapsed > cooldown_period:
234
+ self.last_prediction = ""
235
+
236
+ def generate_ai_interpretation(self, gestures):
237
+ """Generate AI interpretation using Gemini"""
238
+ if not gestures or not self.gemini_model:
239
+ return "No interpretation available"
240
+
241
+ try:
242
+ gesture_text = " ".join(gestures)
243
+ prompt = f"""
244
+ Convert these sign language gestures into a natural, meaningful sentence: {gesture_text}
245
+
246
+ Guidelines:
247
+ - Form complete, grammatically correct sentences
248
+ - Add appropriate articles, prepositions, and conjunctions
249
+ - Consider context and common sign language patterns
250
+ - If it's a sequence of letters, try to form words
251
+ - If it's numbers, consider if they represent quantities, dates, or codes
252
+ - Make the output conversational and natural
253
+
254
+ Gestures: {gesture_text}
255
+
256
+ Natural interpretation:
257
+ """
258
+
259
+ response = self.gemini_model.generate_content(prompt)
260
+ return response.text.strip()
261
+ except Exception as e:
262
+ return f"AI interpretation error: {str(e)}"
263
+
264
+ def recv(self, frame):
265
+ image = frame.to_ndarray(format="bgr24")
266
+
267
+ # Extract landmarks
268
+ landmarks = self.extract_landmarks(image)
269
+
270
+ if landmarks is not None:
271
+ # Predict gesture
272
+ gesture, confidence = self.predict_gesture(landmarks)
273
+
274
+ # Smooth prediction
275
+ gesture, confidence = self.smooth_prediction(gesture, confidence)
276
+
277
+ # Update sentence builder
278
+ self.update_sentence_builder(gesture, confidence)
279
+
280
+ # Draw hand landmarks
281
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
282
+ results = self.hands.process(image_rgb)
283
+
284
+ if results.multi_hand_landmarks:
285
+ for hand_landmarks in results.multi_hand_landmarks:
286
+ self.mp_drawing.draw_landmarks(
287
+ image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS)
288
+
289
+ # Draw prediction info
290
+ color = (0, 255, 0) if confidence > self.confidence_threshold else (0, 165, 255)
291
+ cv2.putText(image, f"{gesture} ({confidence:.2f})",
292
+ (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2)
293
+
294
+ # Store current prediction for UI
295
+ st.session_state.current_gesture = gesture
296
+ st.session_state.current_confidence = confidence
297
+ st.session_state.sentence_builder = self.sentence_builder.copy()
298
+ else:
299
+ st.session_state.current_gesture = "No hand detected"
300
+ st.session_state.current_confidence = 0.0
301
+
302
+ return av.VideoFrame.from_ndarray(image, format="bgr24")
303
+
304
+ def main():
305
+ # Header
306
+ st.markdown("""
307
+ <div class="main-header">
308
+ <h1>🤟 Enhanced AI Sign Language Interpreter</h1>
309
+ <p>Advanced real-time interpretation with AI-powered sentence formation</p>
310
+ </div>
311
+ """, unsafe_allow_html=True)
312
+
313
+ # Initialize session state
314
+ if 'current_gesture' not in st.session_state:
315
+ st.session_state.current_gesture = "Ready to start"
316
+ st.session_state.current_confidence = 0.0
317
+ st.session_state.sentence_builder = []
318
+ st.session_state.ai_interpretation = ""
319
+
320
+ # Sidebar controls
321
+ with st.sidebar:
322
+ st.markdown("### 🎛️ Control Panel")
323
+
324
+ confidence_threshold = st.slider(
325
+ "Confidence Threshold",
326
+ min_value=0.1,
327
+ max_value=1.0,
328
+ value=0.7,
329
+ step=0.1,
330
+ help="Minimum confidence required for gesture recognition"
331
+ )
332
+
333
+ sequence_length = st.slider(
334
+ "Sequence Memory",
335
+ min_value=5,
336
+ max_value=20,
337
+ value=10,
338
+ help="Number of gestures to remember for sentence building"
339
+ )
340
+
341
+ gesture_hold_time = st.slider(
342
+ "Gesture Hold Time (seconds)",
343
+ min_value=0.5,
344
+ max_value=3.0,
345
+ value=1.5,
346
+ step=0.1,
347
+ help="Time to hold gesture for confirmation"
348
+ )
349
+
350
+ st.markdown("### 🎯 Quick Actions")
351
+ if st.button("🗑️ Clear Sentence", use_container_width=True):
352
+ st.session_state.sentence_builder = []
353
+ st.session_state.ai_interpretation = ""
354
+
355
+ if st.button("🤖 Generate AI Interpretation", use_container_width=True):
356
+ if st.session_state.sentence_builder:
357
+ interpreter = EnhancedSignInterpreter()
358
+ st.session_state.ai_interpretation = interpreter.generate_ai_interpretation(
359
+ st.session_state.sentence_builder
360
+ )
361
+
362
+ # Display supported gestures
363
+ st.markdown("### 📋 Supported Gestures")
364
+ st.markdown("**Numbers:** 1, 2, 4, 5, 6, 7, 8")
365
+ st.markdown("**Letters:** A-X (except E)")
366
+
367
+ # Main content
368
+ col1, col2 = st.columns([2, 1])
369
+
370
+ with col1:
371
+ st.markdown("### 📹 Live Camera Feed")
372
+
373
+ # WebRTC configuration
374
+ rtc_configuration = RTCConfiguration({
375
+ "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
376
+ })
377
+
378
+ # Video streamer
379
+ webrtc_ctx = webrtc_streamer(
380
+ key="enhanced-sign-interpreter",
381
+ video_transformer_factory=lambda: EnhancedSignInterpreter(
382
+ confidence_threshold=confidence_threshold,
383
+ sequence_length=sequence_length
384
+ ),
385
+ rtc_configuration=rtc_configuration,
386
+ media_stream_constraints={"video": True, "audio": False},
387
+ async_processing=True,
388
+ )
389
+
390
+ with col2:
391
+ st.markdown("### 🎯 Recognition Results")
392
+
393
+ # Current prediction
394
+ st.markdown(f"""
395
+ <div class="prediction-box">
396
+ <h3>{st.session_state.current_gesture}</h3>
397
+ <div class="confidence-meter">
398
+ <div style="background: {'#28a745' if st.session_state.current_confidence > confidence_threshold else '#ffc107'};
399
+ width: {st.session_state.current_confidence * 100}%;
400
+ height: 20px;
401
+ border-radius: 10px;
402
+ transition: all 0.3s ease;">
403
+ </div>
404
+ <small>Confidence: {st.session_state.current_confidence:.1%}</small>
405
+ </div>
406
+ </div>
407
+ """, unsafe_allow_html=True)
408
+
409
+ # Sentence builder
410
+ st.markdown("### 📝 Sentence Builder")
411
+ sentence_text = " → ".join(st.session_state.sentence_builder) if st.session_state.sentence_builder else "Start making gestures..."
412
+
413
+ st.markdown(f"""
414
+ <div class="sentence-builder">
415
+ <strong>Gestures:</strong><br>
416
+ {sentence_text}
417
+ </div>
418
+ """, unsafe_allow_html=True)
419
+
420
+ # AI Interpretation
421
+ if st.session_state.ai_interpretation:
422
+ st.markdown("### 🤖 AI Interpretation")
423
+ st.markdown(f"""
424
+ <div class="feature-card">
425
+ <strong>Natural Language:</strong><br>
426
+ {st.session_state.ai_interpretation}
427
+ </div>
428
+ """, unsafe_allow_html=True)
429
+
430
+ # Statistics and tips
431
+ st.markdown("---")
432
+ col1, col2, col3 = st.columns(3)
433
+
434
+ with col1:
435
+ st.markdown("""
436
+ <div class="feature-card">
437
+ <h4>💡 Tips for Better Recognition</h4>
438
+ <ul>
439
+ <li>Ensure good lighting</li>
440
+ <li>Keep hand centered in frame</li>
441
+ <li>Hold gestures steady</li>
442
+ <li>Avoid cluttered backgrounds</li>
443
+ </ul>
444
+ </div>
445
+ """, unsafe_allow_html=True)
446
+
447
+ with col2:
448
+ st.markdown(f"""
449
+ <div class="feature-card">
450
+ <h4>📊 Session Statistics</h4>
451
+ <ul>
452
+ <li>Gestures Captured: {len(st.session_state.sentence_builder)}</li>
453
+ <li>Current Confidence: {st.session_state.current_confidence:.1%}</li>
454
+ <li>Threshold: {confidence_threshold:.1%}</li>
455
+ </ul>
456
+ </div>
457
+ """, unsafe_allow_html=True)
458
+
459
+ with col3:
460
+ st.markdown("""
461
+ <div class="feature-card">
462
+ <h4>🚀 Enhanced Features</h4>
463
+ <ul>
464
+ <li>Confidence threshold control</li>
465
+ <li>Gesture sequence memory</li>
466
+ <li>AI sentence formation</li>
467
+ <li>Real-time smoothing</li>
468
+ </ul>
469
+ </div>
470
+ """, unsafe_allow_html=True)
471
+
472
+ if __name__ == "__main__":
473
+ main()
app_new.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase, RTCConfiguration
3
+ import cv2
4
+ import numpy as np
5
+ import mediapipe as mp
6
+ import tensorflow as tf
7
+ import threading
8
+ import time
9
+ import google.generativeai as genai
10
+ from av import VideoFrame
11
+
12
+ # Page config
13
+ st.set_page_config(
14
+ page_title="AI Sign Language Interpreter",
15
+ page_icon="🤟",
16
+ layout="wide",
17
+ initial_sidebar_state="expanded"
18
+ )
19
+
20
+ # Custom CSS
21
+ st.markdown("""
22
+ <style>
23
+ .main {
24
+ background-color: #f8f9fa;
25
+ }
26
+ .stApp {
27
+ max-width: 1200px;
28
+ margin: 0 auto;
29
+ }
30
+ .prediction-box {
31
+ background-color: white;
32
+ padding: 2rem;
33
+ border-radius: 10px;
34
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
35
+ margin-bottom: 1rem;
36
+ }
37
+ .interpretation-box {
38
+ background-color: #f0f7ff;
39
+ padding: 2rem;
40
+ border-radius: 10px;
41
+ border: 1px solid #cce5ff;
42
+ margin-bottom: 1rem;
43
+ }
44
+ .stButton>button {
45
+ background-color: #4c51bf;
46
+ color: white;
47
+ font-weight: bold;
48
+ }
49
+ .stButton>button:hover {
50
+ background-color: #434190;
51
+ }
52
+ </style>
53
+ """, unsafe_allow_html=True)
54
+
55
+ # Initialize Gemini AI
56
+ @st.cache_resource
57
+ def initialize_gemini():
58
+ try:
59
+ genai.configure(api_key=st.secrets["GEMINI_API_KEY"])
60
+ model = genai.GenerativeModel('gemini-pro')
61
+ return model
62
+ except Exception as e:
63
+ st.error(f"Error initializing Gemini AI: {e}")
64
+ return None
65
+
66
+ # RTC Configuration
67
+ RTC_CONFIGURATION = RTCConfiguration({
68
+ "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
69
+ })
70
+
71
+ # Global variables for thread-safe prediction sharing
72
+ prediction_lock = threading.Lock()
73
+ current_prediction = {
74
+ "gesture": "Show your hand",
75
+ "confidence": 0.0,
76
+ "interpretation": ""
77
+ }
78
+
79
+ # Load model and labels
80
+ @st.cache_resource
81
+ def load_model():
82
+ try:
83
+ interpreter = tf.lite.Interpreter(model_path="models/model.tflite")
84
+ interpreter.allocate_tensors()
85
+
86
+ with open("models/labels.txt", 'r') as f:
87
+ labels = [line.strip() for line in f.readlines()]
88
+
89
+ return interpreter, labels
90
+ except Exception as e:
91
+ st.error(f"Error loading model: {e}")
92
+ return None, []
93
+
94
+ interpreter, labels = load_model()
95
+ gemini_model = initialize_gemini()
96
+
97
+ # MediaPipe setup
98
+ @st.cache_resource
99
+ def get_mediapipe_hands():
100
+ mp_hands = mp.solutions.hands
101
+ mp_drawing = mp.solutions.drawing_utils
102
+ hands = mp_hands.Hands(
103
+ static_image_mode=False,
104
+ max_num_hands=1,
105
+ min_detection_confidence=0.7,
106
+ min_tracking_confidence=0.5
107
+ )
108
+ return mp_hands, mp_drawing, hands
109
+
110
+ mp_hands, mp_drawing, hands = get_mediapipe_hands()
111
+
112
+ def extract_hand_features(landmarks):
113
+ """Extract features from hand landmarks."""
114
+ try:
115
+ landmarks_array = np.array([(lm.x, lm.y, lm.z) for lm in landmarks.landmark])
116
+ if len(landmarks_array) != 21:
117
+ return np.zeros(63)
118
+
119
+ wrist = landmarks_array[0]
120
+ normalized = landmarks_array - wrist
121
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist)
122
+
123
+ if hand_size > 0:
124
+ normalized = normalized / hand_size
125
+
126
+ features = normalized.flatten()
127
+ if len(features) != 63:
128
+ features = np.pad(features, (0, max(0, 63 - len(features))))[:63]
129
+
130
+ return features
131
+ except Exception as e:
132
+ print(f"Error extracting features: {e}")
133
+ return np.zeros(63)
134
+
135
+ def predict_gesture(features):
136
+ """Predict gesture from features."""
137
+ if interpreter is None:
138
+ return "Model not loaded", 0.0
139
+
140
+ try:
141
+ input_data = np.expand_dims(features, axis=0).astype(np.float32)
142
+ input_details = interpreter.get_input_details()
143
+ output_details = interpreter.get_output_details()
144
+
145
+ interpreter.set_tensor(input_details[0]['index'], input_data)
146
+ interpreter.invoke()
147
+
148
+ output = interpreter.get_tensor(output_details[0]['index'])
149
+ predicted_class = np.argmax(output[0])
150
+
151
+ exp_output = np.exp(output[0] - np.max(output[0]))
152
+ softmax_output = exp_output / np.sum(exp_output)
153
+ confidence = float(softmax_output[predicted_class])
154
+
155
+ gesture = labels[predicted_class] if predicted_class < len(labels) else str(predicted_class)
156
+ return gesture, confidence
157
+ except Exception as e:
158
+ print(f"Prediction error: {e}")
159
+ return "Error", 0.0
160
+
161
+ def get_gesture_interpretation(gesture_sequence):
162
+ """Get interpretation from Gemini AI."""
163
+ if not gemini_model or not gesture_sequence:
164
+ return ""
165
+
166
+ try:
167
+ prompt = f"""
168
+ As a sign language interpreter, analyze this sequence of gestures and provide a natural interpretation:
169
+ Gestures detected: {', '.join(gesture_sequence)}
170
+
171
+ Provide a clear and concise interpretation. If the gestures form a word or phrase, explain its meaning.
172
+ If they're individual letters/numbers, format them clearly.
173
+ """
174
+
175
+ response = gemini_model.generate_content(prompt)
176
+ return response.text
177
+ except Exception as e:
178
+ print(f"Gemini AI error: {e}")
179
+ return ""
180
+
181
+ class GestureRecognizer(VideoTransformerBase):
182
+ def __init__(self):
183
+ self.frame_count = 0
184
+ self.last_prediction_time = 0
185
+ self.prediction_interval = 0.2
186
+ self.gesture_history = []
187
+ self.last_interpretation_time = 0
188
+ self.interpretation_interval = 2.0
189
+
190
+ def recv(self, frame: VideoFrame) -> VideoFrame:
191
+ image = frame.to_ndarray(format="bgr24")
192
+ self.frame_count += 1
193
+ current_time = time.time()
194
+
195
+ if current_time - self.last_prediction_time > self.prediction_interval:
196
+ self.last_prediction_time = current_time
197
+
198
+ try:
199
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
200
+ results = hands.process(rgb_image)
201
+
202
+ if results.multi_hand_landmarks:
203
+ hand_landmarks = results.multi_hand_landmarks[0]
204
+
205
+ mp_drawing.draw_landmarks(
206
+ image, hand_landmarks, mp_hands.HAND_CONNECTIONS,
207
+ mp_drawing.DrawingSpec(color=(0, 0, 255), thickness=2, circle_radius=2),
208
+ mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2)
209
+ )
210
+
211
+ features = extract_hand_features(hand_landmarks)
212
+ gesture, confidence = predict_gesture(features)
213
+
214
+ if confidence > 0.5:
215
+ self.gesture_history.append(gesture)
216
+ self.gesture_history = self.gesture_history[-10:] # Keep last 10 gestures
217
+
218
+ with prediction_lock:
219
+ current_prediction["gesture"] = gesture
220
+ current_prediction["confidence"] = confidence
221
+
222
+ # Update interpretation periodically
223
+ if current_time - self.last_interpretation_time > self.interpretation_interval:
224
+ self.last_interpretation_time = current_time
225
+ interpretation = get_gesture_interpretation(self.gesture_history)
226
+ if interpretation:
227
+ current_prediction["interpretation"] = interpretation
228
+ else:
229
+ with prediction_lock:
230
+ current_prediction["gesture"] = "No hand detected"
231
+ current_prediction["confidence"] = 0.0
232
+ except Exception as e:
233
+ print(f"Processing error: {e}")
234
+ with prediction_lock:
235
+ current_prediction["gesture"] = "Processing error"
236
+ current_prediction["confidence"] = 0.0
237
+
238
+ # Add text overlay
239
+ with prediction_lock:
240
+ display_text = current_prediction["gesture"]
241
+ confidence = current_prediction["confidence"]
242
+
243
+ color = (0, 255, 0) if confidence > 0.5 else (0, 0, 255)
244
+ cv2.putText(
245
+ image,
246
+ f"{display_text} ({confidence:.2f})",
247
+ (20, 40),
248
+ cv2.FONT_HERSHEY_SIMPLEX,
249
+ 1.0,
250
+ color,
251
+ 2
252
+ )
253
+
254
+ return VideoFrame.from_ndarray(image, format="bgr24")
255
+
256
+ def main():
257
+ st.title("🤟 AI Sign Language Interpreter")
258
+ st.markdown("Transform sign language into text with real-time AI interpretation")
259
+
260
+ # Create columns for layout
261
+ col1, col2 = st.columns([2, 1])
262
+
263
+ with col1:
264
+ st.markdown("### 📹 Live Camera Feed")
265
+ ctx = webrtc_streamer(
266
+ key="gesture_recognition",
267
+ video_processor_factory=GestureRecognizer,
268
+ rtc_configuration=RTC_CONFIGURATION,
269
+ media_stream_constraints={"video": True, "audio": False},
270
+ async_processing=True,
271
+ )
272
+
273
+ with col2:
274
+ st.markdown("### 🎯 Recognition Results")
275
+
276
+ # Create placeholders for dynamic updates
277
+ prediction_placeholder = st.empty()
278
+ confidence_bar = st.empty()
279
+ interpretation_placeholder = st.empty()
280
+
281
+ if ctx.state.playing:
282
+ while ctx.state.playing:
283
+ with prediction_lock:
284
+ gesture = current_prediction["gesture"]
285
+ confidence = current_prediction["confidence"]
286
+ interpretation = current_prediction["interpretation"]
287
+
288
+ # Display current gesture
289
+ with prediction_placeholder.container():
290
+ st.markdown("""
291
+ <div class="prediction-box">
292
+ <h3 style="font-size: 1.5rem; margin-bottom: 0.5rem;">Current Gesture</h3>
293
+ <p style="font-size: 2rem; font-weight: bold; color: #4c51bf;">{}</p>
294
+ </div>
295
+ """.format(gesture), unsafe_allow_html=True)
296
+
297
+ # Display confidence bar
298
+ if confidence > 0.5 and gesture not in ["No hand detected", "Processing error", "Error"]:
299
+ confidence_bar.progress(confidence)
300
+
301
+ # Display interpretation
302
+ if interpretation:
303
+ with interpretation_placeholder.container():
304
+ st.markdown("""
305
+ <div class="interpretation-box">
306
+ <h3 style="font-size: 1.5rem; margin-bottom: 0.5rem;">AI Interpretation</h3>
307
+ <p style="font-size: 1.2rem;">{}</p>
308
+ </div>
309
+ """.format(interpretation), unsafe_allow_html=True)
310
+ else:
311
+ confidence_bar.progress(0)
312
+ if gesture == "No hand detected":
313
+ interpretation_placeholder.info("Show your hand to begin")
314
+ else:
315
+ interpretation_placeholder.warning("Gesture not recognized clearly")
316
+
317
+ time.sleep(0.1)
318
+ else:
319
+ st.warning("📷 Turn on your webcam to start!")
320
+
321
+ # Instructions and supported gestures
322
+ st.markdown("---")
323
+
324
+ # Display supported gestures in an organized grid
325
+ st.markdown("### 🎯 Supported Gestures")
326
+
327
+ # Numbers
328
+ st.markdown("#### 🔢 Numbers")
329
+ number_labels = [label for label in labels if label.isdigit()]
330
+ if number_labels:
331
+ cols = st.columns(len(number_labels))
332
+ for i, label in enumerate(number_labels):
333
+ with cols[i]:
334
+ st.markdown(f"""
335
+ <div style="text-align: center; padding: 1rem; background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
336
+ <h3 style="font-size: 1.5rem; color: #4c51bf;">{label}</h3>
337
+ </div>
338
+ """, unsafe_allow_html=True)
339
+
340
+ # Letters
341
+ st.markdown("#### 🔤 Letters")
342
+ letter_labels = [label for label in labels if label.isalpha()]
343
+ if letter_labels:
344
+ # Display letters in rows of 8
345
+ for i in range(0, len(letter_labels), 8):
346
+ row_labels = letter_labels[i:i+8]
347
+ cols = st.columns(len(row_labels))
348
+ for j, label in enumerate(row_labels):
349
+ with cols[j]:
350
+ st.markdown(f"""
351
+ <div style="text-align: center; padding: 1rem; background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
352
+ <h3 style="font-size: 1.5rem; color: #4c51bf;">{label}</h3>
353
+ </div>
354
+ """, unsafe_allow_html=True)
355
+
356
+ if __name__ == "__main__":
357
+ main()
create_sample_model.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ from utils.model_utils import create_model, convert_to_tflite
4
+
5
+ def create_sample_model():
6
+ """Create a sample model for demonstration purposes."""
7
+ print("Creating sample sign language model...")
8
+
9
+ # Model parameters
10
+ num_classes = 10 # Number of signs in labels.txt
11
+ sequence_length = 30
12
+ num_features = 126 # 63 landmarks + 63 velocity features
13
+
14
+ # Create model
15
+ model = create_model(num_classes, sequence_length, num_features)
16
+
17
+ # Generate dummy training data
18
+ X_train = np.random.random((1000, sequence_length, num_features)).astype(np.float32)
19
+ y_train = np.random.randint(0, num_classes, (1000,))
20
+
21
+ # Generate dummy validation data
22
+ X_val = np.random.random((200, sequence_length, num_features)).astype(np.float32)
23
+ y_val = np.random.randint(0, num_classes, (200,))
24
+
25
+ print("Training sample model...")
26
+ # Train for a few epochs
27
+ history = model.fit(
28
+ X_train, y_train,
29
+ validation_data=(X_val, y_val),
30
+ epochs=5,
31
+ batch_size=32,
32
+ verbose=1
33
+ )
34
+
35
+ # Save the model
36
+ model.save('models/sign_language_model.h5')
37
+ print("Model saved as 'models/sign_language_model.h5'")
38
+
39
+ # Convert to TFLite
40
+ print("Converting to TensorFlow Lite...")
41
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
42
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
43
+ tflite_model = converter.convert()
44
+
45
+ with open('models/model.tflite', 'wb') as f:
46
+ f.write(tflite_model)
47
+
48
+ print("TFLite model saved as 'models/model.tflite'")
49
+ print("Sample model creation completed!")
50
+
51
+ if __name__ == "__main__":
52
+ create_sample_model()
create_simple_model.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import os
3
+
4
+ def create_simple_tflite_model():
5
+ """Create a simple dummy TFLite model for testing."""
6
+
7
+ # Create a minimal TFLite model structure
8
+ # This is a placeholder that will work for basic testing
9
+
10
+ # For now, we'll create a simple file that can be loaded
11
+ # In a real scenario, you'd use TensorFlow to create this
12
+
13
+ print("Creating simple model structure...")
14
+
15
+ # Create a dummy model file (this is just for structure)
16
+ model_data = b'\x18\x00\x00\x00TFL3\x00\x00\x00\x00\x14\x00\x18\x00\x04\x00\x08\x00\x0c\x00\x10\x00\x14\x00\x00\x00'
17
+
18
+ with open('models/model.tflite', 'wb') as f:
19
+ f.write(model_data)
20
+
21
+ print("Simple model structure created!")
22
+ print("Note: This is a placeholder. For a real model, install TensorFlow and run create_sample_model.py")
23
+
24
+ if __name__ == "__main__":
25
+ create_simple_tflite_model()
data_collection.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import mediapipe as mp
3
+ import numpy as np
4
+ import os
5
+ import time
6
+ from sign_utils import MediaPipeLandmarksModel
7
+
8
+ class DataCollector:
9
+ def __init__(self):
10
+ self.landmark_model = MediaPipeLandmarksModel()
11
+ self.mp_drawing = mp.solutions.drawing_utils
12
+ self.mp_hands = mp.solutions.hands
13
+
14
+ def collect_sign_data(self, sign_name, num_sequences=30, sequence_length=30):
15
+ """Collect data for a specific sign."""
16
+
17
+ # Create directory for the sign
18
+ data_dir = f"dataset/{sign_name}"
19
+ os.makedirs(data_dir, exist_ok=True)
20
+
21
+ cap = cv2.VideoCapture(0)
22
+
23
+ # Arrays to store sequences
24
+ sequences = []
25
+
26
+ print(f"Collecting data for sign: {sign_name}")
27
+ print(f"Will collect {num_sequences} sequences of {sequence_length} frames each")
28
+ print("Press 's' to start collecting, 'q' to quit")
29
+
30
+ collecting = False
31
+ sequence = []
32
+ sequence_count = 0
33
+ frame_count = 0
34
+
35
+ while True:
36
+ ret, frame = cap.read()
37
+ if not ret:
38
+ break
39
+
40
+ # Flip frame horizontally for mirror effect
41
+ frame = cv2.flip(frame, 1)
42
+
43
+ # Extract landmarks
44
+ landmarks = self.landmark_model.predict_landmarks(frame)
45
+
46
+ # Draw landmarks
47
+ if landmarks:
48
+ frame = self.landmark_model.draw_landmarks(frame, landmarks)
49
+
50
+ # Display status
51
+ if collecting:
52
+ cv2.putText(frame, f'Collecting: {sign_name}', (20, 50),
53
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
54
+ cv2.putText(frame, f'Sequence: {sequence_count + 1}/{num_sequences}', (20, 100),
55
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
56
+ cv2.putText(frame, f'Frame: {frame_count}/{sequence_length}', (20, 150),
57
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
58
+
59
+ if landmarks:
60
+ # Extract features
61
+ features = self.landmark_model.extract_features(landmarks)
62
+ sequence.append(features)
63
+ frame_count += 1
64
+
65
+ if frame_count >= sequence_length:
66
+ # Save sequence
67
+ sequences.append(sequence.copy())
68
+ sequence = []
69
+ frame_count = 0
70
+ sequence_count += 1
71
+
72
+ if sequence_count >= num_sequences:
73
+ break
74
+
75
+ # Wait before next sequence
76
+ time.sleep(2)
77
+ else:
78
+ cv2.putText(frame, f'Ready to collect: {sign_name}', (20, 50),
79
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
80
+ cv2.putText(frame, "Press 's' to start", (20, 100),
81
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
82
+
83
+ cv2.imshow('Data Collection', frame)
84
+
85
+ key = cv2.waitKey(1) & 0xFF
86
+ if key == ord('s') and not collecting:
87
+ collecting = True
88
+ print("Started collecting...")
89
+ elif key == ord('q'):
90
+ break
91
+
92
+ cap.release()
93
+ cv2.destroyAllWindows()
94
+
95
+ # Save collected data
96
+ if sequences:
97
+ np.save(f"{data_dir}/sequences.npy", np.array(sequences))
98
+ print(f"Saved {len(sequences)} sequences for {sign_name}")
99
+
100
+ return sequences
101
+
102
+ def main():
103
+ collector = DataCollector()
104
+
105
+ # List of signs to collect
106
+ signs = ['hello', 'thank_you', 'please', 'sorry', 'yes', 'no', 'good', 'bad', 'help', 'stop']
107
+
108
+ print("Sign Language Data Collection Tool")
109
+ print("Available signs:", signs)
110
+
111
+ while True:
112
+ print("\nOptions:")
113
+ print("1. Collect data for a specific sign")
114
+ print("2. Collect data for all signs")
115
+ print("3. Exit")
116
+
117
+ choice = input("Enter your choice (1-3): ")
118
+
119
+ if choice == '1':
120
+ sign_name = input("Enter sign name: ").lower().replace(' ', '_')
121
+ num_sequences = int(input("Number of sequences (default 30): ") or 30)
122
+ collector.collect_sign_data(sign_name, num_sequences)
123
+
124
+ elif choice == '2':
125
+ for sign in signs:
126
+ print(f"\nPrepare to collect data for: {sign}")
127
+ input("Press Enter when ready...")
128
+ collector.collect_sign_data(sign, 30)
129
+
130
+ elif choice == '3':
131
+ break
132
+ else:
133
+ print("Invalid choice!")
134
+
135
+ if __name__ == "__main__":
136
+ main()
final_app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import cv2
3
+ import numpy as np
4
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase, RTCConfiguration
5
+ from av import VideoFrame
6
+ import mediapipe as mp
7
+ import tensorflow as tf
8
+ import threading
9
+ import time
10
+
11
+ # Page config
12
+ st.set_page_config(
13
+ page_title="Sign Language Digits Interpreter",
14
+ page_icon="🔢",
15
+ layout="wide"
16
+ )
17
+
18
+ # RTC Configuration for better WebRTC performance
19
+ RTC_CONFIGURATION = RTCConfiguration({
20
+ "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}]
21
+ })
22
+
23
+ # Global variables for thread-safe prediction sharing
24
+ prediction_lock = threading.Lock()
25
+ current_prediction = {"digit": "Show your hand", "confidence": 0.0}
26
+
27
+ # Load model and labels
28
+ @st.cache_resource
29
+ def load_model():
30
+ try:
31
+ interpreter = tf.lite.Interpreter(model_path="models/model.tflite")
32
+ interpreter.allocate_tensors()
33
+
34
+ with open("models/labels.txt", 'r') as f:
35
+ labels = [line.strip() for line in f.readlines()]
36
+
37
+ return interpreter, labels
38
+ except Exception as e:
39
+ st.error(f"Error loading model: {e}")
40
+ return None, []
41
+
42
+ interpreter, labels = load_model()
43
+
44
+
45
+ @st.cache_resource
46
+ def get_mediapipe_hands():
47
+ mp_hands = mp.solutions.hands
48
+ mp_drawing = mp.solutions.drawing_utils
49
+ hands = mp_hands.Hands(
50
+ static_image_mode=False,
51
+ max_num_hands=1,
52
+ min_detection_confidence=0.7,
53
+ min_tracking_confidence=0.5
54
+ )
55
+ return mp_hands, mp_drawing, hands
56
+
57
+ mp_hands, mp_drawing, hands = get_mediapipe_hands()
58
+
59
+ def extract_hand_features(landmarks):
60
+ """Extract 63 correct features from hand landmarks."""
61
+ try:
62
+ # Convert landmarks to numpy array
63
+ landmarks_array = np.array([(lm.x, lm.y, lm.z) for lm in landmarks.landmark])
64
+
65
+ # Ensure we have exactly 21 landmarks
66
+ if len(landmarks_array) != 21:
67
+ return np.zeros(63)
68
+
69
+ # Normalize relative to wrist (first landmark)
70
+ wrist = landmarks_array[0]
71
+ normalized = landmarks_array - wrist
72
+
73
+ # Scale by hand size (distance from wrist to middle finger tip)
74
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist)
75
+ if hand_size > 0:
76
+ normalized = normalized / hand_size
77
+
78
+ # Flatten to 63 features
79
+ features = normalized.flatten()
80
+
81
+ # Ensure exactly 63 features
82
+ if len(features) != 63:
83
+ features = np.pad(features, (0, max(0, 63 - len(features))))[:63]
84
+
85
+ return features
86
+ except Exception as e:
87
+ print(f"Error extracting features: {e}")
88
+ return np.zeros(63)
89
+
90
+ def predict_digit(features):
91
+ """Predict digit from features."""
92
+ if interpreter is None:
93
+ return "Model not loaded", 0.0
94
+
95
+ try:
96
+ # Prepare input
97
+ input_data = np.expand_dims(features, axis=0).astype(np.float32)
98
+
99
+ # Run inference
100
+ input_details = interpreter.get_input_details()
101
+ output_details = interpreter.get_output_details()
102
+
103
+ interpreter.set_tensor(input_details[0]['index'], input_data)
104
+ interpreter.invoke()
105
+
106
+ # Get prediction
107
+ output = interpreter.get_tensor(output_details[0]['index'])
108
+ predicted_class = np.argmax(output[0])
109
+ confidence = float(np.max(output[0]))
110
+
111
+ # Apply softmax to get better confidence scores
112
+ exp_output = np.exp(output[0] - np.max(output[0]))
113
+ softmax_output = exp_output / np.sum(exp_output)
114
+ confidence = float(softmax_output[predicted_class])
115
+
116
+ digit = labels[predicted_class] if predicted_class < len(labels) else str(predicted_class)
117
+ return digit, confidence
118
+ except Exception as e:
119
+ print(f"Prediction error: {e}")
120
+ return "Error", 0.0
121
+
122
+ class DigitRecognizer(VideoTransformerBase):
123
+ def __init__(self):
124
+ self.frame_count = 0
125
+ self.last_prediction_time = 0
126
+ self.prediction_interval = 0.2 # Predict every 200ms
127
+
128
+ def recv(self, frame: VideoFrame) -> VideoFrame:
129
+ image = frame.to_ndarray(format="bgr24")
130
+ self.frame_count += 1
131
+
132
+ current_time = time.time()
133
+
134
+ # Process prediction at regular intervals
135
+ if current_time - self.last_prediction_time > self.prediction_interval:
136
+ self.last_prediction_time = current_time
137
+
138
+ try:
139
+ # Convert to RGB for MediaPipe
140
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
141
+ results = hands.process(rgb_image)
142
+
143
+ if results.multi_hand_landmarks:
144
+ # Get first hand
145
+ hand_landmarks = results.multi_hand_landmarks[0]
146
+
147
+ # Draw landmarks
148
+ mp_drawing.draw_landmarks(
149
+ image, hand_landmarks, mp_hands.HAND_CONNECTIONS,
150
+ mp_drawing.DrawingSpec(color=(0, 0, 255), thickness=2, circle_radius=2),
151
+ mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2)
152
+ )
153
+
154
+ # Extract features and predict
155
+ features = extract_hand_features(hand_landmarks)
156
+ digit, confidence = predict_digit(features)
157
+
158
+ # Update global prediction
159
+ with prediction_lock:
160
+ current_prediction["digit"] = digit
161
+ current_prediction["confidence"] = confidence
162
+ else:
163
+ with prediction_lock:
164
+ current_prediction["digit"] = "No hand detected"
165
+ current_prediction["confidence"] = 0.0
166
+ except Exception as e:
167
+ print(f"Processing error: {e}")
168
+ with prediction_lock:
169
+ current_prediction["digit"] = "Processing error"
170
+ current_prediction["confidence"] = 0.0
171
+
172
+ # Add text overlay
173
+ with prediction_lock:
174
+ display_text = current_prediction["digit"]
175
+ confidence = current_prediction["confidence"]
176
+
177
+ color = (0, 255, 0) if confidence > 0.5 else (0, 0, 255)
178
+ cv2.putText(
179
+ image,
180
+ f"{display_text} ({confidence:.2f})",
181
+ (20, 40),
182
+ cv2.FONT_HERSHEY_SIMPLEX,
183
+ 1.0,
184
+ color,
185
+ 2
186
+ )
187
+
188
+ return VideoFrame.from_ndarray(image, format="bgr24")
189
+
190
+ # Main app
191
+ st.title("🤟 AI Sign Language Interpreter")
192
+ st.markdown("Show hand signs for numbers and letters to the camera!")
193
+
194
+ # Sidebar controls
195
+ st.sidebar.title("🎛️ Controls")
196
+ confidence_threshold = st.sidebar.slider("Confidence Threshold", 0.0, 1.0, 0.5, 0.1)
197
+ st.sidebar.markdown("---")
198
+ st.sidebar.markdown("### Model Info")
199
+ if interpreter:
200
+ st.sidebar.success("✅ Model loaded successfully")
201
+ st.sidebar.info(f"📊 Classes: {len(labels)}")
202
+ st.sidebar.info("🔢 Numbers: 1,2,4,5,6,7,8")
203
+ st.sidebar.info("🔤 Letters: A-X (except E)")
204
+ else:
205
+ st.sidebar.error("❌ Model not loaded")
206
+
207
+ # Create columns
208
+ col1, col2 = st.columns([2, 1])
209
+
210
+ with col1:
211
+ st.markdown("### 📹 Live Camera Feed")
212
+ # Webcam component
213
+ ctx = webrtc_streamer(
214
+ key="digit_recognition",
215
+ video_processor_factory=DigitRecognizer,
216
+ rtc_configuration=RTC_CONFIGURATION,
217
+ media_stream_constraints={"video": True, "audio": False},
218
+ async_processing=True,
219
+ )
220
+
221
+ with col2:
222
+ st.markdown("### 🎯 Current Prediction")
223
+
224
+ # Create placeholders for dynamic updates
225
+ prediction_placeholder = st.empty()
226
+ confidence_bar = st.empty()
227
+ confidence_text = st.empty()
228
+
229
+ # Update display in real-time
230
+ if ctx.state.playing:
231
+ while ctx.state.playing:
232
+ with prediction_lock:
233
+ digit = current_prediction["digit"]
234
+ confidence = current_prediction["confidence"]
235
+
236
+ # Display prediction
237
+ if confidence > confidence_threshold and digit not in ["No hand detected", "Processing error", "Error"]:
238
+ prediction_placeholder.markdown(f"# {digit}")
239
+ confidence_bar.progress(confidence)
240
+ confidence_text.success(f"Confidence: {confidence:.2f}")
241
+ else:
242
+ prediction_placeholder.markdown("# -")
243
+ confidence_bar.progress(0)
244
+ if digit == "No hand detected":
245
+ confidence_text.info("Show your hand to the camera")
246
+ else:
247
+ confidence_text.warning(f"Low confidence: {confidence:.2f}")
248
+
249
+ time.sleep(0.1) # Update every 100ms
250
+ else:
251
+ st.warning("📷 Turn on your webcam to start!")
252
+
253
+ # Instructions
254
+ st.markdown("---")
255
+ st.markdown("""
256
+ ### 📋 Instructions:
257
+ 1. **Allow camera access** when prompted
258
+ 2. **Position your hand** clearly in the camera view
259
+ 3. **Make gesture signs** for numbers or letters
260
+ 4. **Hold the sign steady** for best results
261
+ 5. **Adjust confidence threshold** if needed
262
+
263
+ ### 💡 Tips for Better Recognition:
264
+ - ✅ Ensure **good lighting**
265
+ - ✅ Keep your **hand centered** in the frame
266
+ - ✅ Make **clear, distinct** gesture signs
267
+ - ✅ **Hold steady** for 1-2 seconds
268
+ - ✅ Try different **hand orientations** if needed
269
+ - ✅ **Avoid background clutter** behind your hand
270
+
271
+ ### 🎯 Supported Gestures:
272
+ """)
273
+
274
+ # Display supported gestures
275
+ st.markdown("#### 🔢 Numbers:")
276
+ number_labels = [label for label in labels if label.isdigit()]
277
+ if number_labels:
278
+ cols = st.columns(min(len(number_labels), 8))
279
+ for i, label in enumerate(number_labels):
280
+ with cols[i % len(cols)]:
281
+ st.info(f"**{label}**")
282
+
283
+ st.markdown("#### 🔤 Letters:")
284
+ letter_labels = [label for label in labels if label.isalpha()]
285
+ if letter_labels:
286
+ # Display letters in rows of 8
287
+ for row_start in range(0, len(letter_labels), 8):
288
+ row_labels = letter_labels[row_start:row_start + 8]
289
+ cols = st.columns(len(row_labels))
290
+ for i, label in enumerate(row_labels):
291
+ with cols[i]:
292
+ st.info(f"**{label}**")
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
requirements_new.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit==1.28.0
2
+ streamlit-webrtc==0.47.1
3
+ opencv-python==4.8.1.78
4
+ mediapipe==0.10.7
5
+ tensorflow==2.13.0
6
+ numpy==1.24.3
7
+ google-generativeai==0.3.2
8
+ flask==2.3.3
9
+ av==10.0.0
10
+ protobuf==3.20.3
requirements_training.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ tensorflow>=2.0.0
2
+ opencv-python>=4.5.0
3
+ mediapipe>=0.8.0
4
+ scikit-learn>=0.24.0
5
+ matplotlib>=3.3.0
6
+ seaborn>=0.11.0
7
+ tqdm>=4.60.0
8
+ numpy>=1.19.0
run_app.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Startup script for AI Sign Language Interpreter
4
+ This script starts both the landing page (Flask) and the main app (Streamlit)
5
+ """
6
+
7
+ import subprocess
8
+ import threading
9
+ import time
10
+ import webbrowser
11
+ import sys
12
+ import os
13
+
14
+ def start_streamlit():
15
+ """Start Streamlit app"""
16
+ print("🚀 Starting Streamlit app...")
17
+ try:
18
+ subprocess.run([
19
+ sys.executable, '-m', 'streamlit', 'run', 'app_enhanced.py',
20
+ '--server.port=8502',
21
+ '--server.headless=true',
22
+ '--server.runOnSave=true'
23
+ ])
24
+ except KeyboardInterrupt:
25
+ print("Streamlit app stopped.")
26
+ except Exception as e:
27
+ print(f"Error starting Streamlit: {e}")
28
+
29
+ def start_flask():
30
+ """Start Flask server for landing page"""
31
+ print("🌐 Starting Flask server...")
32
+ try:
33
+ subprocess.run([sys.executable, 'server.py'])
34
+ except KeyboardInterrupt:
35
+ print("Flask server stopped.")
36
+ except Exception as e:
37
+ print(f"Error starting Flask: {e}")
38
+
39
+ def main():
40
+ print("=" * 60)
41
+ print("🤟 AI Sign Language Interpreter")
42
+ print("=" * 60)
43
+ print("Starting application servers...")
44
+ print()
45
+
46
+ # Check if required files exist
47
+ required_files = ['app_enhanced.py', 'server.py', 'models/model.tflite', 'models/labels.txt']
48
+ missing_files = [f for f in required_files if not os.path.exists(f)]
49
+
50
+ if missing_files:
51
+ print("❌ Missing required files:")
52
+ for file in missing_files:
53
+ print(f" - {file}")
54
+ print("\nPlease ensure all required files are present.")
55
+ return
56
+
57
+ print("✅ All required files found.")
58
+ print()
59
+
60
+ try:
61
+ # Start Streamlit in a separate thread
62
+ streamlit_thread = threading.Thread(target=start_streamlit, daemon=True)
63
+ streamlit_thread.start()
64
+
65
+ # Give Streamlit time to start
66
+ print("⏳ Waiting for Streamlit to initialize...")
67
+ time.sleep(5)
68
+
69
+ # Start Flask (this will block)
70
+ print("🌐 Flask server starting on http://localhost:5000")
71
+ print("🤖 Streamlit app available on http://localhost:8501")
72
+ print()
73
+ print("📖 Instructions:")
74
+ print(" 1. Visit http://localhost:5000 for the landing page")
75
+ print(" 2. Click 'Try it Out' to access the AI interpreter")
76
+ print(" 3. Allow camera access when prompted")
77
+ print(" 4. Start making sign language gestures!")
78
+ print()
79
+ print("Press Ctrl+C to stop all servers")
80
+ print("=" * 60)
81
+
82
+ # Open browser automatically
83
+ time.sleep(2)
84
+ webbrowser.open('http://localhost:5000')
85
+
86
+ # Start Flask server
87
+ start_flask()
88
+
89
+ except KeyboardInterrupt:
90
+ print("\n🛑 Shutting down servers...")
91
+ print("👋 Thank you for using AI Sign Language Interpreter!")
92
+ except Exception as e:
93
+ print(f"❌ Error: {e}")
94
+
95
+ if __name__ == "__main__":
96
+ main()
run_simple_app.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple startup script for the streamlined Sign Language Interpreter
4
+ """
5
+
6
+ import subprocess
7
+ import sys
8
+ import os
9
+ import time
10
+
11
+ def check_requirements():
12
+ """Check if required files exist"""
13
+ required_files = ['simple_sign_interpreter.py', 'sign_utils.py', 'models/model.tflite', 'models/labels.txt']
14
+ missing_files = [f for f in required_files if not os.path.exists(f)]
15
+
16
+ if missing_files:
17
+ print("❌ Missing required files:")
18
+ for file in missing_files:
19
+ print(f" - {file}")
20
+ print("\nPlease ensure all required files are present.")
21
+ return False
22
+
23
+ print("✅ All required files found.")
24
+ return True
25
+
26
+ def start_streamlit():
27
+ """Start the simple sign interpreter app"""
28
+ print("🤟 Starting Simple Sign Language Interpreter...")
29
+ print("=" * 50)
30
+
31
+ if not check_requirements():
32
+ return
33
+
34
+ try:
35
+ print("🚀 Starting Streamlit app on http://localhost:8501")
36
+ print("📖 Instructions:")
37
+ print(" 1. Allow camera access when prompted")
38
+ print(" 2. Make sign language gestures for numbers (1,2,4,5,6,7,8) or letters (A-X)")
39
+ print(" 3. Hold gestures steady for best recognition")
40
+ print(" 4. Adjust confidence threshold if needed")
41
+ print()
42
+ print("Press Ctrl+C to stop the application")
43
+ print("=" * 50)
44
+
45
+ # Start Streamlit
46
+ subprocess.run([
47
+ sys.executable, '-m', 'streamlit', 'run', 'simple_sign_interpreter.py',
48
+ '--server.port=8501',
49
+ '--server.headless=false'
50
+ ])
51
+
52
+ except KeyboardInterrupt:
53
+ print("\n🛑 Application stopped.")
54
+ print("👋 Thank you for using the Sign Language Interpreter!")
55
+ except Exception as e:
56
+ print(f"❌ Error starting application: {e}")
57
+
58
+ if __name__ == "__main__":
59
+ start_streamlit()
server.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, redirect, url_for
2
+ import subprocess
3
+ import threading
4
+ import time
5
+ import os
6
+
7
+ app = Flask(__name__)
8
+
9
+ # Global variable to track Streamlit process
10
+ streamlit_process = None
11
+
12
+ def start_streamlit():
13
+ """Start Streamlit app in background"""
14
+ global streamlit_process
15
+ try:
16
+ streamlit_process = subprocess.Popen([
17
+ 'streamlit', 'run', 'app_enhanced.py',
18
+ '--server.port=8502',
19
+ '--server.headless=true'
20
+ ])
21
+ print("Streamlit app started on port 8502")
22
+ except Exception as e:
23
+ print(f"Error starting Streamlit: {e}")
24
+
25
+ @app.route('/')
26
+ def landing_page():
27
+ """Serve the landing page"""
28
+ return render_template('index.html')
29
+
30
+ @app.route('/app')
31
+ def launch_app():
32
+ """Redirect to Streamlit app"""
33
+ return redirect('http://localhost:8502')
34
+
35
+ @app.route('/start-streamlit')
36
+ def start_streamlit_endpoint():
37
+ """Start Streamlit if not running"""
38
+ global streamlit_process
39
+ if streamlit_process is None or streamlit_process.poll() is not None:
40
+ threading.Thread(target=start_streamlit, daemon=True).start()
41
+ time.sleep(3) # Give Streamlit time to start
42
+ return redirect('http://localhost:8502')
43
+
44
+ if __name__ == '__main__':
45
+ # Start Streamlit in background
46
+ threading.Thread(target=start_streamlit, daemon=True).start()
47
+
48
+ # Start Flask server
49
+ print("Starting Flask server on http://localhost:5000")
50
+ print("Streamlit app will be available on http://localhost:8502")
51
+ app.run(host='0.0.0.0', port=5000, debug=True)
setup.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ import sys
4
+
5
+ def install_requirements():
6
+ """Install required packages."""
7
+ print("Installing required packages...")
8
+ try:
9
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
10
+ print("✅ Requirements installed successfully!")
11
+ except subprocess.CalledProcessError as e:
12
+ print(f"❌ Error installing requirements: {e}")
13
+ return False
14
+ return True
15
+
16
+ def create_directories():
17
+ """Create necessary directories."""
18
+ directories = [
19
+ "models",
20
+ "dataset",
21
+ "static/css",
22
+ "templates",
23
+ "utils",
24
+ "logs"
25
+ ]
26
+
27
+ for directory in directories:
28
+ os.makedirs(directory, exist_ok=True)
29
+ print(f"✅ Created directory: {directory}")
30
+
31
+ def create_sample_model():
32
+ """Create a sample model for testing."""
33
+ print("Creating sample model...")
34
+ try:
35
+ subprocess.check_call([sys.executable, "create_sample_model.py"])
36
+ print("✅ Sample model created successfully!")
37
+ except subprocess.CalledProcessError as e:
38
+ print(f"❌ Error creating sample model: {e}")
39
+ return False
40
+ return True
41
+
42
+ def main():
43
+ print("🤟 Sign Language Interpreter Setup")
44
+ print("=" * 40)
45
+
46
+ # Create directories
47
+ print("\n1. Creating directories...")
48
+ create_directories()
49
+
50
+ # Install requirements
51
+ print("\n2. Installing requirements...")
52
+ if not install_requirements():
53
+ print("Setup failed. Please install requirements manually.")
54
+ return
55
+
56
+ # Create sample model
57
+ print("\n3. Creating sample model...")
58
+ if not create_sample_model():
59
+ print("Warning: Sample model creation failed. You can create it later.")
60
+
61
+ print("\n🎉 Setup completed successfully!")
62
+ print("\nNext steps:")
63
+ print("1. Run 'python data_collection.py' to collect your own sign data")
64
+ print("2. Run 'python train_model.py' to train a custom model")
65
+ print("3. Run 'streamlit run app.py' to start the application")
66
+
67
+ if __name__ == "__main__":
68
+ main()
sign_utils.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import mediapipe as mp
3
+ import numpy as np
4
+ from typing import List, Tuple, Optional
5
+
6
+ # Try to import TensorFlow, handle gracefully if not available
7
+ try:
8
+ import tensorflow as tf
9
+ TF_AVAILABLE = True
10
+ except ImportError:
11
+ TF_AVAILABLE = False
12
+ print("Warning: TensorFlow not available. Some features may be limited.")
13
+
14
+ class MediaPipeLandmarksModel:
15
+ def __init__(self):
16
+ self.mp_hands = mp.solutions.hands
17
+ self.mp_drawing = mp.solutions.drawing_utils
18
+ self.hands = self.mp_hands.Hands(
19
+ static_image_mode=False,
20
+ max_num_hands=2,
21
+ min_detection_confidence=0.5,
22
+ min_tracking_confidence=0.5
23
+ )
24
+ self.sequence = []
25
+ self.sequence_length = 15 # Reduced for faster response
26
+
27
+ def predict_landmarks(self, image: np.ndarray) -> Optional[List[Tuple[float, float, float]]]:
28
+ """Extract hand landmarks from image."""
29
+ image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
30
+ results = self.hands.process(image_rgb)
31
+
32
+ if results.multi_hand_landmarks:
33
+ landmarks = []
34
+ for hand_landmarks in results.multi_hand_landmarks:
35
+ for landmark in hand_landmarks.landmark:
36
+ landmarks.append((landmark.x, landmark.y, landmark.z))
37
+ return landmarks
38
+ return None
39
+
40
+ def draw_landmarks(self, image: np.ndarray, landmarks) -> np.ndarray:
41
+ """Draw hand landmarks on image."""
42
+ if landmarks:
43
+ results = self.hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
44
+ if results.multi_hand_landmarks:
45
+ for hand_landmarks in results.multi_hand_landmarks:
46
+ self.mp_drawing.draw_landmarks(
47
+ image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS)
48
+ return image
49
+
50
+ def normalize_landmarks(self, landmarks: List[Tuple[float, float, float]]) -> np.ndarray:
51
+ """Normalize landmarks relative to wrist position."""
52
+ if not landmarks:
53
+ return np.zeros((21, 3))
54
+
55
+ landmarks_array = np.array(landmarks)
56
+
57
+ # Get wrist position (first landmark)
58
+ wrist = landmarks_array[0]
59
+
60
+ # Normalize relative to wrist
61
+ normalized = landmarks_array - wrist
62
+
63
+ # Scale by hand size (distance from wrist to middle finger tip)
64
+ if len(landmarks_array) >= 12:
65
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist)
66
+ if hand_size > 0:
67
+ normalized = normalized / hand_size
68
+
69
+ return normalized.flatten()
70
+
71
+ def extract_features(self, landmarks: List[Tuple[float, float, float]]) -> np.ndarray:
72
+ """Extract features from landmarks for model input."""
73
+ if not landmarks:
74
+ return np.zeros(63) # 21 landmarks * 3 coordinates
75
+
76
+ # Ensure we have exactly 21 landmarks (one hand)
77
+ if len(landmarks) > 21:
78
+ landmarks = landmarks[:21] # Take first hand only
79
+ elif len(landmarks) < 21:
80
+ # Pad with zeros if we have fewer landmarks
81
+ while len(landmarks) < 21:
82
+ landmarks.append((0.0, 0.0, 0.0))
83
+
84
+ # Convert to numpy array
85
+ landmarks_array = np.array(landmarks)
86
+
87
+ # Get wrist position (first landmark)
88
+ wrist = landmarks_array[0]
89
+
90
+ # Normalize relative to wrist
91
+ normalized = landmarks_array - wrist
92
+
93
+ # Scale by hand size (distance from wrist to middle finger tip)
94
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist) if len(landmarks_array) >= 12 else 1.0
95
+ if hand_size > 0:
96
+ normalized = normalized / hand_size
97
+
98
+ # Flatten to 63 dimensions (21 landmarks * 3 coordinates)
99
+ features = normalized.flatten()
100
+
101
+ # Ensure we have exactly 63 dimensions
102
+ if len(features) > 63:
103
+ features = features[:63]
104
+ elif len(features) < 63:
105
+ features = np.pad(features, (0, 63 - len(features)))
106
+
107
+ return features
108
+
109
+ def update_sequence(self, landmarks: List[Tuple[float, float, float]]) -> np.ndarray:
110
+ """Update sequence for temporal modeling."""
111
+ features = self.extract_features(landmarks)
112
+ self.sequence.append(features)
113
+
114
+ # Keep only last sequence_length frames
115
+ if len(self.sequence) > self.sequence_length:
116
+ self.sequence.pop(0)
117
+
118
+ # Pad sequence if needed
119
+ if len(self.sequence) < self.sequence_length:
120
+ padding = np.zeros((self.sequence_length - len(self.sequence), features.shape[0]))
121
+ sequence_array = np.vstack([padding, np.array(self.sequence)])
122
+ else:
123
+ sequence_array = np.array(self.sequence)
124
+
125
+ return sequence_array
126
+
127
+ def reset_sequence(self):
128
+ """Reset the sequence buffer."""
129
+ self.sequence = []
130
+
131
+ class SignLanguagePredictor:
132
+ def __init__(self, model_path: str, labels_path: str):
133
+ self.landmark_model = MediaPipeLandmarksModel()
134
+
135
+ # Load labels
136
+ try:
137
+ with open(labels_path, 'r') as f:
138
+ self.labels = [line.strip() for line in f.readlines()]
139
+ except FileNotFoundError:
140
+ print(f"Warning: Labels file not found at {labels_path}")
141
+ self.labels = ["Unknown"]
142
+
143
+ # Initialize TFLite interpreter if available
144
+ if TF_AVAILABLE:
145
+ try:
146
+ self.interpreter = tf.lite.Interpreter(model_path=model_path)
147
+ self.interpreter.allocate_tensors()
148
+ self.input_details = self.interpreter.get_input_details()
149
+ self.output_details = self.interpreter.get_output_details()
150
+ self.model_loaded = True
151
+ except Exception as e:
152
+ print(f"Warning: Could not load model: {e}")
153
+ self.model_loaded = False
154
+ else:
155
+ print("Warning: TensorFlow not available. Running in landmark-only mode.")
156
+ self.model_loaded = False
157
+
158
+ def predict(self, image: np.ndarray) -> Tuple[str, float]:
159
+ """Predict sign from image using static prediction for compatibility."""
160
+ # Use static prediction instead of temporal for now to avoid dimension issues
161
+ return self.predict_static(image)
162
+
163
+ def predict_static(self, image: np.ndarray) -> Tuple[str, float]:
164
+ """Predict sign from static image (single frame)."""
165
+ landmarks = self.landmark_model.predict_landmarks(image)
166
+
167
+ if not landmarks:
168
+ return "No hand detected", 0.0
169
+
170
+ if not self.model_loaded:
171
+ return "Hand detected (Model not available)", 0.5
172
+
173
+ try:
174
+ # Extract features
175
+ features = self.landmark_model.extract_features(landmarks)
176
+
177
+ # Get expected input shape from model
178
+ input_shape = self.input_details[0]['shape']
179
+
180
+ # Reshape features to match expected input shape
181
+ if len(input_shape) == 2: # If model expects 2D input (batch_size, features)
182
+ input_data = features.reshape(1, -1).astype(np.float32)
183
+ elif len(input_shape) == 3: # If model expects 3D input (batch_size, time_steps, features)
184
+ input_data = features.reshape(1, 1, -1).astype(np.float32)
185
+ else:
186
+ raise ValueError(f"Unexpected input shape: {input_shape}")
187
+
188
+ # Run inference
189
+ self.interpreter.set_tensor(self.input_details[0]['index'], input_data)
190
+ self.interpreter.invoke()
191
+
192
+ # Get prediction
193
+ output = self.interpreter.get_tensor(self.output_details[0]['index'])
194
+ predicted_class = np.argmax(output[0])
195
+ confidence = float(np.max(output[0]))
196
+
197
+ return self.labels[predicted_class], confidence
198
+ except Exception as e:
199
+ print(f"Error during static prediction: {e}")
200
+ return "Error during prediction", 0.0
simple_app.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import cv2
3
+ import numpy as np
4
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase
5
+ from av import VideoFrame
6
+ import mediapipe as mp
7
+ import tensorflow as tf
8
+
9
+ # Page config
10
+ st.set_page_config(
11
+ page_title="Sign Language Digits Interpreter",
12
+ page_icon="🔢",
13
+ layout="wide"
14
+ )
15
+
16
+ # Load model and labels
17
+ @st.cache_resource
18
+ def load_model():
19
+ try:
20
+ interpreter = tf.lite.Interpreter(model_path="models/model.tflite")
21
+ interpreter.allocate_tensors()
22
+
23
+ with open("models/labels.txt", 'r') as f:
24
+ labels = [line.strip() for line in f.readlines()]
25
+
26
+ return interpreter, labels
27
+ except Exception as e:
28
+ st.error(f"Error loading model: {e}")
29
+ return None, []
30
+
31
+ interpreter, labels = load_model()
32
+
33
+ # MediaPipe setup
34
+ mp_hands = mp.solutions.hands
35
+ mp_drawing = mp.solutions.drawing_utils
36
+ hands = mp_hands.Hands(
37
+ static_image_mode=False,
38
+ max_num_hands=1,
39
+ min_detection_confidence=0.5,
40
+ min_tracking_confidence=0.5
41
+ )
42
+
43
+ def extract_hand_features(landmarks):
44
+ """Extract 63 features from hand landmarks."""
45
+ if not landmarks:
46
+ return np.zeros(63)
47
+
48
+ # Convert landmarks to numpy array
49
+ landmarks_array = np.array([(lm.x, lm.y, lm.z) for lm in landmarks.landmark])
50
+
51
+ # Normalize relative to wrist (first landmark)
52
+ wrist = landmarks_array[0]
53
+ normalized = landmarks_array - wrist
54
+
55
+ # Scale by hand size
56
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist)
57
+ if hand_size > 0:
58
+ normalized = normalized / hand_size
59
+
60
+ # Flatten to 63 features
61
+ features = normalized.flatten()
62
+
63
+ # Ensure exactly 63 features
64
+ if len(features) > 63:
65
+ features = features[:63]
66
+ elif len(features) < 63:
67
+ features = np.pad(features, (0, 63 - len(features)))
68
+
69
+ return features
70
+
71
+ def predict_digit(features):
72
+ """Predict digit from features."""
73
+ if interpreter is None:
74
+ return "Model not loaded", 0.0
75
+
76
+ try:
77
+ # Prepare input
78
+ input_data = np.expand_dims(features, axis=0).astype(np.float32)
79
+
80
+ # Run inference
81
+ input_details = interpreter.get_input_details()
82
+ output_details = interpreter.get_output_details()
83
+
84
+ interpreter.set_tensor(input_details[0]['index'], input_data)
85
+ interpreter.invoke()
86
+
87
+ # Get prediction
88
+ output = interpreter.get_tensor(output_details[0]['index'])
89
+ predicted_class = np.argmax(output[0])
90
+ confidence = float(np.max(output[0]))
91
+
92
+ return labels[predicted_class] if predicted_class < len(labels) else str(predicted_class), confidence
93
+ except Exception as e:
94
+ return f"Error: {str(e)}", 0.0
95
+
96
+ class DigitRecognizer(VideoTransformerBase):
97
+ def __init__(self):
98
+ self.prediction = "Show your hand"
99
+ self.confidence = 0.0
100
+ self.frame_count = 0
101
+
102
+ def recv(self, frame: VideoFrame) -> VideoFrame:
103
+ image = frame.to_ndarray(format="bgr24")
104
+ self.frame_count += 1
105
+
106
+ # Process every 5th frame to reduce computation
107
+ if self.frame_count % 5 == 0:
108
+ # Convert to RGB for MediaPipe
109
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
110
+ results = hands.process(rgb_image)
111
+
112
+ if results.multi_hand_landmarks:
113
+ # Get first hand
114
+ hand_landmarks = results.multi_hand_landmarks[0]
115
+
116
+ # Draw landmarks
117
+ mp_drawing.draw_landmarks(
118
+ image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
119
+
120
+ # Extract features and predict
121
+ features = extract_hand_features(hand_landmarks)
122
+ self.prediction, self.confidence = predict_digit(features)
123
+ else:
124
+ self.prediction = "No hand detected"
125
+ self.confidence = 0.0
126
+
127
+ # Add text overlay
128
+ color = (0, 255, 0) if self.confidence > 0.5 else (0, 0, 255)
129
+ cv2.putText(
130
+ image,
131
+ f"{self.prediction} ({self.confidence:.2f})",
132
+ (20, 40),
133
+ cv2.FONT_HERSHEY_SIMPLEX,
134
+ 1.0,
135
+ color,
136
+ 2
137
+ )
138
+
139
+ return VideoFrame.from_ndarray(image, format="bgr24")
140
+
141
+ # Main app
142
+ st.title("🔢 Sign Language Digits Interpreter")
143
+ st.markdown("Show hand signs for digits 0-9 to the camera!")
144
+
145
+ # Sidebar controls
146
+ st.sidebar.title("Controls")
147
+ confidence_threshold = st.sidebar.slider("Confidence Threshold", 0.0, 1.0, 0.5)
148
+
149
+ # Create columns
150
+ col1, col2 = st.columns([2, 1])
151
+
152
+ with col1:
153
+ # Webcam component
154
+ ctx = webrtc_streamer(
155
+ key="digit_recognition",
156
+ video_processor_factory=DigitRecognizer,
157
+ media_stream_constraints={"video": True, "audio": False},
158
+ async_processing=True,
159
+ )
160
+
161
+ with col2:
162
+ st.markdown("### Current Prediction")
163
+
164
+ if ctx.video_processor:
165
+ # Display current prediction
166
+ prediction_placeholder = st.empty()
167
+ confidence_placeholder = st.empty()
168
+
169
+ if hasattr(ctx.video_processor, 'prediction'):
170
+ if ctx.video_processor.confidence > confidence_threshold:
171
+ prediction_placeholder.markdown(f"# {ctx.video_processor.prediction}")
172
+ else:
173
+ prediction_placeholder.markdown("# -")
174
+
175
+ confidence_placeholder.progress(int(ctx.video_processor.confidence * 100))
176
+ st.text(f"Confidence: {ctx.video_processor.confidence:.2f}")
177
+ else:
178
+ st.warning("Turn on your webcam to start!")
179
+
180
+ # Instructions
181
+ st.markdown("---")
182
+ st.markdown("""
183
+ ### Instructions:
184
+ 1. Allow camera access when prompted
185
+ 2. Show your hand clearly in the camera view
186
+ 3. Make digit signs (0-9) with your hand
187
+ 4. Hold the sign steady for best results
188
+ 5. Adjust confidence threshold if needed
189
+
190
+ ### Tips:
191
+ - Ensure good lighting
192
+ - Keep your hand in the center of the frame
193
+ - Make clear, distinct digit signs
194
+ - Wait a moment between different signs
195
+ """)
simple_sign_interpreter.py ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from streamlit_webrtc import webrtc_streamer, VideoTransformerBase
3
+ import cv2
4
+ import numpy as np
5
+ from av import VideoFrame
6
+ from sign_utils import SignLanguagePredictor, MediaPipeLandmarksModel
7
+
8
+ # Page config
9
+ st.set_page_config(
10
+ page_title="Sign Language Interpreter",
11
+ page_icon="🤟",
12
+ layout="wide"
13
+ )
14
+
15
+ # Custom CSS for better UI
16
+ st.markdown("""
17
+ <style>
18
+ .main-header {
19
+ padding: 1rem;
20
+ border-radius: 10px;
21
+ margin-bottom: 2rem;
22
+ text-align: center;
23
+ }
24
+ .prediction-box {
25
+ background-color: #f0f2f6;
26
+ border-radius: 10px;
27
+ padding: 1rem;
28
+ margin: 1rem 0;
29
+ text-align: center;
30
+ }
31
+ .instruction-box {
32
+ background-color: #ffffff;
33
+ border: 1px solid #e0e0e0;
34
+ border-radius: 10px;
35
+ padding: 1rem;
36
+ margin: 1rem 0;
37
+ }
38
+ </style>
39
+ """, unsafe_allow_html=True)
40
+
41
+ # Initialize models
42
+ @st.cache_resource
43
+ def load_models():
44
+ try:
45
+ predictor = SignLanguagePredictor(
46
+ model_path="models/model.tflite",
47
+ labels_path="models/labels.txt"
48
+ )
49
+ return predictor
50
+ except Exception as e:
51
+ st.error(f"Error loading models: {str(e)}")
52
+ return None
53
+
54
+ predictor = load_models()
55
+
56
+ class SignInterpreter(VideoTransformerBase):
57
+ def __init__(self, confidence_threshold=0.7):
58
+ self.predictor = predictor
59
+ self.landmark_model = MediaPipeLandmarksModel()
60
+ self.result_text = "Show your hand sign"
61
+ self.confidence = 0.0
62
+ self.landmarks = None
63
+ self.prediction_counter = {}
64
+ self.last_prediction = None
65
+ self.confidence_threshold = confidence_threshold
66
+ self.frame_count = 0
67
+ self.prediction_history = []
68
+ self.stable_prediction = None
69
+ self.stable_confidence = 0.0
70
+
71
+ # Enhanced prediction smoothing
72
+ self.prediction_buffer = []
73
+ self.buffer_size = 8 # Reduced for faster response
74
+ self.min_consistent_frames = 3 # Reduced for faster response
75
+
76
+ def recv(self, frame: VideoFrame) -> VideoFrame:
77
+ image = frame.to_ndarray(format="bgr24")
78
+ self.frame_count += 1
79
+
80
+ if self.predictor:
81
+ # Get landmarks
82
+ self.landmarks = self.landmark_model.predict_landmarks(image)
83
+
84
+ # Get prediction if hand is detected
85
+ if self.landmarks:
86
+ try:
87
+ # Use temporal prediction instead of static
88
+ prediction, raw_confidence = self.predictor.predict(image)
89
+
90
+ # Add to prediction buffer with higher threshold for confidence
91
+ if raw_confidence > 0.4: # Only add predictions with decent confidence
92
+ self.prediction_buffer.append((prediction, raw_confidence))
93
+ if len(self.prediction_buffer) > self.buffer_size:
94
+ self.prediction_buffer.pop(0)
95
+
96
+ # Get smoothed prediction
97
+ smoothed_prediction, smoothed_confidence = self._get_smoothed_prediction()
98
+
99
+ # Update current values
100
+ self.confidence = smoothed_confidence
101
+
102
+ # Check if prediction meets confidence threshold
103
+ if smoothed_confidence > self.confidence_threshold:
104
+ # Count consecutive predictions
105
+ if smoothed_prediction not in self.prediction_counter:
106
+ self.prediction_counter = {smoothed_prediction: 1}
107
+ else:
108
+ self.prediction_counter[smoothed_prediction] += 1
109
+
110
+ # Update stable prediction if we have enough consistent frames
111
+ max_pred = max(self.prediction_counter.items(), key=lambda x: x[1])
112
+ if max_pred[1] >= self.min_consistent_frames:
113
+ self.stable_prediction = smoothed_prediction
114
+ self.stable_confidence = smoothed_confidence
115
+ self.result_text = f"Sign: {smoothed_prediction}"
116
+ self.last_prediction = smoothed_prediction
117
+ else:
118
+ # Reset if confidence is too low
119
+ if self.frame_count % 30 == 0: # Reset every 30 frames
120
+ self.prediction_counter = {}
121
+ self.result_text = "Show your hand sign"
122
+
123
+ except Exception as e:
124
+ self.result_text = "Processing..."
125
+ self.confidence = 0.0
126
+ self.prediction_counter = {}
127
+ else:
128
+ self.result_text = "No hand detected"
129
+ self.confidence = 0.0
130
+ self.prediction_counter = {}
131
+ self.prediction_buffer = []
132
+
133
+ # Draw landmarks with better visualization
134
+ if self.landmarks:
135
+ image = self.landmark_model.draw_landmarks(image, self.landmarks)
136
+
137
+ # Enhanced text overlay with better positioning
138
+ self._draw_prediction_overlay(image)
139
+
140
+ return VideoFrame.from_ndarray(image, format="bgr24")
141
+
142
+ def _get_smoothed_prediction(self):
143
+ """Get smoothed prediction from buffer"""
144
+ if not self.prediction_buffer:
145
+ return "No prediction", 0.0
146
+
147
+ # Get predictions from last few frames
148
+ recent_predictions = self.prediction_buffer[-5:] if len(self.prediction_buffer) >= 5 else self.prediction_buffer
149
+
150
+ # Count occurrences of each prediction
151
+ prediction_counts = {}
152
+ confidence_sums = {}
153
+
154
+ for pred, conf in recent_predictions:
155
+ if pred not in prediction_counts:
156
+ prediction_counts[pred] = 0
157
+ confidence_sums[pred] = 0.0
158
+ prediction_counts[pred] += 1
159
+ confidence_sums[pred] += conf
160
+
161
+ # Get most frequent prediction
162
+ if prediction_counts:
163
+ most_frequent = max(prediction_counts.items(), key=lambda x: x[1])
164
+ avg_confidence = confidence_sums[most_frequent[0]] / prediction_counts[most_frequent[0]]
165
+ return most_frequent[0], avg_confidence
166
+
167
+ return "No prediction", 0.0
168
+
169
+ def _draw_prediction_overlay(self, image):
170
+ """Draw enhanced prediction overlay"""
171
+ height, width = image.shape[:2]
172
+
173
+ # Background rectangle for better text visibility
174
+ overlay = image.copy()
175
+ cv2.rectangle(overlay, (10, 10), (width-10, 100), (0, 0, 0), -1)
176
+ cv2.addWeighted(overlay, 0.7, image, 0.3, 0, image)
177
+
178
+ # Main prediction text
179
+ color = (0, 255, 0) if self.confidence > self.confidence_threshold else (0, 165, 255)
180
+ cv2.putText(
181
+ image,
182
+ self.result_text,
183
+ (20, 40),
184
+ cv2.FONT_HERSHEY_SIMPLEX,
185
+ 1.0,
186
+ color,
187
+ 2
188
+ )
189
+
190
+ # Confidence text
191
+ cv2.putText(
192
+ image,
193
+ f"Confidence: {self.confidence:.2f}",
194
+ (20, 70),
195
+ cv2.FONT_HERSHEY_SIMPLEX,
196
+ 0.6,
197
+ (255, 255, 255),
198
+ 1
199
+ )
200
+
201
+ # Threshold indicator
202
+ threshold_color = (0, 255, 0) if self.confidence > self.confidence_threshold else (0, 0, 255)
203
+ cv2.putText(
204
+ image,
205
+ f"Threshold: {self.confidence_threshold:.2f}",
206
+ (20, 90),
207
+ cv2.FONT_HERSHEY_SIMPLEX,
208
+ 0.5,
209
+ threshold_color,
210
+ 1
211
+ )
212
+
213
+ def main():
214
+ # Header
215
+ st.markdown("""
216
+ <div class="main-header">
217
+ <h1>🤟 Sign Language Interpreter</h1>
218
+ <p>Real-time sign language recognition using computer vision</p>
219
+ </div>
220
+ """, unsafe_allow_html=True)
221
+
222
+ # Sidebar controls
223
+ st.sidebar.title("⚙️ Controls")
224
+ confidence_threshold = st.sidebar.slider(
225
+ "Detection Confidence",
226
+ min_value=0.0,
227
+ max_value=1.0,
228
+ value=0.5, # Lowered for better live detection
229
+ help="Adjust the confidence threshold for sign detection"
230
+ )
231
+
232
+ # Create two columns for layout
233
+ col1, col2 = st.columns([2, 1])
234
+
235
+ with col1:
236
+ # Webcam component
237
+ st.markdown("### 📹 Camera Feed")
238
+ ctx = webrtc_streamer(
239
+ key="sign_language",
240
+ video_processor_factory=lambda: SignInterpreter(confidence_threshold),
241
+ media_stream_constraints={"video": True, "audio": False},
242
+ async_processing=True,
243
+ )
244
+
245
+ with col2:
246
+ # Current prediction display
247
+ st.markdown("### 🎯 Current Sign")
248
+ prediction_placeholder = st.empty()
249
+ confidence_meter = st.progress(0)
250
+
251
+ # Update prediction and confidence
252
+ if ctx.video_transformer:
253
+ if ctx.video_transformer.last_prediction and ctx.video_transformer.confidence > confidence_threshold:
254
+ prediction_placeholder.markdown(f"""
255
+ <div class="prediction-box">
256
+ <h2>{ctx.video_transformer.last_prediction}</h2>
257
+ </div>
258
+ """, unsafe_allow_html=True)
259
+ else:
260
+ prediction_placeholder.markdown("""
261
+ <div class="prediction-box">
262
+ <h2>-</h2>
263
+ </div>
264
+ """, unsafe_allow_html=True)
265
+
266
+ confidence_meter.progress(int(ctx.video_transformer.confidence * 100))
267
+ else:
268
+ st.info("📷 Start the webcam to begin recognition")
269
+
270
+ # Instructions
271
+ st.markdown("---")
272
+
273
+ # Create three columns for better layout
274
+ col1, col2, col3 = st.columns(3)
275
+
276
+ with col1:
277
+ st.markdown("### 📝 Instructions")
278
+ st.markdown("""
279
+ 1. Allow camera access when prompted
280
+ 2. Position your hand clearly in the camera view
281
+ 3. Make hand signs for:
282
+ - **Numbers:** 1, 2, 4, 5, 6, 7, 8
283
+ - **Letters:** A through X (except E)
284
+ 4. Hold each sign steady for best recognition
285
+ 5. Adjust the confidence threshold if needed
286
+ """)
287
+
288
+ with col2:
289
+ st.markdown("### 💡 Tips for Better Recognition")
290
+ st.markdown("""
291
+ - Ensure good lighting on your hands
292
+ - Keep your hand within the camera frame
293
+ - Make clear, distinct gestures
294
+ - Avoid rapid movements
295
+ - Use a plain background
296
+ - Keep hand at comfortable distance from camera
297
+ """)
298
+
299
+ with col3:
300
+ st.markdown("### 📊 Supported Gestures")
301
+ st.markdown("**Numbers:**")
302
+ st.markdown("1, 2, 4, 5, 6, 7, 8")
303
+ st.markdown("**Letters:**")
304
+ st.markdown("A, B, C, D, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X")
305
+
306
+ if __name__ == "__main__":
307
+ main()
test_system.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import os
4
+ import sys
5
+ from sign_utils import MediaPipeLandmarksModel, SignLanguagePredictor
6
+
7
+ def test_camera():
8
+ """Test if camera is working."""
9
+ print("Testing camera...")
10
+ cap = cv2.VideoCapture(0)
11
+
12
+ if not cap.isOpened():
13
+ print("❌ Camera not accessible")
14
+ return False
15
+
16
+ ret, frame = cap.read()
17
+ if not ret:
18
+ print("❌ Cannot read from camera")
19
+ cap.release()
20
+ return False
21
+
22
+ print("✅ Camera working")
23
+ cap.release()
24
+ return True
25
+
26
+ def test_mediapipe():
27
+ """Test MediaPipe hand detection."""
28
+ print("Testing MediaPipe...")
29
+ try:
30
+ landmark_model = MediaPipeLandmarksModel()
31
+
32
+ # Create a dummy image
33
+ dummy_image = np.zeros((480, 640, 3), dtype=np.uint8)
34
+ landmarks = landmark_model.predict_landmarks(dummy_image)
35
+
36
+ print("✅ MediaPipe working")
37
+ return True
38
+ except Exception as e:
39
+ print(f"❌ MediaPipe error: {e}")
40
+ return False
41
+
42
+ def test_model_files():
43
+ """Test if model files exist."""
44
+ print("Testing model files...")
45
+
46
+ model_path = "models/model.tflite"
47
+ labels_path = "models/labels.txt"
48
+
49
+ if not os.path.exists(model_path):
50
+ print(f"❌ Model file not found: {model_path}")
51
+ return False
52
+
53
+ if not os.path.exists(labels_path):
54
+ print(f"❌ Labels file not found: {labels_path}")
55
+ return False
56
+
57
+ print("✅ Model files found")
58
+ return True
59
+
60
+ def test_predictor():
61
+ """Test the sign language predictor."""
62
+ print("Testing predictor...")
63
+
64
+ if not test_model_files():
65
+ return False
66
+
67
+ try:
68
+ predictor = SignLanguagePredictor(
69
+ model_path="models/model.tflite",
70
+ labels_path="models/labels.txt"
71
+ )
72
+
73
+ # Create a dummy image
74
+ dummy_image = np.zeros((480, 640, 3), dtype=np.uint8)
75
+ prediction, confidence = predictor.predict(dummy_image)
76
+
77
+ print(f"✅ Predictor working - Sample prediction: {prediction} ({confidence:.2f})")
78
+ return True
79
+ except Exception as e:
80
+ print(f"❌ Predictor error: {e}")
81
+ return False
82
+
83
+ def test_dependencies():
84
+ """Test if all dependencies are installed."""
85
+ print("Testing dependencies...")
86
+
87
+ required_modules = [
88
+ 'cv2', 'numpy', 'tensorflow', 'mediapipe',
89
+ 'streamlit', 'sklearn'
90
+ ]
91
+
92
+ missing_modules = []
93
+
94
+ for module in required_modules:
95
+ try:
96
+ __import__(module)
97
+ except ImportError:
98
+ missing_modules.append(module)
99
+
100
+ if missing_modules:
101
+ print(f"❌ Missing modules: {missing_modules}")
102
+ return False
103
+
104
+ print("✅ All dependencies installed")
105
+ return True
106
+
107
+ def run_full_test():
108
+ """Run complete system test."""
109
+ print("🤟 Sign Language Interpreter - System Test")
110
+ print("=" * 50)
111
+
112
+ tests = [
113
+ ("Dependencies", test_dependencies),
114
+ ("Camera", test_camera),
115
+ ("MediaPipe", test_mediapipe),
116
+ ("Model Files", test_model_files),
117
+ ("Predictor", test_predictor)
118
+ ]
119
+
120
+ passed = 0
121
+ total = len(tests)
122
+
123
+ for test_name, test_func in tests:
124
+ print(f"\n{test_name}:")
125
+ if test_func():
126
+ passed += 1
127
+ else:
128
+ print(f" Fix this issue before proceeding")
129
+
130
+ print(f"\n{'='*50}")
131
+ print(f"Test Results: {passed}/{total} tests passed")
132
+
133
+ if passed == total:
134
+ print("🎉 All tests passed! System is ready to use.")
135
+ print("\nNext steps:")
136
+ print("1. Run 'streamlit run app.py' to start the application")
137
+ print("2. Or run 'python data_collection.py' to collect custom data")
138
+ else:
139
+ print("❌ Some tests failed. Please fix the issues above.")
140
+
141
+ if not test_model_files():
142
+ print("\n💡 Tip: Run 'python create_sample_model.py' to create a sample model")
143
+
144
+ def main():
145
+ if len(sys.argv) > 1 and sys.argv[1] == "--quick":
146
+ # Quick test - just check if basic components work
147
+ if test_dependencies() and test_mediapipe():
148
+ print("✅ Quick test passed!")
149
+ else:
150
+ print("❌ Quick test failed!")
151
+ else:
152
+ run_full_test()
153
+
154
+ if __name__ == "__main__":
155
+ main()
train_digits_model.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import tensorflow as tf
3
+ from tensorflow.keras.models import Sequential
4
+ from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization
5
+ from tensorflow.keras.preprocessing.image import ImageDataGenerator
6
+ from sklearn.model_selection import train_test_split
7
+ import cv2
8
+ import os
9
+ import glob
10
+ from sign_utils import MediaPipeLandmarksModel
11
+ import matplotlib.pyplot as plt
12
+
13
+ class DigitSignTrainer:
14
+ def __init__(self):
15
+ self.landmark_model = MediaPipeLandmarksModel()
16
+ self.img_size = (224, 224)
17
+
18
+ def load_image_dataset(self, dataset_path="dataset"):
19
+ """Load digit images and extract landmarks."""
20
+ X_images = []
21
+ X_landmarks = []
22
+ y = []
23
+
24
+ print("Loading and processing images...")
25
+
26
+ # Process each digit folder
27
+ for digit in range(10):
28
+ digit_path = os.path.join(dataset_path, str(digit))
29
+ if not os.path.exists(digit_path):
30
+ continue
31
+
32
+ image_files = glob.glob(os.path.join(digit_path, "*.JPG"))
33
+ print(f"Processing {len(image_files)} images for digit {digit}")
34
+
35
+ for img_file in image_files:
36
+ # Load and preprocess image
37
+ img = cv2.imread(img_file)
38
+ if img is None:
39
+ continue
40
+
41
+ # Resize image
42
+ img_resized = cv2.resize(img, self.img_size)
43
+
44
+ # Extract landmarks
45
+ landmarks = self.landmark_model.predict_landmarks(img)
46
+
47
+ if landmarks is not None:
48
+ # Normalize landmarks
49
+ landmarks_normalized = self.landmark_model.normalize_landmarks(landmarks)
50
+
51
+ X_images.append(img_resized)
52
+ X_landmarks.append(landmarks_normalized)
53
+ y.append(digit)
54
+
55
+ print(f"Successfully processed {len(X_images)} images with landmarks")
56
+
57
+ # Convert to numpy arrays
58
+ X_images = np.array(X_images) / 255.0 # Normalize pixel values
59
+ X_landmarks = np.array(X_landmarks)
60
+ y = np.array(y)
61
+
62
+ return X_images, X_landmarks, y
63
+
64
+ def create_cnn_model(self, num_classes=10):
65
+ """Create CNN model for image classification."""
66
+ model = Sequential([
67
+ Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)),
68
+ BatchNormalization(),
69
+ MaxPooling2D(2, 2),
70
+
71
+ Conv2D(64, (3, 3), activation='relu'),
72
+ BatchNormalization(),
73
+ MaxPooling2D(2, 2),
74
+
75
+ Conv2D(128, (3, 3), activation='relu'),
76
+ BatchNormalization(),
77
+ MaxPooling2D(2, 2),
78
+
79
+ Conv2D(256, (3, 3), activation='relu'),
80
+ BatchNormalization(),
81
+ MaxPooling2D(2, 2),
82
+
83
+ Flatten(),
84
+ Dense(512, activation='relu'),
85
+ Dropout(0.5),
86
+ Dense(256, activation='relu'),
87
+ Dropout(0.3),
88
+ Dense(num_classes, activation='softmax')
89
+ ])
90
+
91
+ model.compile(
92
+ optimizer='adam',
93
+ loss='sparse_categorical_crossentropy',
94
+ metrics=['accuracy']
95
+ )
96
+
97
+ return model
98
+
99
+ def create_landmark_model(self, num_classes=10):
100
+ """Create model for landmark-based classification."""
101
+ model = Sequential([
102
+ Dense(128, activation='relu', input_shape=(63,)), # 21 landmarks * 3 coordinates
103
+ BatchNormalization(),
104
+ Dropout(0.3),
105
+
106
+ Dense(64, activation='relu'),
107
+ BatchNormalization(),
108
+ Dropout(0.3),
109
+
110
+ Dense(32, activation='relu'),
111
+ Dropout(0.2),
112
+
113
+ Dense(num_classes, activation='softmax')
114
+ ])
115
+
116
+ model.compile(
117
+ optimizer='adam',
118
+ loss='sparse_categorical_crossentropy',
119
+ metrics=['accuracy']
120
+ )
121
+
122
+ return model
123
+
124
+ def create_combined_model(self, num_classes=10):
125
+ """Create combined model using both images and landmarks."""
126
+ # Image branch
127
+ image_input = tf.keras.Input(shape=(224, 224, 3), name='image_input')
128
+ x1 = Conv2D(32, (3, 3), activation='relu')(image_input)
129
+ x1 = BatchNormalization()(x1)
130
+ x1 = MaxPooling2D(2, 2)(x1)
131
+ x1 = Conv2D(64, (3, 3), activation='relu')(x1)
132
+ x1 = BatchNormalization()(x1)
133
+ x1 = MaxPooling2D(2, 2)(x1)
134
+ x1 = Conv2D(128, (3, 3), activation='relu')(x1)
135
+ x1 = BatchNormalization()(x1)
136
+ x1 = MaxPooling2D(2, 2)(x1)
137
+ x1 = Flatten()(x1)
138
+ x1 = Dense(256, activation='relu')(x1)
139
+ x1 = Dropout(0.5)(x1)
140
+
141
+ # Landmark branch
142
+ landmark_input = tf.keras.Input(shape=(63,), name='landmark_input')
143
+ x2 = Dense(128, activation='relu')(landmark_input)
144
+ x2 = BatchNormalization()(x2)
145
+ x2 = Dropout(0.3)(x2)
146
+ x2 = Dense(64, activation='relu')(x2)
147
+ x2 = Dropout(0.3)(x2)
148
+
149
+ # Combine branches
150
+ combined = tf.keras.layers.concatenate([x1, x2])
151
+ combined = Dense(128, activation='relu')(combined)
152
+ combined = Dropout(0.3)(combined)
153
+ output = Dense(num_classes, activation='softmax')(combined)
154
+
155
+ model = tf.keras.Model(inputs=[image_input, landmark_input], outputs=output)
156
+ model.compile(
157
+ optimizer='adam',
158
+ loss='sparse_categorical_crossentropy',
159
+ metrics=['accuracy']
160
+ )
161
+
162
+ return model
163
+
164
+ def train_model(self, model_type='combined'):
165
+ """Train the model."""
166
+ # Load dataset
167
+ X_images, X_landmarks, y = self.load_image_dataset()
168
+
169
+ if len(X_images) == 0:
170
+ print("No valid images found with hand landmarks!")
171
+ return None
172
+
173
+ # Split data
174
+ if model_type == 'combined':
175
+ X_img_train, X_img_test, X_land_train, X_land_test, y_train, y_test = train_test_split(
176
+ X_images, X_landmarks, y, test_size=0.2, random_state=42, stratify=y
177
+ )
178
+ X_img_train, X_img_val, X_land_train, X_land_val, y_train, y_val = train_test_split(
179
+ X_img_train, X_land_train, y_train, test_size=0.2, random_state=42, stratify=y_train
180
+ )
181
+ else:
182
+ X_train, X_test, y_train, y_test = train_test_split(
183
+ X_images if model_type == 'cnn' else X_landmarks, y,
184
+ test_size=0.2, random_state=42, stratify=y
185
+ )
186
+ X_train, X_val, y_train, y_val = train_test_split(
187
+ X_train, y_train, test_size=0.2, random_state=42, stratify=y_train
188
+ )
189
+
190
+ print(f"Training set: {len(y_train)} samples")
191
+ print(f"Validation set: {len(y_val)} samples")
192
+ print(f"Test set: {len(y_test)} samples")
193
+
194
+ # Create model
195
+ if model_type == 'cnn':
196
+ model = self.create_cnn_model()
197
+ train_data = (X_train, y_train)
198
+ val_data = (X_val, y_val)
199
+ test_data = (X_test, y_test)
200
+ elif model_type == 'landmark':
201
+ model = self.create_landmark_model()
202
+ train_data = (X_train, y_train)
203
+ val_data = (X_val, y_val)
204
+ test_data = (X_test, y_test)
205
+ else: # combined
206
+ model = self.create_combined_model()
207
+ train_data = ([X_img_train, X_land_train], y_train)
208
+ val_data = ([X_img_val, X_land_val], y_val)
209
+ test_data = ([X_img_test, X_land_test], y_test)
210
+
211
+ print(f"\nModel Summary ({model_type}):")
212
+ model.summary()
213
+
214
+ # Callbacks
215
+ callbacks = [
216
+ tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True),
217
+ tf.keras.callbacks.ReduceLROnPlateau(factor=0.5, patience=5),
218
+ tf.keras.callbacks.ModelCheckpoint(
219
+ f'models/best_{model_type}_model.h5',
220
+ save_best_only=True
221
+ )
222
+ ]
223
+
224
+ # Train model
225
+ print(f"\nTraining {model_type} model...")
226
+ history = model.fit(
227
+ train_data[0], train_data[1],
228
+ validation_data=val_data,
229
+ epochs=50,
230
+ batch_size=32,
231
+ callbacks=callbacks,
232
+ verbose=1
233
+ )
234
+
235
+ # Evaluate model
236
+ print(f"\nEvaluating {model_type} model...")
237
+ test_loss, test_accuracy = model.evaluate(test_data[0], test_data[1], verbose=0)
238
+ print(f"Test Accuracy: {test_accuracy:.4f}")
239
+
240
+ # Save model
241
+ model.save(f'models/{model_type}_digit_model.h5')
242
+
243
+ # Convert to TFLite
244
+ self.convert_to_tflite(model, model_type)
245
+
246
+ # Save labels
247
+ labels = [str(i) for i in range(10)]
248
+ with open('models/labels.txt', 'w') as f:
249
+ f.write('\n'.join(labels))
250
+
251
+ return model, history
252
+
253
+ def convert_to_tflite(self, model, model_type):
254
+ """Convert model to TensorFlow Lite."""
255
+ try:
256
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
257
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
258
+
259
+ tflite_model = converter.convert()
260
+
261
+ with open(f'models/{model_type}_model.tflite', 'wb') as f:
262
+ f.write(tflite_model)
263
+
264
+ # Also save as the default model.tflite
265
+ with open('models/model.tflite', 'wb') as f:
266
+ f.write(tflite_model)
267
+
268
+ print(f"Model converted to TFLite: {model_type}_model.tflite")
269
+
270
+ except Exception as e:
271
+ print(f"TFLite conversion failed: {e}")
272
+
273
+ def plot_training_history(self, history, model_type):
274
+ """Plot training history."""
275
+ plt.figure(figsize=(12, 4))
276
+
277
+ plt.subplot(1, 2, 1)
278
+ plt.plot(history.history['accuracy'], label='Training Accuracy')
279
+ plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
280
+ plt.title(f'{model_type.title()} Model Accuracy')
281
+ plt.xlabel('Epoch')
282
+ plt.ylabel('Accuracy')
283
+ plt.legend()
284
+
285
+ plt.subplot(1, 2, 2)
286
+ plt.plot(history.history['loss'], label='Training Loss')
287
+ plt.plot(history.history['val_loss'], label='Validation Loss')
288
+ plt.title(f'{model_type.title()} Model Loss')
289
+ plt.xlabel('Epoch')
290
+ plt.ylabel('Loss')
291
+ plt.legend()
292
+
293
+ plt.tight_layout()
294
+ plt.savefig(f'logs/{model_type}_training_history.png')
295
+ plt.show()
296
+
297
+ def main():
298
+ trainer = DigitSignTrainer()
299
+
300
+ print("Sign Language Digits Model Training")
301
+ print("=" * 40)
302
+
303
+ # Check if dataset exists
304
+ if not os.path.exists("dataset"):
305
+ print("Dataset not found!")
306
+ return
307
+
308
+ # Train different models
309
+ models_to_train = ['landmark', 'cnn', 'combined']
310
+
311
+ for model_type in models_to_train:
312
+ print(f"\n{'='*50}")
313
+ print(f"Training {model_type.upper()} model")
314
+ print(f"{'='*50}")
315
+
316
+ try:
317
+ model, history = trainer.train_model(model_type)
318
+ if model is not None:
319
+ trainer.plot_training_history(history, model_type)
320
+ print(f"{model_type.title()} model training completed!")
321
+ else:
322
+ print(f"Failed to train {model_type} model")
323
+ except Exception as e:
324
+ print(f"Error training {model_type} model: {e}")
325
+
326
+ print("\nTraining completed!")
327
+ print("You can now run the main application: streamlit run app.py")
328
+
329
+ if __name__ == "__main__":
330
+ main()
train_gesture_dataset.py ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ import mediapipe as mp
5
+ import tensorflow as tf
6
+ from tensorflow import keras
7
+ from sklearn.model_selection import train_test_split
8
+ from sklearn.preprocessing import LabelEncoder
9
+ import matplotlib.pyplot as plt
10
+ import seaborn as sns
11
+ from tqdm import tqdm
12
+ import json
13
+ import pickle
14
+ from datetime import datetime
15
+
16
+ class GestureDatasetTrainer:
17
+ def __init__(self, dataset_path, output_dir="models"):
18
+ self.dataset_path = dataset_path
19
+ self.output_dir = output_dir
20
+ self.mp_hands = mp.solutions.hands
21
+ self.hands = self.mp_hands.Hands(
22
+ static_image_mode=True,
23
+ max_num_hands=1,
24
+ min_detection_confidence=0.7,
25
+ min_tracking_confidence=0.5
26
+ )
27
+
28
+ # Create output directory
29
+ os.makedirs(output_dir, exist_ok=True)
30
+
31
+ # Initialize data storage
32
+ self.features = []
33
+ self.labels = []
34
+ self.class_names = []
35
+ self.label_encoder = LabelEncoder()
36
+
37
+ def extract_hand_landmarks(self, image_path):
38
+ """Extract hand landmarks from image."""
39
+ try:
40
+ # Read image
41
+ image = cv2.imread(image_path)
42
+ if image is None:
43
+ return None
44
+
45
+ # Convert to RGB
46
+ rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
47
+
48
+ # Process with MediaPipe
49
+ results = self.hands.process(rgb_image)
50
+
51
+ if results.multi_hand_landmarks:
52
+ # Get first hand landmarks
53
+ hand_landmarks = results.multi_hand_landmarks[0]
54
+
55
+ # Convert to numpy array
56
+ landmarks_array = np.array([(lm.x, lm.y, lm.z) for lm in hand_landmarks.landmark])
57
+
58
+ # Normalize relative to wrist (first landmark)
59
+ wrist = landmarks_array[0]
60
+ normalized = landmarks_array - wrist
61
+
62
+ # Scale by hand size (distance from wrist to middle finger tip)
63
+ hand_size = np.linalg.norm(landmarks_array[12] - wrist)
64
+ if hand_size > 0:
65
+ normalized = normalized / hand_size
66
+
67
+ # Flatten to 63 features (21 landmarks * 3 coordinates)
68
+ features = normalized.flatten()
69
+
70
+ # Ensure exactly 63 features
71
+ if len(features) != 63:
72
+ features = np.pad(features, (0, max(0, 63 - len(features))))[:63]
73
+
74
+ return features
75
+ else:
76
+ return None
77
+
78
+ except Exception as e:
79
+ print(f"Error processing {image_path}: {e}")
80
+ return None
81
+
82
+ def load_dataset(self, max_samples_per_class=500, sample_ratio=0.1):
83
+ """Load and process the dataset."""
84
+ print("Loading dataset...")
85
+
86
+ # Get all class directories
87
+ class_dirs = [d for d in os.listdir(self.dataset_path)
88
+ if os.path.isdir(os.path.join(self.dataset_path, d))]
89
+ class_dirs.sort() # Ensure consistent ordering
90
+
91
+ self.class_names = class_dirs
92
+ print(f"Found {len(class_dirs)} classes: {class_dirs}")
93
+
94
+ # Process each class
95
+ for class_name in tqdm(class_dirs, desc="Processing classes"):
96
+ class_path = os.path.join(self.dataset_path, class_name)
97
+
98
+ # Get all image files
99
+ image_files = [f for f in os.listdir(class_path)
100
+ if f.lower().endswith(('.jpg', '.jpeg', '.png'))]
101
+
102
+ # Sample images if there are too many
103
+ if len(image_files) > max_samples_per_class:
104
+ # Take every nth image to get a good distribution
105
+ step = len(image_files) // max_samples_per_class
106
+ image_files = image_files[::step][:max_samples_per_class]
107
+ else:
108
+ # If fewer images, sample a percentage
109
+ sample_size = max(1, int(len(image_files) * sample_ratio))
110
+ image_files = image_files[:sample_size]
111
+
112
+ print(f"Processing {len(image_files)} images for class '{class_name}'")
113
+
114
+ # Process images
115
+ class_features = []
116
+ for image_file in tqdm(image_files, desc=f"Class {class_name}", leave=False):
117
+ image_path = os.path.join(class_path, image_file)
118
+ features = self.extract_hand_landmarks(image_path)
119
+
120
+ if features is not None:
121
+ class_features.append(features)
122
+
123
+ # Add to dataset
124
+ if class_features:
125
+ self.features.extend(class_features)
126
+ self.labels.extend([class_name] * len(class_features))
127
+ print(f"Added {len(class_features)} samples for class '{class_name}'")
128
+ else:
129
+ print(f"Warning: No valid samples found for class '{class_name}'")
130
+
131
+ # Convert to numpy arrays
132
+ self.features = np.array(self.features)
133
+ self.labels = np.array(self.labels)
134
+
135
+ # Encode labels
136
+ self.labels_encoded = self.label_encoder.fit_transform(self.labels)
137
+
138
+ print(f"\nDataset loaded successfully!")
139
+ print(f"Total samples: {len(self.features)}")
140
+ print(f"Feature shape: {self.features.shape}")
141
+ print(f"Number of classes: {len(self.class_names)}")
142
+
143
+ # Print class distribution
144
+ unique, counts = np.unique(self.labels, return_counts=True)
145
+ print("\nClass distribution:")
146
+ for class_name, count in zip(unique, counts):
147
+ print(f" {class_name}: {count} samples")
148
+
149
+ def create_model(self, input_shape=63, num_classes=36):
150
+ """Create the neural network model with improved architecture."""
151
+ model = keras.Sequential([
152
+ # Input layer with data augmentation
153
+ keras.layers.Dense(512, activation='relu', input_shape=(input_shape,)),
154
+ keras.layers.Dropout(0.4),
155
+ keras.layers.BatchNormalization(),
156
+
157
+ keras.layers.Dense(256, activation='relu'),
158
+ keras.layers.Dropout(0.3),
159
+ keras.layers.BatchNormalization(),
160
+
161
+ keras.layers.Dense(128, activation='relu'),
162
+ keras.layers.Dropout(0.3),
163
+ keras.layers.BatchNormalization(),
164
+
165
+ keras.layers.Dense(64, activation='relu'),
166
+ keras.layers.Dropout(0.2),
167
+ keras.layers.BatchNormalization(),
168
+
169
+ keras.layers.Dense(32, activation='relu'),
170
+ keras.layers.Dropout(0.2),
171
+
172
+ keras.layers.Dense(num_classes, activation='softmax')
173
+ ])
174
+
175
+ model.compile(
176
+ optimizer=keras.optimizers.Adam(learning_rate=0.001),
177
+ loss='sparse_categorical_crossentropy',
178
+ metrics=['accuracy']
179
+ )
180
+
181
+ return model
182
+
183
+ def train_model(self, test_size=0.2, validation_split=0.2, epochs=50, batch_size=64):
184
+ """Train the model with improved configuration."""
185
+ print("\nPreparing data for training...")
186
+
187
+ # Filter out classes with insufficient samples for stratified split
188
+ unique_labels, label_counts = np.unique(self.labels_encoded, return_counts=True)
189
+ min_samples_required = max(2, int(1 / test_size)) # At least 2 samples, or enough for test split
190
+
191
+ # Find classes with sufficient samples
192
+ valid_classes = unique_labels[label_counts >= min_samples_required]
193
+
194
+ if len(valid_classes) < len(unique_labels):
195
+ print(f"Warning: Removing {len(unique_labels) - len(valid_classes)} classes with insufficient samples")
196
+ print("Classes with insufficient samples:")
197
+ for label, count in zip(unique_labels, label_counts):
198
+ if count < min_samples_required:
199
+ class_name = self.class_names[label] if label < len(self.class_names) else f"Class_{label}"
200
+ print(f" {class_name}: {count} samples (minimum required: {min_samples_required})")
201
+
202
+ # Filter data to only include valid classes
203
+ valid_indices = np.isin(self.labels_encoded, valid_classes)
204
+ filtered_features = self.features[valid_indices]
205
+ filtered_labels = self.labels_encoded[valid_indices]
206
+
207
+ # Update class names to only include valid classes
208
+ valid_class_names = [self.class_names[i] for i in valid_classes if i < len(self.class_names)]
209
+
210
+ # Remap labels to be continuous (0, 1, 2, ...)
211
+ label_mapping = {old_label: new_label for new_label, old_label in enumerate(valid_classes)}
212
+ remapped_labels = np.array([label_mapping[label] for label in filtered_labels])
213
+
214
+ print(f"Training with {len(valid_classes)} classes and {len(filtered_features)} samples")
215
+
216
+ # Calculate class weights for handling imbalance
217
+ from sklearn.utils.class_weight import compute_class_weight
218
+ class_weights = compute_class_weight(
219
+ 'balanced',
220
+ classes=np.unique(remapped_labels),
221
+ y=remapped_labels
222
+ )
223
+ class_weight_dict = dict(enumerate(class_weights))
224
+
225
+ # Split data - use stratify only if we have enough samples
226
+ try:
227
+ X_train, X_test, y_train, y_test = train_test_split(
228
+ filtered_features, remapped_labels,
229
+ test_size=test_size,
230
+ random_state=42,
231
+ stratify=remapped_labels
232
+ )
233
+ except ValueError as e:
234
+ print(f"Warning: Stratified split failed ({e}), using random split instead")
235
+ X_train, X_test, y_train, y_test = train_test_split(
236
+ filtered_features, remapped_labels,
237
+ test_size=test_size,
238
+ random_state=42
239
+ )
240
+
241
+ # Update class names for saving
242
+ self.class_names = valid_class_names
243
+
244
+ print(f"Training samples: {len(X_train)}")
245
+ print(f"Testing samples: {len(X_test)}")
246
+
247
+ # Create model
248
+ num_classes = len(self.class_names)
249
+ model = self.create_model(num_classes=num_classes)
250
+
251
+ print("\nModel architecture:")
252
+ model.summary()
253
+
254
+ # Enhanced callbacks
255
+ callbacks = [
256
+ keras.callbacks.EarlyStopping(
257
+ monitor='val_accuracy',
258
+ patience=15,
259
+ restore_best_weights=True,
260
+ verbose=1
261
+ ),
262
+ keras.callbacks.ReduceLROnPlateau(
263
+ monitor='val_loss',
264
+ factor=0.3,
265
+ patience=7,
266
+ min_lr=1e-7,
267
+ verbose=1
268
+ ),
269
+ keras.callbacks.ModelCheckpoint(
270
+ os.path.join(self.output_dir, 'best_model.h5'),
271
+ monitor='val_accuracy',
272
+ save_best_only=True,
273
+ verbose=1
274
+ )
275
+ ]
276
+
277
+ # Train model with class weights
278
+ print("\nStarting training...")
279
+ history = model.fit(
280
+ X_train, y_train,
281
+ validation_split=validation_split,
282
+ epochs=epochs,
283
+ batch_size=batch_size,
284
+ class_weight=class_weight_dict,
285
+ callbacks=callbacks,
286
+ verbose=1
287
+ )
288
+
289
+ # Evaluate on test set
290
+ print("\nEvaluating on test set...")
291
+ test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
292
+ print(f"Test accuracy: {test_accuracy:.4f}")
293
+ print(f"Test loss: {test_loss:.4f}")
294
+
295
+ # Save model and metadata
296
+ self.save_model_and_metadata(model, history, test_accuracy)
297
+
298
+ # Plot training history
299
+ self.plot_training_history(history)
300
+
301
+ return model, history
302
+
303
+ def save_model_and_metadata(self, model, history, test_accuracy):
304
+ """Save the trained model and associated metadata."""
305
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
306
+
307
+ # Save Keras model
308
+ model_path = os.path.join(self.output_dir, f'gesture_model_{timestamp}.h5')
309
+ model.save(model_path)
310
+ print(f"Model saved to: {model_path}")
311
+
312
+ # Convert to TensorFlow Lite
313
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
314
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
315
+ tflite_model = converter.convert()
316
+
317
+ tflite_path = os.path.join(self.output_dir, f'gesture_model_{timestamp}.tflite')
318
+ with open(tflite_path, 'wb') as f:
319
+ f.write(tflite_model)
320
+ print(f"TFLite model saved to: {tflite_path}")
321
+
322
+ # Save labels
323
+ labels_path = os.path.join(self.output_dir, f'labels_{timestamp}.txt')
324
+ with open(labels_path, 'w') as f:
325
+ for label in self.class_names:
326
+ f.write(f"{label}\n")
327
+ print(f"Labels saved to: {labels_path}")
328
+
329
+ # Save label encoder
330
+ encoder_path = os.path.join(self.output_dir, f'label_encoder_{timestamp}.pkl')
331
+ with open(encoder_path, 'wb') as f:
332
+ pickle.dump(self.label_encoder, f)
333
+
334
+ # Save training metadata
335
+ metadata = {
336
+ 'timestamp': timestamp,
337
+ 'test_accuracy': float(test_accuracy),
338
+ 'num_classes': len(self.class_names),
339
+ 'class_names': self.class_names,
340
+ 'total_samples': len(self.features),
341
+ 'feature_shape': self.features.shape,
342
+ 'model_path': model_path,
343
+ 'tflite_path': tflite_path,
344
+ 'labels_path': labels_path
345
+ }
346
+
347
+ metadata_path = os.path.join(self.output_dir, f'training_metadata_{timestamp}.json')
348
+ with open(metadata_path, 'w') as f:
349
+ json.dump(metadata, f, indent=2)
350
+ print(f"Metadata saved to: {metadata_path}")
351
+
352
+ def plot_training_history(self, history):
353
+ """Plot training history."""
354
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
355
+
356
+ # Plot accuracy
357
+ ax1.plot(history.history['accuracy'], label='Training Accuracy')
358
+ ax1.plot(history.history['val_accuracy'], label='Validation Accuracy')
359
+ ax1.set_title('Model Accuracy')
360
+ ax1.set_xlabel('Epoch')
361
+ ax1.set_ylabel('Accuracy')
362
+ ax1.legend()
363
+ ax1.grid(True)
364
+
365
+ # Plot loss
366
+ ax2.plot(history.history['loss'], label='Training Loss')
367
+ ax2.plot(history.history['val_loss'], label='Validation Loss')
368
+ ax2.set_title('Model Loss')
369
+ ax2.set_xlabel('Epoch')
370
+ ax2.set_ylabel('Loss')
371
+ ax2.legend()
372
+ ax2.grid(True)
373
+
374
+ plt.tight_layout()
375
+
376
+ # Save plot
377
+ plot_path = os.path.join(self.output_dir, 'training_history.png')
378
+ plt.savefig(plot_path, dpi=300, bbox_inches='tight')
379
+ print(f"Training history plot saved to: {plot_path}")
380
+ plt.show()
381
+
382
+ def main():
383
+ # Configuration
384
+ DATASET_PATH = r"C:\Users\krishna\Downloads\archive (2)\Gesture Image Data"
385
+ OUTPUT_DIR = "models"
386
+
387
+ # Training parameters - Optimized for better performance
388
+ MAX_SAMPLES_PER_CLASS = 500 # Increased for better generalization
389
+ SAMPLE_RATIO = 0.1 # Use 10% of available images if less than max
390
+ TEST_SIZE = 0.2
391
+ VALIDATION_SPLIT = 0.2
392
+ EPOCHS = 50
393
+ BATCH_SIZE = 64 # Increased for faster training
394
+
395
+ print("=== Sign Language Gesture Dataset Training ===")
396
+ print(f"Dataset path: {DATASET_PATH}")
397
+ print(f"Output directory: {OUTPUT_DIR}")
398
+ print(f"Max samples per class: {MAX_SAMPLES_PER_CLASS}")
399
+ print(f"Sample ratio: {SAMPLE_RATIO}")
400
+
401
+ # Initialize trainer
402
+ trainer = GestureDatasetTrainer(DATASET_PATH, OUTPUT_DIR)
403
+
404
+ # Load dataset
405
+ trainer.load_dataset(
406
+ max_samples_per_class=MAX_SAMPLES_PER_CLASS,
407
+ sample_ratio=SAMPLE_RATIO
408
+ )
409
+
410
+ # Train model
411
+ model, history = trainer.train_model(
412
+ test_size=TEST_SIZE,
413
+ validation_split=VALIDATION_SPLIT,
414
+ epochs=EPOCHS,
415
+ batch_size=BATCH_SIZE
416
+ )
417
+
418
+ print("\n=== Training Complete ===")
419
+ print("Check the 'models' directory for saved files:")
420
+ print("- .h5 file: Keras model")
421
+ print("- .tflite file: TensorFlow Lite model")
422
+ print("- labels.txt: Class labels")
423
+ print("- training_metadata.json: Training information")
424
+ print("- training_history.png: Training plots")
425
+
426
+ if __name__ == "__main__":
427
+ main()
train_model.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import tensorflow as tf
3
+ from sklearn.model_selection import train_test_split
4
+ from sklearn.preprocessing import LabelEncoder
5
+ import os
6
+ import glob
7
+ from utils.model_utils import create_model, get_training_callbacks, evaluate_model
8
+ from utils.data_utils import DataProcessor
9
+
10
+ class SignLanguageTrainer:
11
+ def __init__(self):
12
+ self.data_processor = DataProcessor()
13
+ self.label_encoder = LabelEncoder()
14
+
15
+ def load_dataset(self, dataset_path="dataset"):
16
+ """Load dataset from collected data."""
17
+ X, y = [], []
18
+ labels = []
19
+
20
+ # Get all sign directories
21
+ sign_dirs = [d for d in os.listdir(dataset_path) if os.path.isdir(os.path.join(dataset_path, d))]
22
+
23
+ for sign_name in sign_dirs:
24
+ sign_path = os.path.join(dataset_path, sign_name)
25
+ sequence_file = os.path.join(sign_path, "sequences.npy")
26
+
27
+ if os.path.exists(sequence_file):
28
+ sequences = np.load(sequence_file)
29
+
30
+ for sequence in sequences:
31
+ X.append(sequence)
32
+ y.append(sign_name)
33
+
34
+ if sign_name not in labels:
35
+ labels.append(sign_name)
36
+
37
+ print(f"Loaded {len(sequences)} sequences for {sign_name}")
38
+
39
+ # Save labels
40
+ with open('models/labels.txt', 'w') as f:
41
+ f.write('\n'.join(labels))
42
+
43
+ # Encode labels
44
+ y_encoded = self.label_encoder.fit_transform(y)
45
+
46
+ return np.array(X), y_encoded, labels
47
+
48
+ def prepare_data(self, X, y, test_size=0.2, val_size=0.2):
49
+ """Prepare data for training."""
50
+ # Split into train and test
51
+ X_train, X_test, y_train, y_test = train_test_split(
52
+ X, y, test_size=test_size, random_state=42, stratify=y
53
+ )
54
+
55
+ # Split train into train and validation
56
+ X_train, X_val, y_train, y_val = train_test_split(
57
+ X_train, y_train, test_size=val_size, random_state=42, stratify=y_train
58
+ )
59
+
60
+ return X_train, X_val, X_test, y_train, y_val, y_test
61
+
62
+ def train_model(self, X_train, y_train, X_val, y_val,
63
+ num_classes, epochs=50, batch_size=32):
64
+ """Train the sign language model."""
65
+
66
+ # Get input shape
67
+ sequence_length, num_features = X_train.shape[1], X_train.shape[2]
68
+
69
+ # Create model
70
+ model = create_model(num_classes, sequence_length, num_features)
71
+
72
+ print("Model Summary:")
73
+ model.summary()
74
+
75
+ # Get callbacks
76
+ callbacks = get_training_callbacks()
77
+
78
+ # Train model
79
+ history = model.fit(
80
+ X_train, y_train,
81
+ validation_data=(X_val, y_val),
82
+ epochs=epochs,
83
+ batch_size=batch_size,
84
+ callbacks=callbacks,
85
+ verbose=1
86
+ )
87
+
88
+ return model, history
89
+
90
+ def save_model(self, model, model_name="sign_language_model"):
91
+ """Save the trained model."""
92
+ # Save Keras model
93
+ model.save(f'models/{model_name}.h5')
94
+
95
+ # Convert to TFLite
96
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
97
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
98
+
99
+ # Quantization for smaller model size
100
+ def representative_dataset():
101
+ for _ in range(100):
102
+ yield [np.random.random((1, 30, 126)).astype(np.float32)]
103
+
104
+ converter.representative_dataset = representative_dataset
105
+ converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
106
+ converter.inference_input_type = tf.uint8
107
+ converter.inference_output_type = tf.uint8
108
+
109
+ try:
110
+ tflite_model = converter.convert()
111
+ with open('models/model.tflite', 'wb') as f:
112
+ f.write(tflite_model)
113
+ print("Model converted to TFLite successfully!")
114
+ except Exception as e:
115
+ print(f"TFLite conversion failed: {e}")
116
+ # Fallback to float32 model
117
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
118
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
119
+ tflite_model = converter.convert()
120
+ with open('models/model.tflite', 'wb') as f:
121
+ f.write(tflite_model)
122
+ print("Model converted to TFLite (float32) successfully!")
123
+
124
+ def main():
125
+ trainer = SignLanguageTrainer()
126
+
127
+ print("Sign Language Model Training")
128
+ print("=" * 40)
129
+
130
+ # Check if dataset exists
131
+ if not os.path.exists("dataset"):
132
+ print("Dataset not found! Please run data_collection.py first.")
133
+ return
134
+
135
+ # Load dataset
136
+ print("Loading dataset...")
137
+ X, y, labels = trainer.load_dataset()
138
+
139
+ if len(X) == 0:
140
+ print("No data found! Please collect data first using data_collection.py")
141
+ return
142
+
143
+ print(f"Loaded {len(X)} sequences for {len(labels)} signs")
144
+ print(f"Signs: {labels}")
145
+
146
+ # Prepare data
147
+ print("Preparing data...")
148
+ X_train, X_val, X_test, y_train, y_val, y_test = trainer.prepare_data(X, y)
149
+
150
+ print(f"Training set: {len(X_train)} sequences")
151
+ print(f"Validation set: {len(X_val)} sequences")
152
+ print(f"Test set: {len(X_test)} sequences")
153
+
154
+ # Train model
155
+ print("Training model...")
156
+ model, history = trainer.train_model(
157
+ X_train, y_train, X_val, y_val,
158
+ num_classes=len(labels),
159
+ epochs=50,
160
+ batch_size=32
161
+ )
162
+
163
+ # Evaluate model
164
+ print("Evaluating model...")
165
+ results = evaluate_model(model, X_test, y_test)
166
+ print(f"Test Accuracy: {results['accuracy']:.4f}")
167
+
168
+ # Save model
169
+ print("Saving model...")
170
+ trainer.save_model(model)
171
+
172
+ print("Training completed successfully!")
173
+ print("You can now run the main application: streamlit run app.py")
174
+
175
+ if __name__ == "__main__":
176
+ main()
train_platform.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import cv2
3
+ import numpy as np
4
+ import os
5
+ import tensorflow as tf
6
+ from tensorflow.keras.models import Sequential
7
+ from tensorflow.keras.layers import LSTM, Dense, Dropout
8
+ from tensorflow.keras.callbacks import ModelCheckpoint, TensorBoard
9
+ import mediapipe as mp
10
+ from utils.data_utils import process_video, extract_landmarks
11
+ from utils.model_utils import create_model, convert_to_tflite
12
+
13
+ def main():
14
+ st.title("Sign Language Model Training Platform")
15
+
16
+ # Sidebar for training configuration
17
+ st.sidebar.header("Training Configuration")
18
+
19
+ # Model parameters
20
+ num_classes = st.sidebar.number_input("Number of Sign Classes", min_value=2, value=10)
21
+ epochs = st.sidebar.number_input("Training Epochs", min_value=1, value=50)
22
+ batch_size = st.sidebar.number_input("Batch Size", min_value=1, value=32)
23
+
24
+ # Dataset upload
25
+ st.header("Dataset Upload")
26
+ dataset_path = st.file_uploader("Upload Dataset (ZIP file containing video folders)", type=['zip'])
27
+
28
+ if dataset_path:
29
+ # Create progress bar
30
+ progress_bar = st.progress(0)
31
+ status_text = st.empty()
32
+
33
+ # Extract dataset
34
+ status_text.text("Extracting dataset...")
35
+ # TODO: Add dataset extraction logic
36
+
37
+ # Process videos and extract landmarks
38
+ status_text.text("Processing videos and extracting landmarks...")
39
+ X_train, y_train = [], []
40
+ # TODO: Add video processing logic using MediaPipe
41
+
42
+ # Create and train model
43
+ if st.button("Start Training"):
44
+ status_text.text("Creating model...")
45
+ model = create_model(num_classes)
46
+
47
+ status_text.text("Training model...")
48
+ history = model.fit(
49
+ X_train, y_train,
50
+ epochs=epochs,
51
+ batch_size=batch_size,
52
+ validation_split=0.2,
53
+ callbacks=[
54
+ ModelCheckpoint('models/best_model.h5', save_best_only=True),
55
+ TensorBoard(log_dir='logs')
56
+ ]
57
+ )
58
+
59
+ # Convert to TFLite
60
+ status_text.text("Converting model to TFLite...")
61
+ convert_to_tflite(model, 'models/model.tflite')
62
+
63
+ st.success("Training completed! Model saved as 'model.tflite'")
64
+
65
+ # Display training metrics
66
+ st.header("Training Results")
67
+ st.line_chart(history.history)
68
+
69
+ if __name__ == "__main__":
70
+ main()
train_simple.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import tensorflow as tf
3
+ from tensorflow.keras.models import Sequential
4
+ from tensorflow.keras.layers import Dense, Dropout, BatchNormalization
5
+ import cv2
6
+ import os
7
+ import glob
8
+ from sign_utils import MediaPipeLandmarksModel
9
+
10
+ def load_and_process_dataset():
11
+ """Load digit images and extract landmarks."""
12
+ landmark_model = MediaPipeLandmarksModel()
13
+
14
+ X_landmarks = []
15
+ y = []
16
+
17
+ print("Loading and processing images...")
18
+
19
+ # Process each digit folder
20
+ for digit in range(10):
21
+ digit_path = os.path.join("dataset", str(digit))
22
+ if not os.path.exists(digit_path):
23
+ continue
24
+
25
+ image_files = glob.glob(os.path.join(digit_path, "*.JPG"))
26
+ print(f"Processing {len(image_files)} images for digit {digit}")
27
+
28
+ count = 0
29
+ for img_file in image_files:
30
+ # Load image
31
+ img = cv2.imread(img_file)
32
+ if img is None:
33
+ continue
34
+
35
+ # Extract landmarks
36
+ landmarks = landmark_model.predict_landmarks(img)
37
+
38
+ if landmarks is not None:
39
+ # Normalize landmarks
40
+ landmarks_normalized = landmark_model.normalize_landmarks(landmarks)
41
+
42
+ X_landmarks.append(landmarks_normalized)
43
+ y.append(digit)
44
+ count += 1
45
+
46
+ # Limit to 100 images per digit for faster training
47
+ if count >= 100:
48
+ break
49
+
50
+ print(f"Successfully processed {count} images for digit {digit}")
51
+
52
+ print(f"Total processed: {len(X_landmarks)} images with landmarks")
53
+
54
+ # Check if we have any data
55
+ if len(X_landmarks) == 0:
56
+ return np.array([]), np.array([])
57
+
58
+ # Ensure all landmark arrays have the same shape
59
+ landmark_shape = X_landmarks[0].shape[0]
60
+ filtered_landmarks = []
61
+ filtered_y = []
62
+
63
+ for i, landmarks in enumerate(X_landmarks):
64
+ if landmarks.shape[0] == landmark_shape:
65
+ filtered_landmarks.append(landmarks)
66
+ filtered_y.append(y[i])
67
+
68
+ print(f"After filtering: {len(filtered_landmarks)} valid samples")
69
+
70
+ return np.array(filtered_landmarks), np.array(filtered_y)
71
+
72
+ def create_simple_model(num_classes=10):
73
+ """Create a simple neural network for landmark classification."""
74
+ model = Sequential([
75
+ Dense(128, activation='relu', input_shape=(63,)), # 21 landmarks * 3 coordinates
76
+ BatchNormalization(),
77
+ Dropout(0.3),
78
+
79
+ Dense(64, activation='relu'),
80
+ BatchNormalization(),
81
+ Dropout(0.3),
82
+
83
+ Dense(32, activation='relu'),
84
+ Dropout(0.2),
85
+
86
+ Dense(num_classes, activation='softmax')
87
+ ])
88
+
89
+ model.compile(
90
+ optimizer='adam',
91
+ loss='sparse_categorical_crossentropy',
92
+ metrics=['accuracy']
93
+ )
94
+
95
+ return model
96
+
97
+ def main():
98
+ print("Simple Sign Language Digits Training")
99
+ print("=" * 40)
100
+
101
+ # Load dataset
102
+ X, y = load_and_process_dataset()
103
+
104
+ if len(X) == 0:
105
+ print("No valid images found with hand landmarks!")
106
+ return
107
+
108
+ # Split data (simple split without sklearn)
109
+ n_samples = len(X)
110
+ n_train = int(0.8 * n_samples)
111
+
112
+ # Shuffle data
113
+ indices = np.random.permutation(n_samples)
114
+ train_indices = indices[:n_train]
115
+ test_indices = indices[n_train:]
116
+
117
+ X_train, X_test = X[train_indices], X[test_indices]
118
+ y_train, y_test = y[train_indices], y[test_indices]
119
+
120
+ print(f"Training set: {len(X_train)} samples")
121
+ print(f"Test set: {len(X_test)} samples")
122
+
123
+ # Create model
124
+ model = create_simple_model()
125
+ print("\nModel Summary:")
126
+ model.summary()
127
+
128
+ # Train model
129
+ print("\nTraining model...")
130
+ history = model.fit(
131
+ X_train, y_train,
132
+ validation_data=(X_test, y_test),
133
+ epochs=30,
134
+ batch_size=32,
135
+ verbose=1
136
+ )
137
+
138
+ # Evaluate model
139
+ print("\nEvaluating model...")
140
+ test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
141
+ print(f"Test Accuracy: {test_accuracy:.4f}")
142
+
143
+ # Save model
144
+ print("\nSaving model...")
145
+
146
+ # Create models directory if it doesn't exist
147
+ os.makedirs('models', exist_ok=True)
148
+
149
+ # Save Keras model
150
+ model.save('models/digit_model.h5')
151
+
152
+ # Convert to TFLite
153
+ try:
154
+ converter = tf.lite.TFLiteConverter.from_keras_model(model)
155
+ converter.optimizations = [tf.lite.Optimize.DEFAULT]
156
+
157
+ tflite_model = converter.convert()
158
+
159
+ with open('models/model.tflite', 'wb') as f:
160
+ f.write(tflite_model)
161
+
162
+ print("Model converted to TFLite successfully!")
163
+
164
+ except Exception as e:
165
+ print(f"TFLite conversion failed: {e}")
166
+
167
+ # Save labels
168
+ labels = [str(i) for i in range(10)]
169
+ with open('models/labels.txt', 'w') as f:
170
+ f.write('\n'.join(labels))
171
+
172
+ print("Training completed successfully!")
173
+ print("You can now run the main application: streamlit run app.py")
174
+
175
+ if __name__ == "__main__":
176
+ main()