Vision
Vision (비전)
Claude의 비전(vision) 역량은 이미지를 이해하고 분석하게 해줘서, 멀티모달 상호작용의 흥미로운 가능성을 열어줘요. 이 가이드는 Claude에 이미지를 보내는 방법, 적용되는 한계와 비용, 그리고 좌표 기반 워크플로에 대한 지침을 다뤄요.
출처: 문서
본문
Claude에 이미지 보내기
Claude의 비전 역량을 다음을 통해 사용하세요:
- claude.ai. 파일처럼 이미지를 업로드하거나, 채팅 창에 이미지를 직접 끌어다 놓으세요.
- Claude 콘솔의 Playground. 어떤 사용자 메시지 블록에도 이미지를 직접 추가하세요.
- API 요청. 다음 예시를 보세요.
API에서 세 가지 소스 타입 중 하나로 image 콘텐츠 블록으로 이미지를 제공해요:
- 요청 본문에 포함된 base64 인코딩 이미지
- 온라인에 호스팅된 이미지에 대한 URL 참조
- Files API가 반환한
file_id(한 번 업로드하고 여러 번 참조)
Base64 인코딩 이미지 예시
curl -sSo ./vision-example.jpg \
https://platform.claude.com/docs/images/vision-example.jpg
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content:
- type: image
source:
type: base64
media_type: image/jpeg
data: "@./vision-example.jpg"
- type: text
text: Describe this image.
YAML
image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
image1_media_type = "image/png"
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": image1_media_type,
"data": image1_data,
},
},
{"type": "text", "text": "Describe this image."},
],
}
],
)
print(message)
const anthropic = new Anthropic();
const message = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "base64",
media_type: "image/jpeg",
data: imageData // Base64-encoded image data as string
}
},
{
type: "text",
text: "Describe this image."
}
]
}
]
});
console.log(message);
using System.Collections.Generic;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
string imageData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new MessageParamContent(new List<ContentBlockParam>
{
new ContentBlockParam(new ImageBlockParam(
new ImageBlockParamSource(new Base64ImageSource()
{
Data = imageData,
MediaType = MediaType.ImagePng,
})
)),
new ContentBlockParam(new TextBlockParam("Describe this image.")),
}),
}
]
});
Console.WriteLine(message);
client := anthropic.NewClient()
imageData := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewImageBlockBase64("image/png", imageData),
anthropic.NewTextBlock("Describe this image."),
),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
String imageData =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
List<ContentBlockParam> contentBlockParams = List.of(
ContentBlockParam.ofImage(
ImageBlockParam.builder()
.source(
Base64ImageSource.builder()
.mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
.data(imageData)
.build()
)
.build()
),
ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
);
Message message = client
.messages()
.create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(contentBlockParams)
.build()
);
IO.println(message);
$client = new Client();
$imageData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/png',
'data' => $imageData,
],
],
['type' => 'text', 'text' => 'Describe this image.'],
],
],
],
model: 'claude-opus-5-5',
);
echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
client = Anthropic::Client.new
image_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
message = client.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: image_data
}
},
{ type: "text", text: "Describe this image." }
]
}
]
)
puts message
URL 기반 이미지 예시
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content:
- type: image
source:
type: url
url: https://platform.claude.com/docs/images/vision-example.jpg
- type: text
text: Describe this image.
YAML
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://platform.claude.com/docs/images/vision-example.jpg",
},
},
{"type": "text", "text": "Describe this image."},
],
}
],
)
print(message)
const anthropic = new Anthropic();
const message = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "url",
url: "https://platform.claude.com/docs/images/vision-example.jpg"
}
},
{
type: "text",
text: "Describe this image."
}
]
}
]
});
console.log(message);
using System.Collections.Generic;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new MessageParamContent(new List<ContentBlockParam>
{
new ContentBlockParam(new ImageBlockParam(
new ImageBlockParamSource(new UrlImageSource()
{
Url = "https://platform.claude.com/docs/images/vision-example.jpg",
})
)),
new ContentBlockParam(new TextBlockParam("Describe this image.")),
}),
}
]
});
Console.WriteLine(message);
client := anthropic.NewClient()
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewImageBlock(anthropic.URLImageSourceParam{
URL: "https://platform.claude.com/docs/images/vision-example.jpg",
}),
anthropic.NewTextBlock("Describe this image."),
),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
List<ContentBlockParam> contentBlockParams = List.of(
ContentBlockParam.ofImage(
ImageBlockParam.builder()
.source(
UrlImageSource.builder()
.url("https://platform.claude.com/docs/images/vision-example.jpg")
.build()
)
.build()
),
ContentBlockParam.ofText(TextBlockParam.builder().text("Describe this image.").build())
);
Message message = client
.messages()
.create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(contentBlockParams)
.build()
);
System.out.println(message);
$client = new Client();
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'image',
'source' => [
'type' => 'url',
'url' => 'https://platform.claude.com/docs/images/vision-example.jpg',
],
],
['type' => 'text', 'text' => 'Describe this image.'],
],
],
],
model: 'claude-opus-5-5',
);
echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "url",
url: "https://platform.claude.com/docs/images/vision-example.jpg"
}
},
{ type: "text", text: "Describe this image." }
]
}
]
)
puts message
Files API 이미지 예시
반복해서 쓸 이미지거나 인코딩 오버헤드를 피하고 싶다면 Files API를 사용하세요. 이미지를 한 번 업로드하고, 이후 메시지에서는 base64 데이터를 다시 보내는 대신 반환된 file_id를 참조하세요.
Then use the returned file_id in your message
curl https://api.anthropic.com/v1/messages
-H "x-api-key: $ANTHR...KEY"
-H "anthropic-version: 2023-06-01"
-H "content-type: application/json"
-d @- <<EOF
{
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "file",
"file_id": "$FILE_ID"
}
},
{
"type": "text",
"text": "Describe this image."
}
]
}
]
}
EOF
```bash CLI
curl -sSo vision-example.jpg \
https://platform.claude.com/docs/images/vision-example.jpg
# First, upload your image to the Files API
FILE_ID=$(ant files upload \
--file ./vision-example.jpg \
--transform id --raw-output)
# Then use the returned file_id in your message
ant messages create \
--transform content --format yaml <<YAML
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content:
- type: image
source:
type: file
file_id: $FILE_ID
- type: text
text: Describe this image.
YAML
client = anthropic.Anthropic()
# Upload the image file
with open("vision-example.jpg", "rb") as f:
file_upload = client.files.upload(file=("vision-example.jpg", f, "image/jpeg"))
# Use the uploaded file in a message
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {"type": "file", "file_id": file_upload.id},
},
{"type": "text", "text": "Describe this image."},
],
}
],
)
print(message.content)
import Anthropic, { toFile } from "@anthropic-ai/sdk";
import fs from "node:fs";
const anthropic = new Anthropic();
// Upload the image file
const fileUpload = await anthropic.files.upload({
file: await toFile(fs.createReadStream("vision-example.jpg"), undefined, {
type: "image/jpeg"
})
});
// Use the uploaded file in a message
const response = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: {
type: "file",
file_id: fileUpload.id
}
},
{
type: "text",
text: "Describe this image."
}
]
}
]
});
console.log(response);
using System.Collections.Generic;
using Anthropic;
using Anthropic.Core;
using Anthropic.Models.Files;
using Anthropic.Models.Messages;
AnthropicClient client = new();
// Upload the image file
var fileUpload = await client.Files.Upload(new FileUploadParams
{
File = new BinaryContent
{
Stream = File.OpenRead("vision-example.jpg"),
FileName = "vision-example.jpg",
ContentType = new("image/jpeg"),
},
});
// Use the uploaded file in a message
var response = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new MessageParamContent(new List<ContentBlockParam>
{
new ContentBlockParam(new ImageBlockParam(
new ImageBlockParamSource(new FileImageSource(fileUpload.ID))
)),
new ContentBlockParam(new TextBlockParam("Describe this image.")),
}),
}
]
});
Console.WriteLine(response);
client := anthropic.NewClient()
// Upload the image file
file, err := os.Open("vision-example.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()
fileUpload, err := client.Files.Upload(context.Background(),
anthropic.FileUploadParams{
File: anthropic.File(file, "vision-example.jpg", "image/jpeg"),
})
if err != nil {
log.Fatal(err)
}
// Use the uploaded file in a message
message, err := client.Messages.New(context.Background(),
anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewImageBlock(anthropic.FileImageSourceParam{
FileID: fileUpload.ID,
}),
anthropic.NewTextBlock("Describe this image."),
),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(message.Content)
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Upload the image file
FileMetadata file = client.files().upload(
FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(Files.newInputStream(Path.of("vision-example.jpg")))
.filename("vision-example.jpg")
.contentType("image/jpeg")
.build()
)
.build()
);
// Use the uploaded file in a message
ImageBlockParam imageParam = ImageBlockParam.builder().fileSource(file.id()).build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(
List.of(
ContentBlockParam.ofImage(imageParam),
ContentBlockParam.ofText(
TextBlockParam.builder().text("Describe this image.").build()
)
)
)
.build();
Message message = client.messages().create(params);
System.out.println(message.content());
use Anthropic\Core\FileParam;
$client = new Client();
// Upload the image file
$fileUpload = $client->files->upload(
file: FileParam::fromResource(fopen('vision-example.jpg', 'rb'), contentType: 'image/jpeg'),
);
// Use the uploaded file in a message
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
[
'type' => 'image',
'source' => ['type' => 'file', 'fileID' => $fileUpload->id],
],
['type' => 'text', 'text' => 'Describe this image.'],
],
],
],
model: 'claude-opus-5-5',
);
echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
client = Anthropic::Client.new
# Upload the image file
file_upload = client.files.upload(
file: Anthropic::FilePart.new(
File.open("vision-example.jpg", "rb"),
content_type: "image/jpeg"
)
)
# Use the uploaded file in a message
message = client.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "image",
source: { type: "file", file_id: file_upload.id }
},
{ type: "text", text: "Describe this image." }
]
}
]
)
puts message.content
더 많은 예시 코드와 파라미터 세부 사항은 Messages API 예시를 보세요.
여러 이미지
하나의 요청에 여러 이미지를 포함할 수 있고, Claude는 그것을 함께 분석해요. 이미지 비교, 차이 질문, 문서 페이지 같은 순서 작업에 유용해요. 여러 이미지를 보낼 때는 각각을 짧은 텍스트 라벨(Image 1:, Image 2: 등)로 소개해서 프롬프트와 후속 턴에서 이름으로 참조할 수 있게 하세요.
ant messages create <<'YAML'
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content:
- type: text
text: "Image 1:"
- type: image
source:
type: base64
media_type: image/png
data: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC
- type: text
text: "Image 2:"
- type: image
source:
type: base64
media_type: image/png
data: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC
- type: text
text: How are these images different?
YAML
image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
image2_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Image 1:"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image1_data,
},
},
{"type": "text", "text": "Image 2:"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image2_data,
},
},
{"type": "text", "text": "How are these images different?"},
],
}
],
)
print(message)
const anthropic = new Anthropic();
const image1Data =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
const image2Data =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";
const message = await anthropic.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{
type: "text",
text: "Image 1:"
},
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: image1Data
}
},
{
type: "text",
text: "Image 2:"
},
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: image2Data
}
},
{
type: "text",
text: "How are these images different?"
}
]
}
]
});
console.log(message);
AnthropicClient client = new();
string image1Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
string image2Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";
var message = await client.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = new MessageParamContent(new List<ContentBlockParam>
{
new ContentBlockParam(new TextBlockParam("Image 1:")),
new ContentBlockParam(new ImageBlockParam(
new ImageBlockParamSource(new Base64ImageSource()
{
Data = image1Data,
MediaType = MediaType.ImagePng,
})
)),
new ContentBlockParam(new TextBlockParam("Image 2:")),
new ContentBlockParam(new ImageBlockParam(
new ImageBlockParamSource(new Base64ImageSource()
{
Data = image2Data,
MediaType = MediaType.ImagePng,
})
)),
new ContentBlockParam(new TextBlockParam("How are these images different?")),
}),
}
]
});
Console.WriteLine(message);
client := anthropic.NewClient()
image1Data := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
image2Data := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewTextBlock("Image 1:"),
anthropic.NewImageBlockBase64("image/png", image1Data),
anthropic.NewTextBlock("Image 2:"),
anthropic.NewImageBlockBase64("image/png", image2Data),
anthropic.NewTextBlock("How are these images different?"),
),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
String image1Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC";
String image2Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC";
List<ContentBlockParam> contentBlockParams = List.of(
ContentBlockParam.ofText(TextBlockParam.builder().text("Image 1:").build()),
ContentBlockParam.ofImage(
ImageBlockParam.builder()
.source(
Base64ImageSource.builder()
.mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
.data(image1Data)
.build()
)
.build()
),
ContentBlockParam.ofText(TextBlockParam.builder().text("Image 2:").build()),
ContentBlockParam.ofImage(
ImageBlockParam.builder()
.source(
Base64ImageSource.builder()
.mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
.data(image2Data)
.build()
)
.build()
),
ContentBlockParam.ofText(
TextBlockParam.builder().text("How are these images different?").build()
)
);
Message message = client
.messages()
.create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessageOfBlockParams(contentBlockParams)
.build()
);
IO.println(message);
$client = new Client();
$image1Data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC';
$image2Data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC';
$message = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => [
['type' => 'text', 'text' => 'Image 1:'],
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/png',
'data' => $image1Data,
],
],
['type' => 'text', 'text' => 'Image 2:'],
[
'type' => 'image',
'source' => [
'type' => 'base64',
'media_type' => 'image/png',
'data' => $image2Data,
],
],
['type' => 'text', 'text' => 'How are these images different?'],
],
],
],
model: 'claude-opus-5-5',
);
echo $message;
client = Anthropic::Client.new
image1_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC"
image2_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGNgYPgPAAEDAQAIicLsAAAAAElFTkSuQmCC"
message = client.messages.create(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
{ type: "text", text: "Image 1:" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: image1_data
}
},
{ type: "text", text: "Image 2:" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: image2_data
}
},
{ type: "text", text: "How are these images different?" }
]
}
]
)
puts message
멀티턴 대화에서는 이후 user 턴에서도 같은 방식으로 새 이미지를 추가하세요. Claude는 이전 턴의 모든 이미지에 접근할 수 있으므로, "이 두 개가 처음 두 개와 비슷해?" 같은 후속 질문은 새 턴 콘텐츠에 이전 이미지를 다시 포함하지 않아도 작동해요.
이미지 한계와 비용
요청 한계
메시지나 요청당 최대 이미지 수는:
- claude.ai에서 메시지당 20개.
- 200k 토큰 컨텍스트 창을 가진 모델의 API에서 요청당 100개.
- 그 외 모든 모델의 API에서 요청당 600개.
이미지당 최대 크기는 8000x8000 px예요.
단일 API 요청에 20개 이상의 이미지가 있으면, 더 엄격한 이미지당 크기 한계가 그 요청의 모든 이미지에 적용돼요. 요청의 모든 image 블록(재전송하는 이전 대화 턴의 이미지와 tool_result 콘텐츠 안에 중첩된 이미지, 예를 들어 computer use 도구에 반환되는 스크린샷)이 이 임계값에 포함돼요. Amazon Bedrock과 Google Cloud에서는 PDF 같은 문서 블록도 이 임계값에 포함돼요. 더 엄격한 한계를 초과하는 이미지는 "many-image requests"를 언급하고 현재 한계를 픽셀로 밝히는 메시지와 함께 invalid_request_error로 거부돼요. 모든 플랫폼에서 한계 아래로 유지하려면 각 이미지를 어느 치수도 2000px를 넘지 않게 크기를 조정하거나, 요청을 20개 이하의 이미지·문서 블록으로 유지하세요.
이미지당 최대 크기는:
- Claude API를 직접 쓸 때 10 MB(base64 인코딩).
- Amazon Bedrock과 Google Cloud에서 5 MB(base64 인코딩).
- claude.ai에서 10 MB.
Files API를 써도 크고 많은 이미지가 있는 요청은 600개에 닿기 전에 실패할 수 있어요. 업로드 전에 이미지 크기나 파일 크기를 줄이세요(예: 다운샘플링). 해상도와 토큰 비용 참고.
지원 형식
Claude는 JPEG, PNG, GIF, WebP 이미지(image/jpeg, image/png, image/gif, image/webp)를 지원해요. 애니메이션은 지원되지 않고 첫 프레임만 사용돼요.
해상도와 토큰 비용
Claude는 이미지를 픽셀이 아니라 패치로 봐요. 각 패치는 이미지의 28×28 픽셀 블록으로, 시각 토큰이라 불러요. 따라서 이미지는 ⌈width / 28⌉ × ⌈height / 28⌉ 시각 토큰 비용이 들어요.
각 모델은 최대 네이티브 이미지 해상도를 가지는데, 긴 변 한계와 시각 토큰 한계로 표현돼요. 어느 한계보다 큰 이미지는 처리 전에 축소돼요. 정확한 규칙은 Claude가 이미지를 크기 조정하고 패딩하는 방식을 보세요. 예외는 computer use와 browser use 도구셋에 반환하는 스크린샷과 줌 이미지예요. API는 모델 한계를 초과하는 tool_result 이미지를 축소하는 대신 검증 오류로 거부하므로, 그런 이미지는 반환하기 전에 애플리케이션에서 크기를 조정하세요. 다른 어떤 거대한 이미지도 축소되는 대신 오류로 거부되게 하려면 이미지 블록의 transformations 필드를 설정하세요.
| 해상도 계층 | 모델 | 최대 긴 변 | 최대 시각 토큰 |
|---|---|---|---|
| 고해상도 | Claude 4.7 이후 모델 | 2576 px | 4784 |
| 표준 | 그 외 모든 모델 | 1568 px | 1568 |
고해상도 지원은 나열된 모델에서 자동이고 베타 헤더나 클라이언트 측 옵트인이 필요 없어요.
다음 표는 각 계층에서 여러 이미지 크기의 축소된 해상도와 시각 토큰 비용을 보여줘요:
| 이미지 크기 | 표준 계층: 축소 후 | 표준 계층: 토큰 | 고해상도 계층: 축소 후 | 고해상도 계층: 토큰 |
|---|---|---|---|---|
| 200x200 px (0.04 megapixels) | Not resized | 64 | Not resized | 64 |
| 1000x1000 px (1 megapixel) | Not resized | 1296 | Not resized | 1296 |
| 1092x1092 px (1.19 megapixels) | Not resized | 1521 | Not resized | 1521 |
| 1920x1080 px (2.07 megapixels) | 1456x819 px | 1560 | Not resized | 2691 |
| 2000x1500 px (3 megapixels) | 1269x952 px | 1564 | Not resized | 3888 |
| 3840x2160 px (8.29 megapixels) | 1456x819 px | 1560 | 2576x1449 px | 4784 |
이미지가 축소될 때 Claude는 종횡비를 유지하면서 계층의 한계에 맞는 가장 큰 크기로 확대/축소해요. 이렇게 토큰 비용이 상한이 돼요. 정확한 규칙과 참조 구현은 Claude가 이미지를 크기 조정하고 패딩하는 방식을 보세요.
비용을 추정하려면 토큰 수에 사용 중인 모델의 토큰당 가격을 곱하세요. 예를 들어 Claude Haiku 4.5의 입력 토큰 백만 개당 $1 USD(표준 계층)에서 1000×1000 이미지는 대략 천 개당 $1.30 USD예요. Claude Opus 5의 백만 개당 $5 USD(고해상도 계층)에서는 같은 이미지가 천 개당 약 $6.48 USD이고 4K 이미지는 천 개당 약 $23.92 USD예요.
고해상도 이미지는 표준 계층 모델의 같은 이미지보다 대략 세 배까지 많은 시각 토큰을 쓸 수 있어요. computer use, 스크린샷 이해, 밀집 문서에서 고해상도가 제공하는 추가 정밀도가 필요 없다면, 토큰 비용을 제어하려고 보내기 전에 이미지를 다운샘플링하세요. 지연 시간을 최소화하고 좌표 기반 워크플로를 단순화하려면 업로드 전에 이미지 크기 조정을 선호하세요.
이미지 품질 지침
Claude에 이미지를 제공할 때 최상의 결과를 위해 다음을 명심하세요:
- 이미지 선명도: 이미지가 선명하고 너무 흐리거나 픽셀화되지 않았는지 확인하세요.
- 텍스트: 이미지에 중요한 텍스트가 있으면 읽기 쉽고 너무 작지 않게 하세요. 텍스트를 키우기 위해 핵심 시각 맥락을 잘라내지 마세요.
- 크기 조정: 이미지가 너무 크면 크기가 조정될 수 있다는 점을 고려하세요(해상도와 토큰 비용 참고). 예를 들어 텍스트가 덜 읽기 쉬워질 수 있어요. 이미지를 미리 조정하거나 잘라내는 것을 고려하세요. 거대한 이미지를 축소되는 대신 오류로 거부되게 하려면(좌표 워크플로에 중요), 이미지 블록을
"oversized_image": "error"로 표시하세요. - 이미지 압축: JPEG이나 WebP(손실 모드) 같은 무손실 형식으로 보내기 전에 압축하면 요청 크기를 줄여 지연 시간을 낮출 수 있어요. 그러나 특히 여러 압축 패스를 적용하면 모델 성능에 해로운 아티팩트가 생길 수 있어요. 예를 들어 심한 JPEG 압축은 텍스트를 읽기 어렵게 만들 수 있어요. 실제로 API에 보내는 이미지를 검사해 압축 설정이 과제에 적절한지 확인하세요.
좌표와 경계 상자
경계 상자, 점, 픽셀 좌표는 좌표와 경계 상자를 보세요. Claude는 크기 조정 뒤에 보는 이미지에 상대적인 절대 픽셀 좌표를 반환해요. 그 가이드는 Claude가 이미지를 크기 조정하고 패딩하는 방식과, 원본 이미지에 좌표가 맞도록 미리 조정하거나 다시 조정하는 방법을 다뤄요.
한계
Claude의 이미지 이해 역량은 최첨단이지만, 알아야 할 한계가 몇 가지 있어요:
- 사람 식별: Claude는 이미지에서 사람 이름을 말하는 데 사용될 수 없고 거부해요.
- 정확도: Claude는 저품질, 회전, 200픽셀 미만의 아주 작은 이미지를 해석할 때 환각하거나 실수할 수 있어요.
- 공간 추론: Claude의 좌표와 위치 출력은 근사적이에요. 좌표와 경계 상자의 지침을 따르고, 의존하기 전에 출력을 검증하세요.
- 세기: Claude는 이미지에서 물체의 근사 개수를 줄 수 있지만 항상 정확하지는 않아요. 특히 많은 작은 물체에서요.
- AI 생성 이미지: Claude는 이미지가 AI 생성인지 판단할 수 없고, 물으면 틀릴 수 있어요. 가짜나 합성 이미지를 탐지하는 데 의존하지 마세요.
- 부적절한 콘텐츠: Claude는 허용 사용 정책을 위반하는 부적절하거나 노골적인 이미지를 처리하지 않아요.
- 의료 애플리케이션: Claude는 일반 의료 이미지를 분석할 수 있지만, CT나 MRI 같은 복잡한 진단 스캔을 해석하도록 설계되지 않았어요. Claude의 출력은 전문 의학적 조언이나 진단을 대체하는 것으로 여겨져서는 안 돼요.
특히 높은 중요도의 사용 사례에서는 항상 Claude의 이미지 해석을 신중히 검토하고 검증하세요. 인간의 감독 없이 완벽한 정밀도나 민감한 이미지 분석이 필요한 과제에 Claude를 사용하지 마세요.
FAQ
1. 이미지가 선명하고 고품질이며 올바르게 방향이 있는지 확인하세요.
2. 결과를 개선하려고 프롬프트 엔지니어링 기법을 시도하세요.
3. 문제가 지속되면 claude.ai에서 출력을 표시하거나(좋아요/싫어요) [지원팀](https://support.claude.com/)에 연락하세요.
피드백이 Claude를 개선하는 데 도움이 돼요!