Compare commits
7 commits
e8f4e6e0ba
...
8130026a38
Author | SHA1 | Date | |
---|---|---|---|
|
8130026a38 | ||
|
fd5ef26e2b | ||
|
ead829011c | ||
|
5a6f3ff744 | ||
|
7d873ee5df | ||
|
3baffaa396 | ||
|
22a2d7b29e |
9
.gitignore
vendored
|
@ -1,4 +1,11 @@
|
||||||
.direnv/*
|
.direnv/*
|
||||||
venv/*
|
venv/*
|
||||||
output/*
|
output/*
|
||||||
.envrc
|
.envrc
|
||||||
|
training_data/data*
|
||||||
|
training_data/info*
|
||||||
|
training_data/training_data_*/
|
||||||
|
training_data/*.vec
|
||||||
|
training_data/backgrounds.txt
|
||||||
|
training_data/negatives
|
||||||
|
training_data/opencv
|
54
Main.py
|
@ -82,12 +82,7 @@ if args.file:
|
||||||
cap = cv2.VideoCapture(args.file)
|
cap = cv2.VideoCapture(args.file)
|
||||||
else:
|
else:
|
||||||
cap = cv2.VideoCapture(0, cv2.IMREAD_GRAYSCALE) # instead of grayscale you can also use -1, 0, or 1.
|
cap = cv2.VideoCapture(0, cv2.IMREAD_GRAYSCALE) # instead of grayscale you can also use -1, 0, or 1.
|
||||||
faceCascade = cv2.CascadeClassifier(r"./cascades/lbpcascade_frontalface.xml") # CHECK THIS FIRST TROUBLE SHOOTING
|
faceCascade = cv2.CascadeClassifier(r"./cascades/cascade_10.xml") # CHECK THIS FIRST TROUBLE SHOOTING
|
||||||
faceCascade_default = cv2.CascadeClassifier(r"./cascades/haarcascade_frontalface_default.xml")
|
|
||||||
faceCascade_alt = cv2.CascadeClassifier(r"./cascades/haarcascade_frontalface_alt.xml")
|
|
||||||
faceCascade_alt2 = cv2.CascadeClassifier(r"./cascades/haarcascade_frontalface_alt2.xml")
|
|
||||||
faceCascade_alttree = cv2.CascadeClassifier(r"./cascades/haarcascade_frontalface_alt_tree.xml")
|
|
||||||
profileFaceCascade = cv2.CascadeClassifier(r"./cascades/haarcascade_profileface.xml")
|
|
||||||
|
|
||||||
datestamp = "{:%Y_%m_%d %H_%M_%S}".format(datetime.datetime.now())
|
datestamp = "{:%Y_%m_%d %H_%M_%S}".format(datetime.datetime.now())
|
||||||
output_dir = r"./output/" + datestamp + r"/"
|
output_dir = r"./output/" + datestamp + r"/"
|
||||||
|
@ -112,51 +107,12 @@ while(True):
|
||||||
# Detect faces in the image
|
# Detect faces in the image
|
||||||
faces = faceCascade.detectMultiScale(
|
faces = faceCascade.detectMultiScale(
|
||||||
gray,
|
gray,
|
||||||
scaleFactor=1.1,
|
scaleFactor=1.2,
|
||||||
minNeighbors=5,
|
minNeighbors=2,
|
||||||
minSize=(30, 30)
|
# minSize=(70, 90)
|
||||||
|
minSize=(200, 200)
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(faces) == 0:
|
|
||||||
faces = faceCascade_default.detectMultiScale(
|
|
||||||
gray,
|
|
||||||
scaleFactor=1.1,
|
|
||||||
minNeighbors=5,
|
|
||||||
minSize=(30,30)
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(faces) == 0:
|
|
||||||
faces = profileFaceCascade.detectMultiScale(
|
|
||||||
gray,
|
|
||||||
scaleFactor=1.1,
|
|
||||||
minNeighbors=5,
|
|
||||||
minSize=(30,30)
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(faces) == 0:
|
|
||||||
faces = faceCascade_alt.detectMultiScale(
|
|
||||||
gray,
|
|
||||||
scaleFactor=1.1,
|
|
||||||
minNeighbors=5,
|
|
||||||
minSize=(30,30)
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(faces) == 0:
|
|
||||||
faces = faceCascade_alt2.detectMultiScale(
|
|
||||||
gray,
|
|
||||||
scaleFactor=1.1,
|
|
||||||
minNeighbors=5,
|
|
||||||
minSize=(30,30)
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(faces) == 0:
|
|
||||||
faces = faceCascade_alttree.detectMultiScale(
|
|
||||||
gray,
|
|
||||||
scaleFactor=1.1,
|
|
||||||
minNeighbors=5,
|
|
||||||
minSize=(30,30)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Draw a rectangle around the faces
|
# Draw a rectangle around the faces
|
||||||
for (x, y, w, h) in faces:
|
for (x, y, w, h) in faces:
|
||||||
if args.training_data:
|
if args.training_data:
|
||||||
|
|
45
generate_datasets.ps1
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
if (-Not (Test-Path -Path ".\training_data\negatives")) {
|
||||||
|
Write-Host "The negatives folder is not where it was expected"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not (Test-Path -Path ".\training_data\positives")) {
|
||||||
|
Write-Host "The positives folder is not where it was expected"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not (Test-Path -Path ".\training_data\1_positive_info")) {
|
||||||
|
New-Item -ItemType Directory -Force -Path ".\training_data\1_positive_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not (Test-Path -Path ".\training_data\2_positive_info")) {
|
||||||
|
New-Item -ItemType Directory -Force -Path ".\training_data\2_positive_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-Not (Test-Path -Path ".\training_data\5_positive_info")) {
|
||||||
|
New-Item -ItemType Directory -Force -Path ".\training_data\5_positive_info"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Location .\training_data
|
||||||
|
|
||||||
|
python .\dat_file_setup.py
|
||||||
|
|
||||||
|
.\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_1.png -bg .\backgrounds.txt -info 1_positive_info/info.lst -pngoutput 1_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
|
||||||
|
.\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_1.png -bg .\backgrounds.txt -info 2_positive_info/info.lst -pngoutput 2_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
.\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_2.png -bg .\backgrounds.txt -info 2_positive_info/info.lst -pngoutput 2_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_1.png -bg .\backgrounds.txt -info 5_positive_info/info.lst -pngoutput 5_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_2.png -bg .\backgrounds.txt -info 5_positive_info/info.lst -pngoutput 5_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_3.png -bg .\backgrounds.txt -info 5_positive_info/info.lst -pngoutput 5_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_4.png -bg .\backgrounds.txt -info 5_positive_info/info.lst -pngoutput 5_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_5.png -bg .\backgrounds.txt -info 5_positive_info/info.lst -pngoutput 5_positive_info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num 1950
|
||||||
|
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -info .\1_positive_info\info.lst -num 1950 -w 20 -h 20 -vec positives_1.vec
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -info .\2_positive_info\info.lst -num 3900 -w 20 -h 20 -vec positives_2.vec
|
||||||
|
& .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -info .\5_positive_info\info.lst -num 9750 -w 20 -h 20 -vec positives_5.vec
|
||||||
|
|
||||||
|
Write-Host "Ready for"
|
||||||
|
Write-Host ".\opencv\build\x64\vc15\bin\opencv_traincascade.exe -data data -vec .\positives-1.vec -bg .\backgrounds.txt -numPos 1500 -numNeg 900 -numStages 15 -w 20 -h 20"
|
||||||
|
Write-Host ".\opencv\build\x64\vc15\bin\opencv_traincascade.exe -data data -vec .\positives-2.vec -bg .\backgrounds.txt -numPos 3000 -numNeg 1500 -numStages 15 -w 20 -h 20"
|
||||||
|
Write-Host ".\opencv\build\x64\vc15\bin\opencv_traincascade.exe -data data -vec .\positives-5.vec -bg .\backgrounds.txt -numPos 9000 -numNeg 1950 -numStages 15 -w 20 -h 20"
|
|
@ -17,4 +17,9 @@ Now you can run the program. It is recommended to run the program with -d and -o
|
||||||
|
|
||||||
|
|
||||||
Training Data:
|
Training Data:
|
||||||
https://www.kaggle.com/datasets/utkarshsaxenadn/landscape-recognition-image-dataset-12k-images
|
https://www.kaggle.com/datasets/utkarshsaxenadn/landscape-recognition-image-dataset-12k-images
|
||||||
|
|
||||||
|
|
||||||
|
create positives from the negatives: \opencv\build\x64\vc15\bin\opencv_createsamples.exe -img .\positives\face_1.png -bg .\bg.txt -info info/info.lst -pngoutput info -maxxangle 0.8 -maxyangle 0.8 -maxzangle 0.8 -num 1950
|
||||||
|
Create vec files from positives: .\opencv\build\x64\vc15\bin\opencv_createsamples.exe -info .\info\info.lst -num 1950 -w 80 -h 80 -vec positives-80.vec
|
||||||
|
(I created a 20, 40, and 80) we have 1650 positives
|
BIN
training_data/TestVideo.mp4
Normal file
93
training_data/create_ground_truth.py
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import cv2
|
||||||
|
import sys
|
||||||
|
|
||||||
|
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
|
||||||
|
|
||||||
|
if __name__ == '__main__' :
|
||||||
|
|
||||||
|
# Set up tracker.
|
||||||
|
# Instead of MIL, you can also use
|
||||||
|
|
||||||
|
tracker_types = ['BOOSTING', 'MIL','KCF', 'TLD', 'MEDIANFLOW', 'GOTURN', 'MOSSE', 'CSRT']
|
||||||
|
tracker_type = tracker_types[1]
|
||||||
|
|
||||||
|
if int(minor_ver) < 3:
|
||||||
|
tracker = cv2.Tracker_create(tracker_type)
|
||||||
|
else:
|
||||||
|
if tracker_type == 'BOOSTING':
|
||||||
|
tracker = cv2.TrackerBoosting_create()
|
||||||
|
if tracker_type == 'MIL':
|
||||||
|
tracker = cv2.TrackerMIL_create()
|
||||||
|
if tracker_type == 'KCF':
|
||||||
|
tracker = cv2.TrackerKCF_create()
|
||||||
|
if tracker_type == 'TLD':
|
||||||
|
tracker = cv2.TrackerTLD_create()
|
||||||
|
if tracker_type == 'MEDIANFLOW':
|
||||||
|
tracker = cv2.TrackerMedianFlow_create()
|
||||||
|
if tracker_type == 'GOTURN':
|
||||||
|
tracker = cv2.TrackerGOTURN_create()
|
||||||
|
if tracker_type == 'MOSSE':
|
||||||
|
tracker = cv2.TrackerMOSSE_create()
|
||||||
|
if tracker_type == "CSRT":
|
||||||
|
tracker = cv2.TrackerCSRT_create()
|
||||||
|
|
||||||
|
# Read video
|
||||||
|
video = cv2.VideoCapture("./TestVideo.mp4")
|
||||||
|
|
||||||
|
# Exit if video not opened.
|
||||||
|
if not video.isOpened():
|
||||||
|
print("Could not open video")
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Read first frame.
|
||||||
|
ok, frame = video.read()
|
||||||
|
if not ok:
|
||||||
|
print('Cannot read video file')
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Define an initial bounding box
|
||||||
|
bbox = (287, 23, 86, 320)
|
||||||
|
|
||||||
|
# Uncomment the line below to select a different bounding box
|
||||||
|
bbox = cv2.selectROI(frame, False)
|
||||||
|
|
||||||
|
# Initialize tracker with first frame and bounding box
|
||||||
|
ok = tracker.init(frame, bbox)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Read a new frame
|
||||||
|
ok, frame = video.read()
|
||||||
|
if not ok:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Start timer
|
||||||
|
timer = cv2.getTickCount()
|
||||||
|
|
||||||
|
# Update tracker
|
||||||
|
ok, bbox = tracker.update(frame)
|
||||||
|
|
||||||
|
# Calculate Frames per second (FPS)
|
||||||
|
fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer);
|
||||||
|
|
||||||
|
# Draw bounding box
|
||||||
|
if ok:
|
||||||
|
# Tracking success
|
||||||
|
p1 = (int(bbox[0]), int(bbox[1]))
|
||||||
|
p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
|
||||||
|
cv2.rectangle(frame, p1, p2, (255,0,0), 2, 1)
|
||||||
|
else :
|
||||||
|
# Tracking failure
|
||||||
|
cv2.putText(frame, "Tracking failure detected", (100,80), cv2.FONT_HERSHEY_SIMPLEX, 0.75,(0,0,255),2)
|
||||||
|
|
||||||
|
# Display tracker type on frame
|
||||||
|
cv2.putText(frame, tracker_type + " Tracker", (100,20), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50),2);
|
||||||
|
|
||||||
|
# Display FPS on frame
|
||||||
|
cv2.putText(frame, "FPS : " + str(int(fps)), (100,50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50,170,50), 2);
|
||||||
|
|
||||||
|
# Display result
|
||||||
|
cv2.imshow("Tracking", frame)
|
||||||
|
|
||||||
|
# Exit if ESC pressed
|
||||||
|
k = cv2.waitKey(1) & 0xff
|
||||||
|
if k == 27 : break
|
BIN
training_data/positives/0.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
training_data/positives/1.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
training_data/positives/2.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
training_data/positives/3.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
training_data/positives/4.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
training_data/positives/5.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
training_data/positives/6.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
training_data/positives/7.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
training_data/positives/8.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
training_data/positives/9.png
Normal file
After Width: | Height: | Size: 27 KiB |
120
training_data/training_data_setup.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
from PIL import Image
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
backgrounds_file_path = "backgrounds.txt"
|
||||||
|
info_base_path = r"./info"
|
||||||
|
negatives_path = r"./negatives"
|
||||||
|
positives_path = r"./positives"
|
||||||
|
training_data_base = r"./training_data_"
|
||||||
|
|
||||||
|
opencv_path = r".\opencv\build\x64\vc15\bin\opencv_createsamples.exe"
|
||||||
|
|
||||||
|
set_sizes = [1, 2, 5, 10]
|
||||||
|
|
||||||
|
max_xangle = 0.5
|
||||||
|
max_yangle = 0.5
|
||||||
|
max_zangle = 0.5
|
||||||
|
|
||||||
|
w, h = 25, 18
|
||||||
|
|
||||||
|
class InfoEntry:
|
||||||
|
info_lst_line: str
|
||||||
|
image_path: str
|
||||||
|
|
||||||
|
def __init__(self, info_line, file_path):
|
||||||
|
self.info_lst_line = info_line
|
||||||
|
self.image_path = file_path
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Image Entry: {self.info_lst_line}, {self.image_path}"
|
||||||
|
|
||||||
|
|
||||||
|
max_x = 750
|
||||||
|
max_y = 800
|
||||||
|
|
||||||
|
# remove too small images
|
||||||
|
for image in os.listdir("./negatives"):
|
||||||
|
im = Image.open(f"./negatives/{image}")
|
||||||
|
width, height = im.size
|
||||||
|
del im
|
||||||
|
if width <= max_x:
|
||||||
|
os.remove(f"./negatives/{image}")
|
||||||
|
elif height <= max_y:
|
||||||
|
os.remove(f"./negatives/{image}")
|
||||||
|
|
||||||
|
# remove any existing file and assume old data
|
||||||
|
if os.path.exists(backgrounds_file_path):
|
||||||
|
os.remove(backgrounds_file_path)
|
||||||
|
|
||||||
|
# regenerate the available negatives list
|
||||||
|
count_negatives = len(os.listdir(negatives_path))
|
||||||
|
for img in os.listdir(negatives_path):
|
||||||
|
line = f"{negatives_path}/" + img + "\n"
|
||||||
|
with open(backgrounds_file_path, 'a') as f:
|
||||||
|
f.write(line)
|
||||||
|
|
||||||
|
info_dirs = []
|
||||||
|
|
||||||
|
if len(os.listdir(positives_path)) > max(set_sizes):
|
||||||
|
print("Your set sizes were larger than the available positive images!")
|
||||||
|
quit(2)
|
||||||
|
|
||||||
|
for img in os.listdir(positives_path):
|
||||||
|
i = len(info_dirs)
|
||||||
|
info_dir = f"{info_base_path}{i}"
|
||||||
|
|
||||||
|
com = f"{opencv_path} -img positives/" + str(i) + ".png -bg backgrounds.txt -info " + info_dir + "/info.lst" + \
|
||||||
|
" -pngoutput " + info_dir + " -maxxangle " + str(max_xangle) + " -maxyangle " + str(max_yangle) + " -maxzangle " + str(max_zangle) + \
|
||||||
|
" -num " + str(count_negatives)
|
||||||
|
|
||||||
|
if not os.path.exists(info_dir):
|
||||||
|
subprocess.call(com, shell=True)
|
||||||
|
|
||||||
|
info_dirs.append(info_dir)
|
||||||
|
|
||||||
|
for i in set_sizes:
|
||||||
|
if not os.path.exists(training_data_base + str(i)):
|
||||||
|
os.makedirs(training_data_base + str(i))
|
||||||
|
|
||||||
|
def join_info_folders(info_dirs: list, output_dir: str):
|
||||||
|
info_dir: str
|
||||||
|
cur_entry_name = 0
|
||||||
|
for info_dir in info_dirs:
|
||||||
|
info_lines = []
|
||||||
|
with open(info_dir + "/info.lst", 'r') as info_file:
|
||||||
|
for line in info_file.readlines():
|
||||||
|
image_path = f"{info_dir}/{line.split(' ')[0]}"
|
||||||
|
info_lines.append(InfoEntry(line.strip(), image_path))
|
||||||
|
|
||||||
|
item: InfoEntry
|
||||||
|
for item in info_lines:
|
||||||
|
shutil.copy(item.image_path, f"{output_dir}/{str(cur_entry_name)}.jpg")
|
||||||
|
with open(f"{output_dir}/info.lst", 'a') as info_file:
|
||||||
|
to_write = []
|
||||||
|
to_write.append(str(cur_entry_name) + ".jpg")
|
||||||
|
to_write = to_write + item.info_lst_line.split(" ")[1:]
|
||||||
|
to_write.append("\n")
|
||||||
|
info_file.write(" ".join(to_write))
|
||||||
|
cur_entry_name += 1
|
||||||
|
|
||||||
|
for i in set_sizes:
|
||||||
|
join_info_folders(info_dirs[:i], training_data_base + str(i))
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
|
||||||
|
for i in set_sizes:
|
||||||
|
num_positives = len(os.listdir(training_data_base + str(i)))
|
||||||
|
if os.path.exists(training_data_base + str(i) + ".vec"):
|
||||||
|
os.remove(training_data_base + str(i) + ".vec")
|
||||||
|
com = f"{opencv_path} -info {training_data_base + str(i)}\info.lst -num {num_positives} -w {w} -h {h} -vec {training_data_base + str(i)}.vec"
|
||||||
|
subprocess.call(com, shell=True)
|
||||||
|
commands.append(f".\opencv\\build\\x64\\vc15\\bin\opencv_traincascade.exe -data data_{str(i)} -vec .\\{training_data_base + str(i)}.vec -bg .\\{backgrounds_file_path} -numPos {num_positives} -numNeg {num_positives / 2} -numStages 15 -w {w} -h {h}")
|
||||||
|
|
||||||
|
if not os.path.exists(".\data_" + str(i)):
|
||||||
|
os.makedirs(".\data_" + str(i))
|
||||||
|
|
||||||
|
for i in commands:
|
||||||
|
print(f"You are ready to train the models with: \n {i}")
|
||||||
|
|