| import os |
| import io |
| import torch |
| from flask import Flask, request, jsonify, send_file, render_template_string |
| from diffusers import StableDiffusionPipeline, StableDiffusionImg2ImgPipeline |
| from PIL import Image, ImageFilter |
| from deep_translator import GoogleTranslator |
| from datetime import datetime |
|
|
| print("STARTING IMAGE PRO AI") |
|
|
| OUTPUT_DIR = "outputs" |
| os.makedirs(OUTPUT_DIR, exist_ok=True) |
|
|
| progress_value = 0 |
|
|
| |
| |
| |
|
|
| def translate(text): |
| try: |
| return GoogleTranslator(source="auto", target="en").translate(text) |
| except: |
| return text |
|
|
|
|
| STYLE_PRESETS = { |
| "cinematic": "cinematic lighting, ultra realistic, 8k, film still, depth of field", |
| "anime": "anime style, clean lines, vibrant colors, detailed illustration", |
| "realistic": "photo realistic, natural lighting, high detail, 8k", |
| "neon": "neon lights, cyberpunk, glowing colors, night city", |
| "cartoon": "cartoon style, bold outlines, flat colors, playful", |
| } |
|
|
|
|
| def enhance(text, style: str | None = None): |
| base = "high detail, professional, sharp focus" |
| extra = base |
| if style and style in STYLE_PRESETS: |
| extra = STYLE_PRESETS[style] + ", " + base |
| return text + ", " + extra |
|
|
|
|
| |
| |
| |
|
|
| print("Loading Stable Diffusion Turbo...") |
|
|
| MODEL_ID = os.getenv("MODEL_ID", "stabilityai/sd-turbo") |
|
|
| pipe = StableDiffusionPipeline.from_pretrained( |
| MODEL_ID, |
| torch_dtype=torch.float32 |
| ) |
| pipe = pipe.to("cpu") |
| pipe.enable_attention_slicing() |
|
|
| img2img_pipe = StableDiffusionImg2ImgPipeline.from_pretrained( |
| MODEL_ID, |
| torch_dtype=torch.float32 |
| ) |
| img2img_pipe = img2img_pipe.to("cpu") |
| img2img_pipe.enable_attention_slicing() |
|
|
| print("MODEL READY") |
|
|
| |
| |
| |
|
|
| api = Flask(__name__) |
|
|
| |
| |
| |
|
|
| HTML = """ |
| <html> |
| <head> |
| <title>IMAGE PRO AI STUDIO</title> |
| <style> |
| body{ |
| background:#05060a; |
| color:#f5f5f5; |
| text-align:center; |
| font-family:Arial, sans-serif; |
| } |
| h1{ |
| margin-top:20px; |
| letter-spacing:2px; |
| } |
| .container{ |
| max-width:900px; |
| margin:0 auto; |
| padding:20px; |
| } |
| button{ |
| background:#ff8c00; |
| border:none; |
| padding:10px 16px; |
| margin:5px; |
| cursor:pointer; |
| font-weight:bold; |
| border-radius:6px; |
| color:#111; |
| } |
| button:hover{ |
| background:#ffb347; |
| } |
| input, select{ |
| width:420px; |
| padding:10px; |
| border-radius:6px; |
| border:1px solid #333; |
| background:#111; |
| color:#f5f5f5; |
| } |
| img{ |
| max-width:650px; |
| margin-top:20px; |
| border:2px solid #ff8c00; |
| border-radius:8px; |
| } |
| #bar{ |
| width:0%; |
| height:20px; |
| background:#ff8c00; |
| border-radius:10px; |
| } |
| #progress{ |
| width:400px; |
| background:#222; |
| margin:10px auto; |
| display:none; |
| border-radius:10px; |
| padding:2px; |
| } |
| .section-title{ |
| margin-top:25px; |
| font-size:18px; |
| text-transform:uppercase; |
| letter-spacing:1px; |
| color:#ffb347; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>IMAGE PRO AI STUDIO</h1> |
| |
| <div> |
| <input id="prompt" placeholder="Enter prompt"> |
| </div> |
| |
| <div class="section-title">Styles</div> |
| <div> |
| <button onclick="stylePreset('cinematic')">CINEMATIC</button> |
| <button onclick="stylePreset('anime')">ANIME</button> |
| <button onclick="stylePreset('realistic')">REALISTIC</button> |
| <button onclick="stylePreset('neon')">NEON</button> |
| <button onclick="stylePreset('cartoon')">CARTOON</button> |
| </div> |
| |
| <div class="section-title">Generate</div> |
| <div> |
| <button onclick="generate()">IMAGE</button> |
| <button onclick="product()">PRODUCT</button> |
| <button onclick="logo()">LOGO</button> |
| <button onclick="banner()">BANNER</button> |
| <button onclick="social()">SOCIAL</button> |
| </div> |
| |
| <div class="section-title">Image Tools</div> |
| <div> |
| <input type="file" id="file"> |
| </div> |
| <div> |
| <button onclick="restore()">RESTORE</button> |
| <button onclick="upscale()">UPSCALE</button> |
| <button onclick="color()">COLORIZE</button> |
| </div> |
| |
| <div class="section-title">Blend PRO</div> |
| <div> |
| <input type="file" id="blendA"> |
| <input type="file" id="blendB"> |
| </div> |
| <div> |
| <label>Mix: <span id="mixLabel">0.5</span></label><br> |
| <input type="range" id="mixRange" min="0" max="1" step="0.05" value="0.5" style="width:300px" oninput="updateMix()"> |
| </div> |
| <div> |
| <button onclick="runBlend()">BLEND</button> |
| <button onclick="runBlendPro()">BLEND PRO (AI)</button> |
| </div> |
| |
| <h3>GENERATING</h3> |
| <div id="progress"> |
| <div id="bar"></div> |
| </div> |
| <p id="percent">0%</p> |
| <h2>RESULT</h2> |
| <img id="result"> |
| </div> |
| |
| <script> |
| let progressTimer |
| let currentStyle = null |
| |
| function startProgress(){ |
| document.getElementById("progress").style.display="block" |
| document.getElementById("bar").style.width="0%" |
| document.getElementById("percent").innerText="0%" |
| progressTimer=setInterval(updateProgress,500) |
| } |
| function updateProgress(){ |
| fetch("/progress") |
| .then(r=>r.json()) |
| .then(data=>{ |
| let p=data.progress |
| document.getElementById("bar").style.width=p+"%" |
| document.getElementById("percent").innerText=p+"%" |
| if(p>=100){ |
| clearInterval(progressTimer) |
| } |
| }) |
| } |
| function show(blob){ |
| document.getElementById("result").src=URL.createObjectURL(blob) |
| } |
| function sendPrompt(endpoint, extraBody){ |
| startProgress() |
| let body = { |
| prompt: document.getElementById("prompt").value, |
| style: currentStyle |
| } |
| if(extraBody){ |
| Object.assign(body, extraBody) |
| } |
| fetch("/"+endpoint,{ |
| method:"POST", |
| headers:{"Content-Type":"application/json"}, |
| body:JSON.stringify(body) |
| }) |
| .then(r=>r.blob()) |
| .then(show) |
| } |
| function generate(){sendPrompt("generate")} |
| function product(){sendPrompt("product")} |
| function logo(){sendPrompt("logo")} |
| function banner(){sendPrompt("banner")} |
| function social(){sendPrompt("social")} |
| |
| function sendImage(endpoint){ |
| let file=document.getElementById("file").files[0] |
| if(!file){ |
| alert("Select image first") |
| return |
| } |
| let data=new FormData() |
| data.append("image",file) |
| startProgress() |
| fetch("/"+endpoint,{method:"POST",body:data}) |
| .then(r=>r.blob()) |
| .then(show) |
| } |
| function restore(){sendImage("restore")} |
| function upscale(){sendImage("upscale")} |
| function color(){sendImage("colorize")} |
| |
| function stylePreset(style){ |
| currentStyle = style |
| alert("Style set: "+style.toUpperCase()) |
| } |
| |
| function updateMix(){ |
| const v = document.getElementById("mixRange").value |
| document.getElementById("mixLabel").innerText = v |
| } |
| |
| function runBlendCore(endpoint){ |
| let a=document.getElementById("blendA").files[0] |
| let b=document.getElementById("blendB").files[0] |
| if(!a || !b){ |
| alert("Select both images (A and B)") |
| return |
| } |
| let data=new FormData() |
| data.append("image_a",a) |
| data.append("image_b",b) |
| data.append("mix",document.getElementById("mixRange").value) |
| if(currentStyle){ |
| data.append("style", currentStyle) |
| } |
| startProgress() |
| fetch("/"+endpoint,{method:"POST",body:data}) |
| .then(r=>r.blob()) |
| .then(show) |
| } |
| |
| function runBlend(){runBlendCore("blend")} |
| function runBlendPro(){runBlendCore("blend-pro")} |
| </script> |
| </body> |
| </html> |
| """ |
|
|
| @api.route("/") |
| def home(): |
| return render_template_string(HTML) |
|
|
| |
| |
| |
|
|
| @api.route("/progress") |
| def progress(): |
| global progress_value |
| return jsonify({"progress": progress_value}) |
|
|
|
|
| |
| |
| |
|
|
| def generate_image(prompt, style: str | None = None): |
|
|
| global progress_value |
| steps = 8 |
|
|
| prompt = translate(prompt) |
| prompt = enhance(prompt, style) |
|
|
| def callback(step, timestep, latents): |
| global progress_value |
| progress_value = int((step/steps)*100) |
|
|
| image = pipe( |
| prompt, |
| num_inference_steps=steps, |
| guidance_scale=1.5, |
| callback=callback, |
| callback_steps=1 |
| ).images[0] |
|
|
| progress_value = 100 |
|
|
| filename = datetime.now().strftime("%Y%m%d%H%M%S") + ".png" |
| path = os.path.join(OUTPUT_DIR, filename) |
| image.save(path) |
|
|
| return path |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/generate", methods=["POST"]) |
| def generate(): |
| try: |
| data = request.get_json(force=True) |
| prompt = data.get("prompt", "") |
| style = data.get("style") |
| path = generate_image(prompt, style) |
| return send_file(path, mimetype="image/png") |
| except Exception as e: |
| return jsonify({"error": str(e)}) |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/product", methods=["POST"]) |
| def product(): |
| data = request.get_json(force=True) |
| prompt = data.get("prompt", "") |
| style = data.get("style") or "realistic" |
| prompt = translate(prompt) + ", product photography, studio lighting, white background" |
| path = generate_image(prompt, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/logo", methods=["POST"]) |
| def logo(): |
| data = request.get_json(force=True) |
| prompt = data.get("prompt", "") |
| style = data.get("style") or "cartoon" |
| prompt = translate(prompt) + ", minimalist vector logo" |
| path = generate_image(prompt, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/banner", methods=["POST"]) |
| def banner(): |
| data = request.get_json(force=True) |
| prompt = data.get("prompt", "") |
| style = data.get("style") or "cinematic" |
| prompt = translate(prompt) + ", modern website banner design" |
| path = generate_image(prompt, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/social", methods=["POST"]) |
| def social(): |
| data = request.get_json(force=True) |
| prompt = data.get("prompt", "") |
| style = data.get("style") or "neon" |
| prompt = translate(prompt) + ", instagram social media post" |
| path = generate_image(prompt, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/restore", methods=["POST"]) |
| def restore(): |
| if "image" not in request.files: |
| return "no image", 400 |
|
|
| file = request.files["image"] |
| img = Image.open(file.stream) |
| img = img.filter(ImageFilter.SHARPEN) |
|
|
| path = os.path.join(OUTPUT_DIR, "restore.png") |
| img.save(path) |
|
|
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/upscale", methods=["POST"]) |
| def upscale(): |
| if "image" not in request.files: |
| return "no image", 400 |
|
|
| file = request.files["image"] |
| img = Image.open(file.stream) |
|
|
| w, h = img.size |
| img = img.resize((w * 2, h * 2), Image.LANCZOS) |
|
|
| path = os.path.join(OUTPUT_DIR, "upscale.png") |
| img.save(path) |
|
|
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| @api.route("/colorize", methods=["POST"]) |
| def colorize(): |
| if "image" not in request.files: |
| return "no image", 400 |
|
|
| file = request.files["image"] |
| img = Image.open(file.stream) |
|
|
| img = img.convert("RGB") |
|
|
| path = os.path.join(OUTPUT_DIR, "color.png") |
| img.save(path) |
|
|
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
| |
| |
|
|
| def _blend_simple(file_a, file_b, mix: float, style: str | None = None): |
| img_a = Image.open(file_a.stream).convert("RGBA") |
| img_b = Image.open(file_b.stream).convert("RGBA") |
|
|
| img_b = img_b.resize(img_a.size) |
| mix = max(0.0, min(1.0, mix)) |
|
|
| blended = Image.blend(img_a, img_b, mix) |
|
|
| path = os.path.join(OUTPUT_DIR, "blend.png") |
| blended.save(path) |
|
|
| return path |
|
|
|
|
| |
| |
| |
|
|
| def _blend_ai(file_a, file_b, mix: float, style: str | None = None): |
| img_a = Image.open(file_a.stream).convert("RGBA") |
| img_b = Image.open(file_b.stream).convert("RGBA") |
|
|
| img_b = img_b.resize(img_a.size) |
| mix = max(0.0, min(1.0, mix)) |
|
|
| base = Image.blend(img_a, img_b, mix).convert("RGB") |
|
|
| prompt = "high quality artistic blend of two images" |
| if style and style in STYLE_PRESETS: |
| prompt += ", " + STYLE_PRESETS[style] |
|
|
| images = img2img_pipe( |
| prompt=prompt, |
| image=base, |
| strength=0.6, |
| num_inference_steps=8, |
| guidance_scale=1.5, |
| ).images |
|
|
| result = images[0] |
|
|
| path = os.path.join(OUTPUT_DIR, "blend_pro.png") |
| result.save(path) |
|
|
| return path |
|
|
|
|
| @api.route("/blend", methods=["POST"]) |
| def blend(): |
| if "image_a" not in request.files or "image_b" not in request.files: |
| return "need image_a and image_b", 400 |
|
|
| file_a = request.files["image_a"] |
| file_b = request.files["image_b"] |
|
|
| try: |
| mix = float(request.form.get("mix", 0.5)) |
| except: |
| mix = 0.5 |
|
|
| style = request.form.get("style") |
|
|
| path = _blend_simple(file_a, file_b, mix, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| @api.route("/blend-pro", methods=["POST"]) |
| def blend_pro(): |
| if "image_a" not in request.files or "image_b" not in request.files: |
| return "need image_a and image_b", 400 |
|
|
| file_a = request.files["image_a"] |
| file_b = request.files["image_b"] |
|
|
| try: |
| mix = float(request.form.get("mix", 0.5)) |
| except: |
| mix = 0.5 |
|
|
| style = request.form.get("style") |
|
|
| path = _blend_ai(file_a, file_b, mix, style) |
| return send_file(path, mimetype="image/png") |
|
|
|
|
| |
|
|
| if __name__ == "__main__": |
| api.run(host="0.0.0.0", port=7860) |
|
|