# ====================================================================
# DISCLAIMER:
# This code is provided as-is for educational and experimental
# purposes only. The author makes no representations or warranties of
# any kind concerning the safety, suitability, or accuracy of this
# code. Use at your own risk. The author assumes no liability for any
# damages, system failures, security breaches, or network issues
# resulting from the use or implementation of this script.
# ====================================================================
import cv2
import time
from picamera2 import Picamera2
import mediapipe as mp
import numpy as np
#################################
import board
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import time
import neopixel_spi as neopixel
import colorsys
spi=board.SPI()
LC = 12
PO = neopixel.GRB
strip = neopixel.NeoPixel_SPI(spi, LC,
pixel_order=PO,auto_write=False)
strip.fill(0)
strip.show()
wOLED = 128
hOLED = 64
I2C_ADDRESS = 0x3C
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(wOLED, hOLED, i2c, addr = I2C_ADDRESS)
fontLarge = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",14)
#################################
W=1280
H=720
tStart = time.time()
fps = 0
piCam = Picamera2(1)
piCam.preview_configuration.main.size = (W, H)
piCam.preview_configuration.main.format = "RGB888"
piCam.preview_configuration.controls.FrameRate = 60
piCam.preview_configuration.align()
piCam.configure("preview")
piCam.start()
textLowerLeft = (int(W*.01),int(H*.05))
fontFace = cv2.FONT_HERSHEY_SIMPLEX
fontThickness = int(W/425)
fontScale = H*.0015
fontColor = (0,0,255)
faceMesh = mp.solutions.face_mesh.FaceMesh(
max_num_faces=1,
refine_landmarks=True,
min_detection_confidence=0.5,
min_tracking_confidence=0.5)
cv2.namedWindow('Camera',cv2.WINDOW_GUI_NORMAL)
cv2.moveWindow('Camera',0,65)
cv2.resizeWindow('Camera',W,H)
while True:
deltaT = time.time() - tStart
tStart=time.time()
fps = fps*.95 + (1/deltaT)*.05
frame = piCam.capture_array()
frame = cv2.flip(frame, -1)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = faceMesh.process(rgb)
if results.multi_face_landmarks:
for faceLandMarks in results.multi_face_landmarks:
#print(faceLandmarks)
image = Image.new("1",(wOLED,hOLED))
draw = ImageDraw.Draw(image)
lmAll = []
j=0
for lm in faceLandMarks.landmark:
xOLED = int(lm.x*wOLED)
yOLED = int(lm.y*hOLED)
draw.point((xOLED,yOLED), fill = 255)
#print(xOLED,yOLED)
lmAll.append((xOLED,yOLED))
x = int(lm.x*W)
y = int(lm.y*H)
#cv2.putText(frame,str(j),(x,y),fontFace, .3, (0,255,0),1)
j = j+1
for idx in mp.solutions.face_mesh.FACEMESH_CONTOURS:
#print(idx)
lineStart = idx[0]
lineEnd = idx[1]
pt1 = lmAll[lineStart]
pt2 = lmAll[lineEnd]
draw.line((pt1[0],pt1[1],pt2[0],pt2[1]), width=1, fill = 255)
keyLM = faceLandMarks.landmark
headRight = 323
headLeft = 93
headWidth = keyLM[headRight].x -keyLM[headLeft].x
cv2.putText(frame,str(headRight),(int(keyLM[headRight].x*W),int(keyLM[headRight].y*H)),fontFace, .3, (0,255,0),1)
cv2.putText(frame,str(headLeft),(int(keyLM[headLeft].x*W),int(keyLM[headLeft].y*H)),fontFace, .3, (0,255,0),1)
leftEyeTop = 159
leftEyeBottom = 145
leftEyeHeight =keyLM[leftEyeBottom].y -keyLM[leftEyeTop].y
leftEyeLeft = 130
leftEyeRight = 154
leftEyeWidth = keyLM[leftEyeRight].x -keyLM[leftEyeLeft].x
leftEyeRatio = leftEyeHeight/leftEyeWidth*100
rightEyeTop = 385
rightEyeBottom = 253
rightEyeHeight = keyLM[rightEyeBottom].y -keyLM[rightEyeTop].y
rightEyeLeft = 465
rightEyeRight = 249
rightEyeWidth = keyLM[rightEyeRight].x -keyLM[rightEyeLeft].x
rightEyeRatio = rightEyeHeight/rightEyeWidth*100
eyeRatio = (rightEyeRatio +leftEyeRatio)/2
mouthLeft = 61
mouthRight = 291
mouthTop = 13
mouthBottom = 15
mouthWidth = keyLM[mouthRight].x-keyLM[mouthLeft].x
mouthHeight = keyLM[mouthBottom].y-keyLM[mouthTop].y
mouthWidthRatio = mouthWidth/headWidth *100
mouthHeightRatio = mouthHeight/headWidth*100
print(round(mouthWidthRatio,1),round(mouthHeightRatio,1),round(eyeRatio,1))
color = (255,255,0)
emotion = 'NEUTRAL'
if mouthWidthRatio >37 and mouthHeightRatio>4 and eyeRatio<74:
emotion = 'SMILE'
color = (0,255,0)
if mouthWidthRatio <37 and mouthHeightRatio>10 and eyeRatio>70:
emotion = 'SURPRISED'
color = (0,255,255)
if mouthWidthRatio <32 and mouthHeightRatio<4 and eyeRatio<57:
emotion = 'ANGRY'
color = (0,0,255)
cv2.putText(frame, emotion, (int(W*.1),int(H*.15)),
fontFace, 2.2, color, 4)
strip.fill((color[2],color[1],color[0]))
strip.show()
#print('XXXXXXXXXXXXXXXXXXx')
draw.text((0,0),emotion,font=fontLarge,fill=255)
oled.image(image)
oled.show()
#print()
# if results.multi_face_landmarks:
# for faceLandmarks in results.multi_face_landmarks:
# # Optional second pass for glow effect
# mp.solutions.drawing_utils.draw_landmarks(
# image=frame,
# landmark_list=faceLandmarks,
# connections=mp.solutions.face_mesh.FACEMESH_TESSELATION,
# landmark_drawing_spec=None,
# connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(
# color=(100, 100, 100),
# thickness=1
# )
# )
# # === Best looking translucent mesh ===
# mp.solutions.drawing_utils.draw_landmarks(
# image=frame,
# landmark_list=faceLandmarks,
# connections=mp.solutions.face_mesh.FACEMESH_CONTOURS,
# landmark_drawing_spec=None, # Hide dots
# connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(
# color=(0, 0, 180), # Bright cyan/teal
# thickness=7
# )
# )
#
# # Optional: Extra detail on irises (makes eyes look better)
# mp.solutions.drawing_utils.draw_landmarks(
# image=frame,
# landmark_list=faceLandmarks,
# connections=mp.solutions.face_mesh.FACEMESH_IRISES,
# landmark_drawing_spec=None,
# connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec(
# color=(255, 0, 0),
# thickness=7
# )
# )
myText = "FPS: "+str(round(fps,1))
cv2.putText(frame,myText,textLowerLeft,fontFace,fontScale,fontColor,fontThickness)
cv2.imshow("Camera", frame)
if cv2.waitKey(1) == ord('q'):
break
cv2.destroyAllWindows()
piCam.stop()
oled.fill(0)
oled.show()
strip.fill(0)
strip.show()