이미지와 비전
이미지와 비전 (Images and vision)
최근 언어 모델은 이미지 입력을 받아 분석할 수 있어요. 이 능력을 비전(vision) 이라고 부르죠. GPT Image 모델은 텍스트와 이미지 입력을 모두 사용해 새로운 이미지를 만들거나 기존 이미지를 편집할 수도 있어요. 이 문서에서는 이미지를 생성·편집하는 방법과, 모델의 비전 능력으로 이미지를 분석하는 방법을 함께 살펴볼게요.
출처: 공식문서
개요 (Overview)
OpenAI API로 이미지 관련 작업을 할 때는 두 갈래로 나눌 수 있어요.
이미지를 분석할지, 생성할지에 따라 엔드포인트를 고르면 됩니다.
| API | 지원되는 사용 사례 |
|---|---|
| Responses API | 이미지 분석, 또는 이미지 생성 도구로 이미지 생성·편집 |
| Images API | 이미지를 출력으로 생성(선택적으로 이미지를 입력으로 사용) |
| Chat Completions API | 이미지 분석과 텍스트 응답 생성 |
모델이 지원하는 입출력 모달리티에 대한 자세한 내용은 모델 페이지를 참고하세요.
이미지 생성 또는 편집
Images API에서는 gpt-image-2.5-sunburst를 골라 텍스트로 이미지를 생성하거나 기존 이미지를 편집할 수 있어요. Responses API에서는 이미지 생성 도구를 지원하는 메인라인 모델을 고르면 되고, GPT Image 모델 선택은 도구가 알아서 처리해요.
Responses로 이미지 생성하기
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.responses.create({
model: "gpt-6-astra",
input:
"Generate an image of gray tabby cat hugging an otter with an orange scarf",
tools: [{ type: "image_generation" }],
});
// Save the image to a file
const imageData = response.output
.filter((output) => output.type === "image_generation_call")
.map((output) => output.result);
if (imageData.length > 0) {
const imageBase64 = imageData[0];
const fs = await import("fs");
fs.writeFileSync("cat_and_otter.png", Buffer.from(imageBase64, "base64"));
}
from openai import OpenAI
import base64
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input="Generate an image of gray tabby cat hugging an otter with an orange scarf",
tools=[{"type": "image_generation"}],
)
# Save the image to a file
image_data = [
output.result
for output in response.output
if output.type == "image_generation_call"
]
if image_data:
image_base64 = image_data[0]
with open("cat_and_otter.png", "wb") as f:
f.write(base64.b64decode(image_base64))
package main
import (
"context"
"encoding/base64"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{
OfString: openai.String("Generate an image of a gray tabby cat hugging an otter with an orange scarf."),
},
Tools: []responses.ToolUnionParam{{
OfImageGeneration: &responses.ToolImageGenerationParam{},
}},
})
if err != nil {
panic(err)
}
for _, output := range response.Output {
if output.Type != "image_generation_call" {
continue
}
image, err := base64.StdEncoding.DecodeString(output.AsImageGenerationCall().Result)
if err != nil {
panic(err)
}
if err := os.WriteFile("cat_and_otter.png", image, 0o600); err != nil {
panic(err)
}
return
}
panic("response did not include an image generation call")
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.Tool;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Generate an image of a gray tabby cat hugging an otter with an orange scarf.")
.addTool(Tool.ImageGeneration.builder().build())
.build();
String imageResult =
client.responses().create(params).output().stream()
.flatMap(item -> item.imageGenerationCall().stream())
.flatMap(call -> call.result().stream())
.findFirst()
.orElseThrow(() -> new IllegalStateException("No generated image returned"));
Files.write(Path.of("cat_and_otter.png"), Base64.getDecoder().decode(imageResult));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
CreateResponseOptions options = new()
{
Model = "gpt-6-astra",
};
options.InputItems.Add(
ResponseItem.CreateUserMessageItem(
"Generate an image of a gray tabby cat hugging an otter with an orange scarf."
)
);
options.Tools.Add(
ResponseTool.CreateImageGenerationTool(model: "gpt-image-2")
);
ResponseResult response = await client.CreateResponseAsync(options);
ImageGenerationCallResponseItem image = response
.OutputItems.OfType<ImageGenerationCallResponseItem>()
.FirstOrDefault()
?? throw new InvalidOperationException("No generated image was returned.");
await File.WriteAllBytesAsync(
"cat_and_otter.png",
image.ImageResultBytes.ToArray()
);
require "base64"
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Generate an image of a gray tabby cat hugging an otter with an orange scarf.",
tools: [{ type: :image_generation }]
)
image_call = response.output.find do |item|
item.is_a?(OpenAI::Models::Responses::ResponseOutputItem::ImageGenerationCall)
end
unless image_call.is_a?(OpenAI::Models::Responses::ResponseOutputItem::ImageGenerationCall)
raise "No image generation call returned"
end
File.binwrite(
"cat_and_otter.png",
Base64.strict_decode64(image_call.result)
)
openai responses create \
--model gpt-6-astra \
--raw-output \
--transform 'output.#(type=="image_generation_call").result' <<'YAML' | base64 --decode > cat_and_otter.png
tools:
- type: image_generation
input: Generate an image of a gray tabby cat hugging an otter with an orange scarf.
YAML
이미지 생성에 대해 더 알아보려면 Image generation 가이드를 참고하세요.
이미지 생성을 위한 세계 지식 활용
GPT Image 모델은 참조 이미지 없이도 세계 지식을 활용할 수 있어요. 예를 들어 반귀석 캐비닛을 요청하면 자수정, 로즈쿼츠, 옥 같은 알아볼 수 있는 보석들이 담긴 장면을 만들어낼 수 있어요.
이미지 분석
비전 가능 모델을 사용하면 이미지를 설명하고, 보이는 텍스트를 읽으며, 사물·모양·색·질감에 대한 질문에 답할 수 있어요. 모델의 답변을 쓸 때는 한계를 반드시 고려해야 합니다.
모델에 이미지를 입력으로 주기
이미지를 분석에 넣는 방법은 여러 가지예요.
- 이미지 파일의 완전한 URL을 제공
- Base64 인코딩 데이터 URL로 이미지를 제공
- Files API로 만든 파일 ID를 제공
한 요청에서 content 배열에 이미지 여러 개를 넣어 여러 이미지를 입력으로 줄 수도 있어요. 다만 이미지도 토큰으로 계산되어 그만큼 과금된다는 점을 기억하세요.
URL 전달하기 — 이미지의 내용 분석
import OpenAI from "openai";
const openai = new OpenAI();
const response = await openai.responses.create({
model: "gpt-6-astra",
input: [
{
role: "user",
content: [
{ type: "input_text", text: "what's in this image?" },
{
type: "input_image",
image_url:
"https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",
detail: "auto",
},
],
},
],
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "what's in this image?"},
{
"type": "input_image",
"image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",
},
],
}
],
)
print(response.output_text)
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{
OfInputItemList: responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage(
responses.ResponseInputMessageContentListParam{
responses.ResponseInputContentParamOfInputText("What's in this image?"),
{OfInputImage: &responses.ResponseInputImageParam{
Detail: responses.ResponseInputImageDetailAuto,
ImageURL: openai.String("https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"),
}},
},
responses.EasyInputMessageRoleUser,
),
},
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputImage;
import com.openai.models.responses.ResponseInputItem;
import java.util.List;
ResponseInputItem imageInput =
ResponseInputItem.ofMessage(
ResponseInputItem.Message.builder()
.role(ResponseInputItem.Message.Role.USER)
.addInputTextContent("What's in this image?")
.addContent(
ResponseInputImage.builder()
.detail(ResponseInputImage.Detail.AUTO)
.imageUrl(
"https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg")
.build())
.build());
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(List.of(imageInput))
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
Uri imageUrl = new(
"https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"
);
ResponseResult response = await client.CreateResponseAsync(
"gpt-6-astra",
[
ResponseItem.CreateUserMessageItem(
[
ResponseContentPart.CreateInputTextPart("What is in this image?"),
ResponseContentPart.CreateInputImagePart(imageUrl),
]
),
]
);
Console.WriteLine(response.GetOutputText());
require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: [
{
role: :user,
content: [
{
type: :input_text,
text: "What's in this image?"
},
{
type: :input_image,
detail: :auto,
image_url: "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"
}
]
}
]
)
puts(response.output_text)
curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "gpt-6-astra",
"input": [
{
"role": "user",
"content": [
{"type": "input_text", "text": "what is in this image?"},
{
"type": "input_image",
"image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg"
}
]
}
]
}'
openai responses create \
--model gpt-6-astra \
--raw-output \
--transform 'output.#(type=="message").content.0.text' <<'YAML'
input:
- role: user
content:
- type: input_text
text: What is in this image?
- type: input_image
image_url: https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg
YAML
Base64 인코딩 이미지 전달하기 — 이미지의 내용 분석
import fs from "fs";
import OpenAI from "openai";
const openai = new OpenAI();
const imagePath = "fixtures/example.jpg";
const base64Image = fs.readFileSync(imagePath, "base64");
const response = await openai.responses.create({
model: "gpt-6-astra",
input: [
{
role: "user",
content: [
{ type: "input_text", text: "what's in this image?" },
{
type: "input_image",
image_url: `data:image/jpeg;base64,${base64Image}`,
detail: "auto",
},
],
},
],
});
console.log(response.output_text);
import base64
from openai import OpenAI
client = OpenAI()
# Function to encode the image
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
# Path to your image
image_path = "path_to_your_image.jpg"
# Getting the Base64 string
base64_image = encode_image(image_path)
response = client.responses.create(
model="gpt-6-astra",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "what's in this image?"},
{
"type": "input_image",
"image_url": f"data:image/jpeg;base64,{base64_image}",
},
],
}
],
)
print(response.output_text)
package main
import (
"context"
"encoding/base64"
"fmt"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
image, err := os.ReadFile("image.png")
if err != nil {
panic(err)
}
imageURL := "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{
OfInputItemList: responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage(
responses.ResponseInputMessageContentListParam{
responses.ResponseInputContentParamOfInputText("What's in this image?"),
{OfInputImage: &responses.ResponseInputImageParam{
Detail: responses.ResponseInputImageDetailAuto,
ImageURL: openai.String(imageURL),
}},
},
responses.EasyInputMessageRoleUser,
),
},
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputImage;
import com.openai.models.responses.ResponseInputItem;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
String imageBase64 =
Base64.getEncoder()
.encodeToString(
Files.readAllBytes(Path.of(System.getenv("OPENAI_EXAMPLE_IMAGE_PATH"))));
ResponseInputItem imageInput =
ResponseInputItem.ofMessage(
ResponseInputItem.Message.builder()
.role(ResponseInputItem.Message.Role.USER)
.addInputTextContent("What's in this image?")
.addContent(
ResponseInputImage.builder()
.detail(ResponseInputImage.Detail.AUTO)
.imageUrl("data:image/png;base64," + imageBase64)
.build())
.build());
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(List.of(imageInput))
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
Uri imageUrl = new(
"https://openai-documentation.vercel.app/images/cat_and_otter.png"
);
using HttpClient http = new();
// Download an image as a stream.
using Stream stream = await http.GetStreamAsync(imageUrl);
BinaryData imageData = BinaryData.FromStream(stream, "image/png");
ResponseResult response1 = await client.CreateResponseAsync(
"gpt-6-astra",
[
ResponseItem.CreateUserMessageItem(
[
ResponseContentPart.CreateInputTextPart("What is in this image?"),
ResponseContentPart.CreateInputImagePart(imageData),
]
),
]
);
Console.WriteLine($"From image stream: {response1.GetOutputText()}");
// Download an image as a byte array.
byte[] bytes = await http.GetByteArrayAsync(imageUrl);
imageData = BinaryData.FromBytes(bytes, "image/png");
ResponseResult response2 = await client.CreateResponseAsync(
"gpt-6-astra",
[
ResponseItem.CreateUserMessageItem(
[
ResponseContentPart.CreateInputTextPart("What is in this image?"),
ResponseContentPart.CreateInputImagePart(imageData),
]
),
]
);
Console.WriteLine($"From byte array: {response2.GetOutputText()}");
require "base64"
require "openai"
client = OpenAI::Client.new
image = Base64.strict_encode64(File.binread("image.png"))
response = client.responses.create(
model: "gpt-6-astra",
input: [
{
role: :user,
content: [
{
type: :input_text,
text: "What's in this image?"
},
{
type: :input_image,
detail: :auto,
image_url: "data:image/png;base64,#{image}"
}
]
}
]
)
puts(response.output_text)
파일 ID 전달하기 — 이미지의 내용 분석
import OpenAI from "openai";
import fs from "fs";
const openai = new OpenAI();
// Function to create a file with the Files API
async function createFile(filePath) {
const fileContent = fs.createReadStream(filePath);
const result = await openai.files.create({
file: fileContent,
purpose: "vision",
});
return result.id;
}
// Getting the file ID
const fileId = await createFile("fixtures/example.jpg");
const response = await openai.responses.create({
model: "gpt-6-astra",
input: [
{
role: "user",
content: [
{ type: "input_text", text: "what's in this image?" },
{
type: "input_image",
file_id: fileId,
detail: "auto",
},
],
},
],
});
console.log(response.output_text);
from openai import OpenAI
client = OpenAI()
# Function to create a file with the Files API
def create_file(file_path):
with open(file_path, "rb") as file_content:
result = client.files.create(
file=file_content,
purpose="vision",
)
return result.id
# Getting the file ID
file_id = create_file("path_to_your_image.jpg")
response = client.responses.create(
model="gpt-6-astra",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "what's in this image?"},
{
"type": "input_image",
"file_id": file_id,
},
],
}
],
)
print(response.output_text)
package main
import (
"context"
"fmt"
"os"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
file, err := os.Open("image.png")
if err != nil {
panic(err)
}
defer file.Close()
uploaded, err := client.Files.New(context.Background(), openai.FileNewParams{
File: file,
Purpose: openai.FilePurposeVision,
})
if err != nil {
panic(err)
}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{
OfInputItemList: responses.ResponseInputParam{
responses.ResponseInputItemParamOfMessage(
responses.ResponseInputMessageContentListParam{
responses.ResponseInputContentParamOfInputText("What's in this image?"),
{OfInputImage: &responses.ResponseInputImageParam{
Detail: responses.ResponseInputImageDetailAuto,
FileID: openai.String(uploaded.ID),
}},
},
responses.EasyInputMessageRoleUser,
),
},
},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.files.FileCreateParams;
import com.openai.models.files.FilePurpose;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseInputImage;
import com.openai.models.responses.ResponseInputItem;
import java.nio.file.Path;
import java.util.List;
var file =
client
.files()
.create(
FileCreateParams.builder()
.file(Path.of(System.getenv("OPENAI_EXAMPLE_FILE_PATH")))
.purpose(FilePurpose.VISION)
.build());
var response =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.inputOfResponse(
List.of(
ResponseInputItem.ofMessage(
ResponseInputItem.Message.builder()
.role(ResponseInputItem.Message.Role.USER)
.addInputTextContent("What's in this image?")
.addContent(
ResponseInputImage.builder()
.detail(ResponseInputImage.Detail.AUTO)
.fileId(file.id())
.build())
.build())))
.build());
response.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
using OpenAI.Files;
using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
string filename = "cat_and_otter.png";
Uri imageUrl = new(
$"https://openai-documentation.vercel.app/images/{filename}"
);
using HttpClient http = new();
// Download an image as a stream.
using Stream stream = await http.GetStreamAsync(imageUrl);
OpenAIFileClient files = new(key);
OpenAIFile file = await files.UploadFileAsync(
stream,
filename,
FileUploadPurpose.Vision
);
ResponseResult response = await client.CreateResponseAsync(
"gpt-6-astra",
[
ResponseItem.CreateUserMessageItem(
[
ResponseContentPart.CreateInputTextPart("what's in this image?"),
ResponseContentPart.CreateInputImagePart(file.Id),
]
),
]
);
Console.WriteLine(response.GetOutputText());
require "openai"
require "pathname"
client = OpenAI::Client.new
uploaded = client.files.create(
file: Pathname("image.png"),
purpose: :vision
)
response = client.responses.create(
model: "gpt-6-astra",
input: [
{
role: :user,
content: [
{
type: :input_text,
text: "What's in this image?"
},
{
type: :input_image,
detail: :auto,
file_id: uploaded.id
}
]
}
]
)
puts(response.output_text)
이미지 입력 요구사항
모델이 분석할 수 있을 만큼 선명한 지원 이미지 파일을 사용해야 해요.
| 요구사항 | 지원되는 입력 |
|---|---|
| 파일 형식 | PNG (.png), JPEG (.jpeg 또는 .jpg), WEBP (.webp), 논애니메이션 GIF (.gif) |
| 요청 크기 | 요청당 최대 512 MB 총 페이로드 |
| 이미지 수 | 요청당 최대 1,500개 이미지 |
패치 기반 이미지 입력의 경우, 선택한 모델과 detail 수준에 대한 리사이징 규칙을 적용한 후 이미지당 최대 30,000개 패치를 지원해요. 이 제한은 지원되는 detail 수준에 걸쳐, 이미지마다 개별로 적용돼요. 요청의 전체 패치 수 합계에 적용되는 게 아닙니다.
모델·detail별 더 작은 리사이징 예산은 여전히 적용돼요. 처리 후 30,000개 패치 제한을 초과하는 이미지는 자동으로 리사이즈되지 않고 거부되어요. 이미지 크기를 줄이고 다시 시도해야 합니다.
이미지 토큰과 나머지 프롬프트도 모델의 입력·컨텍스트 제한에 맞아야 해요. 토큰 추정치가 모든 입력 제한을 만족한다는 보장은 없어요. 이미지 사용은 이용 정책을 따라야 합니다.
이미지 detail 수준 선택
detail 파라미터는 이미지 전처리를 제어해요. 지원되는 값은 모델에 따라 low, high, original, auto 중 하나예요. 이 파라미터를 생략하면 Responses API와 Chat Completions API 모두 기본값 auto로 동작해요. 모델별 대응 동작은 모델 리사이징 표에서 확인할 수 있어요.
{
"type": "input_image",
"image_url": "https://api.nga.gov/iiif/a2e6da57-3cd1-4235-b20e-95dcaefed6c8/full/!800,800/0/default.jpg",
"detail": "original"
}
detail 수준을 고르는 기준은 다음과 같아요.
| Detail 수준 | 가장 적합한 경우 |
|---|---|
low |
대략적인 이미지 이해. 리사이즈와 토큰 사용은 모델에 따라 달라지며, low가 항상 high보다 토큰을 적게 쓰는 건 아니에요. |
high |
정밀한 원본 이미지 좌표가 필요하지 않을 때의 표준 고충실도 이미지 이해. |
original |
모델이 지원할 때, 크고 밀도가 높거나 공간에 민감한, 또는 컴퓨터 사용(computer-use) 이미지. |
auto |
모델 크기 표에 나온 모델의 기본 리사이징 동작 사용. |
OCR, 작은 객체 탐지, 컴퓨터 사용처럼 미세한 시각적 디테일이나 정밀한 좌표가 필요한 작업이라면, 지원할 때 "detail": "original"을 사용해요. original detail도 모델의 픽셀 치수 제한이나 리사이징 패치 예산에 맞추기 위해 이미지를 리사이즈할 수는 있지만, 별도의 30,000패치 거부 제한을 맞추기 위해 리사이즈하진 않아요. 좌표에 민감한 작업이라면 보내기 전에 이미지를 그 제한에 맞게 리사이즈하고, 반환된 좌표를 원본 이미지에 다시 매핑하는 것이 좋아요. 좌표 처리에 대한 자세한 내용은 컴퓨터 사용 가이드를 참고하세요.
모델 리사이징 동작 (Model sizing behavior)
아래 표는 범용 비전 모델의 리사이징 동작을 요약해요. 다른 모델이나 특수 변형은 다른 제한을 쓸 수 있어요. 모든 리사이징은 작은 이미지를 키우지 않고 비율을 유지해요.
| 모델 계열 | 지원되는 detail 수준 | 패치·리사이징 동작 |
|---|---|---|
gpt-6-astra |
low, high, original, auto |
low는 512 × 512 픽셀 안에 맞춰요. high는 최대 2,500 패치와 65,535 픽셀 최대 치수를 허용해요. 두 제한이 모두 적용돼요. original은 이미지 치수를 보존하되, 한쪽이 65,535 픽셀보다 큰 이미지는 그 제한에 맞게 축소돼요. 결과 이미지가 30,000 패치를 초과하면 API가 거부하고, 이미지를 패치 제한에 맞게 리사이즈하지 않아요. auto는 original과 동일한 리사이징 동작을 써요. |
gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna |
low, high, original, auto |
low는 512 × 512 픽셀 안에 맞춰요. high는 2048 × 2048 픽셀과 2,500 패치 안에 맞춰요. original은 이미지 치수를 보존하되, 한쪽이 65,535 픽셀보다 큰 이미지는 그 제한에 맞게 축소돼요. 결과 이미지가 30,000 패치를 초과하면 API가 거부하고, 리사이즈하지 않아요. auto는 original과 동일한 동작이에요. |
gpt-5.5 |
low, high, original, auto |
low는 512 × 512 픽셀 안에 맞춰요. high는 최대 2,500 패치와 2048 픽셀 최대 치수를 허용해요. original은 최대 10,000 패치와 6000 픽셀 최대 치수를 허용해요. 두 제한이 모두 적용돼요. auto는 original과 동일한 동작이에요. |
gpt-5.4, gpt-5.4-mini, gpt-5.4-nano |
low, high, original, auto |
low는 2048 픽셀 최대 치수와 6,144 패치 예산을 써서, high보다 더 많은 토큰을 쓸 수 있어요. high는 최대 2,500 패치와 2048 픽셀 최대 치수를 허용해요. original은 최대 10,000 패치와 6000 픽셀 최대 치수를 허용해요. 두 제한이 모두 적용돼요. auto는 high와 동일한 동작이에요. |
gpt-5.2, gpt-4.1-mini |
low, high, auto |
이 detail 수준들은 같은 리사이징 제한을 써요: 2048 픽셀 최대 치수와 6,144 패치 예산. original은 지원되지 않아요. |
gpt-5.1, gpt-4.1, gpt-4o, gpt-4o-mini |
low, high, auto |
low는 고정 토큰 수를 써요. high와 auto는 타일 기반 리사이징 규칙을 사용해요. |
비용 계산
비전 모델은 이미지 입력을 과금 가능한 입력 토큰으로 변환해요. 이 섹션의 이미지 입력 비용 계산기와 패치/타일 규칙은 비전 모델 입력을 다루며, GPT Image 생성·편집은 해당하지 않아요. 그 별도 요금제는 GPT Image 모델 입력을 참고하세요.
이미지 토큰은 분당 토큰(TPM) 한도에도 포함돼요. 계산기는 표준 입력 요금으로 이미지 한 장을 추정하며, 나머지 프롬프트나 모델 출력은 포함하지 않아요.
이미지 입력 비용 계산기
이미지 입력 비용 계산기로 모델, 이미지 크기, detail 수준별로 이미지 한 장의 입력 토큰과 비용을 추정해 보세요.
패치 기반 이미지 토큰화
일부 모델은 이미지를 32px × 32px 패치로 덮어 토큰화해요. 많은 모델·detail 수준 조합이 리사이징 패치 예산을 정의해요. 먼저 API는 선택한 detail 수준의 픽셀 치수 제한 안에 이미지를 맞추되, 비율을 보존하고 정수 픽셀로 반올림하며 작은 이미지는 키우지 않아요. 그런 다음 토큰 비용은 다음과 같이 결정돼요.
A. 픽셀 치수 제한을 적용한 후 이미지를 덮는 데 필요한 32px × 32px 패치 수를 계산해요. 패치는 이미지 경계를 벗어나도 돼요.
patch_count = ceil(width/32)×ceil(height/32)
B. 선택한 모델과 detail 수준이 리사이징 패치 예산을 지정하면, 이미지가 그 예산을 초과할 때 비례적으로 축소해요. 그렇지 않으면 이 단계를 건너뛰어요. 정수 픽셀 치수로 변환하고 패치 커버리지를 계산한 후에도 예산 안에 머물도록 배율을 조정해요. 최종 치수를 계산하기 전까지는 전체 정밀도를 유지해요.
shrink_factor = sqrt((32^2 * patch_budget) / (width * height))
adjusted_shrink_factor = shrink_factor * min(
floor(width * shrink_factor / 32) / (width * shrink_factor / 32),
floor(height * shrink_factor / 32) / (height * shrink_factor / 32)
)
C. B 단계에서 이미지를 리사이즈했다면, 최종 배율 너비·높이를 정수 픽셀로 내림해요. 결과 이미지를 덮는 데 필요한 패치를 계산해요. 이 값이 모델 승수 적용 전의 이미지 토큰 수예요. 패치 예산이 적용될 때 이 수는 그 예산 안에 머물러요.
resized_patch_count = ceil(resized_width/32)×ceil(resized_height/32)
이 수가 30,000 패치를 초과하면 API가 요청을 거부해요. 토큰 승수를 적용하기 전에 이 제한을 확인하세요.
D. 패치 수에 모델의 승수를 곱하고 올림해서 과금 가능한 이미지 입력 토큰을 얻어요. 모델의 입력 가격은 그 토큰에 한 번만 적용돼요. 승수는 다른 프롬프트 토큰이나 가격에 다시 적용되지 않아요.
| 모델 | 승수 |
|---|---|
gpt-6-astra |
1.2 |
gpt-5.6-sol |
1.2 |
gpt-5.6-terra |
1.2 |
gpt-5.6-luna |
1.2 |
gpt-5.5 |
1.2 |
gpt-5.4 |
1.2 |
gpt-5.4-mini |
1.2 |
gpt-5.4-nano |
1.2 |
gpt-5.2 |
1.2 |
gpt-5-mini* |
1.2 |
gpt-5-nano* |
1.5 |
gpt-4.1-mini |
1.62 |
gpt-4.1-nano* (2025-04-14 snapshot) |
2.46 |
o4-mini* |
1.72 |
gpt-4.1-mini의 경우 2025-04-14 스냅샷에 적용돼요.
* 폐기되어 종료 예정이에요. 날짜와 대체 모델은 deprecation 스케줄을 참고하세요. 이 모델들은 계산기나 위 모델 리사이징 표에 포함되지 않아요.
gpt-6-astra를 detail: high로 쓸 때의 이미지 토큰 계산 예시
이 조합은 65,535 픽셀 최대 치수, 2,500 패치 예산, 1.2× 승수를 사용해요.
- 1024 × 1024 이미지는
32 × 32 = 1024패치가 필요해요. 리사이즈는 필요 없죠. 과금 가능한 이미지 입력은ceil(1024 × 1.2) = 1229토큰이에요. - 2048 × 2048 이미지는 처음에
64 × 64 = 4096패치가 필요해요. 패치 예산이 이를 1600 × 1600 픽셀, 즉50 × 50 = 2500패치로 줄여요. 추정치는ceil(2500 × 1.2) = 3000토큰이에요. - 4096 × 512 이미지는 원래 크기를 유지해요:
128 × 16 = 2048패치와ceil(2048 × 1.2) = 2458토큰이에요.
과금에서의 부동소수점 반올림 때문에 최종 수가 추정치와 한 토큰 차이날 수 있어요.
타일 기반 이미지 토큰화
이 표의 모델들은 기본 토큰 수에 이미지 타일 토큰을 더한 방식을 써요.
| 모델 | 기본 토큰 | 타일 토큰 |
|---|---|---|
gpt-5.1 |
70 | 140 |
gpt-5* |
70 | 140 |
gpt-4o, gpt-4.1 |
85 | 170 |
gpt-4o-mini |
2833 | 5667 |
o1*, o1-pro*, o3* |
75 | 150 |
* 폐기되어 종료 예정이에요. 날짜와 대체 모델은 deprecation 스케줄을 참고하세요. 이 모델들은 계산기나 위 모델 리사이징 표에 포함되지 않아요.
"detail": "low"에서는 이미지 크기와 무관하게 모델의 기본 토큰만 비용이 들어요. "detail": "high" 또는 "detail": "auto"에서는:
- 비율을 유지하며 2048px × 2048px 정사각형 안에 맞도록 축소해요. 작은 이미지는 확대되지 않아요.
- 짧은 쪽이 768px를 초과하면 768px로 축소하고 다른 쪽 치수를 내림해요.
- 이미지를 덮는 데 필요한 512px 정사각형 수를 세어요. 각 정사각형이 모델의 타일 토큰을 사용해요.
- 모델의 기본 토큰을 타일 토큰에 더해요.
GPT Image 모델 입력
GPT Image 모델은 생성과 편집에 별도의 이미지 토큰 가격을 사용해요. 비전 계산기는 이들의 입력·출력 비용을 추정하지 않아요. 현재 요금은 이미지 생성 가격을, 생성·편집 워크플로는 Image generation 가이드를 참고하세요.
GPT Image 1
gpt-image-1에는 다음 입력 토큰 규칙이 적용돼요. 타일 기반 이미지 리사이징을 사용하되, 짧은 쪽을 768px 대신 512px로 축소해요. 토큰 사용은 이미지 치수와 Images API의 input_fidelity 파라미터에 따라 달라져요.
입력 충실도가 low일 때 기본 비용은 이미지 토큰 65개이고, 각 타일은 이미지 토큰 129개예요. 입력 충실도가 high일 때는 위 이미지 토큰들에 더해 이미지 종횡비에 따라 정해진 수의 토큰을 추가해요.
- 이미지가 정사각형이면 추가 입력 이미지 토큰 4160개를 더해요.
- 세로 또는 가로에 가까우면 추가 토큰 6240개를 더해요.
이미지 입력 토큰 가격은 이미지 가격 섹션을 참고하세요.
한계 (Limitations)
비전 모델도 실수할 수 있어요. 애플리케이션을 설계할 때 이런 한계를 고려해야 합니다.
- 의료 이미지: CT 스캔 같은 전문 의료 이미지를 해석하는 데 적합하지 않으며, 의료 조언에 사용하면 안 돼요.
- 비영어: 일본어나 한국어 같은 비로마 문자 알파벳의 텍스트가 있는 이미지를 처리할 때 성능이 좋지 않을 수 있어요.
- 작은 텍스트: 이미지 안의 텍스트를 키워 가독성을 높이세요. 가능하면
"detail": "original"을 쓰는 것도 성능에 도움이 돼요. - 회전: 회전되거나 뒤집힌 텍스트와 이미지를 잘못 해석할 수 있어요.
- 시각적 요소: 실선, 점선, 파선처럼 색이나 스타일이 다양한 그래프나 텍스트를 이해하는 데 어려움을 겪을 수 있어요.
- 공간 추론: 체스 위치 식별처럼 정밀한 공간 위치 파악이 필요한 작업에 어려움을 겪어요.
- 정확도: 특정 시나리오에서 부정확한 설명이나 캡션을 생성할 수 있어요.
- 이미지 모양: 파노라마나 어안 렌즈 이미지에 어려움을 겪어요.
- 메타데이터와 리사이징: 모델은 원본 파일 이름이나 메타데이터를 처리하지 않아요. 분석 전에 이미지가 리사이즈될 수 있으며
originaldetail도 마찬가지예요. 모델별 제한은 모델 리사이징 동작을 참고하세요. - 세기: 이미지 안의 객체 수를 근사치로 줄 수 있어요.
- CAPTCHA: 안전상의 이유로 CAPTCHA 제출은 차단돼요.
더 알아보기 (Learn more)
- Image generation 가이드 — GPT Image 모델로 이미지를 생성하고 편집하는 방법을 자세히 살펴보세요.