Spaces:
Running
Running
hatmanstack
commited on
Commit
•
1963e4b
1
Parent(s):
76f94b4
Fixed tabs
Browse files- README.md +27 -21
- app.py +387 -61
- requirements.txt +1 -2
README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
title: AWS Nova Canvas
|
2 |
emoji: 🖼️
|
3 |
colorFrom: blue
|
@@ -6,35 +7,40 @@ sdk: gradio
|
|
6 |
sdk_version: 5.6.0
|
7 |
app_file: app.py
|
8 |
pinned: false
|
9 |
-
license:
|
10 |
short_description: Generate image variations
|
|
|
11 |
|
12 |
|
13 |
-
#
|
14 |
|
15 |
-
|
16 |
|
17 |
-
##
|
18 |
|
19 |
-
- **Text to Image**: Generate
|
20 |
-
- **Inpainting**: Modify specific
|
21 |
-
- **Outpainting**: Extend
|
22 |
-
- **Image Variation**: Create
|
23 |
-
- **Image Conditioning**: Generate
|
24 |
-
- **Color Guided Content**:
|
25 |
-
- **Background Removal**: Remove
|
26 |
|
27 |
-
##
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
4. **Image Variation**: Upload an image, provide a text description, and click "Generate" to create variations.
|
33 |
-
5. **Image Conditioning**: Upload an image, provide a text prompt, and click "Generate" to condition the image.
|
34 |
-
6. **Color Guided Content**: Upload an image, provide a text prompt and color palette, and click "Generate" to guide content generation.
|
35 |
-
7. **Background Removal**: Upload an image and click "Generate" to remove the background.
|
36 |
|
37 |
-
##
|
38 |
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
40 |
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
title: AWS Nova Canvas
|
3 |
emoji: 🖼️
|
4 |
colorFrom: blue
|
|
|
7 |
sdk_version: 5.6.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
+
license: apache-2.0
|
11 |
short_description: Generate image variations
|
12 |
+
---
|
13 |
|
14 |
|
15 |
+
# AWS Nova Canvas Image Generation
|
16 |
|
17 |
+
A Gradio application for advanced image generation using Amazon Nova Canvas, offering comprehensive image manipulation capabilities.
|
18 |
|
19 |
+
## Capabilities
|
20 |
|
21 |
+
- **Text to Image**: Generate images from text prompts
|
22 |
+
- **Inpainting**: Modify specific image areas
|
23 |
+
- **Outpainting**: Extend image boundaries
|
24 |
+
- **Image Variation**: Create image variations
|
25 |
+
- **Image Conditioning**: Generate images based on input image and text
|
26 |
+
- **Color Guided Content**: Create images using reference color palettes
|
27 |
+
- **Background Removal**: Remove image backgrounds
|
28 |
|
29 |
+
## Prerequisites
|
30 |
|
31 |
+
- AWS credentials configured
|
32 |
+
- Boto3 Python library
|
33 |
+
- Gradio 5.6.0
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
## Technical Details
|
36 |
|
37 |
+
- Model: Amazon Nova Canvas (amazon.nova-canvas-v1:0)
|
38 |
+
- Image Generation Parameters:
|
39 |
+
- Default resolution: 1024x1024
|
40 |
+
- Quality: Standard
|
41 |
+
- CFG Scale: 8.0
|
42 |
+
- Configurable seed
|
43 |
|
44 |
+
## Documentation
|
45 |
+
|
46 |
+
For detailed usage, visit [AWS Nova documentation](https://docs.aws.amazon.com/nova/latest/userguide/what-is-nova.html).
|
app.py
CHANGED
@@ -4,7 +4,10 @@ import json
|
|
4 |
import logging
|
5 |
import boto3
|
6 |
from PIL import Image
|
|
|
|
|
7 |
import gradio as gr
|
|
|
8 |
|
9 |
# Set up logging
|
10 |
logger = logging.getLogger(__name__)
|
@@ -16,27 +19,96 @@ class ImageError(Exception):
|
|
16 |
self.message = message
|
17 |
|
18 |
model_id = 'amazon.nova-canvas-v1:0'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
# Function to generate an image using Amazon Nova Canvas model
|
20 |
def generate_image(body):
|
21 |
logger.info("Generating image with Amazon Nova Canvas model %s", model_id)
|
22 |
-
session = boto3.Session(aws_access_key_id=aws_id, aws_secret_access_key=aws_secret, region_name='us-east-1')
|
23 |
-
bedrock = session.client('bedrock-runtime')
|
24 |
-
accept = "application/json"
|
25 |
-
content_type = "application/json"
|
26 |
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
# Function to display image from bytes
|
42 |
def display_image(image_bytes):
|
@@ -44,68 +116,229 @@ def display_image(image_bytes):
|
|
44 |
return image
|
45 |
|
46 |
# Gradio functions for each task
|
47 |
-
def text_to_image(prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
body = json.dumps({
|
49 |
"taskType": "TEXT_IMAGE",
|
50 |
-
"textToImageParams":
|
51 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
})
|
53 |
image_bytes = generate_image(body)
|
54 |
return display_image(image_bytes)
|
55 |
|
56 |
-
def inpainting(image, mask_prompt):
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
body = json.dumps({
|
59 |
"taskType": "INPAINTING",
|
60 |
-
"inPaintingParams":
|
61 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
})
|
63 |
image_bytes = generate_image(body)
|
64 |
return display_image(image_bytes)
|
65 |
|
66 |
-
def outpainting(image, mask_image, text):
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
body = json.dumps({
|
70 |
"taskType": "OUTPAINTING",
|
71 |
-
"outPaintingParams":
|
72 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
})
|
74 |
image_bytes = generate_image(body)
|
75 |
return display_image(image_bytes)
|
76 |
|
77 |
-
def image_variation(
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
body = json.dumps({
|
80 |
"taskType": "IMAGE_VARIATION",
|
81 |
-
"imageVariationParams":
|
82 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
})
|
84 |
image_bytes = generate_image(body)
|
85 |
return display_image(image_bytes)
|
86 |
|
87 |
-
def image_conditioning(
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
body = json.dumps({
|
90 |
"taskType": "TEXT_IMAGE",
|
91 |
-
"textToImageParams":
|
92 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
})
|
94 |
image_bytes = generate_image(body)
|
95 |
return display_image(image_bytes)
|
96 |
|
97 |
-
def color_guided_content(
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
body = json.dumps({
|
100 |
"taskType": "COLOR_GUIDED_GENERATION",
|
101 |
-
"colorGuidedGenerationParams":
|
102 |
-
"imageGenerationConfig": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
})
|
104 |
image_bytes = generate_image(body)
|
105 |
return display_image(image_bytes)
|
106 |
|
107 |
def background_removal(image):
|
108 |
-
input_image =
|
109 |
body = json.dumps({
|
110 |
"taskType": "BACKGROUND_REMOVAL",
|
111 |
"backgroundRemovalParams": {"image": input_image}
|
@@ -115,60 +348,153 @@ def background_removal(image):
|
|
115 |
|
116 |
# Gradio Interface
|
117 |
with gr.Blocks() as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
gr.Markdown("# Amazon Nova Canvas Image Generation")
|
119 |
|
120 |
with gr.Tab("Text to Image"):
|
121 |
with gr.Column():
|
122 |
gr.Markdown("Generate an image from a text prompt using the Amazon Nova Canvas model.")
|
123 |
-
prompt = gr.Textbox(label="Prompt")
|
124 |
output = gr.Image()
|
125 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
with gr.Tab("Inpainting"):
|
128 |
with gr.Column():
|
129 |
-
gr.Markdown("
|
|
|
|
|
|
|
|
|
|
|
130 |
image = gr.Image(type='pil', label="Input Image")
|
131 |
-
mask_prompt = gr.Textbox(label="Mask Prompt")
|
|
|
|
|
|
|
132 |
output = gr.Image()
|
133 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
with gr.Tab("Outpainting"):
|
136 |
with gr.Column():
|
137 |
gr.Markdown("Extend an image beyond its original borders using a mask and text prompt.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
image = gr.Image(type='pil', label="Input Image")
|
139 |
-
|
140 |
-
|
|
|
|
|
141 |
output = gr.Image()
|
142 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
with gr.Tab("Image Variation"):
|
145 |
with gr.Column():
|
146 |
-
gr.Markdown("
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
149 |
output = gr.Image()
|
150 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
|
152 |
with gr.Tab("Image Conditioning"):
|
153 |
with gr.Column():
|
154 |
-
gr.Markdown("
|
155 |
-
|
156 |
-
|
|
|
|
|
|
|
|
|
157 |
output = gr.Image()
|
158 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
|
160 |
with gr.Tab("Color Guided Content"):
|
161 |
with gr.Column():
|
162 |
-
gr.Markdown("
|
163 |
-
|
164 |
-
|
165 |
-
|
|
|
|
|
|
|
|
|
166 |
output = gr.Image()
|
167 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
with gr.Tab("Background Removal"):
|
170 |
with gr.Column():
|
171 |
-
gr.Markdown("
|
|
|
|
|
|
|
|
|
172 |
image = gr.Image(type='pil', label="Input Image")
|
173 |
output = gr.Image()
|
174 |
gr.Button("Generate").click(background_removal, inputs=image, outputs=output)
|
|
|
4 |
import logging
|
5 |
import boto3
|
6 |
from PIL import Image
|
7 |
+
from botocore.config import Config
|
8 |
+
from botocore.exceptions import ClientError
|
9 |
import gradio as gr
|
10 |
+
import os
|
11 |
|
12 |
# Set up logging
|
13 |
logger = logging.getLogger(__name__)
|
|
|
19 |
self.message = message
|
20 |
|
21 |
model_id = 'amazon.nova-canvas-v1:0'
|
22 |
+
aws_id = os.getenv('AWS_ID')
|
23 |
+
aws_secret = os.getenv('AWS_SECRET')
|
24 |
+
|
25 |
+
def process_and_encode_image(image, min_size=320, max_size=4096, max_pixels=4194304):
|
26 |
+
if image is None:
|
27 |
+
raise ValueError("Input image is required.")
|
28 |
+
if not isinstance(image, Image.Image):
|
29 |
+
image = Image.open(image)
|
30 |
+
|
31 |
+
# Convert to RGB mode if necessary
|
32 |
+
if image.mode not in ('RGB', 'RGBA'):
|
33 |
+
image = image.convert('RGB')
|
34 |
+
elif image.mode == 'RGBA':
|
35 |
+
# Convert RGBA to RGB by compositing on white background
|
36 |
+
background = Image.new('RGB', image.size, (255, 255, 255))
|
37 |
+
background.paste(image, mask=image.split()[3]) # Use alpha channel as mask
|
38 |
+
image = background
|
39 |
+
|
40 |
+
# Ensure 8-bit color depth
|
41 |
+
if image.mode == 'RGB' and isinstance(image.getpixel((0,0)), tuple) and len(image.getpixel((0,0))) == 3:
|
42 |
+
if not all(0 <= x <= 255 for x in image.getpixel((0,0))):
|
43 |
+
image = image.convert('RGB')
|
44 |
+
|
45 |
+
current_pixels = image.width * image.height
|
46 |
+
# If image exceeds max pixels, scale it down while maintaining aspect ratio
|
47 |
+
if current_pixels > max_pixels:
|
48 |
+
aspect_ratio = image.width / image.height
|
49 |
+
if aspect_ratio > 1: # Width > Height
|
50 |
+
new_width = int((max_pixels * aspect_ratio) ** 0.5)
|
51 |
+
new_height = int(new_width / aspect_ratio)
|
52 |
+
else: # Height >= Width
|
53 |
+
new_height = int((max_pixels / aspect_ratio) ** 0.5)
|
54 |
+
new_width = int(new_height * aspect_ratio)
|
55 |
+
|
56 |
+
image = image.resize((new_width, new_height), Image.LANCZOS)
|
57 |
+
|
58 |
+
# Ensure dimensions are within valid range
|
59 |
+
if image.width < min_size or image.width > max_size or image.height < min_size or image.height > max_size:
|
60 |
+
new_width = min(max(image.width, min_size), max_size)
|
61 |
+
new_height = min(max(image.height, min_size), max_size)
|
62 |
+
image = image.resize((new_width, new_height), Image.LANCZOS)
|
63 |
+
|
64 |
+
# Convert to bytes and encode to base64
|
65 |
+
image_bytes = io.BytesIO()
|
66 |
+
# Save as PNG with maximum compatibility
|
67 |
+
image.save(image_bytes, format='PNG', optimize=True)
|
68 |
+
encoded_image = base64.b64encode(image_bytes.getvalue()).decode('utf8')
|
69 |
+
|
70 |
+
return encoded_image
|
71 |
+
|
72 |
# Function to generate an image using Amazon Nova Canvas model
|
73 |
def generate_image(body):
|
74 |
logger.info("Generating image with Amazon Nova Canvas model %s", model_id)
|
|
|
|
|
|
|
|
|
75 |
|
76 |
+
# Configure the client with a longer timeout
|
77 |
+
bedrock = boto3.client(
|
78 |
+
service_name='bedrock-runtime',
|
79 |
+
aws_access_key_id=aws_id,
|
80 |
+
aws_secret_access_key=aws_secret,
|
81 |
+
region_name='us-east-1',
|
82 |
+
config=Config(read_timeout=300) # Add 5-minute timeout
|
83 |
+
)
|
84 |
+
|
85 |
+
print(body)
|
86 |
+
|
87 |
+
try:
|
88 |
+
response = bedrock.invoke_model(
|
89 |
+
body=body,
|
90 |
+
modelId=model_id,
|
91 |
+
accept="application/json",
|
92 |
+
contentType="application/json"
|
93 |
+
)
|
94 |
+
|
95 |
+
response_body = json.loads(response.get("body").read())
|
96 |
|
97 |
+
# Check for error before processing the image
|
98 |
+
if "error" in response_body:
|
99 |
+
raise ImageError(f"Image generation error. Error is {response_body['error']}")
|
100 |
|
101 |
+
base64_image = response_body.get("images")[0]
|
102 |
+
base64_bytes = base64_image.encode('ascii')
|
103 |
+
image_bytes = base64.b64decode(base64_bytes)
|
104 |
|
105 |
+
logger.info("Successfully generated image with Amazon Nova Canvas model %s", model_id)
|
106 |
+
return image_bytes
|
107 |
+
|
108 |
+
except ClientError as err:
|
109 |
+
message = err.response["Error"]["Message"]
|
110 |
+
logger.error("A client error occurred: %s", message)
|
111 |
+
raise ImageError(f"Client error during image generation: {message}")
|
112 |
|
113 |
# Function to display image from bytes
|
114 |
def display_image(image_bytes):
|
|
|
116 |
return image
|
117 |
|
118 |
# Gradio functions for each task
|
119 |
+
def text_to_image(prompt, negative_text=None, height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
120 |
+
# Prepare the textToImageParams dictionary
|
121 |
+
text_to_image_params = {
|
122 |
+
"text": prompt
|
123 |
+
}
|
124 |
+
|
125 |
+
# Conditionally add negativeText if it is not None and not empty
|
126 |
+
if negative_text:
|
127 |
+
text_to_image_params["negativeText"] = negative_text
|
128 |
+
|
129 |
body = json.dumps({
|
130 |
"taskType": "TEXT_IMAGE",
|
131 |
+
"textToImageParams": text_to_image_params,
|
132 |
+
"imageGenerationConfig": {
|
133 |
+
"numberOfImages": 1,
|
134 |
+
"height": height,
|
135 |
+
"width": width,
|
136 |
+
"quality": quality,
|
137 |
+
"cfgScale": cfg_scale,
|
138 |
+
"seed": seed
|
139 |
+
}
|
140 |
})
|
141 |
image_bytes = generate_image(body)
|
142 |
return display_image(image_bytes)
|
143 |
|
144 |
+
def inpainting(image, mask_prompt=None, mask_image=None, text=None, negative_text=None, height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
145 |
+
if image is not None:
|
146 |
+
input_image = process_and_encode_image(image)
|
147 |
+
else:
|
148 |
+
raise ValueError("Input image is required.")
|
149 |
+
|
150 |
+
if mask_image is not None:
|
151 |
+
mask_image_encoded = process_and_encode_image(image)
|
152 |
+
else:
|
153 |
+
mask_image_encoded = None
|
154 |
+
|
155 |
+
if not mask_prompt and not mask_image:
|
156 |
+
raise ValueError("You must specify either maskPrompt or maskImage.")
|
157 |
+
|
158 |
+
# Prepare the inPaintingParams dictionary
|
159 |
+
if mask_prompt and mask_image_encoded:
|
160 |
+
raise ValueError("You must specify either maskPrompt or maskImage, but not both.")
|
161 |
+
if not mask_prompt and not mask_image_encoded:
|
162 |
+
raise ValueError("You must specify either maskPrompt or maskImage.")
|
163 |
+
|
164 |
+
# Prepare the inPaintingParams dictionary with the appropriate mask parameter
|
165 |
+
in_painting_params = {
|
166 |
+
"image": input_image
|
167 |
+
}
|
168 |
+
|
169 |
+
if mask_prompt:
|
170 |
+
in_painting_params["maskPrompt"] = mask_prompt
|
171 |
+
elif mask_image_encoded:
|
172 |
+
in_painting_params["maskImage"] = mask_image_encoded
|
173 |
+
if text:
|
174 |
+
in_painting_params["text"] = text
|
175 |
+
if negative_text:
|
176 |
+
in_painting_params["negativeText"] = negative_text
|
177 |
+
|
178 |
body = json.dumps({
|
179 |
"taskType": "INPAINTING",
|
180 |
+
"inPaintingParams": in_painting_params,
|
181 |
+
"imageGenerationConfig": {
|
182 |
+
"numberOfImages": 1,
|
183 |
+
"height": height,
|
184 |
+
"width": width,
|
185 |
+
"quality": quality,
|
186 |
+
"cfgScale": cfg_scale,
|
187 |
+
"seed": seed
|
188 |
+
}
|
189 |
})
|
190 |
image_bytes = generate_image(body)
|
191 |
return display_image(image_bytes)
|
192 |
|
193 |
+
def outpainting(image, mask_prompt=None, mask_image=None, text=None, negative_text=None, outpainting_mode="DEFAULT", height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
194 |
+
if image is not None:
|
195 |
+
input_image = process_and_encode_image(image)
|
196 |
+
else:
|
197 |
+
raise ValueError("Input image is required.")
|
198 |
+
|
199 |
+
if mask_image is not None:
|
200 |
+
mask_bytes = io.BytesIO()
|
201 |
+
mask_image.save(mask_bytes, format='PNG')
|
202 |
+
mask_image_encoded = base64.b64encode(mask_bytes.getvalue()).decode('utf8')
|
203 |
+
else:
|
204 |
+
mask_image_encoded = None
|
205 |
+
|
206 |
+
if not mask_prompt and not mask_image:
|
207 |
+
raise ValueError("You must specify either maskPrompt or maskImage.")
|
208 |
+
|
209 |
+
# Prepare the outPaintingParams dictionary
|
210 |
+
out_painting_params = {
|
211 |
+
"image": input_image,
|
212 |
+
"outPaintingMode": outpainting_mode,
|
213 |
+
"maskPrompt": mask_prompt or "" # Ensure maskPrompt is always included
|
214 |
+
}
|
215 |
+
|
216 |
+
# Conditionally add parameters if they are not None
|
217 |
+
if mask_image_encoded:
|
218 |
+
out_painting_params["maskImage"] = mask_image_encoded
|
219 |
+
if text:
|
220 |
+
out_painting_params["text"] = text
|
221 |
+
if negative_text:
|
222 |
+
out_painting_params["negativeText"] = negative_text
|
223 |
+
|
224 |
body = json.dumps({
|
225 |
"taskType": "OUTPAINTING",
|
226 |
+
"outPaintingParams": out_painting_params,
|
227 |
+
"imageGenerationConfig": {
|
228 |
+
"numberOfImages": 1,
|
229 |
+
"height": height,
|
230 |
+
"width": width,
|
231 |
+
"quality": quality,
|
232 |
+
"cfgScale": cfg_scale,
|
233 |
+
"seed": seed
|
234 |
+
}
|
235 |
})
|
236 |
image_bytes = generate_image(body)
|
237 |
return display_image(image_bytes)
|
238 |
|
239 |
+
def image_variation(images, text=None, negative_text=None, similarity_strength=0.5, height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
240 |
+
encoded_images = []
|
241 |
+
for image_path in images:
|
242 |
+
with open(image_path, "rb") as image_file:
|
243 |
+
encoded_images.append(process_and_encode_image(image_file))
|
244 |
+
|
245 |
+
# Prepare the imageVariationParams dictionary
|
246 |
+
image_variation_params = {
|
247 |
+
"images": encoded_images,
|
248 |
+
"similarityStrength": similarity_strength
|
249 |
+
}
|
250 |
+
|
251 |
+
# Conditionally add parameters if they are not None
|
252 |
+
if text:
|
253 |
+
image_variation_params["text"] = text
|
254 |
+
if negative_text:
|
255 |
+
image_variation_params["negativeText"] = negative_text
|
256 |
+
|
257 |
body = json.dumps({
|
258 |
"taskType": "IMAGE_VARIATION",
|
259 |
+
"imageVariationParams": image_variation_params,
|
260 |
+
"imageGenerationConfig": {
|
261 |
+
"numberOfImages": 1,
|
262 |
+
"height": height,
|
263 |
+
"width": width,
|
264 |
+
"quality": quality,
|
265 |
+
"cfgScale": cfg_scale,
|
266 |
+
"seed": seed
|
267 |
+
}
|
268 |
})
|
269 |
image_bytes = generate_image(body)
|
270 |
return display_image(image_bytes)
|
271 |
|
272 |
+
def image_conditioning(condition_image, text, negative_text=None, control_mode="CANNY_EDGE", control_strength=0.7, height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
273 |
+
if condition_image is not None:
|
274 |
+
condition_image_encoded = process_and_encode_image(condition_image)
|
275 |
+
else:
|
276 |
+
raise ValueError("Input image is required.")
|
277 |
+
|
278 |
+
# Prepare the textToImageParams dictionary
|
279 |
+
text_to_image_params = {
|
280 |
+
"text": text,
|
281 |
+
"conditionImage": condition_image_encoded,
|
282 |
+
"controlMode": control_mode,
|
283 |
+
"controlStrength": control_strength
|
284 |
+
}
|
285 |
+
|
286 |
+
# Conditionally add negativeText if it is not None
|
287 |
+
if negative_text:
|
288 |
+
text_to_image_params["negativeText"] = negative_text
|
289 |
+
|
290 |
body = json.dumps({
|
291 |
"taskType": "TEXT_IMAGE",
|
292 |
+
"textToImageParams": text_to_image_params,
|
293 |
+
"imageGenerationConfig": {
|
294 |
+
"numberOfImages": 1,
|
295 |
+
"height": height,
|
296 |
+
"width": width,
|
297 |
+
"quality": quality,
|
298 |
+
"cfgScale": cfg_scale,
|
299 |
+
"seed": seed
|
300 |
+
}
|
301 |
})
|
302 |
image_bytes = generate_image(body)
|
303 |
return display_image(image_bytes)
|
304 |
|
305 |
+
def color_guided_content(text=None, reference_image=None, negative_text=None, colors=None, height=1024, width=1024, quality="standard", cfg_scale=8.0, seed=0):
|
306 |
+
# Encode the reference image if provided
|
307 |
+
if reference_image is not None:
|
308 |
+
reference_image_encoded = process_and_encode_image(reference_image)
|
309 |
+
else:
|
310 |
+
reference_image_encoded = None
|
311 |
+
if not colors:
|
312 |
+
colors = "#FF5733,#33FF57,#3357FF,#FF33A1,#33FFF5,#FF8C33,#8C33FF,#33FF8C,#FF3333,#33A1FF"
|
313 |
+
# Prepare the colorGuidedGenerationParams dictionary
|
314 |
+
color_guided_generation_params = {
|
315 |
+
"text": text,
|
316 |
+
"colors": colors.split(',')
|
317 |
+
}
|
318 |
+
|
319 |
+
# Conditionally add parameters if they are not None
|
320 |
+
if negative_text:
|
321 |
+
color_guided_generation_params["negativeText"] = negative_text
|
322 |
+
if reference_image_encoded:
|
323 |
+
color_guided_generation_params["referenceImage"] = reference_image_encoded
|
324 |
+
|
325 |
body = json.dumps({
|
326 |
"taskType": "COLOR_GUIDED_GENERATION",
|
327 |
+
"colorGuidedGenerationParams": color_guided_generation_params,
|
328 |
+
"imageGenerationConfig": {
|
329 |
+
"numberOfImages": 1,
|
330 |
+
"height": height,
|
331 |
+
"width": width,
|
332 |
+
"quality": quality,
|
333 |
+
"cfgScale": cfg_scale,
|
334 |
+
"seed": seed
|
335 |
+
}
|
336 |
})
|
337 |
image_bytes = generate_image(body)
|
338 |
return display_image(image_bytes)
|
339 |
|
340 |
def background_removal(image):
|
341 |
+
input_image = process_and_encode_image(image)
|
342 |
body = json.dumps({
|
343 |
"taskType": "BACKGROUND_REMOVAL",
|
344 |
"backgroundRemovalParams": {"image": input_image}
|
|
|
348 |
|
349 |
# Gradio Interface
|
350 |
with gr.Blocks() as demo:
|
351 |
+
gr.HTML("""
|
352 |
+
<style>
|
353 |
+
#component-0 {
|
354 |
+
max-width: 800px;
|
355 |
+
margin: 0 auto;
|
356 |
+
}
|
357 |
+
</style>
|
358 |
+
""")
|
359 |
gr.Markdown("# Amazon Nova Canvas Image Generation")
|
360 |
|
361 |
with gr.Tab("Text to Image"):
|
362 |
with gr.Column():
|
363 |
gr.Markdown("Generate an image from a text prompt using the Amazon Nova Canvas model.")
|
364 |
+
prompt = gr.Textbox(label="Prompt", placeholder="Enter a text prompt (1-1024 characters)", max_lines=1)
|
365 |
output = gr.Image()
|
366 |
+
with gr.Accordion("Advanced Options", open=False):
|
367 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Enter text to exclude (1-1024 characters)", max_lines=1)
|
368 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
369 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
370 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
371 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
372 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
373 |
+
|
374 |
+
gr.Button("Generate").click(text_to_image, inputs=[prompt, negative_text, height, width, quality, cfg_scale, seed], outputs=output)
|
375 |
|
376 |
with gr.Tab("Inpainting"):
|
377 |
with gr.Column():
|
378 |
+
gr.Markdown("""
|
379 |
+
<div style="text-align: center;">
|
380 |
+
Modify specific areas of your image using inpainting. Upload your image and choose one of two ways to specify the areas you want to edit:
|
381 |
+
You can use a photo editing tool to draw masks (using pure black for areas to edit and pure white for areas to preserve) or use the Mask Prompt field to allow the model to infer the mask.
|
382 |
+
</div>
|
383 |
+
""")
|
384 |
image = gr.Image(type='pil', label="Input Image")
|
385 |
+
mask_prompt = gr.Textbox(label="Mask Prompt", placeholder="Describe regions to edit", max_lines=1)
|
386 |
+
with gr.Accordion("Mask Image", open=False):
|
387 |
+
text = gr.Textbox(label="Text", placeholder="Describe what to generate (1-1024 characters)", max_lines=1)
|
388 |
+
mask_image = gr.Image(type='pil', label="Mask Image")
|
389 |
output = gr.Image()
|
390 |
+
with gr.Accordion("Advanced Options", open=False):
|
391 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Describe what not to include (1-1024 characters)", max_lines=1)
|
392 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
393 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
394 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
395 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
396 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
397 |
+
|
398 |
+
gr.Button("Generate").click(inpainting, inputs=[image, mask_prompt, mask_image, text, negative_text, height, width, quality, cfg_scale, seed], outputs=output)
|
399 |
|
400 |
with gr.Tab("Outpainting"):
|
401 |
with gr.Column():
|
402 |
gr.Markdown("Extend an image beyond its original borders using a mask and text prompt.")
|
403 |
+
gr.Markdown("""
|
404 |
+
<div style="text-align: center;">
|
405 |
+
Modify areas outside of your image using outpainting. Upload your image and choose one of two ways to specify the areas you want to edit:
|
406 |
+
You can use a photo editing tool to draw masks extended outside of an images original borders (using pure black for areas to edit and pure
|
407 |
+
white for areas to preserve) or use the Mask Prompt field to allow the model to infer the mask.
|
408 |
+
</div>
|
409 |
+
""")
|
410 |
image = gr.Image(type='pil', label="Input Image")
|
411 |
+
mask_prompt = gr.Textbox(label="Mask Prompt", placeholder="Describe regions to edit", max_lines=1)
|
412 |
+
with gr.Accordion("Mask Image", open=False):
|
413 |
+
text = gr.Textbox(label="Text", placeholder="Describe what to generate (1-1024 characters)", max_lines=1)
|
414 |
+
mask_image = gr.Image(type='pil', label="Mask Image")
|
415 |
output = gr.Image()
|
416 |
+
with gr.Accordion("Advanced Options", open=False):
|
417 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Describe what not to include (1-1024 characters)", max_lines=1)
|
418 |
+
outpainting_mode = gr.Radio(choices=["DEFAULT", "PRECISE"], value="DEFAULT", label="Outpainting Mode")
|
419 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
420 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
421 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
422 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
423 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
424 |
+
|
425 |
+
gr.Button("Generate").click(outpainting, inputs=[image, mask_prompt, mask_image, text, negative_text, outpainting_mode, height, width, quality, cfg_scale, seed], outputs=output)
|
426 |
|
427 |
with gr.Tab("Image Variation"):
|
428 |
with gr.Column():
|
429 |
+
gr.Markdown("""
|
430 |
+
<div style="text-align: center;">
|
431 |
+
Create a variation image based on up to 5 other images and a text description (optional).
|
432 |
+
</div>
|
433 |
+
""")
|
434 |
+
images = gr.File(type='filepath', label="Input Images", file_count="multiple", file_types=["image"])
|
435 |
+
text = gr.Textbox(label="Text", placeholder="Enter a text prompt (1-1024 characters)", max_lines=1)
|
436 |
output = gr.Image()
|
437 |
+
with gr.Accordion("Advanced Options", open=False):
|
438 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Enter text to exclude (1-1024 characters)", max_lines=1)
|
439 |
+
similarity_strength = gr.Slider(minimum=0.2, maximum=1.0, step=0.1, value=0.7, label="Similarity Strength")
|
440 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
441 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
442 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
443 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
444 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
445 |
+
|
446 |
+
gr.Button("Generate").click(image_variation, inputs=[images, text, negative_text, similarity_strength, height, width, quality, cfg_scale, seed], outputs=output)
|
447 |
|
448 |
with gr.Tab("Image Conditioning"):
|
449 |
with gr.Column():
|
450 |
+
gr.Markdown("""
|
451 |
+
<div style="text-align: center;">
|
452 |
+
Generate an image conditioned on an input image and a text prompt (required).
|
453 |
+
</div>
|
454 |
+
""")
|
455 |
+
condition_image = gr.Image(type='pil', label="Condition Image")
|
456 |
+
text = gr.Textbox(label="Text", placeholder="Enter a text prompt (1-1024 characters)", max_lines=1)
|
457 |
output = gr.Image()
|
458 |
+
with gr.Accordion("Advanced Options", open=False):
|
459 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Describe what not to include (1-1024 characters)", max_lines=1)
|
460 |
+
control_mode = gr.Radio(choices=["CANNY_EDGE", "SEGMENTATION"], value="CANNY_EDGE", label="Control Mode")
|
461 |
+
control_strength = gr.Slider(minimum=0.0, maximum=1.0, step=0.1, value=0.7, label="Control Strength")
|
462 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
463 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
464 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
465 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
466 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
467 |
+
|
468 |
+
gr.Button("Generate").click(image_conditioning, inputs=[condition_image, text, negative_text, control_mode, control_strength, height, width, quality, cfg_scale, seed], outputs=output)
|
469 |
|
470 |
with gr.Tab("Color Guided Content"):
|
471 |
with gr.Column():
|
472 |
+
gr.Markdown("""
|
473 |
+
<div style="text-align: center;">
|
474 |
+
Generate an image using a color palette from a reference image or a text prompt. Starter colors are provided.
|
475 |
+
</div>
|
476 |
+
""")
|
477 |
+
reference_image = gr.Image(type='pil', label="Reference Image")
|
478 |
+
colors = gr.Textbox(label="Colors", placeholder="Enter up to 10 colors as hex values, e.g., #00FF00,#FCF2AB", max_lines=1)
|
479 |
+
text = gr.Textbox(label="Text", placeholder="Enter a text prompt (1-1024 characters)", max_lines=1)
|
480 |
output = gr.Image()
|
481 |
+
with gr.Accordion("Advanced Options", open=False):
|
482 |
+
negative_text = gr.Textbox(label="Negative Prompt", placeholder="Enter text to exclude (1-1024 characters)", max_lines=1)
|
483 |
+
width = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Width")
|
484 |
+
height = gr.Slider(minimum=256, maximum=2048, step=64, value=1024, label="Height")
|
485 |
+
quality = gr.Radio(choices=["standard", "premium"], value="standard", label="Quality")
|
486 |
+
cfg_scale = gr.Slider(minimum=1.0, maximum=20.0, step=0.1, value=8.0, label="CFG Scale")
|
487 |
+
seed = gr.Slider(minimum=1, maximum=2000, step=1, value=8, label="Seed")
|
488 |
+
|
489 |
+
gr.Button("Generate").click(color_guided_content, inputs=[text, reference_image, negative_text, colors, height, width, quality, cfg_scale, seed], outputs=output)
|
490 |
|
491 |
with gr.Tab("Background Removal"):
|
492 |
with gr.Column():
|
493 |
+
gr.Markdown("""
|
494 |
+
<div style="text-align: center;">
|
495 |
+
Remove the background from an image.
|
496 |
+
</div>
|
497 |
+
""")
|
498 |
image = gr.Image(type='pil', label="Input Image")
|
499 |
output = gr.Image()
|
500 |
gr.Button("Generate").click(background_removal, inputs=image, outputs=output)
|
requirements.txt
CHANGED
@@ -1,3 +1,2 @@
|
|
1 |
-
|
2 |
-
gradio
|
3 |
boto3
|
|
|
1 |
+
Pillow
|
|
|
2 |
boto3
|