Related
Here's a picture of a maze solver, when the BFS function goes over the image it goes around the image
I know it's possible to just crop the image manually, but I want a function where the computer can automatically detect non black edges and delete them. How would I do this?
#Program that will read an image and pass through to folder using OpenCV, select starting and ending points, and solve the maze using a BFS function
#will be imported on to flask backend
'''
Requirements: pip3 install opencv-python (or contrib version depending on console)
'''
#libraries
import imghdr
import cv2
import numpy as np
import threading
import colorsys
#first we will read the image, and countour it to a gray image so reading the image will be easier
#set threshhold value
img = cv2.imread("Mazes/maze3.jpg", cv2.IMREAD_GRAYSCALE)
_, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
h, w = img.shape[:2]
#seperate function to display image
def solution():
global img
#show image
cv2.imshow("Maze Solver", img)
#run mouse pointer function for user to click points
cv2.setMouseCallback('Maze Solver', maze_points)
while True:
#after thread is ran display image
cv2.imshow("Maze Solver", img)
cv2.waitKey(1)
#this is a function that will map the cursor as a point so user can press twice to determine the start and end points
class Point(object):
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
#variable that will store the # of times mouse is clicked
num_of_clicks = 0
#determine how big pointer will appear on screen
mouse_click_size = 2
#initialize start and end pointer so user clicks
start_point = Point()
end_point = Point()
#subtree that will point nodes in directions that they go in
subtree = [Point(0, -1), Point(0, 1), Point(1, 0), Point(-1, 0)]
#this function is to call the mouse to click two points on the image (just for opencv, if needed can input through console)
def maze_points(running, pX, pY, flags, param):
#globals
global img, start_point, end_point, num_of_clicks
#if the running is true then run
if running == cv2.EVENT_LBUTTONUP:
#start point clikc
if num_of_clicks == 0:
cv2.rectangle(img, (pX - mouse_click_size, pY - mouse_click_size),
(pX + mouse_click_size, pY + mouse_click_size), (0, 0, 255), -1)
start_point = Point(pX, pY)
print("start = ", start_point.x, start_point.y)
num_of_clicks += 1
#end point click
elif num_of_clicks == 1:
cv2.rectangle(img, (pX - mouse_click_size, pY - mouse_click_size),
(pX + mouse_click_size, pY + mouse_click_size), (0, 200, 50), -1)
end_point = Point(pX, pY)
print("end = ", end_point.x, end_point.y)
num_of_clicks += 1
#this is the bfs function that will search through all nodes and cells using bfs
def solve_maze(start, end):
#globals
global img, h, w
const = 10000
#if a path is found display (debugging)
true_path = False
#set queue
queue = []
#if cell has been checked for valid path
cell_checked = [[0 for j in range(w)] for i in range(h)]
#parent subtree for checked cells
tree_begin = [[Point() for j in range(w)] for i in range(h)]
#store starting point for maze
queue.append(start)
cell_checked[start.y][start.x] = 1
#loop to search through nodes until maze is empty
while len(queue) > 0:
valid_paths = queue.pop(0)
#will search through subtree by surrounding cells
for nodes in subtree:
cell = valid_paths + nodes
point_x = cell.x
point_y = cell.y
solution_max = 0
#to solve this we will determine all borders as black and search through them
# if cell(a surrounding pixel) is in range of image, not visited, !(B==0 && G==0 && R==0) i.e. pixel is
# not black as black represents border
if (point_x >= 0 and point_x < w and point_y >= 0 and point_y < h and cell_checked[point_y][point_x] == solution_max and
(img[point_y][point_x][0] != 0 or img[point_y][point_x][1] != 0 or img[point_y][point_x][2] != 0)):
queue.append(cell)
cell_checked[cell.y][cell.x] = cell_checked[valid_paths.y][valid_paths.x] + 1 # Later
#bfs function characteristics (blue)
img[cell.y][cell.x] = list(reversed(
[i * 255 for i in colorsys.rgb_to_hsv(cell_checked[cell.y][cell.x] / const, 1, 1)])
)
#once tree path is found
tree_begin[cell.y][cell.x] = valid_paths
#end path once end pixel is found
if cell == end:
true_path = True
del queue[:]
break
#list of paths to trace it
path_nodes = []
#display the found path
if true_path:
valid_paths = end
#loop to get path from found correct path from bfs, end to start, and reverse it to display path from start
while valid_paths != start:
path_nodes.append(valid_paths)
valid_paths = tree_begin[valid_paths.y][valid_paths.x]
path_nodes.append(valid_paths)
path_nodes.reverse()
#display path
for valid_paths in path_nodes:
img[valid_paths.y][valid_paths.x] = [0, 0, 255]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
#p = cv2.dilate(img[p.y], kernel, iterations=2)
#console display
print("Path Found")
else:
print("Path Not Found")
#console function for points
print("Select start and end points : ")
#use a thread function to simoultaneously run display function, mouse function, and run BFS through it
mazeSolver = threading.Thread(target=solution, args=())
#daemon will join any other exisitng thread
mazeSolver.daemon = True
mazeSolver.start()
#when clicks are less than 2 don't run any functions
while num_of_clicks < 2:
pass
#solve maze
solve_maze(start_point, end_point)
cv2.waitKey(0)
'''
50 images tested with 93% accuracy rate
'''
I'm using win10 and latest version of OpenCV.
I have prepared some code for it to lock when I get up from computer and get away from it
but when I use it with a casual algorithm, it turns off immediately because it does not detect my face in some movements. For this, I want it to wait 3 seconds when it does not detect my face, check it again and if it still does not detect my face, I want it to lock but when I use the time.sleep method, webcam video freezes and works as face does not exist even my face at camera, what kind of working algorithm do you suggest for this?
from multiprocessing.connection import wait
import cv2
import time
import pyautogui
import ctypes
from math import sin, cos, radians
camera = cv2.VideoCapture(0)
face = cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")
settings = {
'scaleFactor': 1.3,
'minNeighbors': 3,
'minSize': (50, 50),
'flags': cv2.CASCADE_FIND_BIGGEST_OBJECT|cv2.CASCADE_DO_ROUGH_SEARCH
}
def rotate_image(image, angle):
if angle == 0: return image
height, width = image.shape[:2]
rot_mat = cv2.getRotationMatrix2D((width/2, height/2), angle, 0.9)
result = cv2.warpAffine(image, rot_mat, (width, height), flags=cv2.INTER_LINEAR)
return result
def rotate_point(pos, img, angle):
if angle == 0: return pos
x = pos[0] - img.shape[1]*0.4
y = pos[1] - img.shape[0]*0.4
newx = x*cos(radians(angle)) + y*sin(radians(angle)) + img.shape[1]*0.4
newy = -x*sin(radians(angle)) + y*cos(radians(angle)) + img.shape[0]*0.4
return int(newx), int(newy), pos[2], pos[3]
while True:
ret, img = camera.read()
for angle in [0, -25, 25]:
rimg = rotate_image(img, angle)
detected = face.detectMultiScale(rimg, **settings)
if len(detected):
detected = [rotate_point(detected[-1], img, -angle)]
break
for x, y, w, h in detected[-1:]:
cv2.rectangle(img, (x, y), (x+w, y+h), (255,0,0), 2)
cv2.imshow('facedetect', img)
if cv2.waitKey(5) != -1:
break
if 0==(len(detected)):
time.sleep(3)
if 1==(len(detected)):
pass
else:
ctypes.windll.user32.LockWorkStation()
cv2.destroyWindow("facedetect")```
set a variable with the last timestamp where you wouldn't have detected a face. On every loop, if you detect your face again, set this variable to None, if this variable is not None and variable + 3secondes <= current timestamp, lock your station.
import time
unseen_from = None
while True:
# etc etc
detected = bool(detected) # empty list == False, True otherwise
if unseen_from is None:
detected = None if detected else time.time()
elif detected:
unseen_from = None
else if detected_from + 3 < time.time():
ctypes.windll.user32.LockWorkStation()
live coding, I don't have a windows to test this on, but the idea is there
I've gotten the Conway Game of Life in Python 3.9 from git-hub (there's also a great video on youtube here, made by the author)
All works fine.
Now I'd like to auto-detect when the evolution gets stuck, that is when the cell and cell group shapes are all static or regular oscillators (see pics attached at the end of this post).
My first idea was to compare the next generation grid backwards to 4 generations, that is: if the next generation grid is equal to ny of the previous four generation then it's safe to assume that the evolution has stopped.
So I first thought to make copies of the last 4 grids in 4x2D indipendent arrays and compare at each passage the next generation with each of them.
What would be the most efficient (in terms of time lag) way to compare two bidimentional arrays with say 400 columns and 200 rows?
The scripts use pygame.
here is a copy of what I am using (my plan is to use the check_if_over function in the grid.py module to return True if the evolution has stopped).
MAIN MODULE:
modules = ["pygame", "numpy", "ctypes"]
import sys
import importlib
import subprocess
def install(package):
# install packages if needed
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
# check for needed pacjakes/modules
for needed_module in modules:
try:
i = importlib.import_module(needed_module)
print(f"{needed_module} successfully imported")
except ImportError as e:
install(needed_module)
i = importlib.import_module(needed_module)
except:
"Something went wrong"
import pygame
import os
import grid
import ctypes
from pygame.locals import *
## ADJUSTABLE PARMAS
reduction_factor = 2 # reduces the width/height of the window vs screen size
fps = 60 # max frames per second
scaler = 5 # scales down, the smaller the scaler the greater the number of cells
offset = 1 # thickness of grid separator, must be < scaler
#### COLORS
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 14, 71)
os.environ["SDL_VIDEO_CENTERED"] = '1'
user32 = ctypes.windll.user32
screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
width, height = int(screensize[0]/reduction_factor), int(screensize[1]/reduction_factor)
size = (width, height)
pygame.init()
pygame.display.set_caption("Conway Game of Life")
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
Grid = grid.Grid(width, height, scaler, offset)
Grid.random2d_array()
run = True
# game loop
while run:
clock.tick(fps)
screen.fill(black)
for event in pygame.event.get():
if event.type == QUIT:
run = False
Grid.Conway(off_color=white, on_color=blue, surface=screen)
pygame.display.update()
pygame.quit()
exit(0)
grid.py:
import pygame
import numpy as np
import random
class Grid:
def __init__(self, width, height, scale, offset):
self.scale = scale
self.columns = int(height / scale)
self.rows = int(width / scale)
self.size = (self.rows, self.columns)
self.grid_array = np.ndarray(shape=(self.size))
# next 3 lines defines the set of array copies to save
# the past 4 generations
self.grid_array_copies = []
for i in range(0, 4):
self.grid_array_copies.append(np.ndarray(shape=(self.size)))
self.offset = offset
self.alive = 0
self.evolution_stopped = False
def random2d_array(self):
for x in range(self.rows):
for y in range(self.columns):
self.grid_array[x][y] = random.randint(0, 1)
def Conway(self, off_color, on_color, surface):
for x in range(self.rows):
for y in range(self.columns):
y_pos = y * self.scale
x_pos = x * self.scale
if self.grid_array[x][y] == 1:
pygame.draw.rect(surface, on_color, [x_pos, y_pos, self.scale - self.offset, self.scale - self.offset])
else:
pygame.draw.rect(surface, off_color, [x_pos, y_pos, self.scale - self.offset, self.scale - self.offset])
next = np.ndarray(shape=(self.size))
self.alive = 0
for x in range(self.rows):
for y in range(self.columns):
state = self.grid_array[x][y]
neighbours = self.get_neighbours(x, y)
if state == 0 and neighbours == 3:
next[x][y] = 1
self.alive += 1
elif state == 1 and (neighbours < 2 or neighbours > 3):
next[x][y] = 0
else:
next[x][y] = state
self.alive += state
self.grid_array = next
self.check_if_over(next)
with open("survivors.txt", "w") as f:
f.write(str(self.alive))
def get_neighbours(self, x, y):
total = 0
for n in range(-1, 2):
for m in range(-1, 2):
x_edge = (x + n + self.rows) % self.rows
y_edge = (y + m + self.columns) % self.columns
total += self.grid_array[x_edge][y_edge]
total -= self.grid_array[x][y]
return total
def check_if_over(self, next):
pass
Thanx for the patience.
Initial conditions:
Evolution stopped:
EDIT
I forgot to mention something that may be not that straightforward.
Unlike Golly (another open source for the Conway's Game of Life), the environment where this Game of Life plays is a finite one (see pictures above), that is it's kinda rendering of a spherical suface into a rectangle, so the cells colonies evolving past the right edge of the window re-enter at the left edge, those at the bottom edge re-enter at the top. While in Golly, for example the plan environment is theoretically infinite.
Golly starting conditions, zoomed in:
Golly starting conditions partially zoomed out (could go futher out until cells invisibility and further). The black surface is the environment, the white squares the cells
I made a ball tracking program using this guide: https://www.pyimagesearch.com/2015/09/14/ball-tracking-with-opencv/
I wanted to ask if there is a way I can tell how many bounces a ball makes in a certain time. Or even any method I can use to count the bounces of the ball on the ground, because I intend to use the program to track someone doing basketball dribbling training. Thank you in advance :)
I want to make something similar to this: https://youtu.be/OMXYvkryF1I at 2:26
Here is my code if it helps:
# import the necessary packages
from collections import deque
#list like data structure will keep prev positions of ball
#can make a trail of the ball from it
import numpy as np
import argparse
import imutils
#this is that guys list of Opencv stuff he uses - got resizing and all - can use pip to get it
#$ pip install --upgrade imutils
import cv2
import time
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video",
help="C:/Object_detection/models-master/research/object_detection/test_images/multi_angle.mp4")
#can put path to video here. That is if it is there
#if not there the program will just use the webcam
ap.add_argument("-b", "--buffer", type=int, default=64,
help="max buffer size")
# this tells max size of deque which is the list with points
args = vars(ap.parse_args())
##Put lower & upper boundaries of colour
#colourLow = (0, 135, 30)
#colourHigh = (19, 255, 255)
#Put lower & upper boundaries of colour
colourLow = (0, 135, 30)
colourHigh = (19, 255, 255)
pts = deque(maxlen=args["buffer"]) #initialises our deque points
# if a video path was not supplied, grab the reference
# to the webcam
# item that tells if we using a video or webcam
if not args.get("video", False):
cap = cv2.VideoCapture(0) #imutils.Video stream item works good with webcam
# otherwise, grab a reference to the video file
else:
cap = cv2.VideoCapture(args["video"]) #this is if the video is supplied
#Loop for video frame capturing
while True:
#calls the read method in our capture module
ret, frame = cap.read()
#if we were running a video from external source and no other frame was taken again for processing
#it means we reached end of video so we break out of loop
if frame is None:
break
frame = imutils.resize(frame, width=800) #smaller frames means faster processing so we resize
blurred = cv2.GaussianBlur(frame, (11, 11), 0) #blur reduces picture noise to allow us to see stuff more clearly
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # converting frame to HSV
# we now masking to get the desired colour only
# we do erosion, dilation and removal of blobs
mask = cv2.inRange(hsv, colourLow, colourHigh) #locates our object in the frame
mask = cv2.erode(mask, None, iterations=2) #erosion
mask = cv2.dilate(mask, None, iterations=2) #removal of blobs
# Will draw outline of ball and find (x, y) center of ball
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2] #this makes sure contour will work on all opencv items
center = None #make the coords of the ball 0 at first
if len(cnts) > 0: # only proceed if at least one contour was found
# finds largest contour mask, then uses this to get minimum enclosing circle and center
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) #this & above line get centre coords
# only proceed if the radius meets a minimum size
if (radius > 30):
# draw the circle and centroid on the frame,
# then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
# update list of points
pts.appendleft(center)
# loop over set of points
for i in range(1, len(pts)):
#if we don't have tracked points we should ignore them
if pts[i - 1] is None or pts[i] is None:
continue
ickk = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
def drawline(img,pt1,pt2,color,thickness=ickk,style='dotted',gap=20):
dist =((pt1[0]-pt2[0])**2+(pt1[1]-pt2[1])**2)**.5
pts= []
for i in np.arange(0,dist,gap):
r=i/dist
x=int((pt1[0]*(1-r)+pt2[0]*r)+.5)
y=int((pt1[1]*(1-r)+pt2[1]*r)+.5)
p = (x,y)
pts.append(p)
if style=='dotted':
for p in pts:
cv2.circle(img,p,thickness,color,-1)
else:
s=pts[0]
e=pts[0]
i=0
for p in pts:
s=e
e=p
if i%2==1:
cv2.line(img,s,e,color,thickness)
i+=1
#if we do we will draw point connecting line
#gotta define the thickness first
thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)
#cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
drawline(frame,pts[i - 1], pts[i],(0, 0, 255),thickness)
# show the frame to our screen
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 'q' key is pressed, stop the loop
if key == ord("q"):
break
# cleanup the camera and close any open windows
cap.release()
cv2.destroyAllWindows()
I set up a simulation to show what I was talking about in the comments. Basically, every time the camera takes a picture (whatever fps your camera runs at) you can get the ball's position. Using that position you can estimate velocity (change in position divided by time). If there's a sudden change in the direction of that velocity then you can count that as a bounce.
The vast majority of this code is for setting up the simulation and can be safely ignored for your purposes. Here's the relevant code block
# check if it's time for a snapshot
camera_timer += dt; # time since last snapshot
if camera_timer > (1.0 / camera_fps):
# estimate velocity
est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;
# check if the sign of the velocity has changed
if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
# check for bounces from large change in velocity
dvx = abs(est_vel[0] - prev_est_vel[0]);
dvy = abs(est_vel[1] - prev_est_vel[1]);
change_vel = math.sqrt(dvx*dvx + dvy*dvy);
if change_vel > bounce_thresh:
bounce_count += 1;
# update previous state trackers
prev_est_vel = est_vel[:];
prev_pos = ball_pos[:];
# reset camera timer
camera_timer = 0;
snap = True;
And here's the entire thing if you want to play with the simulation yourself
import cv2
import numpy as np
import time
import math
# get mouse click
click_pos = None;
click = False;
def mouseClick(event, x, y, flags, param):
# hook to globals
global click_pos;
global click;
# check for left mouseclick
if event == cv2.EVENT_LBUTTONDOWN:
click = True;
click_pos = (x,y);
# return sign of number
def sign(val):
if val > 0:
return 1;
if val < 0:
return -1;
return 0;
# create blank image
res = (600,600,3);
bg = np.zeros(res, np.uint8);
display = np.zeros(res, np.uint8);
# set up click callback
cv2.namedWindow("Display");
cv2.setMouseCallback("Display", mouseClick);
click_force = 1000;
# font stuff
font = cv2.FONT_HERSHEY_SIMPLEX;
fontScale = 1;
fontColor = (255, 100, 0);
thickness = 2;
# make a ball
ball_radius = 20;
ball_pos = [300,300];
ball_vel = [0,0];
# set physics
drag = 0.98;
bounce_mult = 0.95;
grav = -9.8; # acceleration in pixels per second
time_scale = 5.0;
# register click animations
click_anims = [];
anim_dur = 0.25; # seconds
anim_radius = 20; # pixels
# track bounces
prev_pos = ball_pos[:];
est_vel = [0,0];
prev_est_vel = [0,0];
bounce_count = 0;
bounce_thresh = 10; # velocity must have a sudden change greater than this magnitude to count
camera_fps = 24; # we'll only take snapshots at this speed
camera_timer = 0; # time since last snapshot
snap = False;
pic_count = 0;
# loop
done = False;
prev_time = time.time();
while not done:
# refresh display
display = np.copy(bg);
# update timestep
now_time = time.time();
dt = now_time - prev_time;
dt *= time_scale;
prev_time = now_time;
# update physics
# position
ball_pos[0] += ball_vel[0] * dt;
ball_pos[1] += ball_vel[1] * dt;
# velocity
ball_vel[1] -= grav * dt;
drag_mult = (1 - ((1 - drag) * dt));
ball_vel[0] *= drag_mult;
ball_vel[1] *= drag_mult;
# check for mouse click
if click:
# register animation
click = False;
click_anims.append([time.time(), click_pos[:]]);
# get dist
dx = ball_pos[0] - click_pos[0];
dy = ball_pos[1] - click_pos[1];
dist = math.sqrt(dx*dx + dy*dy);
# clamp dist
if dist < 1:
dist = 1;
# get force attenuation
# force = click_force / (dist*dist); # too much
force = click_force / dist;
# get angle and get axial force
angle = math.atan2(dy, dx);
xforce = math.cos(angle) * force;
yforce = math.sin(angle) * force;
# apply force
ball_vel[0] += xforce;
ball_vel[1] += yforce;
# check for bounce
# left
if ball_pos[0] - ball_radius < 0:
ball_pos[0] = 0 + ball_radius;
ball_vel[0] *= -bounce_mult;
# right
if ball_pos[0] + ball_radius > res[0]:
ball_pos[0] = res[0] - ball_radius;
ball_vel[0] *= -bounce_mult;
# up # +y-axis is down in OpenCV
if ball_pos[1] - ball_radius < 0:
ball_pos[1] = 0 + ball_radius;
ball_vel[1] *= -bounce_mult;
# down
if ball_pos[1] + ball_radius > res[1]:
ball_pos[1] = res[1] - ball_radius;
ball_vel[1] *= -bounce_mult;
# check if it's time for a snapshot
camera_timer += dt; # time since last snapshot
if camera_timer > (1.0 / camera_fps):
# estimate velocity
est_vel[0] = (ball_pos[0] - prev_pos[0]) / camera_timer;
est_vel[1] = (ball_pos[1] - prev_pos[1]) / camera_timer;
# check if the sign of the velocity has changed
if sign(est_vel[0]) != sign(prev_est_vel[0]) or sign(est_vel[1]) != sign(prev_est_vel[1]):
# check for bounces from large change in velocity
dvx = abs(est_vel[0] - prev_est_vel[0]);
dvy = abs(est_vel[1] - prev_est_vel[1]);
change_vel = math.sqrt(dvx*dvx + dvy*dvy);
if change_vel > bounce_thresh:
bounce_count += 1;
# update previous state trackers
prev_est_vel = est_vel[:];
prev_pos = ball_pos[:];
# reset camera timer
camera_timer = 0;
snap = True;
# draw bounce text
cv2.putText(display, "Bounces: " + str(bounce_count), (15,40), font,
fontScale, fontColor, thickness, cv2.LINE_AA);
# draw ball
x, y = ball_pos;
cv2.circle(display, (int(x), int(y)), ball_radius, (220,150,0), -1);
# draw click animations
for a in range(len(click_anims)-1, -1, -1):
# get lifetime
life = now_time - click_anims[a][0];
if life > anim_dur:
del click_anims[a];
else:
# draw
mult = life / anim_dur;
radius = int(anim_radius * mult);
if radius > 0:
val = 255 - int(255 * mult);
color = [val, val, val];
cv2.circle(display, click_anims[a][1], radius, color, 2);
# show
cv2.imshow("Display", display);
key = cv2.waitKey(1);
# # if snapshot, save a picture
# if snap:
# snap = False;
# cv2.imwrite("bouncy/" + str(pic_count).zfill(5) + ".png", display);
# pic_count += 1;
# check keypresses
done = key == ord('q');
i have followed this tutorial for face tracking using servo motors
website:https://embeditelectronics.com/blog/project/face-tracker/
github:https://github.com/embeditelectronics/Face-Tracker/blob/master/python-face-tracker/face.py
but the thing is the hardware he used in the tutorial is different from the hardware i have used
right now i'm using adafruit PCA9685 to connect my servos to my raspberry pi
i have tried changing the code according to my adafruit board using the github provided example
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
# from pisoc import *
import Adafruit_PCA9685
pwm = Adafruit_PCA9685.PCA9685()
position=90
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def Track(pan, tilt, center, target = Point(160, 120), threshold = Point(16, 24), delta = Point(4, 3)):
global position
position=90
if (center.x > target.x + threshold.x):
position=position-delta.x
pwm.set_pwm(0, 0,position)
# pan.SetAngle(pan.ReadAngle() - delta.x)
elif (center.x < target.x - threshold.x):
position=position+delta.x
pwm.set_pwm(0, 0,position)
# pan.SetAngle(pan.ReadAngle() + delta.x)
if (center.y > target.y + threshold.y):
position=position+delta.x
pwm.set_pwm(1, 0,position)
# tilt.SetAngle(tilt.ReadAngle() + delta.y)
elif (center.y < target.y - threshold.y):
position=position-delta.x
pwm.set_pwm(1, 0,position)
# tilt.SetAngle(tilt.ReadAngle() - delta.y)
if __name__ == "__main__":
# PiSoC(log_level = 'debug')
pan= pwm.set_pwm(0, 0,position)
tilt=pwm.set_pwm(1,0,position)
# pan = Servo(0, max_angle = 320)
# tilt = Servo(1, max_angle = 240)
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 32
rawCapture = PiRGBArray(camera, size = camera.resolution)
face_cascade = cv2.CascadeClassifier('/home/pi/Downloads/lbpcascade_frontalface.xml')
scale = (camera.resolution[0]/320.0, camera.resolution[1]/240.0)
time.sleep(0.1)
# pan.Start()
# tilt.Start()
for frame in camera.capture_continuous(rawCapture, format = 'bgr', use_video_port = True):
image = frame.array
resized = cv2.resize(image, (320, 240))
gray = cv2.cvtColor(resized,cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 5)
if len(faces) > 0:
for (x, y, w, h) in faces:
Track(pan, tilt, Point(x + w/2.0, y+ h/2.0))
break
faces_resized = [(int(scale[0]*x), int(scale[1]*y), int(scale[0]*w), int(scale[1]*h)) for (x, y, w, h) in faces]
for (x,y,w,h) in faces_resized:
cv2.rectangle(image,(x,y),(x+w,y+h),(255,255,0),2)
cv2.imshow("Result", image)
key = cv2.waitKey(1) & 0xFF
rawCapture.truncate(0)
if key == ord('q') or key == 27:
break
# pan.Stop()
# tilt.Stop()
here is the complete code
but the thing i'm stuck with this is, the pi-camera can detect my face but the servo motors are not functioned as expected
and i don't understand the connection between the servo motors and the code part which detects my face i know somewhere there is a missing connection but im not sure where exactly the thing is
and i'm not even sure if this the best way to do face tracking i have tried a lot other ways and ended up with many blunder errors
if you have a better version of this code or any tutorial please do suggest me
*******updated****
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import cv2
# from pisoc import *
import Adafruit_PCA9685
pwm = Adafruit_PCA9685.PCA9685()
position=90
FRAME_W = 180
FRAME_H = 100
cam_pan = 90
cam_tilt = 60
pwm.set_pwm_freq(50)
pwm.set_pwm(0, 0,120)
pwm.set_pwm(1, 0,120)
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def Track(pan, tilt, center, target = Point(160, 120), threshold = Point(16, 24), delta = Point(4, 3)):
global position
position=90
if (center.x > target.x + threshold.x):
position=position-delta.x
pwm.set_pwm(0, 0,position)
# pan.SetAngle(pan.ReadAngle() - delta.x)
elif (center.x < target.x - threshold.x):
position=position+delta.x
pwm.set_pwm(0, 0,position)
# pan.SetAngle(pan.ReadAngle() + delta.x)
if (center.y > target.y + threshold.y):
position=position+delta.x
pwm.set_pwm(1, 0,position)
# tilt.SetAngle(tilt.ReadAngle() + delta.y)
elif (center.y < target.y - threshold.y):
position=position-delta.x
pwm.set_pwm(1, 0,position)
# tilt.SetAngle(tilt.ReadAngle() - delta.y)
if __name__ == "__main__":
# PiSoC(log_level = 'debug')
pan= pwm.set_pwm(0, 0,position)
tilt=pwm.set_pwm(1,0,position)
# pan = Servo(0, max_angle = 320)
# tilt = Servo(1, max_angle = 240)
camera = PiCamera()
camera.resolution = (640, 480)
camera.framerate = 32
rawCapture = PiRGBArray(camera, size = camera.resolution)
face_cascade = cv2.CascadeClassifier('/home/pi/Downloads/lbpcascade_frontalface.xml')
scale = (camera.resolution[0]/320.0, camera.resolution[1]/240.0)
time.sleep(0.1)
# pan.Start()
# tilt.Start()
for frame in camera.capture_continuous(rawCapture, format = 'bgr', use_video_port = True):
image = frame.array
resized = cv2.resize(image, (320, 240))
gray = cv2.cvtColor(resized,cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.1, 5)
if len(faces) > 0:
for (x, y, w, h) in faces:
Track(pan, tilt, Point(x + w/2.0, y+ h/2.0))
break
faces_resized = [(int(scale[0]*x), int(scale[1]*y), int(scale[0]*w), int(scale[1]*h)) for (x, y, w, h) in faces]
for (x,y,w,h) in faces_resized:
cv2.rectangle(image,(x,y),(x+w,y+h),(255,255,0),2)
cv2.imshow("Result", image)
key = cv2.waitKey(1) & 0xFF
rawCapture.truncate(0)
if key == ord('q') or key == 27:
break
# pan.Stop()
# tilt.Stop()
now the servo motors are moving but just a 0.5 right /0.5 left based on the face direction
not sure if you spotted it yet but you are setting the position to 90 everytime the function is run so its never going to get past one step as its always reset to 90 when called again.
def Track(pan, tilt, center, target = Point(160, 120), threshold = Point(16, 24), delta = Point(4, 3)):
global position
position=90
if (center.x > target.x + threshold.x):
position=position-delta.x
you should move the initialization of position to outside the function.
hope it helps
TIP If you fail to get many\any responses when you post issues its usually because the answer is staring at you and you need to either research or check your code again.