playdata/daily

12주차 : Day 4 (9/26)

soojin1 2024. 9. 30. 09:10

 

이미지 파일이 핫도그인지, 아닌지 판별하는 프로그램이다.

FastAPI를 사용하여 predict api를 생성한다.

$ pdm add fastapi uvicorn
$ pip install jinja2

 

 

1단계 : 큰 틀 만들기

[main.py]

서버에 접속하면 이미지 url을 지정해놓은 hotdog , coolcat 사진이 랜덤으로 출력된 화면을 볼 수 있다.

return 부분을 보면 "index.html" 파일에 request와 image_url 변수가 전달된다.

이 HTML 파일은 public 폴더 안에 있어야한다. 왜냐하면 html = Jinja2Templates(directory="public") 이기 때문에.

해당 파일에서 Jinja2 문법을 사용하여 변수를 출력할 수 있다.

from typing import Union
from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from fastapi import Request

import random

app = FastAPI()

html = Jinja2Templates(directory="public")

@app.get("/hello")
def read_root():
    return {"Hello👋": "hotdog🌭"}

@app.get("/")
async def home(request: Request):
    hotdog = "https://www.dailiang.co.kr/news/photo/201111/34714_19009_5246.jpg"
    coolcat = "https://image.fmkorea.com/files/attach/new3/20230527/486616/5032171247/5810153990/e5bc995f67dc592ba0121714373baf8c.jpeg"
    image_url = random.choice([hotdog, coolcat])
    return html.TemplateResponse("index.html",{"request":request, "image_url": image_url})


@app.get("/predict")
def hotdog():

    return {"Hello": random.choice(["hotdog", "not hotdog"])}

 

[index.html]

inja2 템플릿 문법을 사용하여, 특정 조건에 따라 이미지 URL을 동적으로 표시하는 것 이다.

여기서 특정 조건이란 {% if image_url %} 부분이다. main.py에서 index.html로 image_url 변수를 넘겼다. 이 값이 존재할 때만 출력하겠다는 의미이다.

<H1>GIVE ME HOTDOG 👻</H1>

{% if image_url %}
<h2>Here is your image:</h2>
<img src="{{ image_url }}" width="300">
{% endif %}

 

[결과]

2 단계:  모델 적용하여 예측

 

[사용 모델]

 

julien-c/hotdog-not-hotdog · Hugging Face

🦀 NimaBoscarino/hotdog-gradio 👁 deepsynthbody/deepfake-ecg-generator 💻 zzz6519003/hot-girl-or-not 📚 Kinley95/kelly-hot-dog-classifier 📚 NimaBoscarino/hotdog-streamlit 👀 dukecsxu/hotdogclassifier

huggingface.co

 

먼저 프론트 부분인 index.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(); // 폼 제출 방지

            // Get the file input element and the file selected by the user
            const fileInput = document.querySelector('input[type="file"]');
            const file = fileInput.files[0];

            // 이미지 미리보기 처리
            if (file) {
                const imageUrl = URL.createObjectURL(file);
                const imagePreview = document.getElementById('imagePreview');
                imagePreview.src = imageUrl;
                imagePreview.style.display = 'block'; // 미리보기 이미지를 보이게 함
            }

            // 서버로 파일 업로드
            const formData = new FormData(event.target);
            const response = await fetch('/uploadfile/', {
                method: 'POST',
                body: formData
            });

            // 서버 응답 처리
            const result = await response.json();
            const label = result.label;
            const p_value = result.p;
            
            console.log('result:', JSON.stringify(result, null, 2));
            console.log('label:' + label);
            

            // 결과에 따라 이미지 변경
            const p_result = document.getElementById('p_result');
            p_result.style.display = 'block';  // 결과 이미지를 보이게 함
            if (label === 'hot dog') {
                p_result.src = "https://cdn.discordapp.com/attachments/1261185494759374900/1288761000039813140/image.png?ex=66f65bb6&is=66f50a36&hm=8b123739ef9158b60715573d54b762ab792163870399137b826e3f11f0406f5f&"; // 핫도그 이미지
            } else {
                p_result.src = "https://ifh.cc/g/0jy8zj.jpg"; // Not hotdog 이미지
            }

            // p 값을 형식에 맞게 화면에 출력
            const p_display = document.getElementById('p_display');
            let output = "";
            p_value.forEach(item => {
                output += `${item.label}: ${item.score}\n`;
            });
            p_display.textContent = output;  // p 값을 보기 좋게 출력
        }
    </script>

</head>
<body>
    
    <h1> GIVE ME HOTDOG 👻 </h1>


    {% if image_url %}
    <img id="resultImage" src="{{ image_url }}" width="300">
    {% endif %}
    
    <form id="uploadForm" onsubmit="uploadImage(event)">
        <input type="file" name="file" accept="image/*" required>
        <button type="submit">Upload</button>
    </form>

    <!-- Section where the selected image will be previewed -->
    <div id="preview">
        <h2>Here is your image:</h2>
        <img id="imagePreview" src="" alt="No Image" style="display:none; width:300px;">
    </div>

    <!-- 이미지 분류 결과와 p 값을 표시할 섹션 -->
    <div id="result-section">
        <img id="p_result" src="" alt="No Image" style="display:none; width:1000px; position:absolute; top:50px; right:20px;">
        <pre id="p_display"></pre> <!-- p 값을 출력할 곳 -->
    </div>
</body>
</html>

 

첫 화면은 이렇게 생겼다.

파일을 선택하고 Upload를 클릭하면 사진이 Here is your image: 아래에 출력된다.

사진 출력과 동시에 백엔드 부분인 FASTAPI로 전달된다.

전달된 이미지는 모델에 적용되어 판별이 수행된다.

@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
    img = Image.open(io.BytesIO(img))  # 이미지 바이트를 PIL 이미지로 변환
    
    p = model(img)
    #label = get_max_label(p)
    label = get_label(p)
    return {"label": label, "p": p} 
    #return {"Tmp" : "tmp" }

 

get_label()은 다음과 같이 구현되었다.

p는 예시로 이렇게 생겼다. >[{'label': 'hot dog', 'score': 0.787665605545044}, {'label': 'not hot dog', 'score': 0.21233440935611725}]

점수가 더 높은 순서대로 나오기 때문에 만약 첫 번째 label이 not hot dog라면 바로 그 label을 리턴하도록 하였고,

 

hotdog인 경우에는

그 점수가 0.8 이상이면 최종적으로 핫도그로 판별하기로 하고, 아니라면 핫도그가 아닌 것으로 판별하고자 했다.

 

def get_label(p):
   for item in p:
      if item['label'] == 'not hot dog':
         return item['label']

      elif item['label'] == 'hot dog':
         if item['score'] >= 0.8 :
            return item['label']
         else:
            return 'not hot dog'

 

이렇게 리턴된 값은 index.html 코드의 uploadImage 부분으로 전달되어 처리된다.

다시 그 부분만 적어보자면 아래와 같다.

그리고 이미지 주소가 있는 부분은 리턴된 라벨값이 핫도그라면 원숭이가 핫도그를 서빙하는 사진이 출력될 것 이고

핫도그가 아니라면 절규짤이 출력될 것 이다 ㅋㅋ

<script>
        async function uploadImage(event) {
            event.preventDefault(); // 폼 제출 방지

            // Get the file input element and the file selected by the user
            const fileInput = document.querySelector('input[type="file"]');
            const file = fileInput.files[0];

            // 이미지 미리보기 처리
            if (file) {
                const imageUrl = URL.createObjectURL(file);
                const imagePreview = document.getElementById('imagePreview');
                imagePreview.src = imageUrl;
                imagePreview.style.display = 'block'; // 미리보기 이미지를 보이게 함
            }

            // 서버로 파일 업로드
            const formData = new FormData(event.target);
            const response = await fetch('/uploadfile/', {
                method: 'POST',
                body: formData
            });

            // 서버 응답 처리
            const result = await response.json();
            const label = result.label;
            const p_value = result.p;
            
            console.log('result:', JSON.stringify(result, null, 2));
            console.log('label:' + label);
            

            // 결과에 따라 이미지 변경
            const p_result = document.getElementById('p_result');
            p_result.style.display = 'block';  // 결과 이미지를 보이게 함
            if (label === 'hot dog') {
                p_result.src = "https://cdn.discordapp.com/attachments/1261185494759374900/1288761000039813140/image.png?ex=66f65bb6&is=66f50a36&hm=8b123739ef9158b60715573d54b762ab792163870399137b826e3f11f0406f5f&"; // 핫도그 이미지
            } else {
                p_result.src = "https://ifh.cc/g/0jy8zj.jpg"; // Not hotdog 이미지
            }

            // p 값을 형식에 맞게 화면에 출력
            const p_display = document.getElementById('p_display');
            let output = "";
            p_value.forEach(item => {
                output += `${item.label}: ${item.score}\n`;
            });
            p_display.textContent = output;  // p 값을 보기 좋게 출력
        }
    </script>

 

[결과]

모델의 성능이 그다지 좋은 것 같지는 않다.

첫 번째 사진에서는 핫도그 사진을 0.94 점수로 핫도그로 판별했고

두 번째 사진의 떡볶이를 0.92의 점수로 핫도그로 판별했다 -_-^

세 번째 사진의 짜장면은 핫도그 점수가 더 높긴 하지만 0.8 이하이기 때문에 핫도그가 아니어서 절규한다.

마지막은 다행히  핫도그 점수가 더 낮았다.

 

오늘 한 내용 넘 재밌다

하지만 오래걸렸다...html 머에요-_-^

'playdata > daily' 카테고리의 다른 글

16주차 : Day 1 (10/21)  (1) 2024.10.21
12주차 : Day 5 (9/27)  (1) 2024.09.30
12주차 : Day 3 (9/25)  (1) 2024.09.30
12주차 : Day 2 (9/24)  (0) 2024.09.30
12주차 : Day 1 (9/23)  (2) 2024.09.24