서론
챗지피티로 이미지에 대한 처리를 해본적이 있다.
그렇다면 API로도 당연히 가능할 것이다!
구글 드라이브 특정 폴더에 있는 이미지들을 일괄적으로 뽑은 후,
이미지 기반의 객관식 문제와 응답을 뽑는 코드를 작성해보자~
나는 아래 순서로 개발 진행했다.
1) 구글 드라이브를 활용한 이미지 가져오기
2) 이미지를 base64로 변환한뒤, gpt api request에 넣기
3) 리턴값을 엑셀로 도출하기
Google Drive API 연결
https://developers.google.com/drive/api/guides/search-files?hl=ko
구글 드라이브 API를 활용하면 특정 폴더 내에 있는 이미지 파일들을 리스트로 가져올 수 있다.
이미지 파일들을 각각 다운로드 받은 후, Base64로 변환하여 chatgpi api 의 리퀘스트로 요청할 수 있다.
이미지 url도 리퀘스트로 요청할 수 있으나, 구글 드라이브 url을 활용해서 요청하니 jpg 혹은 png로 끝나지 않아 불가능하다는 오류가 발생했다.
# Google Drive API 인증 및 초기화
auth.authenticate_user()
drive_service = build('drive', 'v3')
def get_image_files_from_folder(folder_id: str):
"""
Google Drive 폴더 ID를 사용하여 이미지 파일 정보를 반환합니다.
"""
query = f"'{folder_id}' in parents and (mimeType contains 'image/')"
results = drive_service.files().list(
q=query,
fields="files(id, name)"
).execute()
files = results.get('files', [])
return [{'id': file['id'], 'name': file['name']} for file in files]
def resize_image_to_1024x1024(image_data):
"""
이미지 데이터를 1024x1024 크기로 조정합니다.
"""
image = Image.open(io.BytesIO(image_data))
image = image.convert("RGB") # 이미지 모드를 RGB로 변환
image = image.resize((1024, 1024)) # 크기 조정
output_buffer = io.BytesIO()
image.save(output_buffer, format="PNG")
output_buffer.seek(0)
return output_buffer.getvalue()
이미지를 base64로 변환하여 gpt api 요청에 첨부하면, 가끔 최대 토큰 수를 초과했다는 에러가 발생한다.
이를 방지하기 위해서는 이미지 크기를 사전에 줄여 토큰 수가 너무 크지 않도록 해야 한다.
나는 1024 * 1024 크기로 이미지들을 일괄적으로 줄여서 처리했다.
ChatGPT API로 이미지 처리하기
이미지를 요청에 집어넣는 것은 2가지 방법이 존재한다.
1) 이미지를 base64로 변환후 문자로 요청
2) 이미지 url을 요청
나는 1번 방법을 사용했다.
이미지를 Base64로 변환 후 문자로 요청
def get_response_with_image_base64(model: str, system_prompt: str, user_text: str, base64_image: str, temperature: float = 0.0):
"""
OpenAI API 요청을 통해 Base64로 인코딩된 이미지를 포함한 질문과 답변을 생성합니다.
"""
try:
response = openai.ChatCompletion.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": [
{"type": "text", "text": user_text},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}}
]}
],
temperature=temperature,
)
return response['choices'][0]['message']['content']
except openai.error.InvalidRequestError as e:
return f"Invalid request error: {e}"
except Exception as e:
return f"Error during API request: {e}"
이미지 url로 GPT API 요청
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4-vision-preview",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "What’s in this image?"},
{
"type": "image_url",
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
},
],
}
],
max_tokens=300,
)
print(response.choices[0])
최종 데이터 엑셀로 변환
제일 아래에 프롬프트를 확인해보면 알겠지만 예시를 활용하여 json 형태로 도출하도록 만들었다.
json 형태로 문자열 응답을 받은 후, 이를 엑셀 파일로 변환하여 저장한다.
def clean_json_response(response: str):
"""
OpenAI API에서 반환된 JSON 응답 문자열을 정리하여 JSON으로 파싱할 수 있도록 만듭니다.
"""
try:
# ```json 및 ``` 제거
response = response.strip().strip("```").replace("json", "").strip()
return json.loads(response) # JSON 파싱
except json.JSONDecodeError as e:
print(f"JSONDecodeError: {e}")
return None
def process_images_with_base64(folder_id: str, model: str, system_prompt: str, user_text: str, output_file: str, question_difficulty: str):
"""
Google Drive 폴더의 이미지 파일을 다운로드하여 Base64로 변환 후 OpenAI API 요청 결과를 Excel 파일로 저장합니다.
"""
results = []
# 폴더 내 이미지 파일 가져오기
image_files = get_image_files_from_folder(folder_id)
print(f"총 {len(image_files)}개의 이미지 파일을 찾았습니다.")
for image in image_files:
file_name = image['name']
file_id = image['id']
print(f"Processing image: {file_name}")
try:
# 이미지 파일을 Base64로 변환
base64_image = download_file_as_base64(file_id)
# OpenAI API 응답 가져오기
raw_response = get_response_with_image_base64(
model=model,
system_prompt=system_prompt,
user_text=user_text,
base64_image=base64_image,
temperature=0.0,
)
print("raw_response :: ", raw_response)
# 응답을 정리하고 JSON으로 변환
response_data = clean_json_response(raw_response)
if not response_data:
raise ValueError("응답에서 JSON 데이터를 파싱할 수 없습니다.")
# 결과 저장
results.append({
"id": remove_extension(file_name),
"image_file": file_name,
"question": response_data.get("question", ""),
"options": response_data.get("options", []),
"answer": response_data.get("answer", ""),
"description": response_data.get("description", ""),
"image_url" : "https://drive.google.com/file/d/"+file_id
})
except Exception as e:
print(f"Error processing {file_name}: {e}")
results.append({
"id": remove_extension(file_name),
"image_file": file_name,
"question": "",
"options": [],
"answer": "",
"description": f"Error: {e}",
})
# 결과를 DataFrame으로 변환 후 Excel로 저장
df = pd.DataFrame(results)
df.to_excel(output_file, index=False)
print(f"Results saved to {output_file}")
전체코드
import os
import openai
import pandas as pd
import json
import base64
from PIL import Image
from google.colab import auth
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload
import io
# OpenAI API 키 설정
openai.api_key = 'sk-...'
# Google Drive API 인증 및 초기화
auth.authenticate_user()
drive_service = build('drive', 'v3')
def remove_extension(file_name):
"""
파일 이름에서 확장자를 제거합니다.
Parameters:
file_name (str): 파일 이름 (확장자를 포함)
Returns:
str: 확장자가 제거된 파일 이름
"""
return os.path.splitext(file_name)[0]
def get_image_files_from_folder(folder_id: str):
"""
Google Drive 폴더 ID를 사용하여 이미지 파일 정보를 반환합니다.
"""
query = f"'{folder_id}' in parents and (mimeType contains 'image/')"
results = drive_service.files().list(
q=query,
fields="files(id, name)"
).execute()
files = results.get('files', [])
return [{'id': file['id'], 'name': file['name']} for file in files]
def resize_image_to_1024x1024(image_data):
"""
이미지 데이터를 1024x1024 크기로 조정합니다.
"""
image = Image.open(io.BytesIO(image_data))
image = image.convert("RGB") # 이미지 모드를 RGB로 변환
image = image.resize((1024, 1024)) # 크기 조정
output_buffer = io.BytesIO()
image.save(output_buffer, format="PNG")
output_buffer.seek(0)
return output_buffer.getvalue()
def download_file_as_base64(file_id: str):
"""
Google Drive 파일 ID를 사용하여 파일을 다운로드하고 Base64로 인코딩합니다.
"""
request = drive_service.files().get_media(fileId=file_id)
fh = io.BytesIO()
downloader = MediaIoBaseDownload(fh, request)
done = False
while not done:
status, done = downloader.next_chunk()
fh.seek(0)
# 이미지 크기 조정
resized_image_data = resize_image_to_1024x1024(fh.read())
# Base64로 인코딩
base64_encoded_image = base64.b64encode(resized_image_data).decode("utf-8")
return base64_encoded_image
def get_response_with_image_base64(model: str, system_prompt: str, user_text: str, base64_image: str, temperature: float = 0.0):
"""
OpenAI API 요청을 통해 Base64로 인코딩된 이미지를 포함한 질문과 답변을 생성합니다.
"""
try:
response = openai.ChatCompletion.create(
model=model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": [
{"type": "text", "text": user_text},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}}
]}
],
temperature=temperature,
)
return response['choices'][0]['message']['content']
except openai.error.InvalidRequestError as e:
return f"Invalid request error: {e}"
except Exception as e:
return f"Error during API request: {e}"
def clean_json_response(response: str):
"""
OpenAI API에서 반환된 JSON 응답 문자열을 정리하여 JSON으로 파싱할 수 있도록 만듭니다.
"""
try:
# ```json 및 ``` 제거
response = response.strip().strip("```").replace("json", "").strip()
return json.loads(response) # JSON 파싱
except json.JSONDecodeError as e:
print(f"JSONDecodeError: {e}")
return None
def process_images_with_base64(folder_id: str, model: str, system_prompt: str, user_text: str, output_file: str):
"""
Google Drive 폴더의 이미지 파일을 다운로드하여 Base64로 변환 후 OpenAI API 요청 결과를 Excel 파일로 저장합니다.
"""
results = []
# 폴더 내 이미지 파일 가져오기
image_files = get_image_files_from_folder(folder_id)
print(f"총 {len(image_files)}개의 이미지 파일을 찾았습니다.")
for image in image_files:
file_name = image['name']
file_id = image['id']
print(f"Processing image: {file_name}")
try:
# 이미지 파일을 Base64로 변환
base64_image = download_file_as_base64(file_id)
# OpenAI API 응답 가져오기
raw_response = get_response_with_image_base64(
model=model,
system_prompt=system_prompt,
user_text=user_text,
base64_image=base64_image,
temperature=0.0,
)
# LLM 응답 확인
print("raw_response :: ", raw_response)
# 응답을 정리하고 JSON으로 변환
response_data = clean_json_response(raw_response)
if not response_data:
raise ValueError("응답에서 JSON 데이터를 파싱할 수 없습니다.")
# 결과 저장
results.append({
"id": remove_extension(file_name),
"image_file": file_name,
"question": response_data.get("question", ""),
"options": response_data.get("options", []),
"answer": response_data.get("answer", ""),
"description": response_data.get("description", ""),
"image_url" : "https://drive.google.com/file/d/"+file_id
})
except Exception as e:
print(f"Error processing {file_name}: {e}")
results.append({
"id": remove_extension(file_name),
"image_file": file_name,
"question": "",
"options": [],
"answer": "",
"description": f"Error: {e}",
"question_difficulty": "error",
})
# 결과를 DataFrame으로 변환 후 Excel로 저장
df = pd.DataFrame(results)
df.to_excel(output_file, index=False)
print(f"Results saved to {output_file}")
if __name__ == "__main__":
# 입력 데이터
model = "gpt-4o-mini"
folder_id = "..." # Google Drive 폴더 ID
output_file = "./results.xlsx" # 결과를 저장할 Excel 파일 경로
user_text = """
해당 이미지를 사용하여 간단한 4지선다형 문제를 만들어 주고 답과 해설을 하나만 제공해줘.
"""
system_prompt = """
질문, 해설은 한글이어야 해.
JSON 형식으로 결과를 반환해야 합니다.
<<return 예시>>
{
"question": "질문 내용",
"options": ["1. ㅇㅇㅇ", "2. ㅌㅌㅌ", "3. ㅇㅇㅇㅇ", "4. ㅇㅇㅇㅇ"],
"answer": "1",
"description": "해설"
}
"""
# 폴더 내 이미지 처리
process_images_with_base64(folder_id, model, system_prompt, user_text, output_file)
'Computer Engineering > AI' 카테고리의 다른 글
실전 LLM chp1 (1) | 2024.08.21 |
---|---|
벤치마크 데이터셋에 대해 (2) | 2024.08.14 |
의료 LLM의 발전에 대해서 (1) | 2024.07.16 |
Data centric AI란? (0) | 2024.07.11 |
요약 - 섹션 1) 커피 한잔 마시며 듣는 AI 이야기 (2) | 2024.07.08 |