Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 313 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,313 @@
# Frontend
# 두피어나 <img alt="두피어나" src="https://github.com/user-attachments/assets/39d7deb7-d0cb-4523-a592-a383fc921dc3" width="100" alt="BULK" src="https://github.com/user-attachments/assets/f3572efa-c327-48b6-914c-87b8fe90d837" align="left" />
한번의 터치로 두피를 진단하다.
<br/><br/>

## **🪮 프로젝트 개요 및 소개**
**두피어나**는 빗으로 두피 상태를 쉽고 빠르게 진단하고, 맞춤형 두피 케어 솔루션을 제공하는 AI 기반의 두피 분석 서비스입니다. **매일 사용하는 빗**을 활용하여 별도의 복잡한 과정 없이 매일 두피 상태를 확인할 수 있도록 도와주는 서비스입니다.

<img width="1920" alt="두피어나" src="https://github.com/user-attachments/assets/febf238e-1cb0-4a50-9090-1f7e6a62a412"/>
</br>

---

## 💡 개발 배경 및 필요성
### 1) 프로젝트 개발 동기 및 목적
<img height="500" alt="프로젝트 개발 동기 및 목적" src="https://github.com/user-attachments/assets/a5fdec17-08d8-4502-902a-a4c4fb49e267" />

- **건강보험심사평가원**의 조사에 따르면 국내 탈모 환자의 수는 2018년 22만 4,840명에서 2022년 24만 7,915명으로 지속적인 증가 추세로, 현대인들은 두피 염증, 비듬, 지루성 두피염 등 다양한 **두피 문제를 호소**하고 있습니다.
- 그러나 정확한 진단을 위해 병원을 방문해야 하는 등 번거로움과 비용, 시간적 제약으로 인해 꾸준한 관리가 어려운 현실입니다.
- 이러한 문제점을 해결하기 위해 **매일 하는 평범한 빗질**에 **하드웨어**와 **AI**를 결합해집에서도 손쉽게 두피 상태를 진단기록하고 개인 맞춤형 관리 솔루션을 제공하는 를 개발하게 되었습니다.

### 2) 프로젝트 특·장점
- 별도의 장비나 병원 방문 없이 두피 상태를 간편하게 진단이 가능합니다.
- 인공지능 분석으로 가성비 높은 맞춤형 두피 관리 솔루션을 제공합니다.
- 올리브영 등 쇼핑몰과 연동하여 최적의 케어 샴푸 등을 추천해줍니다.
- 모바일 앱으로 실시간 진단 결과 확인 및 두피 이력 관리가 가능합니다.

---

## ⚙️ 주요 기능
### 1) 🪮 SmartBrush를 활용한 두피 진단
SmartBrush에 내장된 카메라와 센서로 두피 이미지를 촬영하고, AI가 **각질·유분·민감도·밀도·두께·탈모**를 종합적으로 분석하여 **양호, 경증, 중등도, 중증으로 진단**합니다.
이를 통해 사용자는 **가정에서도 자신의 두피 상태를 빠르고 간단하게 알 수 있습니다.**
<table>
<tr>
<td>
<img height="300" alt="Smartbrush" src="https://github.com/user-attachments/assets/7ee1de0e-c8ed-420a-a3e6-4af2d977d956" />
</td>
<td>
<img height="300" alt="결과페이지" src="https://github.com/user-attachments/assets/01096816-8daf-403e-85a6-de463fec3efa" />
</td>
</tr>
</table>

### 2) 🧴 두피 MBTI별 제품 추천
사용자의 두피 진단 결과를 한눈에 이해할 수 있도록 **두피 MBTI(트러블 폭풍형, 지성 민감형, 민감 건조형, 지성 비듬형, 건조 비듬형, 건조 트러블형, 깔끔 지성형, 밸런스형)로 유형을 분류**합니다. 분류된 MBTI 유형에 맞춰 샴푸, 린스, 트리트먼트/팩, 두피 토닉, 헤어 에센스를 **맞춤 추천**합니다.
<table>
<tr>
<td>
<img height="300" alt="추천제품" src="https://github.com/user-attachments/assets/2ece0a95-8bc5-496a-83ec-c0a098cd9e14" />
</td>
<td>
<img height="300" alt="추천제품상세페이지" src="https://github.com/user-attachments/assets/0a4ad2a9-af41-48e3-a372-3ebf3779fde9" />
</td>
</tr>
</table>

### 3) ✅ 두피 관련 질문과 진단을 통한 습관 챌린지
두피 관련 질문과 진단을 통해 사용자의 두피 상태와 생활 습관을 분석하고, 결과에 따라 **습관 챌린지**를 매일 다르게 추천합니다. 챌린지는 **LIFESTYLE**(수면·스트레스·활동 습관 개선), **SCALP**(세정·건조·스타일링 루틴), **NUTRITION**(영양 균형·섭취 가이드) 세 가지 카테고리로 구성되며, 하루에 각 2개씩 제공됩니다.
<table>
<tr>
<td>
<img height="300" alt="습관챌린지" src="https://github.com/user-attachments/assets/d378b2fe-542c-43e1-98d8-a61087cffe15"" />
</td>
<td>
<img height="300" alt="질문페이지" src="https://github.com/user-attachments/assets/238d7fab-b961-4152-9d46-b298988498d3" />
</td>
</tr>
</table>

### 4) 📜 두피 월별 레포트
사용자가 매일 SmartBrush로 진단한 누적 진단 데이터를 기반으로 월별 변화 추이를 시각화하여 보여줍니다. 사용자의 진단 데이터가 매일 저장되고 한 달 단위로 쌓이게 되면, **개인별 기준선이 형성**되어 **더 정확하고 신뢰도 높은 판단이 가능**해집니다.
<table>
<tr>
<td>
<img height="300" alt="월별레포트" src="https://github.com/user-attachments/assets/0d7dd15a-8a54-410e-88f8-02e0a059223c" />
</td>
</tr>
</table>

### 5) 🗓️ 진단 캘린더
두피 분석 캘린더를 통해 사용자는 **월별 두피 상태 변화를 한눈에 확인**할 수 있습니다. 또한 원하는 날짜를 클릭하면 해당일의 진단 결과를 상세히 확인할 수 있습니다.
<table>
<tr>
<td>
<img height="300" alt="달력" src="https://github.com/user-attachments/assets/64322bd7-07c5-4a87-a875-f685342f3c2e" />
</td>
<td>
<img height="300" alt="달력_진단후" src="https://github.com/user-attachments/assets/a5ccddd2-267c-470e-9bfb-93a89afd9f59" />
</td>
</tr>
</table>

---

## ✨ 기대효과 및 활용분야
- **일상 속 두피 진단** : 빗 하나로 언제 어디서나 간편하게 두피 상태 확인이 가능합니다.
- **전문 서비스 대체** : 고가 장비 없이 정밀 진단 가능, 비용·시간 부담 해소됩니다.
- **산업 연계 확장** : 뷰티샵·클리닉·의료기관과 협업 가능한 플랫폼 확장이 가능합니다.
- **기업 연동 강화** : 쇼핑몰과 연계해 제품 추천 및 구매까지 연결이 가능합니다.
- **개인화 관리 지원** : 데이터 기반 맞춤 케어와 두피 관리 가이드 제공이 가능합니다.

---

## 🛠️ 기술 스택
- 프론트엔드<br/>
[![My Skills](https://skillicons.dev/icons?i=typescript,tailwind)](https://skillicons.dev)

- 백엔드<br/>
[![My Skills](https://skillicons.dev/icons?i=java,spring,mysql)](https://skillicons.dev)

- AI<br/>
[![My Skills](https://skillicons.dev/icons?i=python,flask)](https://skillicons.dev)

- 하드웨어<br/>
[![My Skills](https://skillicons.dev/icons?i=arduino,cpp)](https://skillicons.dev)

- 배포 및 관리<br/>
[![My Skills](https://skillicons.dev/icons?i=aws,redis,s3)](https://skillicons.dev)

---

## 👥 팀원
| 김희원 | 남시윤 | 박효진 | 장다연 |
|:------:|:------:|:------:|:------:|
| <img width="120px" src="https://avatars.githubusercontent.com/heeone1" /> |<img width="120px" src="https://avatars.githubusercontent.com/nadomola" />| <img width="120px" src="https://avatars.githubusercontent.com/phjlia2430" /> | <center><img width="120px" src="https://avatars.githubusercontent.com/noeyadd" /> |
| <center> [@heeone1](https://github.com/heeone1) </center> | <center> [@nadomola](https://github.com/nadomola) </center> | <center> [@phjlia2430](https://github.com/phjlia2430) </center> | <center> [@noeyadd](https://github.com/noeyadd) </center> |



---
## 💡 시스템 구성도
### 🔹 아키텍처 구조
<img height="400" alt="아키텍처 구조" src="https://github.com/user-attachments/assets/6c26541a-9781-41a4-88ee-ba2b50ff4557" />

### 🔹 하드웨어 구조
<table>
<tr>
<td style="text-align:center; font-weight:bold;">
하드웨어 설계도
</td>
<td style="text-align:center; font-weight:bold;">
SmartBrush 내부
</td>
</tr>
<tr>
<td>
<img height="400" alt="하드웨어설계도" src="https://github.com/user-attachments/assets/831afe5b-e3a9-4aea-b808-303af854e55e" />
</td>
<td>
<img height="400" alt="Smartbrush내부" src="https://github.com/user-attachments/assets/ef77a921-1543-48dc-abf9-4821b81a224c" />
</td>
</tr>
</table>

---

## 🎥 작품 소개 영상
[![시연 영상 보기](https://img.youtube.com/vi/UiVeHTItPyk/0.jpg)](https://youtu.be/cVYWDxybcYY?si=ZxseDR4PNyukEOeK)

---
## 🖥️ 핵심 소스코드
<details>
<summary><h3>UV 측정 및 이미지 자동 업로드</h3></summary>

```C++
Adafruit_VEML6070 uvSensor = Adafruit_VEML6070();
float uvEMA = 0.0f, meanUV = 0.0f, varUV = 0.0f;
uint16_t baseUV = 0;

void calibrateUV() {
setIT(IT_8T);
uint32_t sum = 0;
for (int i = 0; i < CALI_SAMPLES; ++i) {
sum += uvSensor.readUV();
delay(20);
}
baseUV = sum / CALI_SAMPLES;
uvEMA = baseUV;
meanUV = baseUV;
varUV = fmaxf(5.0f, (float)baseUV * 0.2f);
}

String getUVState(uint16_t uv) {
float sigma = sqrtf(fmaxf(1.0f, varUV));
float z = ((float)uv - (float)baseUV) / fmaxf(1.0f, sigma);
if (z <= 1.0f) return "건성";
else if (z <= 2.5f) return "보통";
else return "지성";
}

~~

void loop() {
uint16_t raw = uvSensor.readUV();
float sigma = sqrtf(fmaxf(1.0f, varUV));
float z = ((float)raw - (float)baseUV) / fmaxf(1.0f, sigma);
float deltaRise = (float)raw - uvEMA;

if (armed && (z >= Z_ON || deltaRise >= DELTA_ON)) {
uint16_t medianUV; float burstEMA;
burstMeasure(medianUV, burstEMA);
String state = getUVState(medianUV);
sendUVToServer(medianUV, state);
captureAndUploadImage();
armed = false;
lastTriggerMs = millis();
} else {
sampleAmbientSlow(raw);
}

delay(IDLE_POLL_MS);
}
```
</details>


<details> <summary><h3>두피 진단 AI 코드</h3></summary>

```python
from flask import Flask, request, jsonify
import torch
from efficientnet_pytorch import EfficientNet
from torchvision import transforms
from PIL import Image
import numpy as np

torch.serialization.add_safe_globals({'EfficientNet': EfficientNet})

app = Flask(__name__)

# Load all models
model_paths = {
"미세각질": "model1_full.pt",
"탈모": "model2_full.pt",
"모낭사이홍반": "model3_full.pt",
"모낭홍반농포": "model4_full.pt",
"비듬": "model5_full.pt",
"피지과다": "model6_full.pt",
"모발밀도": "model7_full.pt"
}

models = {}
for name, path in model_paths.items():
m = torch.load(path, map_location=torch.device('cpu'), weights_only=False)
m.eval()
models[name] = m

# Preprocessing
transform = transforms.Compose([
transforms.Resize([600, 600]),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])

@app.route("/ai", methods=["POST"])
def ai():
# image 필드로 여러 개 업로드
files = request.files.getlist('image')
if not files:
return jsonify({'error': 'No image provided'}), 400

# 질환별 확률 누적
sum_probs = {disease: None for disease in models.keys()}
valid_count = 0

with torch.inference_mode():
for f in files:
try:
image = Image.open(f.stream).convert('RGB')
except Exception:
# 잘못된 이미지는 평균에서 제외
continue

x = transform(image).unsqueeze(0) # [1, 3, H, W]

for disease, model in models.items():
logits = model(x) # [1, C]
prob = torch.softmax(logits[0], dim=0).cpu().numpy() # [C]

if sum_probs[disease] is None:
sum_probs[disease] = prob.copy()
else:
sum_probs[disease] += prob

valid_count += 1

if valid_count == 0:
return jsonify({'error': 'All images were invalid'}), 400

# 평균 확률 → 최종 결과
results = {}
for disease, s in sum_probs.items():
mean_prob = s / float(valid_count)
pred = int(np.argmax(mean_prob))
conf = float(mean_prob[pred])
results[disease] = {
"class_index": pred,
"confidence": round(conf, 3)
}

return jsonify({
"count": valid_count, # 평균에 사용된 유효 이미지 수
"results": results
})

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
#app.run(host="0.0.0.0", port=8000)
```

</details>

Loading