Amazon Nova Canvas Workshop: 创建 Octank 狗粮的视觉广告

简介

欢迎来到 Amazon Nova Canvas 工作坊! 在这个动手实践环节中, 我们将探索 Amazon Nova Canvas 强大的功能, 为 Octank 这家高端狗粮公司创建引人注目的视觉广告。

用例

Octank 正在推出一款新的狗粮系列, 需要创造各种视觉资产:

  • 生成初始产品包装设计
  • 创建包装设计的变体, 包括油画风格版本
  • 设计使用特定品牌颜色的特殊促销包装
  • 制作专业外观的广告, 将产品置于厨房环境中
  • 隔离产品图像以用于各种营销材料

工作坊目标

通过本次工作坊, 我们将:

  • 了解 Amazon Nova Canvas 的关键功能
  • 学习如何将这些功能应用于真实的营销场景
  • 获得使用 Amazon Bedrock API 进行图像生成任务的实践经验

设置

# 内置库
import base64
import io
import json
import os
import sys

# 外部依赖
import boto3
import botocore
import numpy as np
import matplotlib.pyplot as plt

from PIL import Image

# 设置 Bedrock 客户端
boto3_bedrock = boto3.client('bedrock-runtime')

以下实用函数可以可视化生成的图像以及可选的参考图像。 它对于显示和比较图像生成任务的结果非常重要, 使我们能够轻松地查看输入、输出和任何相关的颜色信息。

# 实用函数: 定义绘图函数
def plot_images(base_images, prompt=None, seed=None, ref_image_path=None, color_codes=None, original_title=None, processed_title=None):
    if ref_image_path and color_codes:
        fig, axes = plt.subplots(1, 3, figsize=(15, 5))
        num_subplots = 3
    elif ref_image_path or color_codes:
        fig, axes = plt.subplots(1, 2, figsize=(12, 5))
        num_subplots = 2
    else:
        fig, axes = plt.subplots(1, 1, figsize=(6, 5))
        num_subplots = 1
    
    axes = np.array(axes).ravel() 
    
    current_subplot = 0
    
    if color_codes:
        num_colors = len(color_codes)
        color_width = 0.8 / num_colors
        for i, color_code in enumerate(color_codes):
            x = i * color_width
            rect = plt.Rectangle((x, 0), color_width, 1, facecolor=f'{color_code}', edgecolor='white')
            axes[current_subplot].add_patch(rect)
        axes[current_subplot].set_xlim(0, 0.8)
        axes[current_subplot].set_ylim(0, 1)
        axes[current_subplot].set_title('颜色代码')
        axes[current_subplot].axis('off')
        current_subplot += 1
    
    if ref_image_path:
        reference_image = Image.open(ref_image_path)
        max_size = (512, 512)
        reference_image.thumbnail(max_size)
        axes[current_subplot].imshow(np.array(reference_image))
        axes[current_subplot].set_title(original_title or '参考图像')
        axes[current_subplot].axis('off')
        current_subplot += 1
    
    axes[current_subplot].imshow(np.array(base_images[0]))
    if processed_title:
        axes[current_subplot].set_title(processed_title)
    elif ref_image_path and seed is not None:
        axes[current_subplot].set_title(f'基于参考图像生成的图像\n种子: {seed}')
    elif seed is not None:
        axes[current_subplot].set_title(f'生成的图像\n种子: {seed}')
    else:
        axes[current_subplot].set_title('处理后的图像')
    axes[current_subplot].axis('off')
    
    if prompt:
        print(f"提示: {prompt}\n")
    
    plt.tight_layout()
    plt.show()
def save_image(base64_image, output_file):
    with open(output_file, 'wb') as file:
        file.write(base64.b64decode(base64_image))

用例实现

步骤 1: 文本到图像

背景

Octank 想根据文本描述生成初始的产品包装设计。

参数

  1. prompt: 描述所需的图像
  2. negative_prompts: 指定要避免在图像中的元素
  3. seed: 用于可重复性
# 定义提示
prompt = "一个白色的高级狗粮包装, 上面有一只美国爱斯基摩犬, 专业的产品摄影。狗粮名称为 Octank。"
negative_prompts = "渲染不佳, 背景细节差, 包装细节差, 文字细节差, 模糊的文字"
seed = 42

# 指定存储输出的路径
output_save_path = "images/after_text-to-image.png" 
# 生成文本到图像
body = json.dumps(
    {
        "taskType": "TEXT_IMAGE",
        "textToImageParams": {
            "text": prompt,                    # 必需
            "negativeText": negative_prompts   # 可选
        },
        "imageGenerationConfig": {
            "numberOfImages": 1,   # 范围: 1 到 5 
            "quality": "standard",  # 选项: standard 或 premium
            "height": 1024,        
            "width": 1024,         
            "cfgScale": 7.5,       # 范围: 1.0 (exclusive) 到 10.0
            "seed": 250 #100            # 范围: 0 到 214783647
        }
    }
)

response = boto3_bedrock.invoke_model(
    body=body, 
    modelId="amazon.nova-canvas-v1:0",
    accept="application/json", 
    contentType="application/json"
)

response_body = json.loads(response.get("body").read())
response_images = [
    Image.open(io.BytesIO(base64.b64decode(base64_image)))
    for base64_image in response_body.get("images")
]

# 保存输出
save_image(response_body.get("images")[0], output_save_path)

# 绘制输出
plot_images(response_images, processed_title="生成的产品包装") 

步骤 2: 图像条件化

背景

Octank 想创建一个他们包装设计的油画风格版本, 同时保持整体布局。

参数

  1. prompt: 描述所需的风格
  2. reference_image_path: 原始包装设计的路径
  3. controlMode: 指定条件模式 (CANNY_EDGE 或 SEGMENTATION)
# 定义提示, 参考图像
prompt = "一个带有白色美国爱斯基摩犬的油画风格狗粮包装, 狗粮公司名称为 Octank"
reference_image_path = "images/after_text-to-image.png"
seed = 42# 可以是 0 到 214783647 之间的任意随机数

# 指定存储输出的路径
output_save_path = "images/after_image_cartooning.png" 
# 编码参考图像
with open(reference_image_path, "rb") as image_file:
    reference_image_base64 = base64.b64encode(image_file.read()).decode("utf-8")
    
# 根据参考图像生成图像
body = json.dumps(
    {
        "taskType": "TEXT_IMAGE",
        "textToImageParams": {
            "text": prompt,  # 必需
            "conditionImage": reference_image_base64, # 可选
            "controlMode": "CANNY_EDGE", # 可选: CANNY_EDGE | SEGMENTATION
            "controlStrength": 0.7,  # 范围: 0.2 到 1.0,
        },
        "imageGenerationConfig": {
                "numberOfImages": 1,
                "seed": seed,
            }
        
    }
)

response = boto3_bedrock.invoke_model(
    body=body, 
    modelId="amazon.nova-canvas-v1:0",
    accept="application/json", 
    contentType="application/json"
)

response_body = json.loads(response.get("body").read())
response_images = [
    Image.open(io.BytesIO(base64.b64decode(base64_image)))
    for base64_image in response_body.get("images")
]

save_image(response_body.get("images")[0], output_save_path)

# 绘制输出
plot_images(response_images, ref_image_path = reference_image_path)

步骤 3: 图像变体

背景

从文本生成图像是强大的, 但在某些情况下, 我们希望模型能够理解某些图像的风格, 并直接将其转移到输出图像中。 而不是从头开始, 图像变体功能使我们能够轻松地进行风格迁移。

现在, Octank 想要一个与参考图像相同风格的狗粮包装, 让我们看看这一步有多容易。

参数

  1. prompt: 描述所需的输出
  2. reference_image_path: 参考风格的路径
# 定义提示, 参考图像
prompt = "一个白色的高级狗粮包装, 上面有一只美国爱斯基摩犬, 专业的产品摄影。狗粮名称为 Octank"
negative_prompt = "质量差, 分辨率低"
reference_image_path = "images/sketch_dog.png"
seed = 600 # 可以是 0 到 214783647 之间的任意随机数

# 指定存储输出的路径
output_save_path = "images/after_image_variation.png" 
# 编码参考图像
with open(reference_image_path, "rb") as image_file:
    reference_image_base64 = base64.b64encode(image_file.read()).decode("utf-8")

body = json.dumps({
     "taskType": "IMAGE_VARIATION",
     "imageVariationParams": {
         "text": prompt,              # 可选
         "negativeText": negative_prompt,   # 可选
         "images": [reference_image_base64],               # 需要一个图像
        #  "similarityStrength": 1.0
     },
     "imageGenerationConfig": {
         "numberOfImages": 1,
         "quality": "premium",
         "height": 1024,
         "width": 1024,
         #"cfgScale": 10,
         "seed": seed
     }
 })

response = boto3_bedrock.invoke_model(
    body=body, 
    modelId="amazon.nova-canvas-v1:0",
    accept="application/json", 
    contentType="application/json"
)

response_body = json.loads(response.get("body").read())
response_images = [
    Image.open(io.BytesIO(base64.b64decode(base64_image)))
    for base64_image in response_body.get("images")
]

save_image(response_body.get("images")[0], output_save_path)

# 绘制输出
plot_images(response_images, ref_image_path = reference_image_path)

步骤 4. 修复图像

背景

Octank 决定通过在包装上展示不同的狗狗品种来刷新他们的产品线。 但是, 他们希望保持整体设计的一致性, 只更改狗狗图像。 这就是修复图像的用武之地。 对于这个任务, Octank 想要将他们当前包装上的美国爱斯基摩犬替换为哈士奇, 同时保持其余设计不变。

让我们使用修复图像来帮助 Octank 用新的狗狗品种更新他们的包装。

参数

  1. prompt: 描述所需的输出
  2. reference_image_path: 参考风格的路径
  3. mask_prompt: 描述要替换的对象
# 定义提示和参考图像
prompt = "一个白色的高级狗粮包装, 上面有哈士奇狗, 专业的产品摄影。狗粮名称为 Octank"
negative_prompts = "质量差, 低分辨率"
reference_image_path = "images/after_image_cartooning.png" 
mask_prompt = "美国爱斯基摩犬"
seed = 2 # 可以是 0 到 214783647 之间的任意随机数
with open(reference_image_path "rb") as image_file:
    reference_image_base64 = base64.b64encode(image_file.read()).decode("utf-8")

    
# 根据参考图像生成图像
body = json.dumps(
    {
        "taskType": "INPAINTING"
        "inPaintingParams": {
            "text": prompt  # 可选 - 在遮罩内部要改变的内容
            "negativeText": negative_prompts,    # 可选
            "image": reference_image_base64  # 必需
            "maskPrompt": mask_prompt,  # "maskImage" 或 "maskPrompt" 是必需的
            # "maskImage": "base64-encoded string",   

        },
        "imageGenerationConfig": {
                "numberOfImages": 1,
                "seed": seed
            }
    }
)

response = boto3_bedrock.invoke_model(
    body=body, 
    modelId="amazon.nova-canvas-v1:0"
    accept="application/json", 
    contentType="application/json"
)

response_body = json.loads(response.get("body").read())
response_images = [
    Image.open(io.BytesIO(base64.b64decode(base64