저번편에는 API 연동 및 STT, TTS 를 사용해 gpt3.5 와 대화하는 것까지 구현을 진행했다.
하지만, 곰곰히 생각해보니 이 기능만으로는 부족하다는 생각이 들었고 추가 기능을 생각해보기로 했다.
추가 기능 구현
노인을 위한 이미지 편집 기능
보통 어르신들이 친구들과 카카오톡 대화를 할 때 카카오톡 이모티콘 만큼 자주 사용하는 사진들이 있다.
바로 요런 사진들!
나는 이미지를 Stable-diffusion 을 이용해 생성을 한 후 사용자가 원하는 글귀를 이미지에 삽입하는 기능을 구상했다.
위의 기능을 구현하기 위해 생각한 흐름을 한 번 보자면
알고리즘
- 이미지로 만들고 싶은 카테고리를 선택한다.
- 원하는 글귀를 선택한다.
- 이미지를 생성한다.
- 원하는 글귀를 이미지에 넣는다.
이미지생성
이미지 생성은 원래 로컬서버에 저장 후 사용하는 방식을 썼는데 그렇게하면 나중에 서버에 여러 이미지가 쌓여 넘칠게 뻔하기 때문에 API 를 이용해 보기로 했다.
async function query() {
console.log("Send Query")
setImageURL('');
var data = { "inputs": props.inputValue,"use_cache":false }
const response = await fetch(
"<https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-2-1>",
{
headers: { Authorization: "Bearer API_KEY" },
method: "POST",
body: JSON.stringify(data),
}
);
var formData = new FormData();
await response.blob().then((res)=>{
formData.append('image',res);
formData.append('textPrompt', props.textPrompt)
axios.post("/api/imgEdit",formData).then((res) => {
console.log("api img edit response", res.data);
setImageURL(res.data.slice(2,-1))
props.setLoading(false)
});
})
}
frontend 에서 이미지를 받아올 것이기 때문에 js 코드로 함수를 구현했다.
huggingface API 를 사용하면 이미지 데이터는 blob 형식으로 받게 된다. 해당 데이터를 서버로 보내 처리하기 위해서는 formdata 에 저장 후 보내야한다.
formData.append(’image’,res) 를 사용해 blob 데이터를 저장한다. 이제 여기서 blob 데이터를 이미지로 출력하면 생성된 이미지가 화면에 나오게 된다.
나는 여기에 텍스트를 편집할것 이기 때문에 서버로 blob 과 텍스트 데이터를 전송하겠다.
이제 서버에 전달받은 데이터를 파이썬 코드를 실행하기 위해 다음과 같은 코드를 작성했다.
async function imgEdit (imgBlob, text) {
return new Promise((resolve, reject) =>{
let dataToSend;
const msg = text;
console.log("img edit : ",text, "img URL : ", imgBlob.path);
const python = spawn(desiredPath, [ msg, imgBlob.path]);
python.stdout.on('data', async function(data){
dataToSend = data.toString();
console.log("imgEdit start", dataToSend);
resolve(dataToSend)
});
python.stderr.on("data", async function(data) {
console.error("Python Error: ", data.toString())
reject(data.toString())
});
python.on('error', (error) => {
console.error("Spawn Error: ", error);
reject(error);
});
})
}
Promise 객체를 생성 후 spawn 을 이용해 데이터를 파이썬 코드로 보낸다. 파이썬에서 처리를 한 데이터를 resolve 를 통해 받아 frontend 로 전달한다.
파이썬 코드는 다음과 같다.
import numpy as np
from PIL import ImageFont, ImageDraw, Image
import cv2
import sys
import base64
from io import BytesIO
def calculate_average_color(image):
# 이미지의 RGB값을 계산
r, g, b = 0, 0, 0
pixels = image.load()
width, height = image.size
for y in range(height):
for x in range(width):
_r, _g, _b = pixels[x, y]
r += _r
g += _g
b += _b
# 평균값을 계산
num_pixels = width * height
r //= num_pixels
g //= num_pixels
b //= num_pixels
return b,g,r
def get_complementary_color(b,g,r):
# 보색을 계산
r, g, b = b,g,r
return 255 - r, 255 - g, 255 - b
try:
image = cv2.imread(sys.argv[2], cv2.IMREAD_ANYCOLOR)
overlay = image.copy()
except Exception as e:
print("error", e)
imgForColor = Image.open(sys.argv[2])
avgB, avgG,avgR = calculate_average_color(imgForColor)
comB,comG,comR = get_complementary_color(avgB,avgG,avgR)
b,g,r= 0,0,0
if((avgB+avgG+avgR)/3 >=128):
b,g,r = 0,0,0
else:
b,g,r = 255,255,255
font_size = 80
h,w,c = image.shape
line_w = w/font_size
height = str(h)
test_str = sys.argv[1]
max_line = (int)(len(test_str)/line_w)+1
str_slice = test_str.split(" ")
exp_str = []
str_token = ""
font = ImageFont.truetype("arial.ttf", font_size)
i = 0
while i != len(str_slice):
if len(str_token) + len(str_slice[i]) + 1 <= line_w:
str_token += " " + str_slice[i]
i += 1
else:
exp_str.append(str_token.strip()) # str_token의 앞뒤 공백 제거 후 추가
str_token = "" # str_token 초기화
if i == len(str_slice) and str_token: # 마지막 요소이고, str_token이 빈 문자열이 아닌 경우
exp_str.append(str_token.strip())
background_h = []
center_w = []
for i in range(len(exp_str)):
left,top,right,bottom = font.getbbox(exp_str[i])
center_w.append(right)
for i in range (len(exp_str)):
background_h.append((int)((h/2) + (i - len(exp_str)/2) * font_size))
cv2.rectangle(overlay, (0, background_h[0]), (w, background_h[len(exp_str)-1]+font_size), (comB, comG, comR), -1)
alpha = 0.7 # 투명도 설정 (0은 완전 투명, 1은 완전 불투명)
image = cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0)
img = np.zeros((200,400,3),np.uint8)
#b,g,r,a = 0,0,0,0
fontpath = "fonts/batang.ttc"
font = ImageFont.truetype(fontpath, font_size)
img_pil = Image.fromarray(image)
draw = ImageDraw.Draw(img_pil)
for i in range (len(exp_str)):
ex = (int)((w-center_w[i])/2)-50
draw.text((ex,(int)((h/2) + (i - len(exp_str)/2) * font_size)), exp_str[i],font=font,fill=(b,g,r))
img = np.array(img_pil)
# 임의의 행렬을 생성
matrix = np.array([...]) # 여기에 실제 행렬을 입력
# 행렬을 이미지로 변환
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
resultImg = Image.fromarray(img)
# 이미지를 base64 문자열로 변환
buffered = BytesIO()
resultImg.save(buffered, format="JPEG")
img_str = base64.b64encode(buffered.getvalue())
print(str(img_str))
cv2.waitKey()
cv2.destroyAllWindows()
텍스트와 이미지를 받아 중앙정렬 후 텍스트를 돋보이기 위한 상자를 배치한다.
정말 가독성이 1도 없는 코드라 바꾸고 싶지만 그럴 시간이 있을까?
위의 프로세스를 수행하면 다음과 같은 그림이 생성된다.
짜란 정말 아무도 안쓸것같은 이미지 완성!
'ChatGPT > 챗봇프로젝트' 카테고리의 다른 글
[프로젝트] 노인을 위한 스마트챗봇 -1- (2) | 2023.03.21 |
---|