DevBlog #2 Image generation using Dalle-2
Tags: aws, python, openai, development
Published: 2023-07-20 04:59:01

Hello Everyone!

This is the 2nd post of a series for my Development Blog, if you want to check my previous post click here: [DevBlog #1 Autogenerated descriptions using OpenAI](https://dev.to/chuymarin/development-blog-1-4kg9)

## Introduction
In the previous post I used Python, AWS Lambda and OpenAI API to generate descriptions for TTRPG Terrains Table and store those descriptions in a DynamoDB Table, now we are going to use dalle-2 to generate images using the description as a prompt and store the iamges in AWS S3.

## Adding generate image function
For this function we are going to use `Image.create` function from openai, by default it will return a url that shows the image, but for this case we are going to retrieve the image in `base64` so that way we can store it directly in AWS S3.

```
import os
import random
import boto3
import openai

terrains_table = [#code]

time_table = [#code]

def roll_dice(number):
    # code

def get_terrain_by_time(terrain_id, time_id):
    # code

def generate_terrain_description(terrain, time):
    # code

def generate_terrain_image(description):
    openai.api_key = os.getenv("OPENAI_API_KEY")

    response = openai.Image.create(
        prompt=description,
        n=1,
        size="512x512",
        response_format="b64_json",
    )
    
    return response["data"][0]["b64_json"]

def create_terrain(terrain_id, time_id, name, time, description):
    # code

def lambda_handler(event, context):
    terrain_id = roll_dice(6)
    time_id = roll_dice(2)
    terrain = terrains_table[terrain_id -1]
    time = time_table[time_id -1]
    item = get_terrain_by_time(terrain_id, time_id)

    if not item:
        description = generate_terrain_description(terrain, time)
        imageBase64 = generate_terrain_image(description)
        item = create_terrain(terrain_id, time_id, terrain, time, description)

    return item
```
At this point we just have the `base64` value of the image stored in the `imageBase64` variable, now we are going to save the image in AWS S3.

## Creating the S3 Bucket and Environment variable
For this we just need to create a simple S3 bucket, no need to specify any properties or permissions as this is just for uploading the images.

Then create a new environment variable named `BUCKET` and assign the name of your S3 Bucket, as it will be used in the next function.

Also, don't forget to add S3 permissions to your Lambda execution IAM Role, so that way your function will have the required permissions for uploading images.

## Adding upload image to s3 function
For this function we are going to specofy the value of the Object Body and assign the value from `imageBase64` variable.

```
import os
import random
import boto3
import openai
import base64

terrains_table = [#code]

time_table = [#code]

def roll_dice(number):
    # code

def get_terrain_by_time(terrain_id, time_id):
    # code

def generate_terrain_description(terrain, time):
    # code

def generate_terrain_image(description):
    # code

def upload_base64_to_s3(bucket, key, base_64_str):
    s3 = boto3.resource('s3')
    s3.Object(bucket, key).put(Body=base64.b64decode(base_64_str))
    return f'https://{bucket}.s3.amazonaws.com/{key}'

def create_terrain(terrain_id, time_id, name, time, description):
    # code

def lambda_handler(event, context):
    terrain_id = roll_dice(6)
    time_id = roll_dice(2)
    terrain = terrains_table[terrain_id -1]
    time = time_table[time_id -1]
    item = get_terrain_by_time(terrain_id, time_id)

    if not item:
        description = generate_terrain_description(terrain, time)
        imageBase64 = generate_terrain_image(description)
        item = create_terrain(terrain_id, time_id, terrain, time, description)
        bucket = os.getenv("BUCKET")
        key = f"terrains/{terrain}-{time}.png"
        image_url = upload_base64_to_s3(bucket, key, imageBase64)
        item = create_terrain(terrain_id, time_id, terrain, time, description, image_url)

    return item
```
Note that we've added a the `base64` import and a new property in the `create_terrain` function, so we need to update that function as well.

## Updating create terrain function


```
import os
import random
import boto3
import openai
import base64

terrains_table = [#code]

time_table = [#code]

def roll_dice(number):
    # code

def get_terrain_by_time(terrain_id, time_id):
    # code

def generate_terrain_description(terrain, time):
    # code

def generate_terrain_image(description):
    # code

def upload_base64_to_s3(bucket, key, base_64_str):
    # code

def create_terrain(terrain_id, time_id, name, time, description, image_url):
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('Terrains')

    item = {
        'terrain_id': terrain_id,
        'time_id': time_id,
        'terrain': name,
        'time': time,
        'description': description,
        'image': image_url
    }

    response = table.put_item(Item=item)
    if response['ResponseMetadata']['HTTPStatusCode'] == 200:
        return item
    else:
        return None

def lambda_handler(event, context):
    terrain_id = roll_dice(6)
    time_id = roll_dice(2)
    terrain = terrains_table[terrain_id -1]
    time = time_table[time_id -1]
    item = get_terrain_by_time(terrain_id, time_id)

    if not item:
        description = generate_terrain_description(terrain, time)
        imageBase64 = generate_terrain_image(description)
        item = create_terrain(terrain_id, time_id, terrain, time, description)
        bucket = os.getenv("BUCKET")
        key = f"terrains/{terrain}-{time}.png"
        image_url = upload_base64_to_s3(bucket, key, imageBase64)
        item = create_terrain(terrain_id, time_id, terrain, time, description, image_url)

    return item
```
Here we just need to add the `image_url` property in the function definition and add a new `image_url` value in the `item` object.

## Final result
If everything went well each time you test your Lambda function a new record will be stored in DynamoDB and store the image in S3, here is an example:

```
{
 "terrain_id": 3,
 "time_id": 2,
 "description": "The night sky is illuminated by the stars and the moon, casting a pale light over the mountain range. The jagged peaks of the mountains are silhouetted against the night sky, creating a majestic and awe-inspiring sight. The air is crisp and cool, and the silence of the night is broken only by the occasional sound of a distant animal.",
 "image": "https://{your-s3-bucket}.s3.amazonaws.com/terrains/Mountains-night.png",
 "terrain": "Mountains",
 "time": "night"
}
```
The image in this post was generated using the above criteria.

That's all for this post, in the next one probably I'll do some refactor as the code can get bigger, and it is better to have it in separate files.

Stay tuned!

Cover generated using OpenAI Dalle-2