Spaces:
Running
Running
azdxit commited on
Commit ·
0e14ea7
1
Parent(s): 3e31bd5
Add image generation capabilities to the application
Browse filesIntroduce new API endpoints for synchronous and asynchronous image generation, integrate image generation options into the frontend UI, and update OpenAPI specifications.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a662ebb5-fd71-4dd7-ad81-6f1890051700
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: efbf4b19-3deb-44d5-b4b1-00474abad1e9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/d9f57912-08a1-48b9-ad13-f36ce06579fd/a662ebb5-fd71-4dd7-ad81-6f1890051700/fWRWvD9
- .replit +0 -4
- app_local.py +148 -2
- templates/index.html +93 -11
.replit
CHANGED
|
@@ -34,10 +34,6 @@ outputType = "webview"
|
|
| 34 |
localPort = 5000
|
| 35 |
externalPort = 80
|
| 36 |
|
| 37 |
-
[[ports]]
|
| 38 |
-
localPort = 38543
|
| 39 |
-
externalPort = 3003
|
| 40 |
-
|
| 41 |
[[ports]]
|
| 42 |
localPort = 38887
|
| 43 |
externalPort = 3000
|
|
|
|
| 34 |
localPort = 5000
|
| 35 |
externalPort = 80
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
[[ports]]
|
| 38 |
localPort = 38887
|
| 39 |
externalPort = 3000
|
app_local.py
CHANGED
|
@@ -382,6 +382,99 @@ class APIHandler(SimpleHTTPRequestHandler):
|
|
| 382 |
except Exception as e:
|
| 383 |
self.send_error(500, f"Error processing image: {str(e)}")
|
| 384 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 385 |
def serve_html(self):
|
| 386 |
html_path = Path("templates/index.html")
|
| 387 |
if html_path.exists():
|
|
@@ -430,8 +523,8 @@ class APIHandler(SimpleHTTPRequestHandler):
|
|
| 430 |
"openapi": "3.1.0",
|
| 431 |
"info": {
|
| 432 |
"title": "AI Image Processing API",
|
| 433 |
-
"description": "Comprehensive AI-powered image processing API.\n\n**Features:**\n- Image enhancement and upscaling (Real-ESRGAN)\n- Background removal (BiRefNet)\n- Noise reduction (OpenCV NLM)\n\n**Note:** This is a preview deployment. Deploy to Hugging Face Spaces for full AI processing.",
|
| 434 |
-
"version": "2.
|
| 435 |
},
|
| 436 |
"servers": [{"url": "/", "description": "Current server"}],
|
| 437 |
"paths": {
|
|
@@ -462,6 +555,59 @@ class APIHandler(SimpleHTTPRequestHandler):
|
|
| 462 |
"responses": {"200": {"description": "Denoised image"}}
|
| 463 |
}
|
| 464 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
"/health": {"get": {"summary": "Health check", "responses": {"200": {"description": "API status"}}}},
|
| 466 |
"/model-info": {"get": {"summary": "Model information", "responses": {"200": {"description": "Model details"}}}}
|
| 467 |
}
|
|
|
|
| 382 |
except Exception as e:
|
| 383 |
self.send_error(500, f"Error processing image: {str(e)}")
|
| 384 |
|
| 385 |
+
def handle_generate_image(self, path, query):
|
| 386 |
+
"""Handle synchronous image generation."""
|
| 387 |
+
try:
|
| 388 |
+
prompt = query.get('prompt', [''])[0]
|
| 389 |
+
if not prompt:
|
| 390 |
+
self.send_error(400, "Missing 'prompt' parameter")
|
| 391 |
+
return
|
| 392 |
+
|
| 393 |
+
width = int(query.get('width', [1024])[0])
|
| 394 |
+
height = int(query.get('height', [1024])[0])
|
| 395 |
+
width = max(256, min(1440, width))
|
| 396 |
+
height = max(256, min(1440, height))
|
| 397 |
+
async_mode = query.get('async_mode', ['false'])[0].lower() == 'true'
|
| 398 |
+
|
| 399 |
+
if async_mode:
|
| 400 |
+
self.handle_generate_image_async(query)
|
| 401 |
+
return
|
| 402 |
+
|
| 403 |
+
from PIL import Image
|
| 404 |
+
image_bytes = generate_image_from_hf(prompt, width, height)
|
| 405 |
+
generated_image = Image.open(io.BytesIO(image_bytes))
|
| 406 |
+
|
| 407 |
+
file_id = str(uuid.uuid4())
|
| 408 |
+
output_path = OUTPUT_DIR / f"{file_id}_generated.png"
|
| 409 |
+
generated_image.save(output_path, "PNG")
|
| 410 |
+
|
| 411 |
+
if "/base64" in path:
|
| 412 |
+
buffer = io.BytesIO()
|
| 413 |
+
generated_image.save(buffer, format="PNG")
|
| 414 |
+
buffer.seek(0)
|
| 415 |
+
img_base64 = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
| 416 |
+
|
| 417 |
+
self.send_json({
|
| 418 |
+
"success": True,
|
| 419 |
+
"image_base64": img_base64,
|
| 420 |
+
"size": {"width": generated_image.width, "height": generated_image.height},
|
| 421 |
+
"model": "FLUX.1-schnell",
|
| 422 |
+
"prompt": prompt
|
| 423 |
+
})
|
| 424 |
+
else:
|
| 425 |
+
self.send_response(200)
|
| 426 |
+
self.send_header('Content-Type', 'image/png')
|
| 427 |
+
self.send_header('Content-Disposition', f'attachment; filename="generated_{file_id[:8]}.png"')
|
| 428 |
+
self.end_headers()
|
| 429 |
+
with open(output_path, 'rb') as f:
|
| 430 |
+
self.wfile.write(f.read())
|
| 431 |
+
|
| 432 |
+
except Exception as e:
|
| 433 |
+
self.send_error(500, f"Error generating image: {str(e)}")
|
| 434 |
+
|
| 435 |
+
def handle_generate_image_async(self, query):
|
| 436 |
+
"""Handle async image generation with progress tracking."""
|
| 437 |
+
try:
|
| 438 |
+
prompt = query.get('prompt', [''])[0]
|
| 439 |
+
if not prompt:
|
| 440 |
+
self.send_error(400, "Missing 'prompt' parameter")
|
| 441 |
+
return
|
| 442 |
+
|
| 443 |
+
width = int(query.get('width', [1024])[0])
|
| 444 |
+
height = int(query.get('height', [1024])[0])
|
| 445 |
+
width = max(256, min(1440, width))
|
| 446 |
+
height = max(256, min(1440, height))
|
| 447 |
+
|
| 448 |
+
token = get_hf_token()
|
| 449 |
+
if not token:
|
| 450 |
+
self.send_error(500, "Hugging Face API token not configured. Please set HF_TOKEN secret.")
|
| 451 |
+
return
|
| 452 |
+
|
| 453 |
+
job_id = str(uuid.uuid4())
|
| 454 |
+
file_id = str(uuid.uuid4())
|
| 455 |
+
output_path = OUTPUT_DIR / f"{file_id}_generated.png"
|
| 456 |
+
|
| 457 |
+
jobs[job_id] = {"status": "pending", "progress": 0, "message": "Starting image generation..."}
|
| 458 |
+
|
| 459 |
+
thread = threading.Thread(
|
| 460 |
+
target=process_generate_image_job,
|
| 461 |
+
args=(job_id, prompt, width, height, output_path)
|
| 462 |
+
)
|
| 463 |
+
thread.start()
|
| 464 |
+
|
| 465 |
+
self.send_json({
|
| 466 |
+
"job_id": job_id,
|
| 467 |
+
"status": "processing",
|
| 468 |
+
"message": "Image generation started. Poll /progress/{job_id} for updates.",
|
| 469 |
+
"progress_url": f"/progress/{job_id}",
|
| 470 |
+
"result_url": f"/result/{job_id}",
|
| 471 |
+
"model": "FLUX.1-schnell",
|
| 472 |
+
"prompt": prompt
|
| 473 |
+
})
|
| 474 |
+
|
| 475 |
+
except Exception as e:
|
| 476 |
+
self.send_error(500, f"Error starting image generation: {str(e)}")
|
| 477 |
+
|
| 478 |
def serve_html(self):
|
| 479 |
html_path = Path("templates/index.html")
|
| 480 |
if html_path.exists():
|
|
|
|
| 523 |
"openapi": "3.1.0",
|
| 524 |
"info": {
|
| 525 |
"title": "AI Image Processing API",
|
| 526 |
+
"description": "Comprehensive AI-powered image processing API.\n\n**Features:**\n- Image enhancement and upscaling (Real-ESRGAN)\n- Background removal (BiRefNet)\n- Noise reduction (OpenCV NLM)\n- Image generation from text (FLUX.1-schnell)\n\n**Note:** This is a preview deployment. Deploy to Hugging Face Spaces for full AI processing.",
|
| 527 |
+
"version": "2.2.0"
|
| 528 |
},
|
| 529 |
"servers": [{"url": "/", "description": "Current server"}],
|
| 530 |
"paths": {
|
|
|
|
| 555 |
"responses": {"200": {"description": "Denoised image"}}
|
| 556 |
}
|
| 557 |
},
|
| 558 |
+
"/generate-image": {
|
| 559 |
+
"post": {
|
| 560 |
+
"summary": "Generate image from text",
|
| 561 |
+
"description": "Generate an image from a text prompt using FLUX.1-schnell AI model.",
|
| 562 |
+
"parameters": [
|
| 563 |
+
{"name": "prompt", "in": "query", "required": True, "schema": {"type": "string"}, "description": "Text prompt describing the image to generate"},
|
| 564 |
+
{"name": "width", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}},
|
| 565 |
+
{"name": "height", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}},
|
| 566 |
+
{"name": "async_mode", "in": "query", "schema": {"type": "boolean", "default": False}, "description": "Use async mode with progress tracking"}
|
| 567 |
+
],
|
| 568 |
+
"responses": {"200": {"description": "Generated image"}}
|
| 569 |
+
}
|
| 570 |
+
},
|
| 571 |
+
"/generate-image/async": {
|
| 572 |
+
"post": {
|
| 573 |
+
"summary": "Generate image (async)",
|
| 574 |
+
"description": "Start async image generation with progress tracking using FLUX.1-schnell.",
|
| 575 |
+
"parameters": [
|
| 576 |
+
{"name": "prompt", "in": "query", "required": True, "schema": {"type": "string"}, "description": "Text prompt describing the image to generate"},
|
| 577 |
+
{"name": "width", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}},
|
| 578 |
+
{"name": "height", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}}
|
| 579 |
+
],
|
| 580 |
+
"responses": {"200": {"description": "Job ID for tracking progress"}}
|
| 581 |
+
}
|
| 582 |
+
},
|
| 583 |
+
"/generate-image/base64": {
|
| 584 |
+
"post": {
|
| 585 |
+
"summary": "Generate image (base64)",
|
| 586 |
+
"description": "Generate an image and return as base64-encoded string.",
|
| 587 |
+
"parameters": [
|
| 588 |
+
{"name": "prompt", "in": "query", "required": True, "schema": {"type": "string"}, "description": "Text prompt describing the image to generate"},
|
| 589 |
+
{"name": "width", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}},
|
| 590 |
+
{"name": "height", "in": "query", "schema": {"type": "integer", "default": 1024, "minimum": 256, "maximum": 1440}}
|
| 591 |
+
],
|
| 592 |
+
"responses": {"200": {"description": "Base64 encoded image"}}
|
| 593 |
+
}
|
| 594 |
+
},
|
| 595 |
+
"/progress/{job_id}": {
|
| 596 |
+
"get": {
|
| 597 |
+
"summary": "Get job progress",
|
| 598 |
+
"description": "Get the progress of an async image processing job.",
|
| 599 |
+
"parameters": [{"name": "job_id", "in": "path", "required": True, "schema": {"type": "string"}}],
|
| 600 |
+
"responses": {"200": {"description": "Job progress"}}
|
| 601 |
+
}
|
| 602 |
+
},
|
| 603 |
+
"/result/{job_id}": {
|
| 604 |
+
"get": {
|
| 605 |
+
"summary": "Get job result",
|
| 606 |
+
"description": "Get the result of a completed async job.",
|
| 607 |
+
"parameters": [{"name": "job_id", "in": "path", "required": True, "schema": {"type": "string"}}],
|
| 608 |
+
"responses": {"200": {"description": "Processed image"}}
|
| 609 |
+
}
|
| 610 |
+
},
|
| 611 |
"/health": {"get": {"summary": "Health check", "responses": {"200": {"description": "API status"}}}},
|
| 612 |
"/model-info": {"get": {"summary": "Model information", "responses": {"200": {"description": "Model details"}}}}
|
| 613 |
}
|
templates/index.html
CHANGED
|
@@ -361,7 +361,7 @@
|
|
| 361 |
<div class="container">
|
| 362 |
<header>
|
| 363 |
<h1>AI Image Processing</h1>
|
| 364 |
-
<p class="subtitle">Enhance, remove backgrounds, denoise,
|
| 365 |
<div class="api-link">
|
| 366 |
<a href="/docs" target="_blank">View API Documentation</a>
|
| 367 |
</div>
|
|
@@ -373,6 +373,7 @@
|
|
| 373 |
<button class="feature-tab" data-feature="remove-bg">Remove Background</button>
|
| 374 |
<button class="feature-tab" data-feature="denoise">Denoise</button>
|
| 375 |
<button class="feature-tab" data-feature="docscan">Doc Scan</button>
|
|
|
|
| 376 |
</div>
|
| 377 |
|
| 378 |
<div class="drop-zone" id="dropZone">
|
|
@@ -451,6 +452,40 @@
|
|
| 451 |
</p>
|
| 452 |
</div>
|
| 453 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 454 |
<button class="process-btn" id="processBtn" disabled>Process Image</button>
|
| 455 |
|
| 456 |
<div class="error" id="error"></div>
|
|
@@ -502,6 +537,10 @@
|
|
| 502 |
<h4>Document Scanning</h4>
|
| 503 |
<p>Auto-crop, align, and enhance documents with AI</p>
|
| 504 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
<div class="info-item">
|
| 506 |
<h4>API Access</h4>
|
| 507 |
<p>Full RESTful API with Swagger documentation at /docs</p>
|
|
@@ -544,12 +583,20 @@
|
|
| 544 |
|
| 545 |
if (currentFeature === 'enhance') {
|
| 546 |
document.getElementById('enhanceOptions').classList.add('active');
|
|
|
|
| 547 |
} else if (currentFeature === 'remove-bg') {
|
| 548 |
document.getElementById('removeBgOptions').classList.add('active');
|
|
|
|
| 549 |
} else if (currentFeature === 'denoise') {
|
| 550 |
document.getElementById('denoiseOptions').classList.add('active');
|
|
|
|
| 551 |
} else if (currentFeature === 'docscan') {
|
| 552 |
document.getElementById('docscanOptions').classList.add('active');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 553 |
}
|
| 554 |
|
| 555 |
updateButtonText();
|
|
@@ -565,7 +612,8 @@
|
|
| 565 |
'enhance': 'Enhance Image',
|
| 566 |
'remove-bg': 'Remove Background',
|
| 567 |
'denoise': 'Denoise Image',
|
| 568 |
-
'docscan': 'Scan Document'
|
|
|
|
| 569 |
};
|
| 570 |
processBtn.textContent = texts[currentFeature] || 'Process Image';
|
| 571 |
}
|
|
@@ -688,10 +736,22 @@
|
|
| 688 |
}
|
| 689 |
|
| 690 |
processBtn.addEventListener('click', async () => {
|
| 691 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
|
| 693 |
const formData = new FormData();
|
| 694 |
-
|
|
|
|
|
|
|
| 695 |
|
| 696 |
let endpoint = '/enhance/async';
|
| 697 |
let params = new URLSearchParams();
|
|
@@ -726,12 +786,27 @@
|
|
| 726 |
params.append('enhance_hd', enhanceHd);
|
| 727 |
loadingText.textContent = 'Scanning and enhancing document...';
|
| 728 |
resultLabel.textContent = 'Scanned Document';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
}
|
| 730 |
|
| 731 |
if (currentFeature !== 'remove-bg') {
|
| 732 |
resultBox.classList.remove('checkerboard');
|
| 733 |
}
|
| 734 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 735 |
loading.classList.add('show');
|
| 736 |
results.classList.remove('show');
|
| 737 |
processBtn.disabled = true;
|
|
@@ -739,10 +814,15 @@
|
|
| 739 |
resetProgress();
|
| 740 |
|
| 741 |
try {
|
| 742 |
-
const
|
| 743 |
-
method: 'POST'
|
| 744 |
-
|
| 745 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
|
| 747 |
if (!response.ok) {
|
| 748 |
const errorData = await response.json();
|
|
@@ -773,10 +853,12 @@
|
|
| 773 |
'enhance': 'enhanced',
|
| 774 |
'remove-bg': 'nobg',
|
| 775 |
'denoise': 'denoised',
|
| 776 |
-
'docscan': 'scanned'
|
|
|
|
| 777 |
};
|
| 778 |
const filename = filenames[currentFeature] || 'processed';
|
| 779 |
-
|
|
|
|
| 780 |
|
| 781 |
loading.classList.remove('show');
|
| 782 |
results.classList.add('show');
|
|
@@ -786,7 +868,7 @@
|
|
| 786 |
loading.classList.remove('show');
|
| 787 |
}
|
| 788 |
|
| 789 |
-
processBtn.disabled =
|
| 790 |
});
|
| 791 |
|
| 792 |
function showError(message) {
|
|
|
|
| 361 |
<div class="container">
|
| 362 |
<header>
|
| 363 |
<h1>AI Image Processing</h1>
|
| 364 |
+
<p class="subtitle">Enhance, remove backgrounds, denoise, scan documents, and generate images with AI</p>
|
| 365 |
<div class="api-link">
|
| 366 |
<a href="/docs" target="_blank">View API Documentation</a>
|
| 367 |
</div>
|
|
|
|
| 373 |
<button class="feature-tab" data-feature="remove-bg">Remove Background</button>
|
| 374 |
<button class="feature-tab" data-feature="denoise">Denoise</button>
|
| 375 |
<button class="feature-tab" data-feature="docscan">Doc Scan</button>
|
| 376 |
+
<button class="feature-tab" data-feature="generate">Generate Image</button>
|
| 377 |
</div>
|
| 378 |
|
| 379 |
<div class="drop-zone" id="dropZone">
|
|
|
|
| 452 |
</p>
|
| 453 |
</div>
|
| 454 |
|
| 455 |
+
<div id="generateOptions" class="feature-options">
|
| 456 |
+
<div class="options">
|
| 457 |
+
<div class="option-group" style="flex: 2;">
|
| 458 |
+
<label for="prompt">Image Prompt</label>
|
| 459 |
+
<input type="text" id="prompt" placeholder="A beautiful sunset over mountains, detailed, 8k quality">
|
| 460 |
+
</div>
|
| 461 |
+
</div>
|
| 462 |
+
<div class="options" style="margin-top: 10px;">
|
| 463 |
+
<div class="option-group">
|
| 464 |
+
<label for="genWidth">Width</label>
|
| 465 |
+
<select id="genWidth">
|
| 466 |
+
<option value="512">512px</option>
|
| 467 |
+
<option value="768">768px</option>
|
| 468 |
+
<option value="1024" selected>1024px</option>
|
| 469 |
+
<option value="1280">1280px</option>
|
| 470 |
+
<option value="1440">1440px</option>
|
| 471 |
+
</select>
|
| 472 |
+
</div>
|
| 473 |
+
<div class="option-group">
|
| 474 |
+
<label for="genHeight">Height</label>
|
| 475 |
+
<select id="genHeight">
|
| 476 |
+
<option value="512">512px</option>
|
| 477 |
+
<option value="768">768px</option>
|
| 478 |
+
<option value="1024" selected>1024px</option>
|
| 479 |
+
<option value="1280">1280px</option>
|
| 480 |
+
<option value="1440">1440px</option>
|
| 481 |
+
</select>
|
| 482 |
+
</div>
|
| 483 |
+
</div>
|
| 484 |
+
<p style="color: #888; font-size: 0.85rem; margin-top: 10px;">
|
| 485 |
+
Generate images from text using FLUX.1-schnell by Black Forest Labs. No image upload needed.
|
| 486 |
+
</p>
|
| 487 |
+
</div>
|
| 488 |
+
|
| 489 |
<button class="process-btn" id="processBtn" disabled>Process Image</button>
|
| 490 |
|
| 491 |
<div class="error" id="error"></div>
|
|
|
|
| 537 |
<h4>Document Scanning</h4>
|
| 538 |
<p>Auto-crop, align, and enhance documents with AI</p>
|
| 539 |
</div>
|
| 540 |
+
<div class="info-item">
|
| 541 |
+
<h4>Image Generation</h4>
|
| 542 |
+
<p>Generate images from text prompts using FLUX.1-schnell</p>
|
| 543 |
+
</div>
|
| 544 |
<div class="info-item">
|
| 545 |
<h4>API Access</h4>
|
| 546 |
<p>Full RESTful API with Swagger documentation at /docs</p>
|
|
|
|
| 583 |
|
| 584 |
if (currentFeature === 'enhance') {
|
| 585 |
document.getElementById('enhanceOptions').classList.add('active');
|
| 586 |
+
dropZone.style.display = 'block';
|
| 587 |
} else if (currentFeature === 'remove-bg') {
|
| 588 |
document.getElementById('removeBgOptions').classList.add('active');
|
| 589 |
+
dropZone.style.display = 'block';
|
| 590 |
} else if (currentFeature === 'denoise') {
|
| 591 |
document.getElementById('denoiseOptions').classList.add('active');
|
| 592 |
+
dropZone.style.display = 'block';
|
| 593 |
} else if (currentFeature === 'docscan') {
|
| 594 |
document.getElementById('docscanOptions').classList.add('active');
|
| 595 |
+
dropZone.style.display = 'block';
|
| 596 |
+
} else if (currentFeature === 'generate') {
|
| 597 |
+
document.getElementById('generateOptions').classList.add('active');
|
| 598 |
+
dropZone.style.display = 'none';
|
| 599 |
+
processBtn.disabled = false;
|
| 600 |
}
|
| 601 |
|
| 602 |
updateButtonText();
|
|
|
|
| 612 |
'enhance': 'Enhance Image',
|
| 613 |
'remove-bg': 'Remove Background',
|
| 614 |
'denoise': 'Denoise Image',
|
| 615 |
+
'docscan': 'Scan Document',
|
| 616 |
+
'generate': 'Generate Image'
|
| 617 |
};
|
| 618 |
processBtn.textContent = texts[currentFeature] || 'Process Image';
|
| 619 |
}
|
|
|
|
| 736 |
}
|
| 737 |
|
| 738 |
processBtn.addEventListener('click', async () => {
|
| 739 |
+
const isGenerate = currentFeature === 'generate';
|
| 740 |
+
|
| 741 |
+
if (!isGenerate && !selectedFile) return;
|
| 742 |
+
|
| 743 |
+
if (isGenerate) {
|
| 744 |
+
const prompt = document.getElementById('prompt').value.trim();
|
| 745 |
+
if (!prompt) {
|
| 746 |
+
showError('Please enter a prompt to generate an image');
|
| 747 |
+
return;
|
| 748 |
+
}
|
| 749 |
+
}
|
| 750 |
|
| 751 |
const formData = new FormData();
|
| 752 |
+
if (!isGenerate) {
|
| 753 |
+
formData.append('file', selectedFile);
|
| 754 |
+
}
|
| 755 |
|
| 756 |
let endpoint = '/enhance/async';
|
| 757 |
let params = new URLSearchParams();
|
|
|
|
| 786 |
params.append('enhance_hd', enhanceHd);
|
| 787 |
loadingText.textContent = 'Scanning and enhancing document...';
|
| 788 |
resultLabel.textContent = 'Scanned Document';
|
| 789 |
+
} else if (currentFeature === 'generate') {
|
| 790 |
+
endpoint = '/generate-image/async';
|
| 791 |
+
const prompt = document.getElementById('prompt').value.trim();
|
| 792 |
+
const width = document.getElementById('genWidth').value;
|
| 793 |
+
const height = document.getElementById('genHeight').value;
|
| 794 |
+
params.append('prompt', prompt);
|
| 795 |
+
params.append('width', width);
|
| 796 |
+
params.append('height', height);
|
| 797 |
+
loadingText.textContent = 'Generating image with FLUX.1-schnell...';
|
| 798 |
+
resultLabel.textContent = 'Generated';
|
| 799 |
+
document.querySelector('.image-box:first-child').style.display = 'none';
|
| 800 |
}
|
| 801 |
|
| 802 |
if (currentFeature !== 'remove-bg') {
|
| 803 |
resultBox.classList.remove('checkerboard');
|
| 804 |
}
|
| 805 |
|
| 806 |
+
if (currentFeature !== 'generate') {
|
| 807 |
+
document.querySelector('.image-box:first-child').style.display = 'block';
|
| 808 |
+
}
|
| 809 |
+
|
| 810 |
loading.classList.add('show');
|
| 811 |
results.classList.remove('show');
|
| 812 |
processBtn.disabled = true;
|
|
|
|
| 814 |
resetProgress();
|
| 815 |
|
| 816 |
try {
|
| 817 |
+
const fetchOptions = {
|
| 818 |
+
method: 'POST'
|
| 819 |
+
};
|
| 820 |
+
|
| 821 |
+
if (!isGenerate) {
|
| 822 |
+
fetchOptions.body = formData;
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
const response = await fetch(`${endpoint}?${params.toString()}`, fetchOptions);
|
| 826 |
|
| 827 |
if (!response.ok) {
|
| 828 |
const errorData = await response.json();
|
|
|
|
| 853 |
'enhance': 'enhanced',
|
| 854 |
'remove-bg': 'nobg',
|
| 855 |
'denoise': 'denoised',
|
| 856 |
+
'docscan': 'scanned',
|
| 857 |
+
'generate': 'generated'
|
| 858 |
};
|
| 859 |
const filename = filenames[currentFeature] || 'processed';
|
| 860 |
+
const baseName = isGenerate ? 'ai_image' : selectedFile.name.split('.')[0];
|
| 861 |
+
downloadBtn.download = `${filename}_${baseName}.png`;
|
| 862 |
|
| 863 |
loading.classList.remove('show');
|
| 864 |
results.classList.add('show');
|
|
|
|
| 868 |
loading.classList.remove('show');
|
| 869 |
}
|
| 870 |
|
| 871 |
+
processBtn.disabled = currentFeature !== 'generate';
|
| 872 |
});
|
| 873 |
|
| 874 |
function showError(message) {
|