+ | ShyEye without anxiety when surrounded

This commit is contained in:
NotArt 2025-03-07 09:05:21 +01:00
parent e3ab5fe8dc
commit ccd8993bf5
2 changed files with 246 additions and 0 deletions

116
ShyEye/eyeMove/eyeMove.ino Normal file
View File

@ -0,0 +1,116 @@
#include "Servo.h"
/*
These are just indications used to understand what value is passed through Serial to Arduino
*/
// Move to the left
#define SERVO_LMOV 0
#define SERVO_LUMOV 1
#define SERVO_LDMOV 2
// Move to the right
#define SERVO_RMOV 3
#define SERVO_RUMOV 4
#define SERVO_RDMOV 5
// Don't move x axis
#define SERVO_NOMOV 6
#define SERVO_UMOV 7
#define SERVO_DMOV 8
#define LOOK_LEFT -45
#define LOOK_RIGHT 45
#define LOOK_UP 45
#define LOOK_DOWN -45
#define START_POS 90
Servo servoX;
Servo servoY;
int eyeDir;
bool isServoXAttached = true;
bool isServoYAttached = true;
void setup() {
servoX.attach(9); // Attach servoX to pin 9
servoY.attach(10); // Attach servoY to pin 10
servoX.write(START_POS);
servoY.write(START_POS);
delay(1000);
Serial.begin(9600);
Serial.setTimeout(1);
// Used for debugging
pinMode(LED_BUILTIN, OUTPUT);
}
void stopServo(Servo &servo, bool &isAttached) {
if (isAttached) { // Only detach if it's currently attached
servo.detach();
isAttached = false;
}
}
void moveServo(Servo &servo, int angle, bool &isAttached, int pin) {
if (!isAttached) { // Only attach if it's currently detached
servo.attach(pin);
isAttached = true;
}
servo.write(angle);
}
void loop() {
while (!Serial.available());
eyeDir = Serial.readString().toInt();
switch (eyeDir) {
case SERVO_LMOV:
moveServo(servoX, START_POS + LOOK_LEFT, isServoXAttached, 9);
stopServo(servoY, isServoYAttached);
break;
case SERVO_LUMOV:
moveServo(servoX, START_POS + LOOK_LEFT, isServoXAttached, 9);
moveServo(servoY, START_POS + LOOK_UP, isServoYAttached, 10);
break;
case SERVO_LDMOV:
moveServo(servoX, START_POS + LOOK_LEFT, isServoXAttached, 9);
moveServo(servoY, START_POS + LOOK_DOWN, isServoYAttached, 10);
break;
case SERVO_RMOV:
moveServo(servoX, START_POS + LOOK_RIGHT, isServoXAttached, 9);
stopServo(servoY, isServoYAttached);
break;
case SERVO_RUMOV:
moveServo(servoX, START_POS + LOOK_RIGHT, isServoXAttached, 9);
moveServo(servoY, START_POS + LOOK_UP, isServoYAttached, 10);
break;
case SERVO_RDMOV:
moveServo(servoX, START_POS + LOOK_RIGHT, isServoXAttached, 9);
moveServo(servoY, START_POS + LOOK_DOWN, isServoYAttached, 10);
break;
case SERVO_UMOV:
stopServo(servoX, isServoXAttached);
moveServo(servoY, START_POS + LOOK_UP, isServoYAttached, 10);
break;
case SERVO_DMOV:
stopServo(servoX, isServoXAttached);
moveServo(servoY, START_POS + LOOK_DOWN, isServoYAttached, 10);
break;
default:
stopServo(servoX, isServoXAttached);
stopServo(servoY, isServoYAttached);
break;
}
}

130
ShyEye/faceDetection.py Normal file
View File

@ -0,0 +1,130 @@
import cv2
import serial
import time
# Used for debug mainly, set to True to see what the camera is recording
DRAW_CAMERA = True
"""
These are just indications used to understand what value is passed through Serial to arduino
In the arduino code, same values will be used but as defines
Setting those in this order enables me to only do +1/+2 to choose up or down in addition to left/right direction
"""
# Move to the left
SERVO_LMOV = 0
SERVO_LUMOV = 1
SERVO_LDMOV = 2
# Move to the right
SERVO_RMOV = 3
SERVO_RUMOV = 4
SERVO_RDMOV = 5
# Don't move x axis
SERVO_NOMOV = 6
SERVO_UMOV = 7
SERVO_DMOV = 8
SERVO_SPD = 2
def draw_square(frame, faces, color):
for (x, y, w, h) in faces:
face_region = frame[y:y+h, x:x+w]
cv2.rectangle(frame, (x, y), (x + w, y + h), color, 1)
return frame
arduino = serial.Serial(port='/dev/ttyACM0', baudrate=9600, timeout=.1)
video_capture = cv2.VideoCapture(0)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
side_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_profileface.xml")
detected_faces = []
detected_profiles = []
ret, frame = video_capture.read()
if ret:
frameH, frameW = frame.shape[:2]
eyeX = int (frameW / 2)
eyeY = int (frameH / 2)
while True:
ret, frame = video_capture.read()
if not ret:
break
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
detected_faces = faces
faces = side_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
detected_profiles = faces
target = [int ((frameW / 2) - 10), int ((frameH / 2) - 10), 20, 20]
eyeDir = SERVO_NOMOV
if len(detected_faces) >= 3 :
# anxiety code
feur = 3
elif len(detected_profiles) != 0 and len(detected_faces) != 0 :
arduino.write(bytes(f"{SERVO_NOMOV}", "utf-8"))
else :
if len(detected_profiles) != 0 and len(detected_faces) == 0 :
# get target as the biggest face detected
for face in detected_profiles :
if face[3] > target[3] : # deep copy as I want to modify target w/out modifying detected_profiles
target[0] = face[0]
target[1] = face[1]
target[2] = face[2]
target[3] = face[3]
# define target as the center of selected face
target[0] = int (target[0] + (target[2] / 2) - 10)
target[1] = int (target[1] + (target[3] / 2) - 10)
target[2] = 20
target[3] = 20
# adjust the 'eye' position as the camera turn around
if eyeX < target[0] or eyeX > (target[0] + target[2]) :
if eyeX < target[0] :
eyeX += SERVO_SPD
eyeDir = SERVO_LMOV
else :
eyeX -= SERVO_SPD
eyeDir = SERVO_RMOV
else :
eyeDir = SERVO_NOMOV
if eyeY < target[1] or eyeY > (target[1] + target[3]) :
if eyeY < target[1] :
eyeY += SERVO_SPD
eyeDir += 2
else :
eyeY -= SERVO_SPD
eyeDir += 1
arduino.write(bytes(f"{eyeDir}", "utf-8"))
if DRAW_CAMERA :
# draw detected_faces/profiles on green/blue
frame = draw_square(frame, detected_faces, (0, 255, 0))
frame = draw_square(frame, detected_profiles, (255, 0, 0))
# draw target rectangle in red
cv2.rectangle(frame, (target[0], target[1]), (target[0] + target[2], target[1] + target[3]), (0, 0, 255), 1)
# draw crosshair in white
cv2.rectangle(frame, (eyeX - 5, eyeY), (eyeX + 5, eyeY), (255, 255, 255), 1)
cv2.rectangle(frame, (eyeX, eyeY - 5), (eyeX, eyeY + 5), (255, 255, 255), 1)
# open a window and show recorded video
cv2.imshow("Faces", frame)
if cv2.waitKey(1) != -1:
break
video_capture.release()
cv2.destroyAllWindows()