๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • ๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป ๐ŸŒฎ ๐Ÿ’ฌ
๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป/python

[python] fastapi ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ์ด๋ฏธ์ง€ ์†ก์ถœ

by ๋ฐ”์ฟ„๋ฆฌ 2024. 10. 21.

์ด๋ฒˆ์— ์ƒˆ๋กœ ์ง„ํ–‰ํ•œ ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ์–ผ๊ตด ์ธ์‹์œผ๋กœ ํ•˜๋Š” ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์ด์—ˆ๋‹ค

์–ผ๊ตด ์ธ์‹ํ•˜๋Š” model์€ cvteam์—์„œ ๋ฐ›์•„์„œ ๋‚˜๋Š” ui์— ๊ธฐ๋Šฅ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋˜๋Š” ์ƒํ™ฉ

main.py

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import cv2

app = FastAPI()

def generate_frames():
    cap = cv2.VideoCapture(0)
    while True:
        success, frame = cap.read()
        if not success:
            break
        ret, buffer = cv2.imencode('.jpg', frame)
        frame = buffer.tobytes()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.get("/video_feed")
def video_feed():
    return StreamingResponse(
    	generate_frames(),
    	media_type="multipart/x-mixed-replace; boundary=frame")

main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Stream</title>
</head>
<body>
    <h1>Live Video Stream</h1>
    <img id="video-stream" src="/video_feed" alt="Video Stream" />
</body>
</html>

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๊ณ , ์ด๋ฏธ์ง€ ํƒœ๊ทธ์˜ src ์†์„ฑ์„ ๋ฐฑ์—”๋“œ ์ŠคํŠธ๋ฆฌ๋ฐ URL๋กœ ์„ค์ •๋งŒ ํ•˜๋ฉด ์ž˜ ์ž‘๋™ํ–ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ ์ถ”๊ฐ€๋กœ ์–ผ๊ตด ์ธ์‹ ์™„๋ฃŒ๋˜๋ฉด ๋“ฑ๋ก๋œ ์ด๋ฆ„์„ ๊ฐ™์ด ์•ž๋‹จ์— ์ „๋‹ฌํ•ด๋‹ฌ๋ผ๋Š” ์š”์ฒญ์„ ๋ฐ›์•˜๋‹ค.

์ŠคํŠธ๋ฆฌ๋ฐ ์ด๋ฏธ์ง€ ์†ก์ถœํ•˜๊ณ  ๋ถ„๋ฆฌ๋˜์–ด์„œ string๋„ ์•ž๋‹จ์— ๋ณด๋‚ด๋ ค๋ฉด media type์„ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

 

๊ธฐ์กด ์ฝ”๋“œ

ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()

yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
@app.get("/video_feed")
def video_feed():
    return StreamingResponse(generate_frames(), media_type="multipart/x-mixed-replace; boundary=frame")

 

๋ณ€๊ฒฝ ์ฝ”๋“œ

media_type = "multipart/x-mixed-replace; boundary=frame" ์œผ๋กœ ์†ก์ถœํ•˜๋˜ frame์„

base24๋กœ encode → 'utf-8'๋กœ decodeํ•ด์„œ dict์— ๋‹ด์•„์„œ yieldํ•˜๊ณ 

์ถ”๊ฐ€๋กœ ์ „๋‹ฌํ•ด์•ผํ•˜๋Š” string๋„ dict์— ๋‹ด์•„์„œ yield ํ•ด์„œ ์•ž๋‹จ์— ์ „๋‹ฌํ–ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  StreamResponse์˜ media_type์„ "text/event-stream"์œผ๋กœ ๋ณ€๊ฒฝํ•ด์คฌ๋‹ค.

ret, buffer = cv2.imencode('.jpg', img)
img = buffer.tobytes()

base64_str = base64.b64encode(img)
base64_string = base64_str.decode('utf-8')
data = { "base64": base64_string }

# yield streaming image
yield f"event: image\ndata: {json.dumps(data)}\n\n"

# yield string
data = {"name": name}
yield f"event: notification\ndata: {json.dumps(data)}\n\n"
@app.get("/video_feed")
def video_feed():
    return StreamingResponse(generate_frames(), media_type="text/event-stream")

 

 

๊ทธ๋žฌ๋”๋‹ˆ Streaming image, String ๋ชจ๋‘ ์•ž๋‹จ์œผ๋กœ ์ž˜ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ ํ™•์ธํ–ˆ๋‹ค.

์ตœ์ข…์ฝ”๋“œ

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import cv2

app = FastAPI()

def generate_frames():
    cap = cv2.VideoCapture(0)
    while True:
        success, frame = cap.read()
        if not success:
            break
        ret, buffer = cv2.imencode('.jpg', frame)
        img = buffer.tobytes()
        
        base64_str = base64.b64encode(img)
        base64_string = base64_str.decode('utf-8')
        data = { "base64": base64_string }
        
        # yield streaming image
        yield f"event: image\ndata: {json.dumps(data)}\n\n"
        
        # yield string
        data = {"name": name}
        yield f"event: notification\ndata: {json.dumps(data)}\n\n"

@app.get("/video_feed")
def video_feed():
    return StreamingResponse(generate_frames(), media_type="text/event-stream")