์ฌํด 3์๋ถํฐ 5์๊น์ง ๋ฃ๋ ICT ์ด๋ ธ๋ฒ ์ด์ ์คํ์ด ์ธ๊ณต์ง๋ฅ ๋น์ ์ปดํจํ ๊ณผ์ ์์ ๋ง์ง๋ง 2์ฃผ๊ฐ ๊ฐ์ธ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ ์๊ฐ์ด ์๋ค
์ด๋ค๊ฑธ ํด๋ณด๋ฉด ์ข์๊น ๊ณ ๋ฏผํ๋ค๊ฐ ์ด์ ์ ๋ฌด์์ ๋งก์๋ ํ์ฌ ํ์ง๋ฅผ ์น์ ์ฌ๋ ค๋ณด๊ณ ์น์ ์ ์ฒด์ ์ธ ํ๋ฆ์ ํ๋ฒ ๊ฒฝํํด๋ณด๋ฉด ์ข๊ฒ ๋ค ์ถ์ด์ ์น์ ์งง์ ํ์ฌ ํด๋ฆฝ ์์์ ์ ๋ก๋ํ๋ฉด ํ์งํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ์๋ ค์ฃผ๋ ๊ฐ๋จํ AI๋ฅผ ์ฌ๋ฆฐ ์น์ฌ์ดํธ๋ฅผ ๋ง๋ค์ด๋ณด๊ธฐ๋ก ํ์๋ค
์น, ํ๋ก ํธ์ ๋ฐฑ์๋ ๋ถ๋ถ์ ์ ํ ๋ค๋ค๋ณธ ์ ์ด ์์๊ธฐ์ ๋ชจ๋ ๋ด์ฉ๋ค์ด ์๋ก์ ๋ค
๋จผ์ ์ ์ฒด์ ์ธ ๊ณํ์ ๋จผ์ ์ธ์๋ณด๊ณ ์ค์ ๋ก ๊ณํ๋๋ก ์งํ๋์๋์ง, ์งํํ๋ฉด์ ์ด๋ ๋ถ๋ถ์ด ๋งํ๊ณ ์ด๋ค ์ด์๋ค์ด ๋ฐ์ํ๋์ง, ๊ณํ์ ์กฐ์ ํ์ฌ ์ด๋๊น์ง ๊ตฌํํ๊ธฐ๋ก ํ๋์ง ๋ฑ ๊ณํ๊ณผ ์ค์ ์งํ ๋ด์ฉ์ ๋น๊ตํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด์๋ค
์์ ๋ด์ฉ ๋ฐ ์๋ฃ๋ค์ ๋ฒ ์ด์ค๋ก ํ์ฌ ๊ธฐ์ค ์ผ๊ณ ์ํ๋ ๋ฐฉํฅ์ฑ์ ๋ง๊ฒ ์ปค์คํ ํ๋ ์์ผ๋ก ์งํํ์๋ค
์ง๊ธ ์ ์ ๊ณํ๋ค์ ๊ฑฐ์ฐฝํ์ผ๋..(๋ด ๊ธฐ์ค) ๋ง์ ํด๋ณด๋ ๋ชจ๋ฅด๋ ๋ถ๋ถ๋ค์ด ์ ๋ง ๋ง์๋ค
๐ ํ๋ก์ ํธ๋ช
FireClip AI: ํด๋ฆฝ ์์ ๊ธฐ๋ฐ ํ์ฌ ํ์ง ์น์๋น์ค
๐ฏ 1. ํ๋ก์ ํธ ๊ฐ์
ํญ๋ชฉ | ๋ด์ฉ |
๋ชฉํ | ์ฌ์ฉ์๊ฐ ์ ๋ก๋ํ ์ค๋ด CCTV ํด๋ฆฝ ์์์์ ์ฐ๊ธฐ ๋๋ ๋ถ๊ฝ์ ์๋ ํ์งํ๊ณ , ํ์ง๋ ๊ฒฐ๊ณผ(๋ฐ์ด๋ฉ๋ฐ์ค)๋ฅผ ๊ทธ๋ฆฐ ์์์ผ๋ก ์นํ์ด์ง์ ํ์ํ๋ ๊ฒฝ๋ ์น์๋น์ค ๊ตฌํ |
๊ธฐ์ ์คํ | GCP, Flask, MongoDB, YOLO, OpenCV |
๐งฑ 2. ๋ฐ์ดํฐ ๊ตฌ์ถ ๋ฐ ๋ชจ๋ธ ํ์ต
ํญ๋ชฉ | ๋ด์ฉ |
์ฌ์ฉ ๋ฐ์ดํฐ ํ์ | CCTV ํ์์ ํด๋ฆฝ ์์ ๋ฐ ์ด๋ฏธ์ง |
๋ผ๋ฒจ ํญ๋ชฉ ์ | ์ฐ๊ธฐ/๋ถ๊ฝ 2๊ฐ ํด๋์ค ํฌํจ |
๊ตฌ์ถ ์ ๋ต (์์ง ๊ฒฝ๋ก) | โ
๊ณต๊ฐ ํ์ฌ/์ฐ๊ธฐ ์ด๋ฏธ์ง ๋ฐ์ดํฐ์
ํ์ฉ (AIhub, kaggle, Roboflow) โ ํ์์ ํฌ๋กค๋ง ๋๋ YouTube ํด๋ฆฝ์ผ๋ก ๋ณด์ โ ๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ ๋ฐ YOLO ํฌ๋งท์ผ๋ก ๋ผ๋ฒจ๋ง |
์์ง๋ | ์ต์ 1000์ฅ ์ด๋ฏธ์ง (๋ผ๋ฒจ ํฌํจ) |
์ด๋ฏธ์ง ํด์๋ | 640×480 ~ 1920×1280 (1280x1080 ์ด์ ๊ถ์ฅ) |
๋ฐ์ดํฐ๋น ํ๊ท ์ฉ๋ | ์ฝ 500 ~ 1,000KB |
๋ผ๋ฒจ๋ง ๋๊ตฌ | Roboflow |
๋ชจ๋ธ ์ฌ์ฉ ์ ๋ต | ๐ ์ํฉ์ ๋ฐ๋ผ ์ ํ โ ์ฌ์ ํ์ต๋ YOLOv8 ๋ถ๊ฝ/์ฐ๊ธฐ ๋ชจ๋ธ ๊ทธ๋๋ก ์ฌ์ฉ โก ๊ฒฝ๋ ์ปค์คํ ๋ฐ์ดํฐ๋ก ํ์ธํ๋ |
๐งฑ 2-1. ๋ผ๋ฒจ๋ง ๊ธฐ์ค ๋ฐ ํ์ง ๊ด๋ฆฌ
ํญ๋ชฉ | ๊ธฐ์ค |
๋ฐ์ด๋ฉ ๋ฐ์ค | ์ต์ ํฌํจ ์์ญ, ํด๋์ค๋ช ํฌํจ |
๋ถ๋ฅ ๋ผ๋ฒจ | ๊ณ ์ ํด๋์ค ID + ์ค๋ช |
๊ฒ์ | ์ํ ๊ฒ์ 20% |
- ์ ์ฅ ๋ฐ ๊ด๋ฆฌ
- ํ์: YOLO TXT
- ์ ์ฅ์: Google Drive
- ๊ด๋ฆฌํด: Roboflow
- ๋ฐ์ดํฐ ์ฆ๊ฐ ๊ณํ
- Noise, saturation, hue, hori flip, brightness
๐ป ์ผ๋ฐํ ์ฑ๋ฅ ๊ฒํ
- ํ์ต๋ฐ์ดํฐ์ ํฌํจ๋์ง ์์ ํ์ง ๋์ ๋ฐ์ดํฐ 20์ฅ ์ด์์ ๋ํ ํ์ง๋ฅ๋ ฅ ํ๊ฐ
- mAP ์ฑ๋ฅ์ ์ฐ์ถํ์ฌ 0.7 ์ด์์ด ๋๋๋ก ํ์ต ๋ฐ์ดํฐ ์กฐ์
๐งฑ 2-2. ๋ฐ์ดํฐ ๊ตฌ์ถ ์งํ
์ฌ์ฉ ๋ฐ์ดํฐ | Roboflow, AIํ๋ธ, Kaggle |
๋ผ๋ฒจ๋ง ํด | Roboflow |
์ค์ CCTV์ ์ ์ฌํ 720p (1280 x 720) / 1080p(1920 x 1080 ) ํฌ๊ธฐ๋ฅผ ๊ฐ์ง ์ค๋ด์ ์ด๋ฏธ์ง๋ฅผ ๊ตฌํ๊ธฐ๋ ์ฝ์ง ์์
- ํนํ CCTV ํ์ฌ ์ด๋ฏธ์ง๋ ๋๋์ฑ ๊ตฌํ๊ธฐ ์ด๋ ต๋ค (๋ณด์ ๊ด๋ จ ์ด์ ๋ฑ ๊ณต์ ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋๋ค์)
- Roboflow Kaggle ์์ ๊ตฌํ ์ด๋ฏธ์ง๋ค์ ํ์ง์ด ๋ฎ์ ์นด๋ฉ๋ผ๋ก ์ฐํ ์์๋ค์ 640x640 ํฌ๊ธฐ๋ก ๋ฆฌ์ฌ์ด์ฆํ๊ธฐ ๋๋ฌธ์ ๋๋ถ๋ถ ์ด๋ฏธ์ง์ ํ๋ฆฌํฐ๊ฐ ๋ง์ด ๋จ์ด์ง
- AIHub์ ๊ฒฝ์ฐ 1920x1080 ํฌ๊ธฐ์ ๊ณ ํ์ง ์ด๋ฏธ์ง์ด์ง๋ง ์ผ์ธ ์ดฌ์ ์ด๋ฏธ์ง
1์ฐจ ๋ฐ์ดํฐ์
์๋ณธ 730์ฅ ๋ผ๋ฒจ๋ง ๋ฐ ์ด๋ฏธ์ง ์ฆ๊ฐ ์ ์ฉ ํ ์ด 1810์ฅ
ํด๋์ค๋ณ ๊ฐ์ฒด ๊ฐ์
- fire : 894 ๊ฐ
- smoke : 250 ๊ฐ
Preprocessing
- Auto-Orient: Applied
- Resize: Stretch to 640x640
Augmentations
- Outputs per training example: 3
- 90° Rotate: Clockwise, Counter-Clockwise, Upside Down
- Saturation: Between -25% and +25%
- Brightness: Between -15% and +15%
- Blur: Up to 2.5px
- Noise: Up to 0.1% of pixels
๋ฐ์ดํฐ์ ๊ตฌ์ฑ
- Train Set : 90%, 1620 ์ฅ
- Valid Set : 8%, 146์ฅ
- Test Set : 2%, 44์ฅ
2์ฐจ ๋ฐ์ดํฐ์
280์ฅ ์ถ๊ฐ ๋ผ๋ฒจ๋ง ๋ฐ ์ด๋ฏธ์ง ์ฆ๊ฐ ์ ์ฉ ํ ์ด 2524์ฅ
ํด๋์ค๋ณ ๊ฐ์ฒด ๊ฐ์
- fire : 1,134 ๊ฐ
- smoke : 518 ๊ฐ
Preprocessing
- Auto-Orient: Applied
- Resize: Stretch to 640x640
Augmentations
- Outputs per training example: 3
- 90° Rotate: Clockwise, Counter-Clockwise, Upside Down
- Saturation: Between -25% and +25%
- Brightness: Between -15% and +15%
- Blur: Up to 2.5px
- Noise: Up to 0.1% of pixels
- Shear: ±10° Horizontal, ±10° Vertical
๋ฐ์ดํฐ์ ๊ตฌ์ฑ
- Train Set : 91%, 2286 ์ฅ
- Valid Set : 7%, 187์ฅ
- Test Set : 2%, 51์ฅ
๋ฐ์ดํฐ ์ถ์ฒ
- [Roboflow] Indoor Fire Detection Dataset
- [AI-Hub] ํ์ฌ ๋ฐ์ ์์ธก ์์ ์ํ ๋ฐ์ดํฐ
- [Kaggle] Fire Detection from CCTV
- [Roboflow] Fire-indoor Object Detection Dataset
๐งฑ 2-3. ๋ชจ๋ธ ํ์ต ๊ฒฐ๊ณผ
ํ์ต ์คํฌ๋ฆฝํธ ๋ฐ ๋ชจ๋ธ ํ๋
ํ๊ฒฝ : Colab
์ผ๋ฐ ํ์ต ์คํฌ๋ฆฝํธ
from ultralytics import YOLO
# ๋ชจ๋ธ ์ด๊ธฐํ (๊ฐ๋ณ๊ฒ ์์: yolov8n.pt)
model = YOLO('yolov8n.pt') # ๋๋ yolov8s.pt, yolov8m.pt ๋ฑ
# ํ์ต ์์
model.train(
data='/content/data/data.yaml',
epochs=50,
imgsz=640,
batch=16,
project='yolov8_fire_detection',
name='exp',
exist_ok=True
)
- 1,2์ฐจ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ์ต
- ๋ ๋ค์ํ ํ๋ผ๋ฏธํฐ ๊ฐ์ผ๋ก ์๋์ผ๋ก ์ต์ ์ ์กฐํฉ ์ฐพ๋๋ก wandb์ sweep ๊ธฐ๋ฅ ์ฌ์ฉ
Wandb Sweep๋ก ๋ชจ๋ธ ์๋ ํ๋
!pip install -U ultralytics
!wandb login <api key>
!yolo settings wandb=True
import wandb
from wandb.integration.ultralytics import add_wandb_callback
from ultralytics import YOLO
# ํ๋ก์ ํธ๋ช
์ง์ (์๋ ์์ฑ๋ ๊ฐ๋ฅ)
wandb.init(project="fire_clip_project", job_type='training')
- wandb ์ฐ๊ฒฐ ๋ฐ sweep ์ค์
sweep_config = {
'method': 'random',
'metric': {'goal': 'maximize', 'name': 'metrics/mAP50-95(B)'},
'parameters': {
'batch_size': {'values': [16, 32, 64]},
'learning_rate': {
'distribution': 'uniform',
'min':0.0001,
'max':0.001
}
}
}
sweep_config = {
'method': 'bayes',
'metric': {'goal': 'maximize', 'name': 'metrics/mAP50-95(B)'},
'parameters': {
'batch_size': {'values': [16, 32]},
'learning_rate': {'distribution': 'uniform', 'min': 0.0001, 'max': 0.001},
'optimizer': {'values': ['SGD', 'Adam', 'AdamW']},
'label_smoothing': {'distribution': 'uniform', 'min': 0.0, 'max': 0.1}
}
}
- ํ๋ํ ํ์ดํผํ๋ผ๋ฏธํฐ ์ง์
- mAP50-95๊ฐ ์ต๋ํ๋๋ ๊ฒ์ ๊ธฐ์ค์ผ๋ก ์ผ์
- ๋ฐฐ์น ์ฌ์ด์ฆ์ ํ์ต๋ฅ ์ ์ฌ๋ฌ ์กฐํฉ๋ณ๋ก ํ์ต ์งํ
- ์ด ์ธ์๋ ๋ ๋ง์ ํ๋ผ๋ฏธํฐ๋ค ์ถ๊ฐ ๊ฐ๋ฅ
- sweep config ์ค์ ํ ํ์ต ๋๋ฆฌ๋ฉด ์กฐํฉ๋ค์ ์๋์ผ๋ก ํ์ต ๋ฐ wandb์ ์ฑ๋ฅ ๋ก๊น
sweep_id = wandb.sweep(sweep=sweep_config, project='fire_clip_project')
def yolo_train():
with wandb.init() as run:
config = wandb.config
# ๋ชจ๋ธ ์ ์ ๋ฐ ํ์ดํผํ๋ผ๋ฏธํฐ ์ฌ์ฉ
model = YOLO('yolov8n.pt')
add_wandb_callback(model, enable_model_checkpointing=True)
results = model.train(
data='/content/data/data.yaml',
name='exp3',
plots=True,
batch=config.batch_size,
epochs=50,
imgsz=640,
cos_lr=True,
lrf=0.01,
lr0=config.learning_rate,
project='yolov8_fire_detection'
)
metrics = results.metrics
wandb.log({
'epoch': 50, # ๋๋ run.step ๋ฑ ์ฌ์ฉ ๊ฐ๋ฅ
'val_loss': results.loss, # ๋๋ ๋ค๋ฅธ ์ ์ ํ loss ์ฌ์ฉ
'metrics/mAP50': metrics.get('metrics/mAP50(B)', None),
'metrics/mAP50-95': metrics.get('metrics/mAP50-95(B)', None)
})
๐ ๏ธ 3. ๊ตฌํ ๋ฒ์ ๋ฐ ๊ธฐ๋ฅ ์ ์
๋ถ๋ฅ | ๊ธฐ๋ฅ | ์ค๋ช |
๐ฅ ์์ ์ ๋ ฅ | 10~30์ด ์์ ํด๋ฆฝ ์ ๋ก๋ (mp4) | ์น UI์์ ์ฌ์ฉ์๊ฐ ์ง์ ์ ๋ก๋ |
๐ง ํ์ฌ ํ์ง | YOLOv8 ๊ธฐ๋ฐ ์ฐ๊ธฐ/๋ถ๊ฝ ํ์ง | Flask API ๋ด๋ถ์์ ํ๋ ์ ์ถ์ถ ํ ํ์ง ์ํ |
๐ผ๏ธ ๊ฒฐ๊ณผ ํ์ | ํ์ง๋ ํ๋ ์ ์ธ๋ค์ผ + ์๊ฐ์ ๋ณด ํ์ | MongoDB์ ๊ฒฐ๊ณผ ์ ์ฅ |
๐ ๊ฒฐ๊ณผ ์ ์ฅ | ํ์ง ๊ฒฐ๊ณผ ์์๊ณผ ๋ฉํ๋ฐ์ดํฐ ์ ์ฅ | ํ์ง ์์ ํ์ผ : GCP VM Server ๋ฉํ ๋ฐ์ดํฐ : MongoDB |
๐ ๏ธ 3-1. ์ฌ์ฉ ๊ธฐ์ ์คํ
ํํธ | ๊ธฐ์ |
์น ์๋ฒ | Flask (GCP Cloud Run ๋ฐฐํฌ) |
DB | MongoDB |
ํด๋ผ์ด์ธํธ | HTML (+CSS) |
์์ ์ฒ๋ฆฌ | OpenCV, FFmpeg |
ํ์ง ๋ชจ๋ธ | YOLOv8s/n |
๐ ๏ธ 3-2. ์ ์ฒด ๊ตฌ์กฐ
FireClip AI ํ๋ก์ ํธ ์ต์ข ๊ธฐํ์๋ฅผ ๊ธฐ์ค์ผ๋ก, ์น์๋น์ค์ ์ ์ฒด ํ๋ฆ์ ๊ณ ๋ คํ ๋ฐฑ์๋ ๋ฐ ์น ๊ตฌ์กฐ(์์คํ ์ํคํ ์ฒ)
GCP + Flask + MongoDB + YOLO ๊ธฐ๋ฐ์ผ๋ก ์๋
์ฌ์ฉ์(๋ธ๋ผ์ฐ์ )
โ
โผ
[Frontend] (HTML+CSS)
- ์์ ์
๋ก๋
- ํ์ง ๊ฒฐ๊ณผ ํ์ธ
โ
โผ
[Flask Backend API Server] (GCP Cloud Run)
- ์์ฒญ ์ฒ๋ฆฌ (REST API)
- YOLOv8 ์ถ๋ก ์ฐ๋
- Cloud Storage & MongoDB ์ฐ๋
โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โผ โผ โผ
[YOLOv8 Inference] [GCS] [MongoDB]
(๋ก์ปฌ or Cloud) (์์ ์ ์ฅ) (๋ฉํ๋ฐ์ดํฐ ์ ์ฅ)
โก๏ธ ์๋๋ ์ ๋ก๋ํ ์์, ํ์ง ๊ฒฐ๊ณผ ์์์ ๊ตฌ๊ธ ํด๋ผ์ฐ๋ ๋ฒ์ผ์ ์ ์ฅํ๋ ค ํ์ผ๋ ์ด ๋ถ๋ถ๊น์ง๋ ๊ตฌํํ์ง ๋ชปํ๋ค
๐ ๋ฐฑ์๋ ๊ตฌ์กฐ (Flask)
๐น ์ฃผ์ ์ฒ๋ฆฌ ํ๋ฆ
- ์ฌ์ฉ์๊ฐ ์์ ์ ๋ก๋
- Flask ์๋ฒ์์ ์์ ์์ ์ ์ฅ
- ์ ์ฅ๋ ์์ ๊ฒฝ๋ก ๊ธฐ๋ฐ์ผ๋ก OpenCV๋ฅผ ํตํด ํ๋ ์ ๋จ์๋ก YOLO ์ถ๋ก ์คํ
- ์ถ๋ก ๊ฒฐ๊ณผ(ํ๋ ์ + ๋ฐ์ค ์ขํ + ํด๋์ค)๋ฅผ ์์์ผ๋ก ๋ง๋ค๊ณ ์ ์ฅ ๋ฐ ์น ํ์ด์ง์ ์๋ณธ๊ณผ ๊ฐ์ด ๋์ฐ๊ธฐ
- MongoDB์ ํด๋น ์์/๊ฒฐ๊ณผ ๋ฉํ๋ฐ์ดํฐ ๊ธฐ๋ก (ํ์ผ ๊ฒฝ๋ก, ํ์ง๊ฒฐ๊ณผ ๋ฑ)
๐ ํ๋ก ํธ์๋ ๊ตฌ์กฐ (HTML + CSS)
๐น ์นํ์ด์ง ํ๋ฉด
ํด๋ฆฝ ์์์ ์ ๋ก๋ ํ๊ธฐ ์ ์ ์ด๋ฏธ์ง๋ก ๊ฐ๋จํ๊ฒ ์งํํด๋ณด์๋ค
์ด๊ธฐ ํ๋ฉด. ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํด์ ์๋ณธ๊ณผ ํ์ง ๊ฒฐ๊ณผ๋ฅผ ๋์ด์ฃผ๋ ์ ๋ง ๊ฐ๋จํ ํํ์ด๋ค
๋ณด๋ฉด ๊ฒฐ๊ณผ๊ฐ BGRํ์์ ์๊ฐ๋ UTC ํ์์ผ๋ก ๋์ด์๋ ๋ฑ ์์ํ ์ด์๋ค์ด ์์๋ค
๊ทธ ๋ค์์ ๊ฐ๋จํ ํด๋ฆฝ ์์์ ์ ๋ก๋ ๋ฐ ์น์์ ์คํธ๋ฆฌ๋ฐ ๋๋์ง๊น์ง ์งํ, ์๋์ฒ๋ผ ๋์จ๋ค
๐น ์ด์ ์ฌํญ
ํ์ง ๊ฒฐ๊ณผ ์์์ด ์น์์ ์คํ์ด ์๋๋ ๋ฌธ์ ๋ฐ์
- ์๋ณธ ์์ ์ ๋ก๋ ๋ฐ ํ์ง ํ ๋ฐ์ด๋ฉ๋ฐ์ค๋ฅผ ํ๋ ์์ ๊ทธ๋ ค์ ์์์ผ๋ก ๋ง๋๋ ๊ฒ๋ ์ ์์ ์ผ๋ก ๋์
- ๋ง๋ค์ด์ง ํ์ง ๊ฒฐ๊ณผ ์์์ ๋ฌธ์ ๊ฐ ์๋ ๋ค์ด๋ฐ์ ํ์ธํด๋ณด๋ ๋ฌธ์ ์์
- ์์๊ณผ html, ์น ๋ถ๋ถ์ ๋ฌธ์ ๊ฐ ์๋ ๊ฒ ๊ฐ๋ค๊ณ ์ ์ถ
โก๏ธ ๋ง๋ค์ด์ง ์์๊ณผ ๋ธ๋ผ์ฐ์ ๊ฐ์ ํธํ์ฑ ๋ฌธ์ ๋ก ์น์์ ์์ ์ฌ์์ด ์๋จ
์ฝ๋ ์ค OpenCV๋ฅผ ํตํด mp4 ํ์ผ์ ์์ฑํ๋ ๋ถ๋ถ์ด ์๋ค
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
์ด๋ ๊ฒ OpenCV๋ก mp4, ์์ ํ์ผ์ ๋ง๋ค๋ ์ด๋ ๊ฒ ์์ฑ๋ mp4 ํ์ผ์ ๋ธ๋ผ์ฐ์ ์์ ํธํ๋์ง ์๋ ์ฝ๋ฑ์ผ ์๋ ์๋ค
mp4๋ ํ์ผ, ์์์ ๋ด์ ์ปจํ ์ด๋์ด๋ฉฐ ๊ฐ์ mp4๋ผ๋ ๋ด๋ถ ์ธ์ฝ๋ฉ ๋ฐฉ๋ฒ, ์ฝ๋ฑ์ ์๋ก ๋ค๋ฅผ ์ ์๋ค
์๋ ๋ธ๋ก๊ทธ์์ ๋น์ทํ ์ด์ ๋ฐ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ๋ค๋ฃจ๊ณ ์๋ค
https://omoknooni.tistory.com/134
[GAMST] VideoWriter์ fourCC, ์ฝ๋ฑ
ํ๋ก์ ํธ ์งํ ์ค input ์์ ์คํธ๋ฆผ์์ ์ํ ๊ตฌ๊ฐ์ ํ์งํด ์๋ก์ด ๋น๋์ค Clip ํ์ผ๋ก ์์ฑํด์ฃผ๋ ๊ตฌ๊ฐ์ด ์์๋ค.์ด ๋ถ๋ถ์์๋ OpenCV์ VideoWriter ํด๋์ค๋ฅผ ์ด์ฉํด ํ์ผ์ ์์ฑํ๋ค. # VideoWriter
blog.omoknooni.me
โก๏ธ ffmpeg๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ์์ ํธํํ๋ ์ฝ๋ฑ์ผ๋ก ์์์ ๋ณํ
- opencv๋ก mp4 ์์ ์์ฑ -> ffmpeg๋ก ์ฝ๋ฑ ๋ณํ ๋ฐ ์ ์ฅํ๋ ์ผ๋ จ์ ๊ณผ์ ์ ๋ณํํ๋ ์๊ฐ ๋๋ฌธ์ ์ค์๊ฐ์ผ๋ก ์์ ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ์๋ ์ ํฉํ์ง ์๋ค
- ์ด ์นํ์ด์ง๋ ์ค์๊ฐ ์์์ ์๋๊ธฐ์ ffmpeg๋ฅผ ์ด์ฉํ์ผ๋ ๋ ๊ณ ๋ํํ๋ค๋ฉด ์ด ๋ถ๋ถ์ ์๋ตํ ๊ฒ์ด๋ค
โ๏ธ MongoDB ๊ตฌ์ฑ
ํ๋๋ช | ๋ฐ์ดํฐ ํ์ | ์ค๋ช |
_id | ObjectId | MongoDB๊ฐ ์๋ ์์ฑํ๋ ๊ณ ์ ID |
original_filename | String | ์ ๋ก๋๋ ์๋ณธ ์์์ ํ์ผ๋ช (UUID ๋ฑ์ผ๋ก ๊ด๋ฆฌ) |
uploaded_at | String | ์์ ์ ๋ก๋ ์๊ฐ (์: "2025-05-07 23:41:39"). KST ๊ธฐ์ค ์๊ฐ์ผ๋ก ๊ธฐ๋ก |
video_length_sec | Float | ์์์ ์ด ๊ธธ์ด (์ด ๋จ์, ์: 29.91) |
fps | Float | ์ด๋น ํ๋ ์ ์ (Frames Per Second) |
resolution | Object | ์์ ํด์๋ ์ ๋ณด. width์ height๋ฅผ ํฌํจ |
โโ width | Integer | ์์ ๊ฐ๋ก ํด์๋ |
โโ height | Integer | ์์ ์ธ๋ก ํด์๋ |
summary | Object | ๊ฐ์ง ๊ฒฐ๊ณผ ์์ฝ ์ ๋ณด |
โโ fire_count | Integer | ์ด Fire ๊ฐ์ฒด ๊ฐ์ง ํ์ |
โโ smoke_count | Integer | ์ด Smoke ๊ฐ์ฒด ๊ฐ์ง ํ์ |
โโ fire_segments | Array[String] | Fire๊ฐ ๊ฐ์ง๋ ์ฃผ์ ๊ตฌ๊ฐ. ์ง์ ๊ฐ์ง ๋๋ ๋จ๋ฐ ๊ฐ์ง ํฌํจ |
โโ smoke_segments | Array[String] | Smoke๊ฐ ๊ฐ์ง๋ ์ฃผ์ ๊ตฌ๊ฐ. ์ง์ ๊ฐ์ง ๋๋ ๋จ๋ฐ ๊ฐ์ง ํฌํจ |
โโ avg_confidence | Float | ์ ์ฒด ๊ฐ์ง์ ํ๊ท ์ ๋ขฐ๋ (0 ~ 1) |
โโ max_confidence | Float | ๊ฐ์ง๋ ๊ฐ์ฒด ์ค ๊ฐ์ฅ ๋์ ์ ๋ขฐ๋ |
detection_details | Array[Object] | ๊ฐ ๊ฐ์ง ๊ฐ์ฒด์ ์์ธ ์ ๋ณด ๋ชฉ๋ก |
โโ label | String | ๊ฐ์ง๋ ๊ฐ์ฒด ํด๋์ค (์: "fire", "smoke") |
โโ timestamp | String | ๊ฐ์ง๋ ์์ (์: "00:29" ํ์์ ํ์์ฝ๋) |
โโ confidence | Float | ํด๋น ๊ฐ์ฒด ๊ฐ์ง์ ์ ๋ขฐ๋ (0~1) |
โโ bbox | Array[Int] | ๋ฐ์ด๋ฉ ๋ฐ์ค ์ขํ: [x1, y1, x2, y2] (์ผ์ชฝ ์, ์ค๋ฅธ์ชฝ ์๋) |
result_video_path | String | ๊ฐ์ง ๊ฒฐ๊ณผ๊ฐ ์ ์ฉ๋ ์์ ํ์ผ ๊ฒฝ๋ก |
original_video_path | String | ์๋ณธ ์ ๋ก๋ ์์์ ๊ฒฝ๋ก |
processed | Boolean | ํด๋น ์์์ด ํ์ง ํ ์ ์ฅ๋์๋์ง ์ฌ๋ถ (True = ์ฒ๋ฆฌ ์๋ฃ) |
fireclip_db> db.video_detections.find()
[
{
_id: ObjectId('681b71234ca3e6db95f8500c'),
original_filename: '9dd8a6ff3c05414687bc8724e539015d.mp4',
uploaded_at: '2025-05-07 23:41:39',
video_length_sec: 29.91,
fps: 29.985945703568117,
resolution: { width: 854, height: 480 },
summary: {
fire_count: 583,
smoke_count: 72,
fire_segments: [
'00:06–00:13 (์ง์์ ์ผ๋ก ๊ฐ์ง)',
'00:19–00:25 (์ง์์ ์ผ๋ก ๊ฐ์ง)',
'00:27–00:29 (์ง์์ ์ผ๋ก ๊ฐ์ง)',
'๋จ๋ฐ์ฑ ๊ฐ์ง: 00:15, 00:16'
],
smoke_segments: [
'00:00–00:04 (์ง์์ ์ผ๋ก ๊ฐ์ง)',
'๋จ๋ฐ์ฑ ๊ฐ์ง: 00:10, 00:18, 00:19, 00:22, 00:28'
],
avg_confidence: 0.5914,
max_confidence: 0.9154
},
detection_details: [
{
label: 'smoke',
timestamp: '00:00',
confidence: 0.6521,
bbox: [ 0, 75, 79, 470 ]
},
...
...
{
label: 'fire',
timestamp: '00:29',
confidence: 0.8521,
bbox: [ 314, 103, 563, 325 ]
},
{
label: 'fire',
timestamp: '00:29',
confidence: 0.8308,
bbox: [ 309, 86, 561, 330 ]
},
{
label: 'fire',
timestamp: '00:29',
confidence: 0.8217,
bbox: [ 309, 136, 561, 329 ]
},
{
label: 'fire',
timestamp: '00:29',
confidence: 0.7908,
bbox: [ 305, 155, 581, 328 ]
}
],
result_video_path: 'static/results/result_9dd8a6ff3c05414687bc8724e539015d.mp4',
original_video_path: 'static/uploads/9dd8a6ff3c05414687bc8724e539015d.mp4',
processed: true
}
]
๐ 4. ๊ฒฐ๊ณผ๋ฌผ
- ์์ ์ฐ์ถ๋ฌผ
- Flask ๊ธฐ๋ฐ ์๋ฒ ์ฝ๋
- YOLOv8 ํ์ง ์คํฌ๋ฆฝํธ (์ปค์คํ or ๊ธฐ์กด)
- MongoDB ๊ฒฐ๊ณผ ์คํค๋ง ๋ฐ ์ํ ๋ฐ์ดํฐ
- ํ์ง ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ๋ฐ ๋ฉํ๋ฐ์ดํฐ ์ํ
- ๋ฐํ์ฉ ์ฌ๋ผ์ด๋ ๋ฐ ์์ฐ ์์
๐ 4-1. ์นํ์ด์ง ํ๋ฉด ๋ฐ ๊ฒฐ๊ณผ
๐ 4-2. ๊ตฌํ ์ฌ๋ถ ์ ๋ฆฌ ๋ฐ ๋ง๋ฌด๋ฆฌ
๊ตฌํ ํญ๋ชฉ | ์ค์ ์งํ |
์น ์์ ์ ๋ ฅ ๋ฐ ์ ๋ก๋ | โ |
Yolo ํ์ฌ ํ์ง | โ |
๊ฒฐ๊ณผ ํ์ ๋ฐ DB ์ ์ฅ | โ |
๊ตฌ๊ธ ํด๋ผ์ฐ๋ ๋ฒ์ผ ์ฐ๋ | โ |
ํ์ง ๊ฒฐ๊ณผ ์กฐํ ๊ธฐ๋ฅ ์น์ ์ถ๊ฐ | โ |
์ฝ๋ ๋ฆฌํฉํ ๋ง ๋ฐ ๊ณ ๋ํ | โ |
๊ณํ ์์ผ๋ก๋ ์ ๋ก๋ํ ์์์ ๋ํด ํ์ง ํ ์๋ณธ๊ณผ ํ์ง ๊ฒฐ๊ณผ ์์์ ๊ตฌ๊ธ ํด๋ผ์ฐ๋์ ์ ์ฅํ๊ณ ๋ฉํ ๋ฐ์ดํฐ๋ MongoDB์ ์ ์ฅํ๋ค
์น์๋ ์กฐํ ๋ฆฌ์คํธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ์ฌ ์ด๋ฐ ํ์ง ๊ฒฐ๊ณผ๋ฅผ ์กฐํํ ์ ์๋๋ก ํ๋ค
์ฝ๋๋ ํ๋ผ์คํฌ ๋์ ์ฝ๋์ธ app.py์์ ๋ชจ๋ธ ์ถ๋ก , ์ด๋ฏธ์ง ์ ํ์ฒ๋ฆฌ, ๋ฉํ ๋ฐ์ดํฐ ์ ์ฅ ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ฅ๋ค์ ํ๋์ py์ ๋ฃ๋ ๊ฒ์ด ์๋๋ผ ์ฌ๋ฌ ๊ฐ์ ๋ผ์ฐํธ๋ก ๊ตฌ์ฑํ์ฌ ๊ธฐ๋ฅ๋ณ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ๋ค
์ต์ข ์ ์ผ๋ก๋ ์นด๋ฉ๋ผ์ RTSP๋ฅผ ๋ฐ์์ ์ค์๊ฐ ์ถ๋ก ์ ํ ์ ์๊ฒ๋ ๋ง๋๋ ๊ฒ์ ์๊ฐํ์๋ค
์ฑ์ด๋ ์บ ๋ฑ์ผ๋ก ์ค์๊ฐ ์์์ AI ์ถ๋ก ์ ์ถ๊ฐํ๊ณ ๋ ธํฐ๋ฅผ ์ฃผ๋ ๋ฑ์ ๋ค์ํ ํํ๊ฐ ๋ ์ ์์ ๊ฒ์ด๋ค
์ด๋ฒ์๋ ๋ง์ ๋ถ๋ถ๋ค๊น์ง ๋ค๋ฃจ๊ณ ๊ตฌํํด๋ณด์ง ๋ชปํ์ผ๋ ํ๋ผ์คํฌ๋ฅผ ์ด์ฉํด ์น์ ์ ๋ฐ์ ์ธ ํ๋ฆ์ ๋ณผ ์ ์์ด์ ์ ๋ง ์ ์ตํ๋ค
๋ค์์๋ ์ด ๊ฒฝํ๋ค์ ๋ฐํ์ผ๋ก ๋ ๋ง์ ๋ถ๋ถ๋ค์ ๋น ๋ฅด๊ฒ ๋ง๋ค์ด๋ด์ผ๊ฒ ๋ค