Sağlık · Yüz noktaları (Face Mesh)
❤️ Kameradan nabız (rPPG)
Yüz rengindeki görünmez değişimden kalp atışını tahmin et.
Kalp her attığında yüzdeki kılcal damarlara biraz daha kan gelir ve deri, gözle görülmeyecek kadar az, yeşil ışığı daha çok soğurur. Uzaktan fotopletismografi (rPPG) bu küçük değişimi kamerayla ölçer. Sinyal çok zayıftır (piksel değerinde 1–2 birim), bu yüzden binlerce pikselin ortalaması alınır ve sinyal işleme yapılır.
Not: Eğitim amaçlıdır, tıbbi değildir. Teşhis ya da sağlık kararı için kullanma; ölçüm ışığa, harekete ve ten rengine göre hatalı olabilir.
Durum: …
Örnek sahne: çizilmiş bir yüz, derisinin yeşil kanalı gözle görülmeyecek kadar az (±2 birim) 68 atım/dk ile değişir; üstüne ışık titremesi, yavaş parlaklık kayması ve küçük baş hareketi eklenmiştir. Ölçüm, çizilen piksellerden gerçekten yapılır.
Nasıl çalışıyor?
- Nokta bulma: MediaPipe Face Mesh ile 478 yüz noktası; alın noktası (151) çevresinde, yüz genişliğinin %36 × %14'ü kadar bir ölçüm alanı (ROI). Alın, az hareket eden ve saçsız bir bölgedir.
- Ham sinyal: her karede ROI içindeki piksellerin yeşil kanal ortalaması ve zamanı kaydedilir.
- Ön işleme: 30 Hz'e eşit aralıklı yeniden örnekleme, normalize etme (g / ortalama − 1), 0,7–3 Hz bant geçiren süzgeç (42–180 atım/dk dışını atar; ileri-geri uygulanır, faz kaymaz).
- Frekans analizi: Hann pencereli Fourier dönüşümü ile 0,5 atım/dk adımlarla güç spektrumu; en yüksek tepe parabolik aradeğerlemeyle inceltilir. BPM = tepe frekansı × 60.
- Güven: tepe gücünün bandın geri kalanına oranı (SNR). SNR düşükse sonuç gösterilir ama "zayıf sinyal" uyarısı verilir.
Kendin dene
Kamera modunda iyi ışıkta, kıpırdamadan 15 sn bekle; sonra parmağını bileğine koyup 15 sn say ve ×4 yap. Başını sallarken ya da ışığı kısınca SNR'ye ne oluyor? Analiz penceresini 6 sn'ye indir: sonuç daha hızlı ama daha oynak.
Python karşılığı (OpenCV / MediaPipe)
import cv2, numpy as np, mediapipe as mpfrom scipy.signal import butter, filtfiltface = mp.solutions.face_mesh.FaceMesh(max_num_faces=1)cap = cv2.VideoCapture(0); FS = cap.get(cv2.CAP_PROP_FPS) or 30greens = []while cap.isOpened():ok, frame = cap.read()if not ok: breakh, w = frame.shape[:2]res = face.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))if res.multi_face_landmarks:lm = res.multi_face_landmarks[0].landmarkfw = abs(lm[454].x - lm[234].x) * w # yüz genişliğicx, cy = lm[151].x * w, lm[151].y * h # alınx0, y0 = int(cx - 0.18 * fw), int(cy - 0.07 * fw)roi = frame[y0:y0 + int(0.14 * fw), x0:x0 + int(0.36 * fw)]greens.append(roi[:, :, 1].mean()) # yeşil kanal (BGR'de 1)if len(greens) >= FS * 10:x = np.array(greens[-int(FS * 10):]); x = x / x.mean() - 1b, a = butter(2, [0.7, 3.0], btype="band", fs=FS)y = filtfilt(b, a, x) * np.hanning(len(x))freqs = np.fft.rfftfreq(len(y) * 8, 1 / FS); power = np.abs(np.fft.rfft(y, len(y) * 8)) ** 2band = (freqs >= 0.7) & (freqs <= 3.0)bpm = freqs[band][power[band].argmax()] * 60cv2.putText(frame, f"{bpm:.0f} BPM (tibbi degil)", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) # not medicalcv2.imshow("rppg", frame)if cv2.waitKey(1) == 27: break
🔒 Kamera görüntüsü yalnız bu cihazda işlenir; hiçbir yere gönderilmez ve kaydedilmez. Sekmeden ya da sayfadan çıkınca kamera kapanır.