imhamburger 님의 블로그
데이터엔지니어 부트캠프 - 사용자 웹페이지에 hotdog 판별하는 API 기능 연결하기 본문
사용자가 이미지를 업로드하면 hotdog인지 not hotdog 인지 판별해주는 API 기능을 만들고, 사용자가 보는 프론트쪽이랑 API 기능을 연결할 것이다. (판별해주는 딥러닝 모델은 이미 만들어져있는 것을 사용할 것이다.)
그러면 우리가 만들어줘야할 건 2가지이다.
- 판별해 결과를 출력해주는 API 기능
- 사용자 웹페이지 (매우 간단하게..!)
사전 준비사항
- 핫도그를 판별해주는 모델 (여기)
핫도그를 판별해주는 모델은 이미지를 업로드하면 다음과 같이 출력된다.
#예시
{
"label": "not hot dog",
"score": 0.7514119744300842
"label": "hot dog",
"score": 0.2524546346134245
}
즉, 업로드한 이미지가 not hot dog 점수가 높다면 그것은 핫도그가 아닌 것이다.
나는 "hot dog" 점수가 높다고하더라도 점수가 0.8 미만이라면 "not hot dog"으로 출력되게 할 것이다.
1. API 기능 구현하기
API 기능에는 실제로 판별해주는 기능을 담당하는 main 파일과 위에서 언급한 점수 조건을 적용해줄 utils.py 2개를 만들었다.
main.py
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from transformers import pipeline
from hnh.utils import get_max_label, get_max_score, get_label
from fastapi import Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
html = Jinja2Templates(directory="public")
@app.get("/")
async def home(request: Request):
hotdog = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQxqqIG2n685k1AS3HyuhXLgMsySGTozbxNvQ&s"
dog = "https://mblogthumb-phinf.pstatic.net/MjAyMjAyMDdfMjEy/MDAxNjQ0MTk0Mzk2MzY3.WAeeVCu2V3vqEz_98aWMOjK2RUKI_yHYbuZxrokf-0Ug.sV3LNWlROCJTkeS14PMu2UBl5zTkwK70aKX8B1w2oKQg.JPEG.41minit/1643900851960.jpg?type=w800"
image_url = "https://cdn.pixabay.com/photo/2015/11/03/09/03/question-mark-1019983_1280.jpg"
return html.TemplateResponse("index.html",{"request":request, "image_url": image_url})
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
# 파일 저장
img = await file.read()
model = pipeline("image-classification", model="julien-c/hotdog-not-hotdog")
from PIL import Image
import io
img = Image.open(io.BytesIO(img))
p = model(img)
max_label = get_max_label(p)
max_score = get_max_score(p)
l = get_label(p)
return l
- html = Jinja2Templates(directory="public"): HTML 템플릿 파일이 있는 디렉토리를 지정
- model = pipeline("image-classification", model="julien-c/hotdog-not-hotdog"): 이미지 분류 모델을 불러온다. 이 모델은 핫도그인지 아닌지를 분류한다.
- img = Image.open(io.BytesIO(img)): 바이트로 읽은 이미지를 io.BytesIO를 통해 이미지 객체로 변환
- p = model(img): img 이미지를 모델에 입력하여 분류를 수행하고, 결과를 변수 p에 저장
- async def home(request: Request): 여기서 request는 Request 타입의 매개변수로, home() 함수가 호출될 때 FastAPI가 자동으로 생성하여 전달
- "image_url": image_url: image_url이라는 키와 그에 대응하는 이미지 URL의 값이 html으로부터 템플릿에 전달
위에서 언급한 점수조건을 적용하여 label이 "hot dog"인지 "not hot dog" 인지 판별해주는 점수를 가져오는 utils.py 기능을 구현했다.
utils.py
def get_max_label(p):
max_score = 0
max_label = ""
for i in p:
if i['score'] > max_score:
max_score = i['score']
max_label = i['label']
return max_label
def get_max_score(p):
max_score = 0
max_label = ""
for i in p:
if i['score'] > max_score:
max_score = i['score']
max_label = i['label']
return max_score
def get_label(p):
win_label = get_max_label(p)
win_score = get_max_score(p)
if win_label == "hot dog" and win_score >= 0.8:
last_label = "hot dog"
return {"label" : last_label, "score": win_score}
else:
last_label = "not hot dog"
return {"label": last_label, "score": win_score}
위에 코드에서 값 p가 최종적으로 main.py에 p 인자로 받아져 출력된다.
2. 간단한 html 구현하기
참고로 나의 html 파일은 다음과 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Classification</title>
<script>
async function uploadImage(event) {
event.preventDefault(); // 폼 제출 방지
const formData = new FormData(event.target);
const response = await fetch('/uploadfile/', {
method: 'POST',
body: formData
});
const result = await response.json();
const label = result.label;
console.log('result:', JSON.stringify(result, null, 2));
console.log('label:' + label)
// 결과에 따라 이미지 변경
const resultImage = document.getElementById('resultImage');
if (label === 'hot dog') {
resultImage.src = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQxqqIG2n685k1AS3HyuhXLgMsySGTozbxNvQ&s"; // Hotdog 이미지
} else {
resultImage.src = "https://mblogthumb-phinf.pstatic.net/MjAyMjAyMDdfMjEy/MDAxNjQ0MTk0Mzk2MzY3.WAeeVCu2V3vqEz_98aWMOjK2RUKI_yHYbuZxrokf-0Ug.sV3LNWlROCJTkeS14PMu2UBl5zTkwK70aKX8B1w2oKQg.JPEG.41minit/1643900851960.jpg?type=w800"; // Not hotdog 이미지
}
}
</script>
</head>
<body>
<h1>IF HOTDOG</h1>
<form id="uploadForm" onsubmit="uploadImage(event)">
<input type="file" name="file" accept="image/*" required>
<button type="submit">Upload</button>
</form>
<h2>Here is your image:</h2>
{% if image_url %}
<img id="resultImage" src="{{ image_url }}" width="300">
{% endif %}
</body>
</html>
사용자 웹페이지
위 웹페이지에 파일을 업로드하면 hot dog 이면, 왼쪽그림이 출력되고 아니면 오른쪽 그림이 출력된다.
결과1 : 핫도그가 아닌 사진을 올렸을 때,
결과2 : 핫도그 사진을 올렸을 때,
사실 직접 html을 만들지않고 streamlit 을 이용해 간단하게 웹페이지를 만들 수 있다.
streamlit은 웹페이지에 시각적으로 보여줄 수 있는 그래프나 사진업로드 기능 추가 등을 간단하게 코드 몇줄로 구현할 수 있어 매우 편리하다.