diff --git a/README.md b/README.md index 23a8c28..da4aac5 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,6 @@ A minimalistic, JavaScript-free web interface, front-end and processing server for Stable Diffusion, designed specifically for being accessed by legacy systems and browsers as clients. This interface relies on the API from AUTOMATIC1111's Stable Diffusion WebUI. ## Features - -Two separate interfaces are provided: -- `index.php`: HTML 4.01+ compliant interface -- `legacy.php`: HTML 3.2 compliant interface for extremely old systems - -Common features across both versions: - No JavaScript dependencies - Lightweight and fast loading - Support for LORA models using square bracket syntax @@ -19,12 +13,11 @@ Common features across both versions: - Sampling steps - CFG Scale - Choice of sampling methods + - Negative Prompt Support (1-19-2025 Update) ## Browser Compatibility -- `index.php`: Tested working on IE4+ and comparable browsers -- `legacy.php`: Tested working on IE3 and comparable browsers -- Both versions work on modern browsers +- `index.php`: Tested working on IE4+, Netscape 4.x and above browsers (HTML 4 compliant) ## Requirements @@ -40,15 +33,14 @@ Common features across both versions: 1. Ensure you have AUTOMATIC1111's Stable Diffusion WebUI running with the `--api` flag 2. Install PHP and ImageMagick on your system -3. Copy both `index.php` and `legacy.php` to your web server directory +3. Copy both `index.php` to your web server directory 4. Ensure the directory is writable by the web server for saved prompts and generated images 5. Access through your web browser: - Use `index.php` for HTML 4.01+ compatible browsers - - Use `legacy.php` for HTML 3.2 compatible browsers ## Usage -1. Enter your prompt in the text area +1. Enter your prompt and negative prompt if applicable in the text area 2. Configure generation parameters as needed 3. Click "Generate Image" to create your image 4. Save frequently used prompts server-side using the "Save Prompt" feature @@ -68,13 +60,6 @@ Example: - The interface works without any client-side scripting - All processing is done server-side - Compatible with browsers from the late 1990s to now -- The HTML 3.2 version (`legacy.php`) provides basic functionality for extremely old mid 1990s browsers - -## Why Two Versions? - -- `index.php` provides a more refined interface with better styling and layout options, suitable for browsers supporting HTML 4.01+ -- `legacy.php` strips down the interface to basic HTML 3.2 for maximum compatibility with vintage systems and browsers -- Both achieve the same core functionality with different levels of visual polish ## Screenshots @@ -87,7 +72,6 @@ Example: ![Screenshot 2024-12-14 143036](https://github.com/user-attachments/assets/9745d00f-57cc-4788-a17d-43782d7e6fa3) - ## Contributing Contributions are welcome! Please feel free to submit a Pull Request or fork. diff --git a/index.php b/index.php index b45b9e4..f8be232 100644 --- a/index.php +++ b/index.php @@ -5,11 +5,12 @@ header('Content-Type: text/html; charset=iso-8859-1'); $response = ""; $generated_image = ""; $prompt = ""; -$width = 512; -$height = 512; -$steps = 20; -$cfg_scale = 7.5; -$sampler = "Euler"; +$negative_prompt = ""; +$width = 768; +$height = 768; +$steps = 35; +$cfg_scale = 3; +$sampler = "DPM++ 3M SDE"; $error_message = ""; $success_message = ""; @@ -27,10 +28,13 @@ function loadSavedPrompts() { } // Save a new prompt -function savePrompt($prompt_text, $prompt_name) { +function savePrompt($prompt_text, $negative_prompt_text, $prompt_name) { global $saved_prompts_file; $prompts = loadSavedPrompts(); - $prompts[$prompt_name] = $prompt_text; + $prompts[$prompt_name] = array( + 'prompt' => $prompt_text, + 'negative_prompt' => $negative_prompt_text + ); file_put_contents($saved_prompts_file, json_encode($prompts, JSON_PRETTY_PRINT)); } @@ -39,7 +43,7 @@ $saved_prompts = loadSavedPrompts(); // List of available samplers $samplers = array( "Euler", - "Euler a", + "Euler a", "Heun", "DPM2", "DPM2 a", @@ -52,29 +56,32 @@ $samplers = array( "DPM adaptive", "LMS", "DPM++ SDE Karras", - "DPM++ 2M SDE Karras", + "DPM++ 2M SDE Karras", "DPM++ 3M SDE Karras", "DDIM", "PLMS" ); if ($_SERVER["REQUEST_METHOD"] == "POST") { + // All requests to the server to save or execute prompts will be POST requests to itself // Handle prompt saving if (isset($_POST['save_prompt']) && isset($_POST['prompt_name'])) { $prompt_to_save = trim($_POST['prompt']); + $negative_prompt_to_save = trim($_POST['negative_prompt']); $prompt_name = trim($_POST['prompt_name']); - + if (!empty($prompt_to_save) && !empty($prompt_name)) { - savePrompt($prompt_to_save, $prompt_name); + savePrompt($prompt_to_save, $negative_prompt_to_save, $prompt_name); $success_message = "Prompt saved successfully!"; $saved_prompts = loadSavedPrompts(); // Reload prompts } else { $error_message = "Both prompt and prompt name are required to save."; } } - // Handle image generation + // Handle image generation else { $prompt = isset($_POST['prompt']) ? $_POST['prompt'] : ''; + $negative_prompt = isset($_POST['negative_prompt']) ? $_POST['negative_prompt'] : ''; $width = isset($_POST['width']) ? (int)$_POST['width'] : 512; $height = isset($_POST['height']) ? (int)$_POST['height'] : 512; $steps = isset($_POST['steps']) ? (int)$_POST['steps'] : 20; @@ -92,6 +99,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { $data = array( 'prompt' => $processed_prompt, + 'negative_prompt' => $negative_prompt, 'steps' => $steps, 'width' => $width, 'height' => $height, @@ -101,7 +109,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { 'override_settings_restore_afterwards' => true ); - // Replace 127.0.0.1 with your Stable diffusion server's IP if running externally + // Replace 127.0.0.1 with your Stable diffusion server's IP if running externally $ch = curl_init('http://127.0.0.1:7860/sdapi/v1/txt2img'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); @@ -114,66 +122,77 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { $response = 'Error: ' . curl_error($ch); } else { $decoded = json_decode($result, true); - if (isset($decoded['images'][0])) { - $image_data = base64_decode($decoded['images'][0]); - - // Save original PNG temporarily - $temp_png = 'temp_' . time() . '_' . bin2hex(random_bytes(8)) . '.png'; - file_put_contents($temp_png, $image_data); - - // Create output JPG filename - $generated_image = 'generated_' . time() . '_' . bin2hex(random_bytes(8)) . '.jpg'; - - // Build ImageMagick convert command with optimization - $quality = 85; // Balanced quality setting - $cmd = "convert \"$temp_png\" -strip -interlace Plane -gaussian-blur 0.05 -quality $quality "; - $cmd .= "-sampling-factor 4:2:0 \"$generated_image\" 2>&1"; - - // Execute conversion - $output = []; - $returnVar = 0; - exec($cmd, $output, $returnVar); - - if ($returnVar === 0 && file_exists($generated_image)) { - // Get file sizes for comparison - $originalSize = filesize($temp_png); - $convertedSize = filesize($generated_image); - - // Clean up temporary PNG file - if (file_exists($temp_png)) { - unlink($temp_png); - } - - // Clean up old generated files - foreach (glob("generated_*.png") as $file) { - if ($file != $generated_image && (time() - filemtime($file)) > 3600) { - unlink($file); - } - } - - // Store size information for display - $size_info = array( - 'original' => round($originalSize / 1024, 2), - 'converted' => round($convertedSize / 1024, 2), - 'reduction' => round((($originalSize - $convertedSize) / $originalSize) * 100, 2) - ); - } else { - // If conversion fails, keep the original PNG - if (file_exists($temp_png)) { - $generated_image = $temp_png; - } - $error_message = "Image conversion failed. Using original PNG format."; - } - } + if (isset($decoded['images'][0])) { + $image_data = base64_decode($decoded['images'][0]); + + // Save original PNG temporarily + $temp_png = 'temp_' . time() . '_' . bin2hex(random_bytes(8)) . '.png'; + file_put_contents($temp_png, $image_data); + + // Create output JPG filename + $generated_image = 'generated_' . time() . '_' . bin2hex(random_bytes(8)) . '.jpg'; + + // Build ImageMagick convert command with optimization + $quality = 85; // Balanced quality setting + $cmd = "convert \"$temp_png\" -strip -interlace Plane -gaussian-blur 0.05 -quality $quality "; + $cmd .= "-sampling-factor 4:2:0 \"$generated_image\" 2>&1"; + + // Execute conversion + $output = []; + $returnVar = 0; + exec($cmd, $output, $returnVar); + + if ($returnVar === 0 && file_exists($generated_image)) { + // Get file sizes for comparison + $originalSize = filesize($temp_png); + $convertedSize = filesize($generated_image); + + // Clean up temporary PNG file + if (file_exists($temp_png)) { + unlink($temp_png); + } + + // Clean up old generated files + foreach (glob("generated_*.png") as $file) { + if ($file != $generated_image && (time() - filemtime($file)) > 3600) { + unlink($file); + } + } + + // Store size information for display + $size_info = array( + 'original' => round($originalSize / 1024, 2), + 'converted' => round($convertedSize / 1024, 2), + 'reduction' => round((($originalSize - $convertedSize) / $originalSize) * 100, 2) + ); + } else { + // If conversion fails, keep the original PNG + if (file_exists($temp_png)) { + $generated_image = $temp_png; + } + $error_message = "Image conversion failed. Using original PNG format."; + } + } } curl_close($ch); } } } else if ($_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['load_prompt'])) { + // For loading prompts we use GET method to send the client's choice of prompt to load // Load saved prompt into textarea $saved_prompts = loadSavedPrompts(); - $prompt = isset($saved_prompts[$_GET['load_prompt']]) ? $saved_prompts[$_GET['load_prompt']] : ''; + if (isset($saved_prompts[$_GET['load_prompt']])) { + // Check if the saved prompt is in the new format (array) or old format (string) + if (is_array($saved_prompts[$_GET['load_prompt']])) { + $prompt = $saved_prompts[$_GET['load_prompt']]['prompt']; + $negative_prompt = $saved_prompts[$_GET['load_prompt']]['negative_prompt']; + } else { + // Handle old format + $prompt = $saved_prompts[$_GET['load_prompt']]; + $negative_prompt = ''; + } + } } ?> @@ -188,10 +207,6 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { margin: 10px 0; border: 1px solid #ddd; } - .lora-help code { - background-color: #eee; - padding: 2px 4px; - } .form-group { margin-bottom: 15px; } @@ -203,6 +218,15 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { font-size: 14px; border: 1px solid #ddd; } + textarea#negative_prompt { + width: 100%; + height: 100px; + padding: 8px; + font-family: Arial, sans-serif; + font-size: 14px; + border: 1px solid #ddd; + background-color: #fff3f3; + } .error-message { color: #dc3545; background-color: #f8d7da; @@ -260,7 +284,8 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { +
+
+ +
+
@@ -319,36 +349,38 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ - -
-

Generated Image:

- Generated image -
- Download Generated Image - -
-

- Original size: KB
- Converted size: KB
- Size reduction: % -

-
- -
- Parameters used:
- Prompt:
- Processed Prompt:
- Size: x
- Steps:
- CFG Scale:
- Sampler: -
-
- + +
+

Generated Image:

+ Generated image +
+ Download Generated Image + +
+

+ Original size: KB
+ Converted size: KB
+ Size reduction: % +

+
+ +
+ Parameters used:
+ Prompt:
+ Negative Prompt:
+ Processed Prompt:
+ Size: x
+ Steps:
+ CFG Scale:
+ Sampler: +
+
+
@@ -358,4 +390,3 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { - diff --git a/legacy.php b/legacy.php deleted file mode 100644 index d6e5eda..0000000 --- a/legacy.php +++ /dev/null @@ -1,339 +0,0 @@ -') !== false) { - $error_message = "Error: Angle brackets are not allowed in the prompt. Please use square brackets [lora-name:weight] for LORAs."; - } - // Only proceed if there's no error - elseif (!empty($prompt)) { - // Process the prompt to handle LORA syntax - $processed_prompt = preg_replace('/\[([\w\s\-]+?):([\d\.]+)\]/', '', $prompt); - - $data = array( - 'prompt' => $processed_prompt, - 'steps' => $steps, - 'width' => $width, - 'height' => $height, - 'cfg_scale' => $cfg_scale, - 'sampler_name' => $sampler, - 'override_settings' => new stdClass(), - 'override_settings_restore_afterwards' => true - ); - - // Replace 127.0.0.1 with your Stable diffusion server's IP if running externally - $ch = curl_init('http://127.0.0.1:7860/sdapi/v1/txt2img'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); - - $result = curl_exec($ch); - - if (curl_errno($ch)) { - $response = 'Error: ' . curl_error($ch); - } else { - $decoded = json_decode($result, true); - if (isset($decoded['images'][0])) { - $image_data = base64_decode($decoded['images'][0]); - - // Save original PNG temporarily - $temp_png = 'temp_' . time() . '_' . bin2hex(random_bytes(8)) . '.png'; - file_put_contents($temp_png, $image_data); - - // Create output JPG filename - $generated_image = 'generated_' . time() . '_' . bin2hex(random_bytes(8)) . '.jpg'; - - // Build ImageMagick convert command with optimization - $quality = 85; // Balanced quality setting - $cmd = "convert \"$temp_png\" -strip -interlace Plane -gaussian-blur 0.05 -quality $quality "; - $cmd .= "-sampling-factor 4:2:0 \"$generated_image\" 2>&1"; - - // Execute conversion - $output = []; - $returnVar = 0; - exec($cmd, $output, $returnVar); - - if ($returnVar === 0 && file_exists($generated_image)) { - // Get file sizes for comparison - $originalSize = filesize($temp_png); - $convertedSize = filesize($generated_image); - - // Clean up temporary PNG file - if (file_exists($temp_png)) { - unlink($temp_png); - } - - // Clean up old generated files - foreach (glob("generated_*.png") as $file) { - if ($file != $generated_image && (time() - filemtime($file)) > 3600) { - unlink($file); - } - } - - // Store size information for display - $size_info = array( - 'original' => round($originalSize / 1024, 2), - 'converted' => round($convertedSize / 1024, 2), - 'reduction' => round((($originalSize - $convertedSize) / $originalSize) * 100, 2) - ); - } else { - // If conversion fails, keep the original PNG - if (file_exists($temp_png)) { - $generated_image = $temp_png; - } - $error_message = "Image conversion failed. Using original PNG format."; - } - } - } - - curl_close($ch); - } - } -} else if ($_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['load_prompt'])) { - // Load saved prompt into textarea - $saved_prompts = loadSavedPrompts(); - $prompt = isset($saved_prompts[$_GET['load_prompt']]) ? $saved_prompts[$_GET['load_prompt']] : ''; -} -?> - - - -Stable Diffusion Image Generator - - - -

Stable Diffusion Image Generator

- - - - - - -
-
- - - - - - - -
-
- - - - - - -
-

How to use LORAs:

-

Include LORAs using: [lora-name:weight]
- Example: a beautiful landscape by [my-artist-lora:0.8]
- Weight values: 0.1 to 1.0
- Note: Do not use angle brackets (< >). Use square brackets instead.

-
- -
- - - - - -
- Saved Prompts
-
"> - - -
-
- -
- -
"> - - - - - - - - - - - - - - - - - - -
Enter your prompt:
-
Width:
-
Height:
-
Steps:
-
CFG Scale:
-
Sampler:
-
- -
-
- -
- - - - - -
-
"> - Save Current Prompt
- Prompt Name:
- - -
-
- - -
- - - - -
-

Generated Image:

- Generated image -
- Download Generated Image - - -

- Original size: KB
- Converted size: KB
- Size reduction: % -

- - -

Parameters used:
- Prompt:
- - Processed Prompt:
- - Size: x
- Steps:
- CFG Scale:
- Sampler:

-
- - - -
- - - - -
-

Error:

-

-
- - - - -