Skip to content

Instantly share code, notes, and snippets.

@ChuckCartledge
Created June 6, 2020 18:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChuckCartledge/4e152f618d2e2174c4f2e3590f659fc6 to your computer and use it in GitHub Desktop.
Save ChuckCartledge/4e152f618d2e2174c4f2e3590f659fc6 to your computer and use it in GitHub Desktop.
Python based Haar detector to create image-map (see http://www.clc-ent.com/TBDE/Docs/faces.pdf)
# https://stackoverflow.com/questions/30505150/how-to-scale-up-picture-in-python
# https://www.techbeamers.com/python-switch-case-statement/
from PIL import Image
import cv2 as cv
import sys
import getopt
import json as js
from tempfile import NamedTemporaryFile
def setffGlobals():
returnValue = {
"scaler": 0.1,
"scaleFactor": 1,
"HaarFile": "./haarcascade_frontalface_default.xml",
"htmlFile": "./temp.html",
"inFile": "./ws-dl-group-1.jpeg",
"markedUpFile": "./tempMarkedUp.jpg",
"monitorFile": "./tempMonitoring.jpg"
}
return(returnValue)
def dumpffGlobals():
print("The current values in 'ffGlobals' are:")
for attribute, value in ffGlobals.items():
print('{} : {}'.format(attribute, value))
def serviceCommandLineArgs (myopts, args):
options = {
"-?": "Print program usage, and exit.",
"-D": "Show current ffGlobals values.",
"-i": "Set name of input image.",
"-o": "Set name of output HTML file.",
"-H": "Set name of Haar cascade file.",
"-m": "Set name of image monitoring file.\n\tFile will be deleted when program exits.",
"-M": "Set name of image marked up file.\n\tFile will be deleted when program exits.",
"-s": "Set ratio of image scaling.",
"-S": "Save the current ffGlobale values to this file.",
"-L": "Load ffGlobals from this file.",
"-R": "Reset ffGlobals to default values."
}
for o, a in myopts:
global ffGlobals
if (o == "-?"):
print("usage: findFaces.py")
print("usage: findFaces.py [-?]")
print("usage: findFaces.py [-option [optionArgument]]")
print("usage: findFaces.py [-option [optionArgument] -option [optionArgument]]")
print("usage: findFaces.py [-option [optionArgument] -option [optionArgument] -option [optionArgument]")
print("and so on.")
print("Options are processed in the order they are read from left to right.")
print("Options are:")
for attribute, value in options.items():
print('{} : {}'.format(attribute, value))
exit()
elif (o == "-D"):
dumpffGlobals()
elif (o == "-i"):
ffGlobals["inFile"] = a
elif (o == "-o"):
ffGlobals["htmlFile"] = a
elif (o == "-H"):
ffGlobals["HaarFile"] = a
elif (o == "-m"):
ffGlobals["monitorFile"] = a
elif (o == "-M"):
ffGlobals["markedUpFile"] = a
elif (o == "-s"):
ffGlobals["scaler"] = float(a)
elif (o == "-S"):
f = open (a, "w")
f.write(js.dumps(ffGlobals) + "\n")
f.close()
elif (o == "-L"):
f = open (a, "r")
ffGlobals = js.loads(f.readline())
f.close()
elif (o == "-R"):
ffGlobals = setffGlobals()
def switch(character):
def up():
return (1 + ffGlobals["scaler"])
def down():
return (1/(1 +ffGlobals["scaler"]))
def default():
return (1)
cases={
'+':up,
'-':down
}
return cases.get(character, default)()
def htmlHeader(htmlFile, imageFile):
temp=Image.open(imageFile)
imageSize=temp.size
outFile = open(htmlFile, "w")
textList=["<html>",
"<title>",
"Now we have clickable faces.",
"</title>",
"<h1>",
"Now we have clickable faces.",
"</h1>",
"<br>",
"<body>",
"\n",
"<script>",
"\n",
"function clickmap(info) {",
"alert(info)",
"}",
"\n",
"</script>",
"\n",
"<img src=\"",
imageFile,
"\"",
" width=\"",
str(imageSize[0]),
"\" ",
"height=\"",
str(imageSize[1]),
"\" ",
"alt=\"faces\" ",
"usemap=\"#facemap\">",
"\n",
"<map name=\"facemap\">",
"\n"
]
outFile.writelines(textList)
outFile.close()
def htmlTrailer(htmlFile):
outFile = open(htmlFile, "a")
textList=[
"</map>",
"\n",
"</body>",
"</html>"
]
outFile.writelines(textList)
outFile.close()
def htmlMap(htmlFile, lowerRight, upperLeft, number, scaler):
lowerRight=tuple(map(lambda x: int(x/scaler), lowerRight))
upperLeft=tuple(map(lambda x: int(x/scaler), upperLeft))
outFile = open(htmlFile, "a")
textList=[
"<area shape='rect' coords=\"",
str(lowerRight[0]),
",",
str(lowerRight[1]),
",",
str(upperLeft[0]),
",",
str(upperLeft[1]),
"\"",
" href=\"#\" onclick=\"clickmap('This is some stuff about face # ",
str(number),
"')\" alt=\"",
str(number),
"\">",
"\n"
]
outFile.writelines(textList)
outFile.close()
def findFaces(inFile):
# global ffGlobals
face_cascade = cv.CascadeClassifier(ffGlobals["HaarFile"])
im = Image.open(inFile)
size=im.size
# help(face_cascade.detectMultiScale)
fcScaleFactor=1.01 # works, slow, lots of false positives
fcScaleFactor=1.10 # works, faster, fair number of false negatives
fcScaleFactor=1.02 # seems like a nice balance. few false negatives, not too many false positives
fcScaleFactor=1.03 # seems like a nice balance. few false negatives, not too many false positives
fcMinNeighbors=4
while True:
scaledSize=tuple(map(lambda x: int(ffGlobals["scaleFactor"]*x), size))
om=im.resize(scaledSize, Image.ANTIALIAS)
f = NamedTemporaryFile()
om.save(f.name, "JPEG")
om = cv.imread(f.name)
savedImage=om.copy()
gray = cv.cvtColor(om, cv.COLOR_BGR2GRAY)
#calculate coordinates
faces = face_cascade.detectMultiScale(gray, fcScaleFactor, fcMinNeighbors)
print("Software has found ", len(faces), " faces (maybe).")
for (x,y,w,h) in faces:
temp=cv.rectangle(om,(x,y),(x+w,y+h),(255,0,0),2)
cv.imwrite(ffGlobals["monitorFile"], om)
inpt = input("Press + to increase, or - to decrease image size, or c to create HTML file > ")
inpt=inpt.lower()
if inpt == 'c' :
break
temp=switch(inpt)
ffGlobals["scaleFactor"] = ffGlobals["scaleFactor"] * temp
print("Input of ", inpt, "resulting in ", temp, " new scaleFactor is ", ffGlobals["scaleFactor"])
return(savedImage, faces)
def createHTMLFile(htmlFile, inFile, savedImage, faces, scaleFactor):
htmlHeader(htmlFile, inFile)
markedUpImage=savedImage.copy()
counter=0
for (x,y,w,h) in faces:
imgTemp = savedImage.copy()
temp=cv.rectangle(imgTemp,(x,y),(x+w,y+h),(255,0,0),5)
cv.imwrite(ffGlobals["monitorFile"],imgTemp)
inpt=input("Press Y/N/E to save/next/exit> ")
inpt=inpt.lower()
if (inpt == "y"):
print("Save faces[", counter, "]")
temp=cv.rectangle(markedUpImage,(x,y),(x+w,y+h),(255,0,0),5)
cv.imwrite(ffGlobals["markedUpFile"],markedUpImage)
inpt=input("What name goes along with face #"+str(counter)+"?")
htmlMap(ffGlobals["htmlFile"], [x, y], [x + w, y + h], str(counter)+" "+inpt, scaleFactor)
if (inpt == "e"):
break
counter=counter+1
htmlTrailer(ffGlobals["htmlFile"])
print("The HTML file has been created at:", ffGlobals["htmlFile"])
def cleanUpTemporaryFiles():
global ffGlobals
import os
filesToDelete=["markedUpFile", "monitorFile"]
for f in range(len(filesToDelete)):
file=ffGlobals[filesToDelete[f]]
print("Deleting: "+file)
os.remove(file)
def main():
dumpffGlobals()
savedImage, faces = findFaces(ffGlobals["inFile"])
createHTMLFile(ffGlobals["htmlFile"], ffGlobals["inFile"],
savedImage, faces,
ffGlobals["scaleFactor"])
cleanUpTemporaryFiles()
print("The program has ended.")
ffGlobals = setffGlobals()
if __name__ == "__main__":
myopts, args = getopt.getopt(sys.argv[1:],"?Di:o:H:m:M:s:S:L:R")
serviceCommandLineArgs(myopts, args)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment