mt2nwdn commited on
Commit
b754303
·
1 Parent(s): d184417

Add AI image enhancement API with local preview and Hugging Face deployment

Browse files

Create FastAPI application for AI image enhancement with a local preview mode and full deployment configuration for Hugging Face Spaces, including necessary API endpoints and documentation.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: bbef0274-cbeb-475e-9405-87aa3768f3bb
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 4d72e6f6-753f-4797-b6ee-c06530917786
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7d1cb5bc-d8ee-4ca0-b38d-ea189c447bda/bbef0274-cbeb-475e-9405-87aa3768f3bb/Z1S6BFo

Files changed (10) hide show
  1. .gitignore +53 -0
  2. .replit +33 -1
  3. README.md +122 -0
  4. app.py +210 -0
  5. app_local.py +335 -0
  6. enhancer.py +132 -0
  7. my_ssh_key.txt +1 -0
  8. replit.md +46 -0
  9. requirements.txt +11 -0
  10. templates/index.html +465 -0
.gitignore ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .env
25
+ .venv
26
+ env/
27
+ venv/
28
+ ENV/
29
+ .pythonlibs/
30
+
31
+ # IDE
32
+ .idea/
33
+ .vscode/
34
+ *.swp
35
+ *.swo
36
+ *~
37
+
38
+ # Project specific
39
+ uploads/
40
+ outputs/
41
+ weights/
42
+ *.pth
43
+ *.pt
44
+
45
+ # OS
46
+ .DS_Store
47
+ Thumbs.db
48
+
49
+ # Logs
50
+ *.log
51
+
52
+ # UV/pip
53
+ uv.lock
.replit CHANGED
@@ -4,4 +4,36 @@ expertMode = true
4
 
5
  [nix]
6
  channel = "stable-25_05"
7
- packages = ["freetype", "lcms2", "libimagequant", "libjpeg", "libjpeg_turbo", "libpng", "libtiff", "libwebp", "libxcrypt", "openjpeg", "tcl", "tk", "which", "zlib"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  [nix]
6
  channel = "stable-25_05"
7
+ packages = ["freetype", "lcms2", "libimagequant", "libjpeg", "libjpeg_turbo", "libpng", "libtiff", "libwebp", "libxcrypt", "openjpeg", "openssh_gssapi", "tcl", "tk", "which", "zlib"]
8
+
9
+ [workflows]
10
+ runButton = "Project"
11
+
12
+ [[workflows.workflow]]
13
+ name = "Project"
14
+ mode = "parallel"
15
+ author = "agent"
16
+
17
+ [[workflows.workflow.tasks]]
18
+ task = "workflow.run"
19
+ args = "AI Image Enhancer"
20
+
21
+ [[workflows.workflow]]
22
+ name = "AI Image Enhancer"
23
+ author = "agent"
24
+
25
+ [[workflows.workflow.tasks]]
26
+ task = "shell.exec"
27
+ args = "python app_local.py"
28
+ waitForPort = 5000
29
+
30
+ [workflows.workflow.metadata]
31
+ outputType = "webview"
32
+
33
+ [[ports]]
34
+ localPort = 5000
35
+ externalPort = 80
36
+
37
+ [[ports]]
38
+ localPort = 43219
39
+ externalPort = 3000
README.md ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: AI Image Enhancer
3
+ emoji: 🖼️
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 4.0.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ # AI Image Enhancer API
14
+
15
+ A powerful image enhancement API using Real-ESRGAN, a state-of-the-art deep learning model for image super-resolution and quality enhancement.
16
+
17
+ ## Features
18
+
19
+ - **Image Super-Resolution**: Upscale images 2x or 4x while maintaining quality
20
+ - **AI-Powered Enhancement**: Uses Real-ESRGAN x4plus model
21
+ - **RESTful API**: Full API with automatic OpenAPI/Swagger documentation
22
+ - **Web Interface**: Simple drag-and-drop interface for testing
23
+ - **Multiple Output Formats**: Download as file or receive as base64
24
+
25
+ ## API Endpoints
26
+
27
+ ### `GET /docs`
28
+ Interactive Swagger UI documentation
29
+
30
+ ### `GET /redoc`
31
+ ReDoc documentation
32
+
33
+ ### `POST /enhance`
34
+ Enhance an image file
35
+
36
+ **Parameters:**
37
+ - `file`: Image file (PNG, JPG, JPEG, WebP, BMP)
38
+ - `scale`: Upscale factor (2 or 4, default: 4)
39
+
40
+ **Returns:** Enhanced image as PNG file
41
+
42
+ ### `POST /enhance/base64`
43
+ Enhance an image and return as base64
44
+
45
+ **Parameters:**
46
+ - `file`: Image file
47
+ - `scale`: Upscale factor (2 or 4)
48
+
49
+ **Returns:** JSON with base64-encoded image and metadata
50
+
51
+ ### `GET /model-info`
52
+ Get information about the loaded AI model
53
+
54
+ ### `GET /health`
55
+ Health check endpoint
56
+
57
+ ## Model Information
58
+
59
+ - **Model**: Real-ESRGAN x4plus
60
+ - **Architecture**: ESRGAN with RRDB blocks
61
+ - **Capabilities**: General image restoration, super-resolution, artifact removal
62
+ - **Source**: [xinntao/Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN)
63
+
64
+ ## Local Development
65
+
66
+ ```bash
67
+ # Install dependencies
68
+ pip install -r requirements.txt
69
+
70
+ # Run the server
71
+ python app.py
72
+ ```
73
+
74
+ The server will start at `http://localhost:7860`
75
+
76
+ ## Deployment to Hugging Face Spaces
77
+
78
+ 1. Create a new Space on Hugging Face
79
+ 2. Select "Gradio" as the SDK (or Docker for more control)
80
+ 3. Upload all files from this repository
81
+ 4. The Space will automatically install dependencies and start
82
+
83
+ ## API Usage Examples
84
+
85
+ ### Python
86
+ ```python
87
+ import requests
88
+
89
+ with open("image.jpg", "rb") as f:
90
+ response = requests.post(
91
+ "https://your-space.hf.space/enhance",
92
+ files={"file": f},
93
+ params={"scale": 4}
94
+ )
95
+
96
+ with open("enhanced.png", "wb") as f:
97
+ f.write(response.content)
98
+ ```
99
+
100
+ ### cURL
101
+ ```bash
102
+ curl -X POST "https://your-space.hf.space/enhance?scale=4" \
103
+ -F "file=@image.jpg" \
104
+ -o enhanced.png
105
+ ```
106
+
107
+ ### JavaScript
108
+ ```javascript
109
+ const formData = new FormData();
110
+ formData.append('file', imageFile);
111
+
112
+ const response = await fetch('/enhance?scale=4', {
113
+ method: 'POST',
114
+ body: formData
115
+ });
116
+
117
+ const blob = await response.blob();
118
+ ```
119
+
120
+ ## License
121
+
122
+ MIT License
app.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import uuid
4
+ from pathlib import Path
5
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Query
6
+ from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
7
+ from fastapi.staticfiles import StaticFiles
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from PIL import Image
10
+
11
+ UPLOAD_DIR = Path("uploads")
12
+ OUTPUT_DIR = Path("outputs")
13
+ UPLOAD_DIR.mkdir(exist_ok=True)
14
+ OUTPUT_DIR.mkdir(exist_ok=True)
15
+
16
+ app = FastAPI(
17
+ title="AI Image Enhancer API",
18
+ description="""
19
+ ## AI-Powered Image Enhancement API
20
+
21
+ This API uses Real-ESRGAN, a state-of-the-art deep learning model for image super-resolution and enhancement.
22
+
23
+ ### Features:
24
+ - **Image Upscaling**: Enhance image resolution up to 4x
25
+ - **Quality Enhancement**: Improve image clarity and reduce artifacts
26
+ - **Multiple Scale Options**: Choose between 2x and 4x upscaling
27
+
28
+ ### Supported Formats:
29
+ - PNG, JPG, JPEG, WebP, BMP
30
+
31
+ ### Model Information:
32
+ - **Model**: Real-ESRGAN x4plus
33
+ - **Architecture**: ESRGAN with RRDB blocks
34
+ - **Training**: Trained on diverse image datasets for general-purpose enhancement
35
+ """,
36
+ version="1.0.0",
37
+ docs_url="/docs",
38
+ redoc_url="/redoc",
39
+ )
40
+
41
+ app.add_middleware(
42
+ CORSMiddleware,
43
+ allow_origins=["*"],
44
+ allow_credentials=True,
45
+ allow_methods=["*"],
46
+ allow_headers=["*"],
47
+ )
48
+
49
+ enhancer = None
50
+
51
+ def get_enhancer():
52
+ global enhancer
53
+ if enhancer is None:
54
+ from enhancer import ImageEnhancer
55
+ enhancer = ImageEnhancer()
56
+ return enhancer
57
+
58
+ @app.get("/", response_class=HTMLResponse)
59
+ async def home():
60
+ """Serve the main HTML page for testing image enhancement."""
61
+ html_path = Path("templates/index.html")
62
+ if html_path.exists():
63
+ return html_path.read_text()
64
+ return """
65
+ <html>
66
+ <head><title>AI Image Enhancer</title></head>
67
+ <body>
68
+ <h1>AI Image Enhancer</h1>
69
+ <p>Visit <a href="/docs">/docs</a> for API documentation.</p>
70
+ </body>
71
+ </html>
72
+ """
73
+
74
+ @app.get("/health")
75
+ async def health_check():
76
+ """Health check endpoint."""
77
+ return {"status": "healthy", "model": "Real-ESRGAN x4plus"}
78
+
79
+ @app.get("/model-info")
80
+ async def model_info():
81
+ """Get information about the loaded AI model."""
82
+ return {
83
+ "model_name": "Real-ESRGAN x4plus",
84
+ "description": "Real-ESRGAN model for general image restoration and super-resolution",
85
+ "upscale_factors": [2, 4],
86
+ "supported_formats": ["png", "jpg", "jpeg", "webp", "bmp"],
87
+ "max_input_size": "2048x2048 recommended",
88
+ "source": "https://github.com/xinntao/Real-ESRGAN"
89
+ }
90
+
91
+ @app.post("/enhance")
92
+ async def enhance_image(
93
+ file: UploadFile = File(..., description="Image file to enhance (PNG, JPG, JPEG, WebP, BMP)"),
94
+ scale: int = Query(default=4, ge=2, le=4, description="Upscale factor (2 or 4)")
95
+ ):
96
+ """
97
+ Enhance an image using Real-ESRGAN AI model.
98
+
99
+ - **file**: Upload an image file (PNG, JPG, JPEG, WebP, BMP)
100
+ - **scale**: Upscaling factor - 2 for 2x resolution, 4 for 4x resolution
101
+
102
+ Returns the enhanced image as a PNG file.
103
+ """
104
+ allowed_types = ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/bmp"]
105
+ if file.content_type not in allowed_types:
106
+ raise HTTPException(
107
+ status_code=400,
108
+ detail=f"Invalid file type. Allowed types: {', '.join(allowed_types)}"
109
+ )
110
+
111
+ try:
112
+ contents = await file.read()
113
+ input_image = Image.open(io.BytesIO(contents))
114
+
115
+ if input_image.mode != "RGB":
116
+ input_image = input_image.convert("RGB")
117
+
118
+ max_size = 2048
119
+ if input_image.width > max_size or input_image.height > max_size:
120
+ ratio = min(max_size / input_image.width, max_size / input_image.height)
121
+ new_size = (int(input_image.width * ratio), int(input_image.height * ratio))
122
+ input_image = input_image.resize(new_size, Image.LANCZOS)
123
+
124
+ file_id = str(uuid.uuid4())
125
+ input_path = UPLOAD_DIR / f"{file_id}_input.png"
126
+ output_path = OUTPUT_DIR / f"{file_id}_enhanced.png"
127
+
128
+ input_image.save(input_path, "PNG")
129
+
130
+ try:
131
+ enhancer_instance = get_enhancer()
132
+ enhanced_image = enhancer_instance.enhance(input_image, scale=scale)
133
+ enhanced_image.save(output_path, "PNG")
134
+ except ImportError:
135
+ enhanced_image = input_image.resize(
136
+ (input_image.width * scale, input_image.height * scale),
137
+ Image.LANCZOS
138
+ )
139
+ enhanced_image.save(output_path, "PNG")
140
+
141
+ return FileResponse(
142
+ output_path,
143
+ media_type="image/png",
144
+ filename=f"enhanced_{file.filename.rsplit('.', 1)[0]}.png"
145
+ )
146
+
147
+ except Exception as e:
148
+ raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")
149
+
150
+ @app.post("/enhance/base64")
151
+ async def enhance_image_base64(
152
+ file: UploadFile = File(..., description="Image file to enhance"),
153
+ scale: int = Query(default=4, ge=2, le=4, description="Upscale factor (2 or 4)")
154
+ ):
155
+ """
156
+ Enhance an image and return it as base64-encoded string.
157
+
158
+ Useful for integrations that prefer base64 over file downloads.
159
+ """
160
+ import base64
161
+
162
+ allowed_types = ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/bmp"]
163
+ if file.content_type not in allowed_types:
164
+ raise HTTPException(
165
+ status_code=400,
166
+ detail=f"Invalid file type. Allowed types: {', '.join(allowed_types)}"
167
+ )
168
+
169
+ try:
170
+ contents = await file.read()
171
+ input_image = Image.open(io.BytesIO(contents))
172
+
173
+ if input_image.mode != "RGB":
174
+ input_image = input_image.convert("RGB")
175
+
176
+ max_size = 2048
177
+ if input_image.width > max_size or input_image.height > max_size:
178
+ ratio = min(max_size / input_image.width, max_size / input_image.height)
179
+ new_size = (int(input_image.width * ratio), int(input_image.height * ratio))
180
+ input_image = input_image.resize(new_size, Image.LANCZOS)
181
+
182
+ try:
183
+ enhancer_instance = get_enhancer()
184
+ enhanced_image = enhancer_instance.enhance(input_image, scale=scale)
185
+ except ImportError:
186
+ enhanced_image = input_image.resize(
187
+ (input_image.width * scale, input_image.height * scale),
188
+ Image.LANCZOS
189
+ )
190
+
191
+ buffer = io.BytesIO()
192
+ enhanced_image.save(buffer, format="PNG")
193
+ buffer.seek(0)
194
+
195
+ img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
196
+
197
+ return JSONResponse({
198
+ "success": True,
199
+ "image_base64": img_base64,
200
+ "original_size": {"width": input_image.width, "height": input_image.height},
201
+ "enhanced_size": {"width": enhanced_image.width, "height": enhanced_image.height},
202
+ "scale_factor": scale
203
+ })
204
+
205
+ except Exception as e:
206
+ raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")
207
+
208
+ if __name__ == "__main__":
209
+ import uvicorn
210
+ uvicorn.run(app, host="0.0.0.0", port=7860)
app_local.py ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Lightweight local preview server for testing the API structure.
3
+ This version uses simple image resizing instead of the AI model.
4
+ For full AI enhancement, deploy to Hugging Face Spaces.
5
+ """
6
+ import os
7
+ import io
8
+ import uuid
9
+ from pathlib import Path
10
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
11
+ import json
12
+ import cgi
13
+ import urllib.parse
14
+
15
+ UPLOAD_DIR = Path("uploads")
16
+ OUTPUT_DIR = Path("outputs")
17
+ UPLOAD_DIR.mkdir(exist_ok=True)
18
+ OUTPUT_DIR.mkdir(exist_ok=True)
19
+
20
+ class APIHandler(SimpleHTTPRequestHandler):
21
+ def do_GET(self):
22
+ parsed = urllib.parse.urlparse(self.path)
23
+ path = parsed.path
24
+
25
+ if path == "/" or path == "":
26
+ self.serve_html()
27
+ elif path == "/docs":
28
+ self.serve_swagger()
29
+ elif path == "/health":
30
+ self.send_json({"status": "healthy", "model": "Real-ESRGAN x4plus (preview mode)"})
31
+ elif path == "/model-info":
32
+ self.send_json({
33
+ "model_name": "Real-ESRGAN x4plus",
34
+ "description": "Real-ESRGAN model for general image restoration and super-resolution",
35
+ "upscale_factors": [2, 4],
36
+ "supported_formats": ["png", "jpg", "jpeg", "webp", "bmp"],
37
+ "max_input_size": "2048x2048 recommended",
38
+ "source": "https://github.com/xinntao/Real-ESRGAN",
39
+ "note": "Running in preview mode - deploy to Hugging Face for full AI enhancement"
40
+ })
41
+ elif path == "/openapi.json":
42
+ self.serve_openapi()
43
+ elif path.startswith("/outputs/"):
44
+ self.serve_file(path)
45
+ else:
46
+ super().do_GET()
47
+
48
+ def do_POST(self):
49
+ parsed = urllib.parse.urlparse(self.path)
50
+ path = parsed.path
51
+ query = urllib.parse.parse_qs(parsed.query)
52
+
53
+ if path == "/enhance" or path == "/enhance/base64":
54
+ self.handle_enhance(path, query)
55
+ else:
56
+ self.send_error(404, "Not Found")
57
+
58
+ def handle_enhance(self, path, query):
59
+ try:
60
+ content_type = self.headers.get('Content-Type', '')
61
+ if 'multipart/form-data' not in content_type:
62
+ self.send_error(400, "Expected multipart/form-data")
63
+ return
64
+
65
+ form = cgi.FieldStorage(
66
+ fp=self.rfile,
67
+ headers=self.headers,
68
+ environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': content_type}
69
+ )
70
+
71
+ if 'file' not in form:
72
+ self.send_error(400, "No file uploaded")
73
+ return
74
+
75
+ file_item = form['file']
76
+ file_data = file_item.file.read()
77
+ filename = file_item.filename
78
+
79
+ scale = int(query.get('scale', [4])[0])
80
+ if scale not in [2, 4]:
81
+ scale = 4
82
+
83
+ from PIL import Image, ImageEnhance
84
+ input_image = Image.open(io.BytesIO(file_data))
85
+
86
+ if input_image.mode != "RGB":
87
+ input_image = input_image.convert("RGB")
88
+
89
+ new_size = (input_image.width * scale, input_image.height * scale)
90
+ upscaled = input_image.resize(new_size, Image.LANCZOS)
91
+
92
+ enhancer = ImageEnhance.Sharpness(upscaled)
93
+ sharpened = enhancer.enhance(1.3)
94
+ enhancer = ImageEnhance.Contrast(sharpened)
95
+ enhanced = enhancer.enhance(1.1)
96
+
97
+ file_id = str(uuid.uuid4())
98
+ output_path = OUTPUT_DIR / f"{file_id}_enhanced.png"
99
+ enhanced.save(output_path, "PNG")
100
+
101
+ if "/base64" in path:
102
+ import base64
103
+ buffer = io.BytesIO()
104
+ enhanced.save(buffer, format="PNG")
105
+ buffer.seek(0)
106
+ img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
107
+
108
+ self.send_json({
109
+ "success": True,
110
+ "image_base64": img_base64,
111
+ "original_size": {"width": input_image.width, "height": input_image.height},
112
+ "enhanced_size": {"width": enhanced.width, "height": enhanced.height},
113
+ "scale_factor": scale,
114
+ "note": "Preview mode - deploy to Hugging Face for AI enhancement"
115
+ })
116
+ else:
117
+ self.send_response(200)
118
+ self.send_header('Content-Type', 'image/png')
119
+ self.send_header('Content-Disposition', f'attachment; filename="enhanced_{filename.rsplit(".", 1)[0]}.png"')
120
+ self.end_headers()
121
+ with open(output_path, 'rb') as f:
122
+ self.wfile.write(f.read())
123
+
124
+ except Exception as e:
125
+ self.send_error(500, f"Error processing image: {str(e)}")
126
+
127
+ def serve_html(self):
128
+ html_path = Path("templates/index.html")
129
+ if html_path.exists():
130
+ self.send_response(200)
131
+ self.send_header('Content-Type', 'text/html')
132
+ self.send_header('Cache-Control', 'no-cache')
133
+ self.end_headers()
134
+ self.wfile.write(html_path.read_bytes())
135
+ else:
136
+ self.send_error(404, "Template not found")
137
+
138
+ def serve_swagger(self):
139
+ swagger_html = """<!DOCTYPE html>
140
+ <html>
141
+ <head>
142
+ <title>AI Image Enhancer - API Documentation</title>
143
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
144
+ <style>
145
+ body { margin: 0; padding: 0; }
146
+ .swagger-ui .topbar { display: none; }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div id="swagger-ui"></div>
151
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
152
+ <script>
153
+ window.onload = function() {
154
+ SwaggerUIBundle({
155
+ url: "/openapi.json",
156
+ dom_id: '#swagger-ui',
157
+ presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
158
+ layout: "BaseLayout"
159
+ });
160
+ };
161
+ </script>
162
+ </body>
163
+ </html>"""
164
+ self.send_response(200)
165
+ self.send_header('Content-Type', 'text/html')
166
+ self.send_header('Cache-Control', 'no-cache')
167
+ self.end_headers()
168
+ self.wfile.write(swagger_html.encode())
169
+
170
+ def serve_openapi(self):
171
+ openapi_spec = {
172
+ "openapi": "3.1.0",
173
+ "info": {
174
+ "title": "AI Image Enhancer API",
175
+ "description": "AI-Powered Image Enhancement API using Real-ESRGAN deep learning model.\n\n**Features:**\n- Image upscaling up to 4x resolution\n- Quality enhancement and artifact removal\n- RESTful API with multiple output formats\n\n**Note:** This is a preview deployment. For full AI enhancement, deploy to Hugging Face Spaces.",
176
+ "version": "1.0.0",
177
+ "contact": {"name": "AI Image Enhancer"}
178
+ },
179
+ "servers": [{"url": "/", "description": "Current server"}],
180
+ "paths": {
181
+ "/enhance": {
182
+ "post": {
183
+ "summary": "Enhance an image",
184
+ "description": "Upload an image and receive an enhanced version with improved resolution and quality.",
185
+ "operationId": "enhance_image",
186
+ "parameters": [
187
+ {
188
+ "name": "scale",
189
+ "in": "query",
190
+ "description": "Upscale factor (2 or 4)",
191
+ "required": False,
192
+ "schema": {"type": "integer", "default": 4, "enum": [2, 4]}
193
+ }
194
+ ],
195
+ "requestBody": {
196
+ "required": True,
197
+ "content": {
198
+ "multipart/form-data": {
199
+ "schema": {
200
+ "type": "object",
201
+ "required": ["file"],
202
+ "properties": {
203
+ "file": {
204
+ "type": "string",
205
+ "format": "binary",
206
+ "description": "Image file (PNG, JPG, JPEG, WebP, BMP)"
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ },
213
+ "responses": {
214
+ "200": {
215
+ "description": "Enhanced image",
216
+ "content": {"image/png": {"schema": {"type": "string", "format": "binary"}}}
217
+ },
218
+ "400": {"description": "Invalid file type"},
219
+ "500": {"description": "Processing error"}
220
+ }
221
+ }
222
+ },
223
+ "/enhance/base64": {
224
+ "post": {
225
+ "summary": "Enhance an image (Base64 response)",
226
+ "description": "Upload an image and receive an enhanced version as a base64-encoded string.",
227
+ "operationId": "enhance_image_base64",
228
+ "parameters": [
229
+ {
230
+ "name": "scale",
231
+ "in": "query",
232
+ "description": "Upscale factor (2 or 4)",
233
+ "required": False,
234
+ "schema": {"type": "integer", "default": 4, "enum": [2, 4]}
235
+ }
236
+ ],
237
+ "requestBody": {
238
+ "required": True,
239
+ "content": {
240
+ "multipart/form-data": {
241
+ "schema": {
242
+ "type": "object",
243
+ "required": ["file"],
244
+ "properties": {
245
+ "file": {
246
+ "type": "string",
247
+ "format": "binary",
248
+ "description": "Image file"
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+ },
255
+ "responses": {
256
+ "200": {
257
+ "description": "Enhanced image data",
258
+ "content": {
259
+ "application/json": {
260
+ "schema": {
261
+ "type": "object",
262
+ "properties": {
263
+ "success": {"type": "boolean"},
264
+ "image_base64": {"type": "string"},
265
+ "original_size": {"type": "object"},
266
+ "enhanced_size": {"type": "object"},
267
+ "scale_factor": {"type": "integer"}
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ }
275
+ },
276
+ "/health": {
277
+ "get": {
278
+ "summary": "Health check",
279
+ "description": "Check if the API is running",
280
+ "operationId": "health_check",
281
+ "responses": {
282
+ "200": {
283
+ "description": "API status",
284
+ "content": {"application/json": {"schema": {"type": "object"}}}
285
+ }
286
+ }
287
+ }
288
+ },
289
+ "/model-info": {
290
+ "get": {
291
+ "summary": "Model information",
292
+ "description": "Get information about the loaded AI model",
293
+ "operationId": "model_info",
294
+ "responses": {
295
+ "200": {
296
+ "description": "Model details",
297
+ "content": {"application/json": {"schema": {"type": "object"}}}
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+ self.send_json(openapi_spec)
305
+
306
+ def serve_file(self, path):
307
+ file_path = Path("." + path)
308
+ if file_path.exists():
309
+ self.send_response(200)
310
+ self.send_header('Content-Type', 'image/png')
311
+ self.end_headers()
312
+ self.wfile.write(file_path.read_bytes())
313
+ else:
314
+ self.send_error(404, "File not found")
315
+
316
+ def send_json(self, data):
317
+ response = json.dumps(data, indent=2)
318
+ self.send_response(200)
319
+ self.send_header('Content-Type', 'application/json')
320
+ self.send_header('Cache-Control', 'no-cache')
321
+ self.end_headers()
322
+ self.wfile.write(response.encode())
323
+
324
+ def run_server(port=5000):
325
+ server_address = ('0.0.0.0', port)
326
+ httpd = HTTPServer(server_address, APIHandler)
327
+ print(f"AI Image Enhancer API running at http://0.0.0.0:{port}")
328
+ print(f"API Documentation: http://0.0.0.0:{port}/docs")
329
+ print(f"Frontend: http://0.0.0.0:{port}/")
330
+ print("\nNote: This is a preview mode using simple upscaling.")
331
+ print("For full AI enhancement, deploy to Hugging Face Spaces.")
332
+ httpd.serve_forever()
333
+
334
+ if __name__ == "__main__":
335
+ run_server()
enhancer.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import numpy as np
4
+ from PIL import Image
5
+ from pathlib import Path
6
+
7
+ class ImageEnhancer:
8
+ """
9
+ AI Image Enhancer using Real-ESRGAN model.
10
+
11
+ This class handles:
12
+ - Automatic model download from Hugging Face Hub
13
+ - Image preprocessing and postprocessing
14
+ - GPU/CPU inference
15
+ """
16
+
17
+ def __init__(self, model_name: str = "RealESRGAN_x4plus"):
18
+ self.model_name = model_name
19
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
20
+ self.model = None
21
+ self._load_model()
22
+
23
+ def _load_model(self):
24
+ """Download and load the Real-ESRGAN model."""
25
+ try:
26
+ from realesrgan import RealESRGANer
27
+ from basicsr.archs.rrdbnet_arch import RRDBNet
28
+ except ImportError:
29
+ print("Installing Real-ESRGAN dependencies...")
30
+ os.system("pip install realesrgan basicsr")
31
+ from realesrgan import RealESRGANer
32
+ from basicsr.archs.rrdbnet_arch import RRDBNet
33
+
34
+ model_path = Path("weights")
35
+ model_path.mkdir(exist_ok=True)
36
+
37
+ model_file = model_path / "RealESRGAN_x4plus.pth"
38
+
39
+ if not model_file.exists():
40
+ print("Downloading Real-ESRGAN x4plus model...")
41
+ import urllib.request
42
+ url = "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth"
43
+ urllib.request.urlretrieve(url, model_file)
44
+ print("Model downloaded successfully!")
45
+
46
+ model = RRDBNet(
47
+ num_in_ch=3,
48
+ num_out_ch=3,
49
+ num_feat=64,
50
+ num_block=23,
51
+ num_grow_ch=32,
52
+ scale=4
53
+ )
54
+
55
+ self.upsampler = RealESRGANer(
56
+ scale=4,
57
+ model_path=str(model_file),
58
+ model=model,
59
+ tile=0,
60
+ tile_pad=10,
61
+ pre_pad=0,
62
+ half=False if self.device.type == "cpu" else True,
63
+ device=self.device
64
+ )
65
+
66
+ print(f"Model loaded on {self.device}")
67
+
68
+ def enhance(self, image: Image.Image, scale: int = 4) -> Image.Image:
69
+ """
70
+ Enhance an image using Real-ESRGAN.
71
+
72
+ Args:
73
+ image: PIL Image to enhance
74
+ scale: Upscaling factor (2 or 4)
75
+
76
+ Returns:
77
+ Enhanced PIL Image
78
+ """
79
+ img_array = np.array(image)
80
+
81
+ if len(img_array.shape) == 2:
82
+ img_array = np.stack([img_array] * 3, axis=-1)
83
+ elif img_array.shape[2] == 4:
84
+ img_array = img_array[:, :, :3]
85
+
86
+ img_bgr = img_array[:, :, ::-1]
87
+
88
+ output, _ = self.upsampler.enhance(img_bgr, outscale=scale)
89
+
90
+ output_rgb = output[:, :, ::-1]
91
+ enhanced_image = Image.fromarray(output_rgb)
92
+
93
+ return enhanced_image
94
+
95
+
96
+ class FallbackEnhancer:
97
+ """
98
+ Fallback enhancer using traditional image processing when AI model is unavailable.
99
+ Uses PIL's high-quality resampling for upscaling.
100
+ """
101
+
102
+ def __init__(self):
103
+ print("Using fallback enhancer (no AI model available)")
104
+
105
+ def enhance(self, image: Image.Image, scale: int = 4) -> Image.Image:
106
+ """
107
+ Enhance image using traditional upscaling with sharpening.
108
+ """
109
+ from PIL import ImageEnhance, ImageFilter
110
+
111
+ new_size = (image.width * scale, image.height * scale)
112
+ upscaled = image.resize(new_size, Image.LANCZOS)
113
+
114
+ enhancer = ImageEnhance.Sharpness(upscaled)
115
+ sharpened = enhancer.enhance(1.3)
116
+
117
+ enhancer = ImageEnhance.Contrast(sharpened)
118
+ enhanced = enhancer.enhance(1.1)
119
+
120
+ return enhanced
121
+
122
+
123
+ def get_enhancer():
124
+ """
125
+ Factory function to get the best available enhancer.
126
+ Returns AI enhancer if available, otherwise falls back to traditional methods.
127
+ """
128
+ try:
129
+ return ImageEnhancer()
130
+ except Exception as e:
131
+ print(f"Could not load AI model: {e}")
132
+ return FallbackEnhancer()
my_ssh_key.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBI7m5FxfcNAbUEc+X3FQ5BolyjdJA6xcr5oizuRXWmL runner@491f6838faa8
replit.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Image Enhancer
2
+
3
+ ## Overview
4
+ An AI-powered image enhancement API using Real-ESRGAN for image super-resolution. The app includes:
5
+ - FastAPI backend with automatic Swagger API documentation
6
+ - Simple web frontend for testing image uploads
7
+ - Designed for deployment to Hugging Face Spaces
8
+
9
+ ## Current State
10
+ - **Local Preview**: Running with simple upscaling (no AI model due to size constraints)
11
+ - **Full AI Mode**: Available when deployed to Hugging Face Spaces
12
+
13
+ ## Project Structure
14
+ ```
15
+ /
16
+ ├── app.py # Full FastAPI app for Hugging Face deployment
17
+ ├── app_local.py # Lightweight local preview server
18
+ ├── enhancer.py # Real-ESRGAN model wrapper (for HF deployment)
19
+ ├── templates/
20
+ │ └── index.html # Frontend interface
21
+ ├── requirements.txt # Dependencies for Hugging Face Spaces
22
+ ├── README.md # Hugging Face Spaces configuration
23
+ ├── uploads/ # Temporary upload storage
24
+ └── outputs/ # Enhanced image outputs
25
+ ```
26
+
27
+ ## API Endpoints
28
+ - `GET /` - Web frontend
29
+ - `GET /docs` - Swagger API documentation
30
+ - `GET /health` - Health check
31
+ - `GET /model-info` - Model information
32
+ - `POST /enhance` - Enhance image (returns PNG file)
33
+ - `POST /enhance/base64` - Enhance image (returns base64 JSON)
34
+
35
+ ## Deploying to Hugging Face Spaces
36
+ 1. Create a new Space on Hugging Face
37
+ 2. Select "Gradio" SDK (or "Docker" for more control)
38
+ 3. Upload all files: `app.py`, `enhancer.py`, `templates/`, `requirements.txt`, `README.md`
39
+ 4. The Space will auto-install dependencies and download the Real-ESRGAN model
40
+
41
+ ## Recent Changes
42
+ - 2025-11-28: Initial creation of AI Image Enhancer API
43
+ - FastAPI backend with Swagger docs
44
+ - Real-ESRGAN integration for Hugging Face
45
+ - Simple frontend for testing
46
+ - Lightweight local preview mode
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.109.0
2
+ uvicorn[standard]==0.27.0
3
+ python-multipart==0.0.6
4
+ pillow==10.2.0
5
+ numpy==1.26.3
6
+ torch>=2.1.0
7
+ torchvision>=0.16.0
8
+ realesrgan==0.3.0
9
+ basicsr==1.4.2
10
+ gfpgan==1.3.8
11
+ pillow
templates/index.html ADDED
@@ -0,0 +1,465 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Image Enhancer</title>
7
+ <style>
8
+ * {
9
+ box-sizing: border-box;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
16
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
17
+ min-height: 100vh;
18
+ color: #fff;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 900px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ header {
28
+ text-align: center;
29
+ margin-bottom: 40px;
30
+ }
31
+
32
+ h1 {
33
+ font-size: 2.5rem;
34
+ margin-bottom: 10px;
35
+ background: linear-gradient(90deg, #00d2ff, #3a7bd5);
36
+ -webkit-background-clip: text;
37
+ -webkit-text-fill-color: transparent;
38
+ background-clip: text;
39
+ }
40
+
41
+ .subtitle {
42
+ color: #888;
43
+ font-size: 1.1rem;
44
+ }
45
+
46
+ .api-link {
47
+ margin-top: 15px;
48
+ }
49
+
50
+ .api-link a {
51
+ color: #00d2ff;
52
+ text-decoration: none;
53
+ padding: 8px 16px;
54
+ border: 1px solid #00d2ff;
55
+ border-radius: 20px;
56
+ transition: all 0.3s;
57
+ }
58
+
59
+ .api-link a:hover {
60
+ background: #00d2ff;
61
+ color: #1a1a2e;
62
+ }
63
+
64
+ .upload-section {
65
+ background: rgba(255, 255, 255, 0.05);
66
+ border-radius: 16px;
67
+ padding: 30px;
68
+ margin-bottom: 30px;
69
+ }
70
+
71
+ .drop-zone {
72
+ border: 2px dashed #3a7bd5;
73
+ border-radius: 12px;
74
+ padding: 40px;
75
+ text-align: center;
76
+ cursor: pointer;
77
+ transition: all 0.3s;
78
+ }
79
+
80
+ .drop-zone:hover, .drop-zone.dragover {
81
+ border-color: #00d2ff;
82
+ background: rgba(0, 210, 255, 0.1);
83
+ }
84
+
85
+ .drop-zone p {
86
+ color: #888;
87
+ margin-top: 10px;
88
+ }
89
+
90
+ .drop-zone-icon {
91
+ font-size: 3rem;
92
+ color: #3a7bd5;
93
+ }
94
+
95
+ #fileInput {
96
+ display: none;
97
+ }
98
+
99
+ .options {
100
+ display: flex;
101
+ gap: 20px;
102
+ margin-top: 20px;
103
+ flex-wrap: wrap;
104
+ }
105
+
106
+ .option-group {
107
+ flex: 1;
108
+ min-width: 200px;
109
+ }
110
+
111
+ .option-group label {
112
+ display: block;
113
+ margin-bottom: 8px;
114
+ color: #888;
115
+ }
116
+
117
+ select {
118
+ width: 100%;
119
+ padding: 12px;
120
+ border-radius: 8px;
121
+ border: 1px solid #333;
122
+ background: #1a1a2e;
123
+ color: #fff;
124
+ font-size: 1rem;
125
+ }
126
+
127
+ .enhance-btn {
128
+ width: 100%;
129
+ padding: 15px;
130
+ margin-top: 20px;
131
+ background: linear-gradient(90deg, #00d2ff, #3a7bd5);
132
+ border: none;
133
+ border-radius: 8px;
134
+ color: #fff;
135
+ font-size: 1.1rem;
136
+ font-weight: 600;
137
+ cursor: pointer;
138
+ transition: transform 0.2s, box-shadow 0.2s;
139
+ }
140
+
141
+ .enhance-btn:hover:not(:disabled) {
142
+ transform: translateY(-2px);
143
+ box-shadow: 0 10px 30px rgba(0, 210, 255, 0.3);
144
+ }
145
+
146
+ .enhance-btn:disabled {
147
+ opacity: 0.5;
148
+ cursor: not-allowed;
149
+ }
150
+
151
+ .results-section {
152
+ display: none;
153
+ }
154
+
155
+ .results-section.show {
156
+ display: block;
157
+ }
158
+
159
+ .image-comparison {
160
+ display: grid;
161
+ grid-template-columns: 1fr 1fr;
162
+ gap: 20px;
163
+ }
164
+
165
+ @media (max-width: 600px) {
166
+ .image-comparison {
167
+ grid-template-columns: 1fr;
168
+ }
169
+ }
170
+
171
+ .image-box {
172
+ background: rgba(255, 255, 255, 0.05);
173
+ border-radius: 12px;
174
+ padding: 15px;
175
+ }
176
+
177
+ .image-box h3 {
178
+ margin-bottom: 10px;
179
+ color: #888;
180
+ font-size: 0.9rem;
181
+ text-transform: uppercase;
182
+ }
183
+
184
+ .image-box img {
185
+ width: 100%;
186
+ border-radius: 8px;
187
+ }
188
+
189
+ .download-btn {
190
+ display: inline-block;
191
+ margin-top: 15px;
192
+ padding: 10px 20px;
193
+ background: #3a7bd5;
194
+ color: #fff;
195
+ text-decoration: none;
196
+ border-radius: 6px;
197
+ transition: background 0.3s;
198
+ }
199
+
200
+ .download-btn:hover {
201
+ background: #00d2ff;
202
+ }
203
+
204
+ .loading {
205
+ display: none;
206
+ text-align: center;
207
+ padding: 40px;
208
+ }
209
+
210
+ .loading.show {
211
+ display: block;
212
+ }
213
+
214
+ .spinner {
215
+ width: 50px;
216
+ height: 50px;
217
+ border: 4px solid rgba(255, 255, 255, 0.1);
218
+ border-top-color: #00d2ff;
219
+ border-radius: 50%;
220
+ animation: spin 1s linear infinite;
221
+ margin: 0 auto 20px;
222
+ }
223
+
224
+ @keyframes spin {
225
+ to { transform: rotate(360deg); }
226
+ }
227
+
228
+ .error {
229
+ background: rgba(255, 50, 50, 0.2);
230
+ border: 1px solid #ff3232;
231
+ padding: 15px;
232
+ border-radius: 8px;
233
+ margin-top: 20px;
234
+ display: none;
235
+ }
236
+
237
+ .error.show {
238
+ display: block;
239
+ }
240
+
241
+ .info-section {
242
+ background: rgba(255, 255, 255, 0.05);
243
+ border-radius: 16px;
244
+ padding: 30px;
245
+ margin-top: 30px;
246
+ }
247
+
248
+ .info-section h2 {
249
+ margin-bottom: 20px;
250
+ color: #00d2ff;
251
+ }
252
+
253
+ .info-grid {
254
+ display: grid;
255
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
256
+ gap: 20px;
257
+ }
258
+
259
+ .info-item {
260
+ background: rgba(255, 255, 255, 0.05);
261
+ padding: 15px;
262
+ border-radius: 8px;
263
+ }
264
+
265
+ .info-item h4 {
266
+ color: #3a7bd5;
267
+ margin-bottom: 5px;
268
+ }
269
+
270
+ .info-item p {
271
+ color: #888;
272
+ font-size: 0.9rem;
273
+ }
274
+ </style>
275
+ </head>
276
+ <body>
277
+ <div class="container">
278
+ <header>
279
+ <h1>AI Image Enhancer</h1>
280
+ <p class="subtitle">Enhance your images using Real-ESRGAN deep learning model</p>
281
+ <div class="api-link">
282
+ <a href="/docs" target="_blank">View API Documentation</a>
283
+ </div>
284
+ </header>
285
+
286
+ <section class="upload-section">
287
+ <div class="drop-zone" id="dropZone">
288
+ <div class="drop-zone-icon">📷</div>
289
+ <p>Drag & drop an image here or click to select</p>
290
+ <p><small>Supports: PNG, JPG, JPEG, WebP, BMP</small></p>
291
+ </div>
292
+ <input type="file" id="fileInput" accept="image/png,image/jpeg,image/jpg,image/webp,image/bmp">
293
+
294
+ <div class="options">
295
+ <div class="option-group">
296
+ <label for="scale">Upscale Factor</label>
297
+ <select id="scale">
298
+ <option value="2">2x - Double resolution</option>
299
+ <option value="4" selected>4x - Quadruple resolution</option>
300
+ </select>
301
+ </div>
302
+ </div>
303
+
304
+ <button class="enhance-btn" id="enhanceBtn" disabled>Enhance Image</button>
305
+
306
+ <div class="error" id="error"></div>
307
+ </section>
308
+
309
+ <div class="loading" id="loading">
310
+ <div class="spinner"></div>
311
+ <p>Enhancing your image with AI magic...</p>
312
+ <p><small>This may take a moment for larger images</small></p>
313
+ </div>
314
+
315
+ <section class="results-section" id="results">
316
+ <div class="image-comparison">
317
+ <div class="image-box">
318
+ <h3>Original</h3>
319
+ <img id="originalImg" src="" alt="Original image">
320
+ </div>
321
+ <div class="image-box">
322
+ <h3>Enhanced</h3>
323
+ <img id="enhancedImg" src="" alt="Enhanced image">
324
+ <a href="#" class="download-btn" id="downloadBtn" download="enhanced_image.png">Download Enhanced Image</a>
325
+ </div>
326
+ </div>
327
+ </section>
328
+
329
+ <section class="info-section">
330
+ <h2>About the Model</h2>
331
+ <div class="info-grid">
332
+ <div class="info-item">
333
+ <h4>Real-ESRGAN x4plus</h4>
334
+ <p>State-of-the-art image super-resolution model</p>
335
+ </div>
336
+ <div class="info-item">
337
+ <h4>Deep Learning</h4>
338
+ <p>Uses RRDB architecture for high-quality results</p>
339
+ </div>
340
+ <div class="info-item">
341
+ <h4>Multi-purpose</h4>
342
+ <p>Works on photos, artwork, and various image types</p>
343
+ </div>
344
+ <div class="info-item">
345
+ <h4>API Access</h4>
346
+ <p>RESTful API with Swagger documentation at /docs</p>
347
+ </div>
348
+ </div>
349
+ </section>
350
+ </div>
351
+
352
+ <script>
353
+ const dropZone = document.getElementById('dropZone');
354
+ const fileInput = document.getElementById('fileInput');
355
+ const enhanceBtn = document.getElementById('enhanceBtn');
356
+ const loading = document.getElementById('loading');
357
+ const results = document.getElementById('results');
358
+ const error = document.getElementById('error');
359
+ const originalImg = document.getElementById('originalImg');
360
+ const enhancedImg = document.getElementById('enhancedImg');
361
+ const downloadBtn = document.getElementById('downloadBtn');
362
+ const scaleSelect = document.getElementById('scale');
363
+
364
+ let selectedFile = null;
365
+
366
+ dropZone.addEventListener('click', () => fileInput.click());
367
+
368
+ dropZone.addEventListener('dragover', (e) => {
369
+ e.preventDefault();
370
+ dropZone.classList.add('dragover');
371
+ });
372
+
373
+ dropZone.addEventListener('dragleave', () => {
374
+ dropZone.classList.remove('dragover');
375
+ });
376
+
377
+ dropZone.addEventListener('drop', (e) => {
378
+ e.preventDefault();
379
+ dropZone.classList.remove('dragover');
380
+ const files = e.dataTransfer.files;
381
+ if (files.length > 0) {
382
+ handleFile(files[0]);
383
+ }
384
+ });
385
+
386
+ fileInput.addEventListener('change', (e) => {
387
+ if (e.target.files.length > 0) {
388
+ handleFile(e.target.files[0]);
389
+ }
390
+ });
391
+
392
+ function handleFile(file) {
393
+ if (!file.type.match(/^image\/(png|jpeg|jpg|webp|bmp)$/)) {
394
+ showError('Please select a valid image file (PNG, JPG, JPEG, WebP, or BMP)');
395
+ return;
396
+ }
397
+
398
+ selectedFile = file;
399
+ enhanceBtn.disabled = false;
400
+ dropZone.innerHTML = `
401
+ <div class="drop-zone-icon">✅</div>
402
+ <p><strong>${file.name}</strong></p>
403
+ <p><small>${(file.size / 1024 / 1024).toFixed(2)} MB</small></p>
404
+ `;
405
+
406
+ const reader = new FileReader();
407
+ reader.onload = (e) => {
408
+ originalImg.src = e.target.result;
409
+ };
410
+ reader.readAsDataURL(file);
411
+
412
+ hideError();
413
+ }
414
+
415
+ enhanceBtn.addEventListener('click', async () => {
416
+ if (!selectedFile) return;
417
+
418
+ const scale = scaleSelect.value;
419
+ const formData = new FormData();
420
+ formData.append('file', selectedFile);
421
+
422
+ loading.classList.add('show');
423
+ results.classList.remove('show');
424
+ enhanceBtn.disabled = true;
425
+ hideError();
426
+
427
+ try {
428
+ const response = await fetch(`/enhance?scale=${scale}`, {
429
+ method: 'POST',
430
+ body: formData
431
+ });
432
+
433
+ if (!response.ok) {
434
+ const errorData = await response.json();
435
+ throw new Error(errorData.detail || 'Enhancement failed');
436
+ }
437
+
438
+ const blob = await response.blob();
439
+ const imageUrl = URL.createObjectURL(blob);
440
+
441
+ enhancedImg.src = imageUrl;
442
+ downloadBtn.href = imageUrl;
443
+
444
+ loading.classList.remove('show');
445
+ results.classList.add('show');
446
+
447
+ } catch (err) {
448
+ showError(err.message);
449
+ loading.classList.remove('show');
450
+ }
451
+
452
+ enhanceBtn.disabled = false;
453
+ });
454
+
455
+ function showError(message) {
456
+ error.textContent = message;
457
+ error.classList.add('show');
458
+ }
459
+
460
+ function hideError() {
461
+ error.classList.remove('show');
462
+ }
463
+ </script>
464
+ </body>
465
+ </html>