졸업프로젝트 주제
시·청각 장애 학습자 보조 프로그램 COMMA
: AI 기반 학습자 맞춤형 실시간 자막/대체텍스트 및 대체학습자료 생성 프로그램
중간 발표 이후, 프로그램 이용 대상을 청각장애인에서 시각장애인/청각장애인으로 확장시켰고, 시각장애인 학습자에게는 GPT-4o vision api를 이용해서 강의 자료에 대한 대체텍스트를 제공해주는 기능을 추가하기로 했습니다.
대체텍스트
시각장애인의 웹 접근성을 위한 대표적인 방법으로, 웹 사이트에 게시된 이미지를 시각장애인이 이해할 수 있도록 설명해주는 글이나 문구
기술 설명 순서
1. Node.js로 GPT-4o vision API 돌리기
2. 강의자료 pdf → png 변환하기
3. pdf → png로 변환 후 GPT-4o vision API로 강의자료에 대한 대체텍스트 생성하기
1. Node.js로 GPT-4o vision API 돌리기
GPT API를 사용할 것이기 때문에 Open AI 가입 후 API 키를 생성합니다. 키를 생성했을 때 뜨는 비밀키는 꼭 복사해서 다른 곳에 저장해놓아야 합니다(다시 못 봐요) 그리고 API를 돌리기 전에 미리 카드 등록과 크레딧을 조금 사두어야 하고, 저는 5달러 미리 결제해서 사용 중입니다
일단 OpenAI 공식 문서에 나와있는 파이썬 코드입니다.
import base64
import requests
# OpenAI API Key
api_key = "YOUR_OPENAI_API_KEY"
# 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)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
payload = {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What’s in this image?"
},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{base64_image}"
}
}
]
}
],
"max_tokens": 300
}
response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
print(response.json())
미리 생성한 api key를 입력하고 input으로 넣을 이미지 경로를 입력합니다. 여기서 payload 코드를 보면, model은 "gpt-4o"를 선택했고, content의 text에서 gpt에게 원하는 프롬프트를 설정할 수 있습니다. 그리고 gpt api에 input으로 들어가는 자료는 img 형식만 가능한 것을 알 수 있습니다.
이를 대체텍스트 생성을 위한 자바스크립트 코드로 바꾸면
/* gptapi.js */
const fs = require('fs');
const axios = require('axios');
// OpenAI API Key
const apiKey = 'YOUR_OPENAI_API_KEY';
// 이미지를 인코딩하는 함수
function encodeImage(imagePath) {
const image = fs.readFileSync(imagePath);
return Buffer.from(image).toString('base64');
}
// 이미지 경로
const imagePath = './time_complexity.png';
// base64 문자열 얻기
const base64Image = encodeImage(imagePath);
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
};
const payload = {
model: 'gpt-4o',
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: '시각장애인을 위해 이 강의자료를 자세히 설명해줘. 사진을 설명한다면 설명 전후에 "사진설명시작", "사진설명끝" 텍스트를 넣어줘.'
},
{
type: 'image_url',
image_url: {
url: `data:image/jpeg;base64,${base64Image}`
}
}
]
}
],
max_tokens: 300
};
axios.post('https://api.openai.com/v1/chat/completions', payload, { headers: headers })
.then(response => {
console.log(response.data.choices[0].message.content);
})
.catch(error => {
console.error(error);
});
node.js 에서는
- fs 모듈을 사용하여 이미지를 읽고 base64로 인코딩합니다. (fs 모듈은 내장 모듈이기 때문에 추가적인 npm 설치 없이 사용 가능)
- axios를 사용하여 HTTP 요청을 보냅니다.
- axios.post 메서드를 사용하여 OpenAI API에 POST 요청을 보냅니다.
- console.log(response.data.choices[0].message.content)를 통해 GPT API 요청에 대한 응답에서 GPT가 생성한 텍스트만 콘솔에 출력합니다.
gpt에게는 "시각장애인을 위해 이 강의자료를 자세히 설명해줘. 사진을 설명한다면 설명 전후에 "사진설명시작", "사진설명끝" 텍스트를 넣어줘" 라고 요청했습니다.
실행 전에 axios 를 설치 후 진행해주세요
npm init
npm install axios
** const apiKey = 'YOUR_OPENAI_API_KEY' 부분은 자신의 키로 바꿔서 실행해야 합니다.
저는 테스트를 위해 다음의 컴퓨터 알고리즘 time complexibility 강의자료를 이용했습니다 (파일 : time_complexity.png)

결과 (실행 방법 : node [js 파일명] )

사진 설명 앞 뒤에 "사진설명시작", "사진설명끝" 을 제대로 출력했음
사진의 제목과 이 사진이 무엇을 의미하는지 설명함
이미지의 세로축과 가로축이 무엇인지 설명함. 위치가 왼쪽, 오른쪽임을 제대로 설명함
시간 복잡도 곡선이 아래에서 위로 갈수록 연산량이 증가함을 설명함
8개의 곡선이 의미하는 것을 설명함
각 곡선의 색을 설명함
→ 시각장애인을 위한 대체텍스트 생성이 잘 됨
GPT-4o가 강의 이미지를 잘 인식하고 시각장애인을 위한 대체텍스트 생성까지 잘 수행합니다.
2. 강의자료 pdf → png 변환
1. 한 페이지로 된 pdf 파일을 png로 변환

위 사진은 실제 이번 학기에 제가 수강하는 강의 자료들입니다. 교수님이 올려주시는 강의자료의 대부분은 pdf 파일입니다. 그러나 GPT-4o vision API는 이미지 파일만을 input으로 받습니다.
그래서 강의자료를 업로드 했을 때 pdf 에서 png(이미지)로 변환하는 과정이 있어야 GPT-4o vision API를 이용할 수 있습니다. pdf 파일을 png 형식으로 변환하는 과정을 구현해보도록 하겠습니다.
우선 이를 구현하기 전에 pdf-poppler 모듈을 설치해야 합니다.
npm install pdf-poppler
설치 후 package.json에 들어가보면 다음과 같이 pdf-poppler 모듈이 추가되어있습니다.

pdf-poppler 모듈
pdf 파일을 이미지로 변환해주는 모듈입니다. 현재는 Window와 Mac OS에서만 지원됩니다.
/* pdf_to_img.js */
const path = require('path');
const pdf = require('pdf-poppler');
const fs = require('fs');
const axios = require('axios');
// PDF 파일
const filePdf = 'dynamic_prog.pdf';
// pdf 파일 이름
const filename = filePdf.split('.')[0];
// 변환된 파일 경로를 저장할 배열
let convertedFilePaths = [];
// 변환된 파일을 저장할 폴더
const imgDir = 'img_save';
// pdf 정보를 가져오고 정보 출력
pdf.info(filePdf)
.then(pdfinfo => {
console.log(pdfinfo);
// img_save 폴더에 저장
return pdf.convert(filePdf, {
format: 'png',
out_dir: path.join(path.dirname(filePdf), imgDir),
out_prefix: filename
});
})
.then(res => {
// 변환된 파일 경로를 저장
const filePath = path.join(path.dirname(filePdf), imgDir, `${filename}.png`);
convertedFilePaths.push(filePath);
console.log('저장된 파일 경로:', convertedFilePaths);
})
.catch(error => console.error(error));
pdf-poppler의 info 로 pdf의 정보를 가져옵니다.
pdf-poppler의 conver로 pdf파일을 png 형식으로 변환합니다. 이때 저장되는 곳은 imgDir로 지정해서 입력했습니다.
저는 테스트를 위해 컴퓨터 알고리즘 dynamic programming pdf 자료를 이용했습니다.(파일 : dynamic_prog.pdf)

파일 구조는 이렇게 되어있습니다

pdf_to_img.js를 실행하여 dynamic_prog.pdf 파일이 png로 변환되어 img_save 폴더에 제대로 저장되는지 테스트해보겠습니다.
결과

우선 pdf.info에서 출력한 pdf 파일의 정보가 제대로 나왔습니다. 페이지 수는 1페이지이고 파일 크기는 88957 bytes 입니다.
그리고 파일이 img_save에 png 형식으로 변환되어 저장되었다고 출력되었습니다. 실제로 비어있던 img_save 파일에 dynamic_prog의 이미지 파일이 저장되었습니다.


한 페이지의 pdf 파일을 png로 변환시키는 데에 성공했습니다.
2. 여러 페이지로 된 pdf 파일 png로 변환
여기서 하나 더 나아가서, 보통 강의자료의 경우 여러 페이지로 구성되어있습니다. 위의 코드를 바탕으로 여러 페이지로 된 pdf 파일도 모두 png로 바꿔서 저장하는 코드를 구현해보겠습니다.
const path = require('path');
const pdf = require('pdf-poppler');
const fs = require('fs');
const axios = require('axios');
// PDF 파일
const filePdf = 'com_algo.pdf';
// pdf 파일 이름
const filename = filePdf.split('.')[0];
/* 추가 */
// pdf 페이지 수
let pageCount = 0;
// 변환된 파일 경로를 저장할 배열
let convertedFilePaths = [];
// 변환된 파일을 저장할 폴더
const imgDir = 'img_save';
// pdf 정보를 가져오고 페이지 수를 정수로 변환
pdf.info(filePdf)
.then(pdfinfo => {
pageCount = parseInt(pdfinfo.pages, 10);
console.log(pdfinfo);
console.log(`pdf파일 페이지 수: ${pageCount}`);
// PDF 변환
return pdf.convert(filePdf, {
format: 'png',
out_dir: path.join(path.dirname(filePdf), imgDir),
out_prefix: filename
});
})
.then(res => {
// 변환된 파일 경로를 배열에 저장
for (let i = 1; i <= pageCount; i++) {
const filePath = path.join(path.dirname(filePdf), imgDir, `${filename}-${i}.png`);
convertedFilePaths.push({ filePath, pageNum: i });
}
console.log('저장된 파일 경로:', convertedFilePaths);
})
.catch(error => console.error(error));
pageCount를 추가하여 pdf.info 로 가져온 pdf 파일의 정보 중에 페이지 수를 뽑아서 정수로 바꿔 넣었습니다. 그리고 pdf 파일의 페이지 수를 출력했습니다. 그리고 변환된 파일 경로를 페이지 수만큼 반복하도록 작성하여 모든 페이지의 파일 경로를 배열에 저장하여 출력되도록 했습니다.
저는 테스트를 위해 4 페이지로 된 컴퓨터 알고리즘 Introduction to Algorithms pdf 자료를 이용했습니다.(파일 : com_algo.pdf)

결과


pdf파일의 페이지 수가 4 페이지로 잘 출력되었고, 저장된 파일 경로도 4개의 페이지 모두 잘 출력되었습니다. 4페이지 모두 png로 변환되어 img_save 폴더에 잘 저장되었습니다.
3. pdf → png로 변환 후 GPT-4o vision API로 강의자료에 대한 대체텍스트 생성하기 (통합)
이제 앞에서 설명한 pdf→png 과정과 GPT-4o vision API 부분을 합치고, 여러 페이지에 대한 대체텍스트를 생성해보겠습니다.
/* server.js */
const path = require('path');
const pdf = require('pdf-poppler');
const fs = require('fs');
const axios = require('axios');
// PDF 파일
const filePdf = 'com_algo.pdf';
// pdf 파일 이름
const filename = filePdf.split('.')[0];
let pageCount = 0;
// OpenAI API Key
const apiKey = 'YOUR_OPENAI_API_KEY';
// 변환된 파일 경로를 저장할 배열
let convertedFilePaths = [];
// 변환된 파일을 저장할 폴더
const imgDir = 'img_save';
// pdf 정보를 가져오고 페이지 수를 정수로 변환
pdf.info(filePdf)
.then(pdfinfo => {
pageCount = parseInt(pdfinfo.pages, 10);
console.log(pdfinfo);
console.log(`pdf파일 페이지 수: ${pageCount}`);
// PDF 변환
return pdf.convert(filePdf, {
format: 'png',
out_dir: path.join(path.dirname(filePdf), imgDir),
out_prefix: filename
});
})
.then(res => {
// 변환된 파일 경로를 배열에 저장
for (let i = 1; i <= pageCount; i++) {
const filePath = path.join(path.dirname(filePdf), imgDir, `${filename}-${i}.png`);
convertedFilePaths.push({ filePath, pageNum: i });
}
console.log('저장된 파일 경로:', convertedFilePaths);
// 변환된 이미지 파일을 이용하여 대체텍스트 생성
createText(convertedFilePaths);
})
.catch(error => console.error(error));
// 대체텍스트 생성 함수
async function createText(filePaths) {
// 파일 경로 배열을 페이지 번호를 기준으로 오름차순 정렬
filePaths.sort((a, b) => a.pageNum - b.pageNum);
for (const { filePath, pageNum } of filePaths) {
console.log(`\n${filePath} (페이지 ${pageNum})`);
function encodeImage(imagePath) {
const image = fs.readFileSync(imagePath);
return Buffer.from(image).toString('base64');
}
const base64Image = encodeImage(filePath);
const headers = {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
};
const payload = {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": '시각장애인을 위해 이 강의자료를 자세히 설명해줘. 사진을 설명한다면 설명 전후에 "사진설명시작", "사진설명끝" 텍스트를 넣어줘.'
},
{
"type": "image_url",
"image_url": {
"url": `data:image/jpeg;base64,${base64Image}`
}
}
]
}
],
"max_tokens": 1000
};
try {
const response = await axios.post("https://api.openai.com/v1/chat/completions", payload, { headers });
console.log(`\n페이지 ${pageNum}에 대한 대체텍스트:`);
console.log(response.data.choices[0].message.content);
} catch (error) {
console.error(`\n페이지 ${pageNum}에 대한 대체텍스트 생성 중 오류 발생:`);
console.error(error);
}
}
}
대체텍스트 생성할 때 각각의 페이지에 대해 다 대체텍스트가 생성되도록 for문을 이용하였습니다. 또한 대체텍스트 출력이 페이지 순서대로 출력되도록 파일 경로 배열을 오름차순으로 바꿔주었습니다.
웹에 띄우기 전에 콘솔에 잘 대체텍스트가 출력되는지 테스트해보도록 하겠습니다. img_save 폴더의 이미지 파일을 모두 지우고, 자료는 위의 com_algo.pdf를 다시 사용해서 테스트를 돌려보겠습니다.
결과
1. pdf → png : 잘 변환되고 저장되었음


2. GPT-4o vision API로 대체텍스트 생성 : 4페이지 각각 대체텍스트가 잘 생성됨




pdf 강의자료를 png 로 변환하여 저장했고, 이 이미지를 GPT-4o vision API로 대체텍스트를 생성한 모습입니다.