Tantawi65 commited on
Commit
d82a135
·
1 Parent(s): 22a70b4

Prepare Image Classification for Hugging Face Spaces deployment

Browse files
Files changed (5) hide show
  1. Dockerfile +28 -6
  2. README.md +120 -43
  3. app.py +12 -0
  4. app/main.py +64 -44
  5. requirements.txt +8 -7
Dockerfile CHANGED
@@ -1,10 +1,32 @@
1
- FROM tensorflow/tensorflow:2.10.0-gpu
2
 
3
- WORKDIR /app
 
4
 
5
- COPY requirements.txt .
6
- RUN pip install --no-cache-dir -r requirements.txt
 
 
 
 
 
 
 
7
 
8
- COPY app/ ./app/
 
9
 
10
- CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
 
3
+ # Set working directory
4
+ WORKDIR /code
5
 
6
+ # Install system dependencies for OpenCV and TensorFlow
7
+ RUN apt-get update && apt-get install -y \
8
+ libglib2.0-0 \
9
+ libsm6 \
10
+ libxext6 \
11
+ libxrender-dev \
12
+ libgomp1 \
13
+ libgl1-mesa-glx \
14
+ && rm -rf /var/lib/apt/lists/*
15
 
16
+ # Copy requirements first for better caching
17
+ COPY ./requirements.txt /code/requirements.txt
18
 
19
+ # Install Python dependencies
20
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
21
+
22
+ # Copy application code
23
+ COPY . /code
24
+
25
+ # Expose port 7860 (required by Hugging Face Spaces)
26
+ EXPOSE 7860
27
+
28
+ # Set environment variables
29
+ ENV PYTHONPATH=/code
30
+
31
+ # Command to run the application
32
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,40 +1,39 @@
1
  ---
2
- title: SkinAI Diagnostics
3
  emoji: 🔬
4
- colorFrom: blue
5
- colorTo: purple
6
  sdk: docker
7
  app_port: 7860
8
- pinned: false
9
  ---
10
 
11
- # Skin Cancer Classifier API
12
 
13
- SkinAI Diagnostics a FastAPI-powered web application for skin cancer image classification using deep learning.
14
 
15
- ---
16
-
17
- ## Overview
18
 
19
- This project delivers a professional web interface for classifying dermatoscopic images. It enables users to upload a skin lesion image and obtain a prediction with calibrated confidence scores, alongside links to technical details of the underlying model.
 
 
 
20
 
21
  ---
22
 
23
  ## Repository Structure
24
 
25
  ```
26
- skin-cancer-api/
27
  ├── app/
28
- │ ├── main.py # FastAPI application entry point
29
  │ ├── predict.py # Image preprocessing and prediction logic
30
  │ ├── model_loader.py # Loads the trained EfficientNetV2S model
31
  │ ├── model/ # Local model storage (auto-downloaded if missing)
32
  │ │ └── efficientnetv2s.h5 # Pretrained model file
33
- ├── test_images/ # Sample images for testing the app
34
- │ └── ...
35
  ├── requirements.txt # Python dependencies
36
  ├── Dockerfile # Docker setup for deployment
37
- ├── .dockerignore # Docker ignore rules
38
  └── README.md # Project documentation (this file)
39
  ```
40
 
@@ -42,19 +41,15 @@ skin-cancer-api/
42
 
43
  ## Features
44
 
45
- - Seven-class classification of dermatoscopic images:
46
- - Actinic Keratoses and Intraepithelial Carcinoma (AKIEC)
47
- - Basal Cell Carcinoma (BCC)
48
- - Benign Keratosis-like Lesions (BKL)
49
- - Dermatofibroma (DF)
50
- - Melanoma (MEL)
51
- - Melanocytic Nevi (NV)
52
- - Vascular Lesions (VASC)
53
- - Professional web interface built with FastAPI and Jinja2
54
- - Temperature Scaling (T-scaling) for calibrated probabilities
55
- - Technical transparency: model architecture, training setup, and metrics
56
- - Confidence visualization with a probability chart
57
- - Sample images available in `test_images/`
58
 
59
  ---
60
 
@@ -91,44 +86,126 @@ Manual download link (optional): https://huggingface.co/Miguel764/efficientnetv2
91
 
92
  ## Installation & Usage
93
 
94
- ### 1) Clone the repository
95
 
96
  ```sh
97
- git clone https://github.com/yourusername/skin-cancer-api.git
98
- cd skin-cancer-api
99
  ```
100
 
101
- ### 2) Install dependencies
 
 
102
 
103
  ```sh
104
- pip install -r requirements.txt
105
  ```
106
 
107
- Note: Requires Python 3.8+ and TensorFlow 2.10.0 (GPU recommended).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- ### 3) Run the application
 
 
110
 
111
  ```sh
112
- uvicorn app.main:app --reload
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  ```
114
 
115
- The app will be available at http://localhost:8000
116
 
117
- Access the API docs at http://127.0.0.1:8000/docs
118
 
119
- ### 4) Try with sample images
120
 
121
- Use the images in the `test_images/` folder to test the classifier.
 
 
 
 
122
 
123
  ---
124
 
125
  ## Docker Deployment
126
 
127
- Build and run the app in a container:
128
 
129
  ```sh
130
- docker build -t skin-cancer-api .
131
- docker run -p 8000:8000 skin-cancer-api
132
  ```
133
 
134
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: GP-Tea Skin Analysis
3
  emoji: 🔬
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: docker
7
  app_port: 7860
 
8
  ---
9
 
10
+ # GP-Tea Skin Analysis API
11
 
12
+ An AI-powered skin condition analysis service that provides medical insights from skin images using deep learning.
13
 
14
+ ## Features
 
 
15
 
16
+ - **Image Classification**: Advanced skin condition detection using TensorFlow EfficientNetV2S
17
+ - **Medical Analysis**: Comprehensive skin health assessment with confidence scoring
18
+ - **Real-time Processing**: Fast inference with optimized model performance
19
+ - **REST API**: Easy integration with mobile and web applications
20
 
21
  ---
22
 
23
  ## Repository Structure
24
 
25
  ```
26
+ api_image/
27
  ├── app/
28
+ │ ├── main.py # FastAPI application with JSON endpoints
29
  │ ├── predict.py # Image preprocessing and prediction logic
30
  │ ├── model_loader.py # Loads the trained EfficientNetV2S model
31
  │ ├── model/ # Local model storage (auto-downloaded if missing)
32
  │ │ └── efficientnetv2s.h5 # Pretrained model file
33
+ │ └── uploads/ # Temporary upload directory (auto-created)
34
+ ├── test_images/ # Sample images for testing the API
35
  ├── requirements.txt # Python dependencies
36
  ├── Dockerfile # Docker setup for deployment
 
37
  └── README.md # Project documentation (this file)
38
  ```
39
 
 
41
 
42
  ## Features
43
 
44
+ - **JSON API Endpoints**: Clean REST API for Flutter integration
45
+ - **CORS Enabled**: Ready for mobile app cross-origin requests
46
+ - **Multi-class Classification**: Supports medical image classification
47
+ - **Confidence Scores**: Returns probability percentages for predictions
48
+ - **File Upload Support**: Handles image file uploads with validation
49
+ - **Error Handling**: Proper HTTP status codes and error messages
50
+ - **Health Check**: Monitoring endpoint for service status
51
+ - **Temperature Scaling**: Calibrated probabilities for reliable predictions
52
+ - **Automatic Cleanup**: Temporary files are automatically removed
 
 
 
 
53
 
54
  ---
55
 
 
86
 
87
  ## Installation & Usage
88
 
89
+ ### 1) Install dependencies
90
 
91
  ```sh
92
+ cd Image_classification/api_image
93
+ pip install -r requirements.txt
94
  ```
95
 
96
+ **Requirements**: Python 3.8 and TensorFlow 2.10.0 (GPU recommended). (very important!!!!! Must be Python 3.8)
97
+
98
+ ### 2) Run the API server
99
 
100
  ```sh
101
+ uvicorn app.main:app --host 0.0.0.0 --port 8003 --reload
102
  ```
103
 
104
+ The API will be available at http://localhost:8003
105
+
106
+ Access the API documentation at http://localhost:8003/docs
107
+
108
+ ### 3) Test the API
109
+
110
+ Use the sample images in `test_images/` folder to test the endpoints.
111
+
112
+ ---
113
+
114
+ ## API Endpoints
115
+
116
+ ### Health Check
117
+
118
+ - **GET** `/health` - Service health status
119
+
120
+ ### Image Classification
121
 
122
+ - **POST** `/api/classify` - Upload and classify medical image
123
+
124
+ #### Example Request (cURL):
125
 
126
  ```sh
127
+ curl -X POST "http://localhost:8000/api/classify" \
128
+ -H "accept: application/json" \
129
+ -H "Content-Type: multipart/form-data" \
130
+ -F "file=@test_images/sample.jpg"
131
+ ```
132
+
133
+ #### Example Response:
134
+
135
+ ```json
136
+ {
137
+ "success": true,
138
+ "prediction": {
139
+ "top_prediction": {
140
+ "label": "Melanoma",
141
+ "confidence": 0.95,
142
+ "confidence_percent": "95.00%"
143
+ },
144
+ "all_predictions": [
145
+ {
146
+ "label": "Melanoma",
147
+ "confidence": 0.95,
148
+ "confidence_percent": "95.00%"
149
+ },
150
+ {
151
+ "label": "Benign Nevus",
152
+ "confidence": 0.03,
153
+ "confidence_percent": "3.00%"
154
+ }
155
+ ]
156
+ }
157
+ }
158
  ```
159
 
160
+ ---
161
 
162
+ ## Flutter Integration
163
 
164
+ ### Base URL Configuration
165
 
166
+ ```dart
167
+ class ApiConfig {
168
+ static const String imageApiUrl = "http://localhost:8000";
169
+ }
170
+ ```
171
 
172
  ---
173
 
174
  ## Docker Deployment
175
 
176
+ Build and run the API in a container:
177
 
178
  ```sh
179
+ docker build -t medical-image-api .
180
+ docker run -p 8000:8000 medical-image-api
181
  ```
182
 
183
  ---
184
+
185
+ ## Error Handling
186
+
187
+ The API returns consistent error responses:
188
+
189
+ ```json
190
+ {
191
+ "detail": "File must be an image"
192
+ }
193
+ ```
194
+
195
+ Common HTTP status codes:
196
+
197
+ - `200` - Success
198
+ - `400` - Bad Request (invalid file, missing data)
199
+ - `422` - Unprocessable Entity (validation error)
200
+ - `500` - Internal Server Error (classification failed)
201
+
202
+ ---
203
+
204
+ ## Technical Notes
205
+
206
+ - **Backend Only**: No web interface, pure JSON API
207
+ - **CORS Enabled**: Ready for mobile app integration
208
+ - **File Validation**: Checks file type and validity
209
+ - **Temporary Storage**: Uploaded files are automatically cleaned up
210
+ - **Model Auto-Download**: Model downloads from Hugging Face on first run
211
+ - **Python 3.8 Compatible**: Uses TensorFlow 2.10.0 for compatibility
app.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - Alternative entry point for Hugging Face Spaces
2
+
3
+ import uvicorn
4
+ from app.main import app
5
+
6
+ if __name__ == "__main__":
7
+ uvicorn.run(
8
+ "app.main:app",
9
+ host="0.0.0.0",
10
+ port=7860,
11
+ reload=False
12
+ )
app/main.py CHANGED
@@ -1,55 +1,75 @@
1
- # app/main.py
2
 
3
  import shutil
4
  import os
5
  import uuid
6
- from fastapi import FastAPI, Request, File, UploadFile, Form
7
- from fastapi.responses import HTMLResponse
8
- from fastapi.templating import Jinja2Templates
9
- from fastapi.staticfiles import StaticFiles
10
  from app.predict import predict_image
11
 
12
- app = FastAPI()
 
 
 
 
13
 
14
- templates = Jinja2Templates(directory="app/templates")
15
- app.mount("/static", StaticFiles(directory="app/static"), name="static")
 
 
 
 
 
 
16
 
 
17
  os.makedirs("app/uploads", exist_ok=True)
18
- app.mount("/uploads", StaticFiles(directory="app/uploads"), name="uploads")
19
 
 
 
 
20
 
21
- @app.get("/", response_class=HTMLResponse)
22
- async def index(request: Request):
23
- return templates.TemplateResponse("main.html", {"request": request})
24
-
25
- @app.get("/model-info", response_class=HTMLResponse)
26
- async def model_info(request: Request):
27
- return templates.TemplateResponse("model_info.html", {"request": request})
28
-
29
- @app.post("/upload-image", response_class=HTMLResponse)
30
- async def upload_image(request: Request, file: UploadFile = File(...)):
31
-
32
- unique_filename = f"{uuid.uuid4().hex}_{file.filename}"
33
- file_path = f"app/uploads/{unique_filename}"
34
-
35
- with open(file_path, "wb") as buffer:
36
- shutil.copyfileobj(file.file, buffer)
37
-
38
- return templates.TemplateResponse("main.html", {
39
- "request": request,
40
- "image_path": f"/uploads/{unique_filename}"
41
- })
42
-
43
-
44
- @app.post("/predict", response_class=HTMLResponse)
45
- async def predict(request: Request, image_path: str = Form(...)):
46
- label, confidence, all_predictions = predict_image(f"app{image_path}")
47
- confidence_percent = f"{confidence * 100:.2f}%"
48
-
49
- return templates.TemplateResponse("main.html", {
50
- "request": request,
51
- "image_path": image_path,
52
- "label": label,
53
- "confidence": confidence_percent,
54
- "predictions": all_predictions
55
- })
 
 
 
 
 
 
 
 
 
1
+ # app/main.py
2
 
3
  import shutil
4
  import os
5
  import uuid
6
+ from fastapi import FastAPI, File, UploadFile, HTTPException
7
+ from fastapi.responses import JSONResponse
8
+ from fastapi.middleware.cors import CORSMiddleware
 
9
  from app.predict import predict_image
10
 
11
+ app = FastAPI(
12
+ title="GP-Tea Skin Analysis API",
13
+ description="AI-powered skin condition analysis service",
14
+ version="1.0.0"
15
+ )
16
 
17
+ # Add CORS middleware for Flutter integration
18
+ app.add_middleware(
19
+ CORSMiddleware,
20
+ allow_origins=["*"],
21
+ allow_credentials=True,
22
+ allow_methods=["*"],
23
+ allow_headers=["*"],
24
+ )
25
 
26
+ # Create uploads directory
27
  os.makedirs("app/uploads", exist_ok=True)
 
28
 
29
+ @app.get("/health")
30
+ async def health_check():
31
+ return {"status": "healthy", "service": "gp-tea-skin-analysis"}
32
 
33
+ @app.post("/analyze_image")
34
+ async def analyze_image(file: UploadFile = File(...)):
35
+ """Analyze skin image for medical conditions"""
36
+ try:
37
+ if not file.content_type or not file.content_type.startswith('image/'):
38
+ raise HTTPException(status_code=400, detail="File must be an image")
39
+
40
+ unique_filename = f"{uuid.uuid4().hex}_{file.filename}"
41
+ file_path = f"app/uploads/{unique_filename}"
42
+
43
+ with open(file_path, "wb") as buffer:
44
+ shutil.copyfileobj(file.file, buffer)
45
+
46
+ label, confidence, all_predictions = predict_image(file_path)
47
+ os.remove(file_path)
48
+
49
+ formatted_predictions = []
50
+ for pred in all_predictions:
51
+ formatted_predictions.append({
52
+ "label": pred["label"],
53
+ "confidence": float(pred["confidence"]),
54
+ "confidence_percent": f"{pred['confidence'] * 100:.2f}%"
55
+ })
56
+
57
+ return JSONResponse(
58
+ status_code=200,
59
+ content={
60
+ "success": True,
61
+ "prediction": {
62
+ "top_prediction": {
63
+ "label": label,
64
+ "confidence": float(confidence),
65
+ "confidence_percent": f"{confidence * 100:.2f}%"
66
+ },
67
+ "all_predictions": formatted_predictions
68
+ }
69
+ }
70
+ )
71
+
72
+ except Exception as e:
73
+ if 'file_path' in locals() and os.path.exists(file_path):
74
+ os.remove(file_path)
75
+ raise HTTPException(status_code=500, detail=f"Classification failed: {str(e)}")
requirements.txt CHANGED
@@ -1,7 +1,8 @@
1
- fastapi
2
- uvicorn[standard]
3
- numpy
4
- pillow
5
- jinja2
6
- python-multipart
7
- huggingface_hub
 
 
1
+ fastapi==0.104.1
2
+ uvicorn[standard]==0.24.0
3
+ pydantic==2.5.0
4
+ python-multipart==0.0.6
5
+ numpy==1.26.0
6
+ Pillow==10.0.0
7
+ tensorflow==2.15.0
8
+ opencv-python-headless==4.8.0.76