얼굴 사진 한 장으로 피부 트러블 정도와 퍼스널 컬러 시즌을 진단하고, Gemini의 자연어 코멘트와 함께 네이버 쇼핑 화장품을 추천하는 미니 웹 서비스.
학습 목적으로 4일간(2026-03-20 ~ 2026-03-24) 진행한 개인 ML 프로젝트입니다.
Forked from jungIH5/miniproject — Flask 스타터 구조만 가져오고, ML/CV 파이프라인 및 외부 API 연동은 직접 구현했습니다.
- 📷 얼굴 사진 업로드 → 트러블 정도 3-class 분류 (
심함 / 약간 / 없음) - 🎨 LAB 색공간 분석으로 퍼스널 컬러 시즌(봄·여름·가을·겨울) 자동 판정
- 🤖 Gemini API로 결과에 대한 자연어 코멘트 생성
- 🛍 네이버 쇼핑 API로 진단 결과에 맞는 화장품 추천
- 👤 회원 가입 / 로그인 / 진단 이력 관리 (MySQL)
Face image
│
▼
[1] OpenCV Haar Cascade ── 얼굴 존재 검증 (없으면 차단)
▼
[2] MediaPipe FaceMesh ── 랜드마크 → 볼(cheek) 영역 크롭
└─ fallback: Cascade ROI 사용
▼
[3] HSV 피부 마스킹 ── 머리카락 / 눈 / 배경 제외
▼
[4] Gray World 보정 ── 형광등·자연광 색온도 차이 정규화
▼
[5-A] CNN 추론 ── 트러블 3-class 확률
[5-B] LAB 색공간 분석 ── L(밝기) / 균일도 / a(붉은기) / a+b(언더톤)
▼
[6] 확신도 < 50% → CNN + OpenCV 가중 평균 블렌딩
▼
Output: 트러블 점수 + 퍼스널 컬러 시즌
| 항목 | 내용 |
|---|---|
| Base model | MobileNetV2 (torchvision) |
| Head 교체 | nn.Linear(1280, 3) — 마지막 classifier만 swap |
| Fine-tuned weight | app/models/skin_model.pth (PyTorch state_dict) |
| 클래스 | ["심함", "약간", "없음"] |
| 입력 | 224×224, ImageNet mean/std normalize |
| 추론 디바이스 | CPU only |
ML / CV PyTorch · MobileNetV2 · OpenCV · MediaPipe · NumPy Backend Flask · MySQL 8 · SQLAlchemy Frontend HTML · CSS · Vanilla JavaScript External API Google Gemini · Naver Shopping Infra Docker · Docker Compose
cp .env.example .env # GEMINI_API_KEY, NAVER_CLIENT_ID/SECRET 입력
docker compose up --build
# → http://localhost:5000이 레포는 fork 시작점만 빌렸고, 아래 부분은 본인이 직접 설계·구현했습니다.
- 모델 fine-tuning — MobileNetV2 head swap → 트러블 3-class state_dict 학습/저장/로드
- CV 전처리 파이프라인 — Haar Cascade → MediaPipe FaceMesh → HSV 마스킹 → Gray World 보정 단계 설계
- Robust 처리 — 얼굴 없는 사진 차단, MediaPipe 실패 시 Cascade fallback, CNN 확신도 기반 블렌딩
- 퍼스널 컬러 판정 — LAB 색공간 기반 4계절 시즌 자동 분류 룰 설계
- 외부 API 통합 — Gemini 자연어 코멘트, Naver Shopping 화장품 추천 매칭 로직
- 풀스택 구성 — Flask + MySQL 8 + Docker Compose 직접 세팅, 회원/이력 시스템
- 클래스 불균형 보정 (현재 라벨 분포 기준 가중치 조정)
- LAB 외 추가 색공간(YCbCr 등) 비교 실험
- MediaPipe → MTCNN 등 더 강건한 얼굴 검출기 비교
- ONNX export로 추론 latency 단축
- 진단 이력 기반 개인화 추천 (현재는 단일 이미지 기준)
CREATE TABLE TB_CS_MEMBERS (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
phone VARCHAR(20) UNIQUE,
birthdate DATE,
gender ENUM('male','female','other') DEFAULT 'other',
status ENUM('active','inactive','deleted') DEFAULT 'active',
last_login TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);시드 데이터는
database/init/seed.sql참고.
app/
models/skin_model.pth # fine-tuned MobileNetV2
static/ (css, js)
templates/
__init__.py config.py db.py routes.py
database/
init/ # DDL + seed.sql
app.py
Dockerfile
docker-compose.yml
requirements.txt