diff --git a/generate-images-openai/README.md b/generate-images-openai/README.md new file mode 100644 index 0000000000..13189b7396 --- /dev/null +++ b/generate-images-openai/README.md @@ -0,0 +1,44 @@ +# How to Generate Images With the OpenAI API + +Learn to use the OpenAI Python library to create images with the GPT Image models. In the associated tutorial on [generating images with the OpenAI API](https://realpython.com/generate-images-openai/), you'll create images from text prompts, tune the size and quality of your results, and edit an image with a follow-up prompt. You'll learn how to interact with the Images API and incorporate this functionality into your Python scripts. + +## Setup + +Create and activate a virtual environment, then install the `openai` package: + +```console +$ python --version +Python 3.14.6 +$ python -m venv venv +$ source venv/bin/activate +(venv) $ python -m pip install openai +``` + +You need to be on Python 3.10 or higher. + +The scripts read your API key from the `OPENAI_API_KEY` environment variable: + +```console +(venv) $ export OPENAI_API_KEY="" +``` + +## Create and Edit Images + +Follow the instructions in [the tutorial](https://realpython.com/generate-images-openai/) to create images from text prompts, edit an image with a follow-up prompt, and convert a Base64 JSON response to a PNG image file. + +You can find the code for each of these steps in dedicated scripts: + +- `create_png.py`: Create an image from a text prompt and write it straight to a PNG file. +- `create.py`: Create an image from a text prompt and save the whole JSON response to a file. +- `convert.py`: Convert a Base64-encoded PNG image delivered in a JSON response to a PNG image file. +- `edit.py`: Read Base64-encoded image data and make an API request to edit that image with a prompt. + +In the tutorial, you'll walk through each of these scripts and their functionality and output in more detail. + +Note that `convert.py` and `edit.py` read a saved response from the `responses/` directory. Run `create.py` first, then update the `JSON_FILE` and `SOURCE_FILE` constants to match the filename that it wrote. + +## A Note on Models + +The GPT Image models replaced DALLĀ·E, which OpenAI [retired on May 12, 2026](https://developers.openai.com/api/docs/deprecations) along with the image variations endpoint. These scripts call `gpt-image-2`. + +OpenAI retires image models on a regular schedule, so if a script starts failing with a message like `The model 'gpt-image-2' does not exist`, then check the deprecations page and swap the model name. diff --git a/generate-images-openai/convert.py b/generate-images-openai/convert.py new file mode 100644 index 0000000000..2d9a24a125 --- /dev/null +++ b/generate-images-openai/convert.py @@ -0,0 +1,18 @@ +import json +from base64 import b64decode +from pathlib import Path + +DATA_DIR = Path.cwd() / "responses" +JSON_FILE = DATA_DIR / "An ec-1786009967.json" +IMAGE_DIR = Path.cwd() / "images" / JSON_FILE.stem + +IMAGE_DIR.mkdir(parents=True, exist_ok=True) + +with open(JSON_FILE, mode="r", encoding="utf-8") as file: + response = json.load(file) + +for index, image_dict in enumerate(response["data"]): + image_data = b64decode(image_dict["b64_json"]) + image_file = IMAGE_DIR / f"{JSON_FILE.stem}-{index}.png" + with open(image_file, mode="wb") as png: + png.write(image_data) diff --git a/generate-images-openai/create.py b/generate-images-openai/create.py new file mode 100644 index 0000000000..2d5386ece2 --- /dev/null +++ b/generate-images-openai/create.py @@ -0,0 +1,24 @@ +import json +from pathlib import Path + +from openai import OpenAI + +client = OpenAI() + +PROMPT = "An eco-friendly computer from the 90s in the style of vaporwave" +DATA_DIR = Path.cwd() / "responses" + +DATA_DIR.mkdir(exist_ok=True) + +response = client.images.generate( + model="gpt-image-2", + prompt=PROMPT, + n=1, + size="1024x1024", + quality="low", +) + +file_name = DATA_DIR / f"{PROMPT[:5]}-{response.created}.json" + +with open(file_name, mode="w", encoding="utf-8") as file: + json.dump(response.to_dict(), file) diff --git a/generate-images-openai/create_png.py b/generate-images-openai/create_png.py new file mode 100644 index 0000000000..0f067736c6 --- /dev/null +++ b/generate-images-openai/create_png.py @@ -0,0 +1,18 @@ +from base64 import b64decode + +from openai import OpenAI + +client = OpenAI() + +PROMPT = "A vaporwave computer" + +response = client.images.generate( + model="gpt-image-2", + prompt=PROMPT, + n=1, + size="1024x1024", + quality="low", +) + +with open("vaporwave.png", mode="wb") as png: + png.write(b64decode(response.data[0].b64_json)) diff --git a/generate-images-openai/edit.py b/generate-images-openai/edit.py new file mode 100644 index 0000000000..f16bd5b3d3 --- /dev/null +++ b/generate-images-openai/edit.py @@ -0,0 +1,29 @@ +import json +from base64 import b64decode +from pathlib import Path + +from openai import OpenAI + +client = OpenAI() + +DATA_DIR = Path.cwd() / "responses" +SOURCE_FILE = DATA_DIR / "An ec-1786009967.json" +EDIT_PROMPT = "Add a large potted plant growing out of the computer" + +with open(SOURCE_FILE, mode="r", encoding="utf-8") as json_file: + saved_response = json.load(json_file) + image_data = b64decode(saved_response["data"][0]["b64_json"]) + +response = client.images.edit( + model="gpt-image-2", + image=("image.png", image_data), + prompt=EDIT_PROMPT, + n=3, + size="1024x1024", + quality="low", +) + +new_file_name = f"edit-{SOURCE_FILE.stem[:5]}-{response.created}.json" + +with open(DATA_DIR / new_file_name, mode="w", encoding="utf-8") as file: + json.dump(response.to_dict(), file) diff --git a/generate-images-openai/requirements.txt b/generate-images-openai/requirements.txt new file mode 100644 index 0000000000..df6c476ae2 --- /dev/null +++ b/generate-images-openai/requirements.txt @@ -0,0 +1,16 @@ +annotated-types==0.8.0 +anyio==4.14.2 +certifi==2026.7.22 +distro==1.9.0 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.18 +jiter==0.16.0 +openai==2.53.0 +pydantic==2.13.4 +pydantic-core==2.46.4 +sniffio==1.3.1 +tqdm==4.70.0 +typing-extensions==4.16.0 +typing-inspection==0.4.2