디지털 문서화, 영수증 관리, 대규모 자료 보관 등 대량의 이미지에서 텍스트를 추출해야 하는 상황은 비즈니스 환경에서 빈번하게 발생합니다. 수동 작업의 비효율성을 극복하기 위해 11개 언어를 지원하는 LightOnOCR-2-1B 모델은 강력한 대안이 됩니다. 본 가이드에서는 해당 모델의 API를 활용하여 대량의 이미지를 자동화된 방식으로 처리하는 기법을 상세히 설명합니다.
1. 환경 구성 및 서비스 활성화
1.1 시스템 요구 사양
모델의 원활한 구동을 위해 다음과 같은 환경이 권장됩니다.
- 운영체제: Linux (Ubuntu 18.04 이상 권장)
- GPU 메모리: 최소 16GB 이상 (모델 로드 및 추론용)
- Python 버전: 3.8 이상
1.2 OCR 서비스 상태 점검
작업을 시작하기 전, OCR 서비스가 활성화되어 있는지 확인해야 합니다. 기본적으로 7860 및 8000 포트가 사용됩니다.
# 활성 포트 확인
ss -tlnp | grep -E "7860|8000"
# 서비스가 중단된 경우 실행 스크립트 가동
cd /root/LightOnOCR-2-1B
bash start.sh
서비스가 정상 실행되면 http://<서버_IP>:7860을 통해 웹 인터페이스 접속이 가능해집니다.
2. API 요청 구조의 이해
LightOnOCR-2-1B의 API는 표준적인 JSON 페이로드를 사용합니다. 이미지 데이터는 Base64로 인코딩되어 전송되어야 합니다.
{
"model": "lightonai/LightOnOCR-2-1B",
"messages": [{
"role": "user",
"content": [{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,<Base64_Encoded_String>"
}
}]
}],
"max_tokens": 4096
}
3. 파이썬을 이용한 자동화 구현
3.1 단일 이미지 처리 함수
이미지 파일을 읽어 API 서버에 전송하고 결과를 반환받는 핵심 함수입니다.
import base64
import requests
import os
def run_ocr_request(image_path, endpoint_url):
"""
단일 이미지의 텍스트를 인식합니다.
"""
with open(image_path, "rb") as file:
encoded_string = base64.b64encode(file.read()).decode('utf-8')
request_data = {
"model": "/root/ai-models/lightonai/LightOnOCR-2-1B",
"messages": [{
"role": "user",
"content": [{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{encoded_string}"}
}]
}],
"max_tokens": 2048
}
headers = {"Content-Type": "application/json"}
resp = requests.post(endpoint_url, json=request_data, headers=headers)
if resp.status_code == 200:
return resp.json()['choices'][0]['message']['content']
else:
raise Exception(f"Error: {resp.status_code} - {resp.text}")
3.2 병렬 처리를 통한 대량 인식
수백 장 이상의 이미지를 효율적으로 처리하기 위해 파이썬의 ThreadPoolExecutor를 활용한 병렬 처리 스크립트를 구성합니다.
import concurrent.futures
from pathlib import Path
def process_directory(input_dir, output_dir, endpoint, workers=4):
"""
디렉토리 내 모든 이미지를 병렬로 처리합니다.
"""
input_path = Path(input_dir)
save_path = Path(output_dir)
save_path.mkdir(parents=True, exist_ok=True)
extensions = ['*.png', '*.jpg', '*.jpeg', '*.bmp']
files = []
for ext in extensions:
files.extend(input_path.glob(ext))
print(f"총 {len(files)}개의 작업을 시작합니다.")
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
future_map = {executor.submit(single_task, f, save_path, endpoint): f for f in files}
for i, future in enumerate(concurrent.futures.as_completed(future_map), 1):
file_ref = future_map[future]
try:
future.result()
print(f"[{i}/{len(files)}] 완료: {file_ref.name}")
except Exception as err:
print(f"[{i}/{len(files)}] 실패: {file_ref.name} -> {err}")
def single_task(file_path, save_path, endpoint):
text_result = run_ocr_request(str(file_path), endpoint)
with open(save_path / f"{file_path.stem}.txt", 'w', encoding='utf-8') as f:
f.write(text_result)
4. 고도화 및 안정성 확보
4.1 재시도 메커니즘 (Retry Logic)
네트워크 불안정성이나 일시적인 서버 부하에 대비하여 tenacity 라이브러리를 사용한 재시도 로직을 적용하는 것이 좋습니다.
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def robust_ocr_request(image_path, endpoint):
return run_ocr_request(image_path, endpoint)
4.2 언어 힌트 활용
특정 언어의 인식률을 높이고 싶다면 프롬프트에 언어 정보를 포함할 수 있습니다. messages 내의 content 배열에 텍스트 타입의 지시사항을 추가하면 됩니다.
# 프롬프트 예시
prompt_content = [
{"type": "text", "text": "이 이미지의 한국어 텍스트를 정확하게 추출해줘."},
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{encoded_string}"}}
]
5. 최적화 및 운영 팁
- 해상도 최적화: 이미지의 가장 긴 변이 1500~1600px를 초과하지 않도록 리사이징하면 인식 속도와 정확도의 균형을 맞출 수 있습니다.
- 리소스 모니터링: 대량 작업 시
nvidia-smi명령어를 통해 GPU 메모리 점유율을 실시간으로 확인하십시오. - 포맷 통일: 가급적 PNG나 고품질 JPEG 형식을 사용하여 압축 손실로 인한 오인식을 방지해야 합니다.
- 실패 로깅: 처리 중 오류가 발생한 파일 목록을 별도의 로그 파일로 기록하여 추후 재처리가 가능하도록 설계하십시오.