Add possibility to use segments for beter face detection
This commit is contained in:
parent
ff4ccd7410
commit
3b0e0eda16
4 changed files with 281 additions and 58 deletions
|
@ -7,3 +7,7 @@ gphoto2 to capture images:
|
||||||
The modified 'webcam demo' to analyse and generate json:
|
The modified 'webcam demo' to analyse and generate json:
|
||||||
|
|
||||||
`/home/crowd/build/opencv-webcam-demo/opencv-webcam-demo --data /home/crowd/affdex-sdk/data --faceMode 1 --numFaces 80 -o /home/crowd/output-backup/ --draw 0`
|
`/home/crowd/build/opencv-webcam-demo/opencv-webcam-demo --data /home/crowd/affdex-sdk/data --faceMode 1 --numFaces 80 -o /home/crowd/output-backup/ --draw 0`
|
||||||
|
|
||||||
|
|
||||||
|
Using split_and_merge_output, frames are split into segments so SMALL_FACES actually detects _really_ small faces. This requires enabling --segments on opencv-webcam-demo (so it detects/writes segment%06d files)
|
||||||
|
split_and_merge_output also remerges these segment%06.json files into a frame%06d.json file.
|
||||||
|
|
|
@ -143,6 +143,7 @@ int main(int argsc, char ** argsv)
|
||||||
int buffer_length = 2;
|
int buffer_length = 2;
|
||||||
unsigned int nFaces = 1;
|
unsigned int nFaces = 1;
|
||||||
bool draw_display = true;
|
bool draw_display = true;
|
||||||
|
bool use_segments = false;
|
||||||
int faceDetectorMode = (int)FaceDetectorMode::SMALL_FACES;
|
int faceDetectorMode = (int)FaceDetectorMode::SMALL_FACES;
|
||||||
boost::filesystem::path imgPath("~/emo_in_file.jpg");
|
boost::filesystem::path imgPath("~/emo_in_file.jpg");
|
||||||
boost::filesystem::path outPath("~/output/");
|
boost::filesystem::path outPath("~/output/");
|
||||||
|
@ -167,6 +168,7 @@ int main(int argsc, char ** argsv)
|
||||||
("faceMode", po::value< int >(&faceDetectorMode)->default_value((int)FaceDetectorMode::SMALL_FACES), "Face detector mode (large faces vs small faces).")
|
("faceMode", po::value< int >(&faceDetectorMode)->default_value((int)FaceDetectorMode::SMALL_FACES), "Face detector mode (large faces vs small faces).")
|
||||||
("numFaces", po::value< unsigned int >(&nFaces)->default_value(1), "Number of faces to be tracked.")
|
("numFaces", po::value< unsigned int >(&nFaces)->default_value(1), "Number of faces to be tracked.")
|
||||||
("draw", po::value< bool >(&draw_display)->default_value(true), "Draw metrics on screen.")
|
("draw", po::value< bool >(&draw_display)->default_value(true), "Draw metrics on screen.")
|
||||||
|
("segments", po::value< bool >(&use_segments)->default_value(use_segments), "Use 'segments' rather than 'frames' (influences detecting frame%06d or segment%06d).")
|
||||||
//~ ("file,f", po::value< boost::filesystem::path >(&imgPath)->default_value(imgPath), "Filename of image that is watched/tracked for changes.")
|
//~ ("file,f", po::value< boost::filesystem::path >(&imgPath)->default_value(imgPath), "Filename of image that is watched/tracked for changes.")
|
||||||
("frameOutput,o", po::value< boost::filesystem::path >(&outPath)->default_value(outPath), "Directory to store the frame in (and json)")
|
("frameOutput,o", po::value< boost::filesystem::path >(&outPath)->default_value(outPath), "Directory to store the frame in (and json)")
|
||||||
;
|
;
|
||||||
|
@ -247,7 +249,7 @@ int main(int argsc, char ** argsv)
|
||||||
int seconds = 1;
|
int seconds = 1;
|
||||||
while(true){ //(cv::waitKey(20) != -1);
|
while(true){ //(cv::waitKey(20) != -1);
|
||||||
char buff[100];
|
char buff[100];
|
||||||
snprintf(buff, sizeof(buff), "frame%06d.jpg", frameNrIn);
|
snprintf(buff, sizeof(buff), (use_segments ? "segment%06d.jpg" : "frame%06d.jpg"), frameNrIn);
|
||||||
boost::filesystem::path imgPath = outPath / buff;
|
boost::filesystem::path imgPath = outPath / buff;
|
||||||
if ( !boost::filesystem::exists( imgPath.native() )|| frameNrIn > frameNrOut ) {
|
if ( !boost::filesystem::exists( imgPath.native() )|| frameNrIn > frameNrOut ) {
|
||||||
// wait for file to appear
|
// wait for file to appear
|
||||||
|
@ -257,7 +259,7 @@ int main(int argsc, char ** argsv)
|
||||||
std::cerr << "Read " << imgPath.native() << std::endl;
|
std::cerr << "Read " << imgPath.native() << std::endl;
|
||||||
|
|
||||||
char buff[100];
|
char buff[100];
|
||||||
snprintf(buff, sizeof(buff), "frame%06d.json", frameNrIn);
|
snprintf(buff, sizeof(buff), (use_segments ? "segment%06d.json" : "frame%06d.json"), frameNrIn);
|
||||||
boost::filesystem::path jsonPath = outPath / buff;
|
boost::filesystem::path jsonPath = outPath / buff;
|
||||||
|
|
||||||
// don't redo existing jsons
|
// don't redo existing jsons
|
||||||
|
@ -300,7 +302,7 @@ int main(int argsc, char ** argsv)
|
||||||
|
|
||||||
// store json
|
// store json
|
||||||
char buff[100];
|
char buff[100];
|
||||||
snprintf(buff, sizeof(buff), "frame%06d.json", frameNrOut);
|
snprintf(buff, sizeof(buff), (use_segments ? "segment%06d.json" : "frame%06d.json"), frameNrOut);
|
||||||
boost::filesystem::path targetFilename = outPath / buff;
|
boost::filesystem::path targetFilename = outPath / buff;
|
||||||
std::ofstream out(targetFilename.native());
|
std::ofstream out(targetFilename.native());
|
||||||
std::cerr << "write "<< targetFilename.native() << std::endl;
|
std::cerr << "write "<< targetFilename.native() << std::endl;
|
||||||
|
|
234
parse_output.py
234
parse_output.py
|
@ -1,18 +1,50 @@
|
||||||
import os
|
import os
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw,ImageTk
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import glob
|
import glob
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
import Tkinter
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
facialParameters = [
|
||||||
|
"smile",
|
||||||
|
"innerBrowRaise",
|
||||||
|
"browRaise",
|
||||||
|
"browFurrow",
|
||||||
|
"noseWrinkle",
|
||||||
|
"upperLipRaise",
|
||||||
|
"lipCornerDepressor",
|
||||||
|
"chinRaise",
|
||||||
|
"lipPucker",
|
||||||
|
"lipPress",
|
||||||
|
"lipSuck",
|
||||||
|
"mouthOpen",
|
||||||
|
"smirk",
|
||||||
|
#~ "attention",
|
||||||
|
"eyeClosure",
|
||||||
|
"eyeWiden",
|
||||||
|
"cheekRaise",
|
||||||
|
"lidTighten",
|
||||||
|
"dimpler",
|
||||||
|
"lipStretch",
|
||||||
|
"jawDrop",
|
||||||
|
]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Parses opencv-webcam-demo json output files and collects statistics')
|
parser = argparse.ArgumentParser(description='Parses opencv-webcam-demo json output files and collects statistics')
|
||||||
parser.add_argument('--frameOutput', '-o', required=True, help='directory to look for frames & json')
|
parser.add_argument('--frameOutput', '-o', required=True, help='directory to look for frames & json')
|
||||||
parser.add_argument('--status', '-s', action='store_true', help='Keep status of last frame')
|
parser.add_argument('--status', action='store_true', help='Keep status of last frame')
|
||||||
parser.add_argument('--cutAllFaces', action='store_true', help='Cut out all faces from all frames')
|
parser.add_argument('--cutAllFaces', action='store_true', help='Cut out all faces from all frames')
|
||||||
parser.add_argument('--sum', action='store_true', help='Get total scores over all time')
|
parser.add_argument('--sum', action='store_true', help='Get total scores over all time')
|
||||||
|
parser.add_argument('--unique', action='store_true', help='Get most unique window')
|
||||||
|
parser.add_argument('--avg', action='store_true', help='Get most average window')
|
||||||
parser.add_argument('--disonant', action='store_true', help='Get most disonant faces over time')
|
parser.add_argument('--disonant', action='store_true', help='Get most disonant faces over time')
|
||||||
|
parser.add_argument('--window-size', '-s', type=int, default=10, help='The nr of frames to group in one sliding window for analysis')
|
||||||
|
parser.add_argument("--params", "-p", type=str, nargs='+', default=facialParameters, choices=facialParameters, help="The parameters used to calculate the statistics")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -29,30 +61,8 @@ class Face:
|
||||||
r = self.data['rect']
|
r = self.data['rect']
|
||||||
return self.frame.getImg().crop((int(r['x']), int(r['y']), int(r['x']+r['w']), int(r['y']+r['h'])))
|
return self.frame.getImg().crop((int(r['x']), int(r['y']), int(r['x']+r['w']), int(r['y']+r['h'])))
|
||||||
|
|
||||||
def getCharacteristicVector(self):
|
def getCharacteristicVector(self, params):
|
||||||
self.vector = np.array([
|
self.vector = [self.data[p] for p in params]
|
||||||
self.data["smile"],
|
|
||||||
self.data["innerBrowRaise"],
|
|
||||||
self.data["browRaise"],
|
|
||||||
self.data["browFurrow"],
|
|
||||||
self.data["noseWrinkle"],
|
|
||||||
self.data["upperLipRaise"],
|
|
||||||
self.data["lipCornerDepressor"],
|
|
||||||
self.data["chinRaise"],
|
|
||||||
self.data["lipPucker"],
|
|
||||||
self.data["lipPress"],
|
|
||||||
self.data["lipSuck"],
|
|
||||||
self.data["mouthOpen"],
|
|
||||||
self.data["smirk"],
|
|
||||||
self.data["eyeClosure"],
|
|
||||||
# self.data["attention"],
|
|
||||||
self.data["eyeWiden"],
|
|
||||||
self.data["cheekRaise"],
|
|
||||||
self.data["lidTighten"],
|
|
||||||
self.data["dimpler"],
|
|
||||||
self.data["lipStretch"],
|
|
||||||
self.data["jawDrop"],
|
|
||||||
])
|
|
||||||
return self.vector
|
return self.vector
|
||||||
|
|
||||||
def setAnomalyScore(self, score):
|
def setAnomalyScore(self, score):
|
||||||
|
@ -81,8 +91,17 @@ class Frame:
|
||||||
#~ # no json file yet?
|
#~ # no json file yet?
|
||||||
#~ return None
|
#~ return None
|
||||||
|
|
||||||
def getImg(self):
|
def getImg(self, markFaces = True):
|
||||||
return Image.open(self.imgPath)
|
img = Image.open(self.imgPath)
|
||||||
|
if not markFaces:
|
||||||
|
return img
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
for f in self.faces:
|
||||||
|
xy1 = (int(f.data['rect']['x']), int(f.data['rect']['y']))
|
||||||
|
xy2 = (int(f.data['rect']['x'] + f.data['rect']['w']), int(f.data['rect']['y'] + f.data['rect']['h']))
|
||||||
|
draw.rectangle([xy1, xy2], outline="#ff0000")
|
||||||
|
return img
|
||||||
|
|
||||||
def getFaces(self):
|
def getFaces(self):
|
||||||
if self.faces is None:
|
if self.faces is None:
|
||||||
|
@ -108,13 +127,13 @@ class Frame:
|
||||||
for face in self.getFaces():
|
for face in self.getFaces():
|
||||||
face.disonanceScore = abs(face.data['valence'] - avgValence)
|
face.disonanceScore = abs(face.data['valence'] - avgValence)
|
||||||
|
|
||||||
def getAverageV(self):
|
def getAverageV(self, params):
|
||||||
vectors = [face.getCharacteristicVector() for face in self.getFaces()]
|
vectors = [face.getCharacteristicVector(params) for face in self.getFaces()]
|
||||||
vAvg = np.mean(vectors, axis=0)
|
vAvg = np.mean(vectors, axis=0)
|
||||||
return vAvg
|
return vAvg
|
||||||
|
|
||||||
def updateAnomalyScores(self):
|
def updateAnomalyScores(self, params):
|
||||||
vAvg = self.getAverageV()
|
vAvg = self.getAverageV(params)
|
||||||
for face in self.getFaces():
|
for face in self.getFaces():
|
||||||
face.setAnomalyScore(np.linalg.norm(face.getCharacteristicVector() - vAvg))
|
face.setAnomalyScore(np.linalg.norm(face.getCharacteristicVector() - vAvg))
|
||||||
|
|
||||||
|
@ -124,6 +143,88 @@ class Frame:
|
||||||
|
|
||||||
frames = {}
|
frames = {}
|
||||||
|
|
||||||
|
class Window:
|
||||||
|
def __init__(self, frameSubset):
|
||||||
|
"""
|
||||||
|
Init a sliding window for given Frame-s
|
||||||
|
"""
|
||||||
|
self.frames = frameSubset
|
||||||
|
self.deviation = None
|
||||||
|
self.standardDeviation = None
|
||||||
|
|
||||||
|
def getFaces(self):
|
||||||
|
faces = []
|
||||||
|
for frame in self.frames:
|
||||||
|
faces.extend(frame.getFaces())
|
||||||
|
return faces
|
||||||
|
|
||||||
|
def getStdDev(self, params):
|
||||||
|
"""
|
||||||
|
Get standard deviation of the faces within the window for given params
|
||||||
|
"""
|
||||||
|
vectors = [f.getCharacteristicVector(params) for f in self.getFaces()]
|
||||||
|
return np.std(vectors)
|
||||||
|
|
||||||
|
def getAverageV(self, params):
|
||||||
|
vectors = [f.getCharacteristicVector(params) for f in self.getFaces()]
|
||||||
|
vAvg = np.mean(vectors, axis=0)
|
||||||
|
return vAvg
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def createWindows(windowSize, frames):
|
||||||
|
"""
|
||||||
|
Give a full list of frames and tunrn it into a collection of sliding windows
|
||||||
|
"""
|
||||||
|
frames = sorted(frames.items(), key=lambda f: f[0])
|
||||||
|
frames = [f[1] for f in frames]
|
||||||
|
windows = []
|
||||||
|
windowCount = len(frames) - windowSize + 1
|
||||||
|
if windowCount < 1:
|
||||||
|
raise Exception("Not enough frames ({}) for a window of size {}".format(len(frames), windowSize))
|
||||||
|
|
||||||
|
for offset in range(0, windowCount):
|
||||||
|
frameSubset = frames[offset:offset+windowSize]
|
||||||
|
windows.append(Window(frameSubset))
|
||||||
|
|
||||||
|
return windows
|
||||||
|
|
||||||
|
class WindowCollection:
|
||||||
|
def __init__(self, windowSize, frames):
|
||||||
|
self.windows = Window.createWindows(windowSize, frames)
|
||||||
|
self.frames = frames
|
||||||
|
#~ self.faces = [face for face in frame.getFaces() for frame in frames]
|
||||||
|
|
||||||
|
#~ def getMostWindowsClosestToMedian(self, nr = 5):
|
||||||
|
#~ """
|
||||||
|
#~ Get windows with the faces closest to the median
|
||||||
|
#~ """
|
||||||
|
#~ self.faces
|
||||||
|
|
||||||
|
def getWindowVectors(self, params):
|
||||||
|
return [window.getAverageV(params) for window in self.windows]
|
||||||
|
|
||||||
|
def getWindowsByDeviation(self, params):
|
||||||
|
vectors = self.getWindowVectors(params)
|
||||||
|
vAvg = np.mean(vectors, axis=0)
|
||||||
|
#~ diffs = [numpy.linalg.norm(v-vAvg) for v in vectors]
|
||||||
|
#~ min_index, min_value = min(enumerate(diffs), key=lambda p: p[1])
|
||||||
|
#~ max_index, max_value = max(enumerate(diffs), key=lambda p: p[1])
|
||||||
|
|
||||||
|
return sorted(self.windows, key=lambda w: np.linalg.norm(w.getAverageV(params)-vAvg))
|
||||||
|
|
||||||
|
def getUniqueWindows(self, params, nr=5):
|
||||||
|
windows = self.getWindowsByDeviation(params)
|
||||||
|
return windows[0: nr]
|
||||||
|
|
||||||
|
def getMostAvgWindows(self, params, nr=5):
|
||||||
|
windows = self.getWindowsByDeviation(params)
|
||||||
|
windows.reverse()
|
||||||
|
return windows[0:nr]
|
||||||
|
|
||||||
|
def getMostContrastingWindows(self, params, nr=5):
|
||||||
|
sortedWindows = sorted(self.windows, key=lambda w: w.getStdDev(params), reverse=True)
|
||||||
|
return sortedWindows[0:nr]
|
||||||
|
|
||||||
def loadFrames(frameDir):
|
def loadFrames(frameDir):
|
||||||
global frames
|
global frames
|
||||||
nr = 2
|
nr = 2
|
||||||
|
@ -185,9 +286,9 @@ def getMostDisonant(nr = 5):
|
||||||
print("Frame %d, face %d, score %d, valence %d" % (face.frame.nr, face.id, face.disonanceScore, face.data['valence']))
|
print("Frame %d, face %d, score %d, valence %d" % (face.frame.nr, face.id, face.disonanceScore, face.data['valence']))
|
||||||
face.getFaceImg().show()
|
face.getFaceImg().show()
|
||||||
|
|
||||||
def getAnomalies(nr = 5):
|
def getAnomalies(params, nr = 5):
|
||||||
for frameNr, frame in loadFrames(args.frameOutput).items():
|
for frameNr, frame in loadFrames(args.frameOutput).items():
|
||||||
frame.updateAnomalyScores()
|
frame.updateAnomalyScores(params)
|
||||||
faces.sort(key=lambda x: x.anomalyScore, reverse=True)
|
faces.sort(key=lambda x: x.anomalyScore, reverse=True)
|
||||||
|
|
||||||
anomalies = faces[:nr]
|
anomalies = faces[:nr]
|
||||||
|
@ -196,10 +297,10 @@ def getAnomalies(nr = 5):
|
||||||
#~ getCharacteristicVector
|
#~ getCharacteristicVector
|
||||||
face.getFaceImg().show()
|
face.getFaceImg().show()
|
||||||
|
|
||||||
def printFrameStats(frame):
|
def printFrameStats(frame, params):
|
||||||
os.system('clear')
|
os.system('clear')
|
||||||
print(time.time())
|
print(time.time())
|
||||||
print( ("Nr: %d" % frame.nr).ljust(40) + ("t: %f" % frame.getJson()['t']) )
|
print( ("Nr: %d" % frame.nr).ljust(40) + ("t: {}".format(frame.getJson()['t'])) )
|
||||||
#~ print
|
#~ print
|
||||||
faces = frame.getFaces()
|
faces = frame.getFaces()
|
||||||
print("Faces: %d" % len(faces))
|
print("Faces: %d" % len(faces))
|
||||||
|
@ -207,24 +308,16 @@ def printFrameStats(frame):
|
||||||
if len(faces) < 1:
|
if len(faces) < 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
params = ['smile', 'browFurrow']
|
print " ".ljust(20), "0%".rjust(13), "q1".rjust(13), "median".rjust(13), "q3".rjust(13), "100%".rjust(13)
|
||||||
|
|
||||||
q0s = [np.percentile(np.array([f.data[param] for f in faces]),0) for param in params]
|
|
||||||
q1s = [np.percentile(np.array([f.data[param] for f in faces]),25) for param in params]
|
|
||||||
q2s = [np.percentile(np.array([f.data[param] for f in faces]),50) for param in params]
|
|
||||||
q3s = [np.percentile(np.array([f.data[param] for f in faces]),75) for param in params]
|
|
||||||
q4s = [np.percentile(np.array([f.data[param] for f in faces]),100) for param in params]
|
|
||||||
|
|
||||||
print " ".ljust(8),
|
|
||||||
for p in params:
|
for p in params:
|
||||||
print p.center(20),
|
q0 = np.percentile(np.array([f.data[p] for f in faces]),0)
|
||||||
print ""
|
q1 = np.percentile(np.array([f.data[p] for f in faces]),25)
|
||||||
|
q2 = np.percentile(np.array([f.data[p] for f in faces]),50)
|
||||||
|
q3 = np.percentile(np.array([f.data[p] for f in faces]),75)
|
||||||
|
q4 = np.percentile(np.array([f.data[p] for f in faces]),100)
|
||||||
|
print p.ljust(20), ("%f%%" % q0).rjust(13), ("%f%%" % q1).rjust(13),("%f%%" % q2).rjust(13),("%f%%" % q3).rjust(13),("%f%%" % q4).rjust(13)
|
||||||
|
|
||||||
print(" 0% " + "".join([("%f%%" % q).rjust(20) for q in q0s]))
|
|
||||||
print(" q1 " + "".join([("%f%%" % q).rjust(20) for q in q1s]))
|
|
||||||
print(" median " + "".join([("%f%%" % q).rjust(20) for q in q2s]))
|
|
||||||
print(" q3 " + "".join([("%f%%" % q).rjust(20) for q in q3s]))
|
|
||||||
print(" 100% " + "".join([("%f%%" % q).rjust(20) for q in q4s]))
|
|
||||||
|
|
||||||
#~ TODO: speaker stats
|
#~ TODO: speaker stats
|
||||||
|
|
||||||
|
@ -233,15 +326,37 @@ def printFrameStats(frame):
|
||||||
#~ dissonantFace.getFaceImg()
|
#~ dissonantFace.getFaceImg()
|
||||||
|
|
||||||
|
|
||||||
def monitorStatus(frameDir):
|
def monitorStatus(frameDir, params):
|
||||||
while True:
|
while True:
|
||||||
frame = getLastFrame(frameDir)
|
frame = getLastFrame(frameDir)
|
||||||
if not frame is None:
|
if not frame is None:
|
||||||
printFrameStats(frame)
|
printFrameStats(frame, params)
|
||||||
|
|
||||||
# don't check too often
|
# don't check too often
|
||||||
time.sleep(.5)
|
time.sleep(.5)
|
||||||
|
|
||||||
|
def playWindowStopmotion(window):
|
||||||
|
"""
|
||||||
|
Play a set of sliding window frames as stop motion video
|
||||||
|
"""
|
||||||
|
root = Tkinter.Tk()
|
||||||
|
root.geometry('%dx%d+%d+%d' % (1000,1000,0,0))
|
||||||
|
canvas = Tkinter.Canvas(root,width=1000,height=1000)
|
||||||
|
canvas.pack()
|
||||||
|
|
||||||
|
old_label_image = None
|
||||||
|
for frame in window.frames:
|
||||||
|
image = frame.getImg()
|
||||||
|
basewidth = 1000
|
||||||
|
wpercent = (basewidth / float(image.size[0]))
|
||||||
|
hsize = int((float(image.size[1]) * float(wpercent)))
|
||||||
|
image = image.resize((basewidth, hsize), Image.ANTIALIAS)
|
||||||
|
|
||||||
|
tkpi = ImageTk.PhotoImage(image)
|
||||||
|
canvas.delete("IMG")
|
||||||
|
imagesprite = canvas.create_image(500,500,image=tkpi, tags="IMG")
|
||||||
|
root.update()
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
validateJsonTimes()
|
validateJsonTimes()
|
||||||
|
|
||||||
|
@ -259,6 +374,17 @@ if args.cutAllFaces:
|
||||||
for frameNr, frame in loadFrames(args.frameOutput).items():
|
for frameNr, frame in loadFrames(args.frameOutput).items():
|
||||||
cutOutFaces(faceDir)
|
cutOutFaces(faceDir)
|
||||||
|
|
||||||
|
if args.unique:
|
||||||
|
collection = WindowCollection(args.window_size, frames)
|
||||||
|
windows = collection.getUniqueWindows(args.params)
|
||||||
|
#~ print(windows)
|
||||||
|
playWindowStopmotion(windows[0])
|
||||||
|
|
||||||
|
if args.avg:
|
||||||
|
collection = WindowCollection(args.window_size, frames)
|
||||||
|
windows = collection.getMostAvgWindows(args.params)
|
||||||
|
#~ print(windows)
|
||||||
|
playWindowStopmotion(windows[0])
|
||||||
|
|
||||||
if args.status:
|
if args.status:
|
||||||
monitorStatus(args.frameOutput)
|
monitorStatus(args.frameOutput, args.params)
|
||||||
|
|
91
split_and_merge_output.py
Normal file
91
split_and_merge_output.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="""
|
||||||
|
Frames are split into segments so SMALL_FACES actually detects _really_ small faces. This requires enabling --segments on opencv-webcam-demo (so it detects/writes segment%06d files) split_and_merge_output.py also remerges these segment%06.json files into a frame%06d.json file.
|
||||||
|
""")
|
||||||
|
parser.add_argument('--frameOutput', '-o', required=True, help='directory to look for frames & json')
|
||||||
|
parser.add_argument('--segmentDir', required=True, help='directory to write segfor frames & json')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.exists(args.frameOutput):
|
||||||
|
raise Exception(args.frameOutput + " does not exist")
|
||||||
|
if not os.path.exists(args.segmentDir):
|
||||||
|
raise Exception(args.segmentDir + " does not exist")
|
||||||
|
|
||||||
|
def getSectionsForSize(size):
|
||||||
|
"""
|
||||||
|
Get sections based on image size, in a tuple format (width, height)
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
( 0, 0, int(size[0]/1.85), int(size[1]/1.85) ),
|
||||||
|
( int(size[0]/2.1), 0, size[0], int(size[1]/1.85) ),
|
||||||
|
( 0, int(size[1]/2.1), int(size[0]/1.85), size[1] ),
|
||||||
|
( int(size[0]/2.1), int(size[1]/2.1), size[0], size[1] ),
|
||||||
|
]
|
||||||
|
|
||||||
|
nextInNr = 1
|
||||||
|
segmentCount = 4 # cannot really be changed for now
|
||||||
|
nextOutNr = segmentCount
|
||||||
|
while True:
|
||||||
|
nextJpg = os.path.join(args.frameOutput, "frame%06d.jpg" % nextInNr)
|
||||||
|
nextInJson = os.path.join(args.frameOutput, "frame%06d.json" % nextInNr)
|
||||||
|
while os.path.exists(nextJpg) and not os.path.exists(nextInJson):
|
||||||
|
print("(SPLIT) Found: {}".format(nextJpg))
|
||||||
|
img = Image.open(nextJpg)
|
||||||
|
|
||||||
|
segments = getSectionsForSize(img.size)
|
||||||
|
|
||||||
|
for i, segment in enumerate(segments):
|
||||||
|
segmentImg = img.crop(segment)
|
||||||
|
segmentNr = (nextInNr-1) * segmentCount + i + 1 # start at 1, increment from there
|
||||||
|
segmentFilename = os.path.join(args.segmentDir, "segment%06d.jpg" % segmentNr)
|
||||||
|
segmentImg.save(segmentFilename, quality=90)
|
||||||
|
print("(SPLIT) Created: {}".format(segmentFilename))
|
||||||
|
|
||||||
|
nextInNr += 1
|
||||||
|
nextJpg = os.path.join(args.frameOutput, "frame%06d.jpg" % nextInNr)
|
||||||
|
nextInJson = os.path.join(args.frameOutput, "frame%06d.jpg" % nextInNr)
|
||||||
|
|
||||||
|
nextJson = os.path.join(args.segmentDir, "segment%06d.json" % nextOutNr)
|
||||||
|
while os.path.exists(nextJson):
|
||||||
|
print("(MERGE) Found: {}".format(nextJson))
|
||||||
|
frameOutNr = (nextOutNr - 1) / segmentCount + 1 # 1,2,3,4 -> 1; 5,6,7,8 -> 2 (uses trick to divide ints: 7/4=1)
|
||||||
|
img = Image.open( os.path.join(args.frameOutput, "frame%06d.jpg" % frameOutNr) )
|
||||||
|
segments = getSectionsForSize(img.size)
|
||||||
|
creationTime = img._getexif()[36867]
|
||||||
|
|
||||||
|
faces = []
|
||||||
|
times = []
|
||||||
|
|
||||||
|
for i, segment in enumerate(segments):
|
||||||
|
segmentNr = nextOutNr - segmentCount + i + 1 # nextOutNr: 4 => 1,2,3,4
|
||||||
|
with open( os.path.join(args.segmentDir, "segment%06d.json" % segmentNr) ) as fp:
|
||||||
|
j = json.load(fp)
|
||||||
|
|
||||||
|
for f in j['faces']:
|
||||||
|
f['rect']['x'] += segment[0]
|
||||||
|
f['rect']['y'] += segment[1]
|
||||||
|
f['id'] = "%d%02d" % ((i+1), f['id'])
|
||||||
|
faces.append(f)
|
||||||
|
times.append(j['t'])
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'nr': int(frameOutNr),
|
||||||
|
't': creationTime,
|
||||||
|
'faces': faces,
|
||||||
|
'sectionTimes': times,
|
||||||
|
}
|
||||||
|
jsonOutFilename = os.path.join(args.frameOutput, "frame%06d.json" % frameOutNr)
|
||||||
|
with open(jsonOutFilename, 'w') as fp:
|
||||||
|
json.dump(data, fp)
|
||||||
|
print("(MERGE) Wrote: {}".format(jsonOutFilename))
|
||||||
|
|
||||||
|
nextOutNr += segmentCount
|
||||||
|
nextJson = os.path.join(args.segmentDir, "segment%06d.json" % nextOutNr)
|
||||||
|
|
||||||
|
time.sleep(0.2)
|
Loading…
Reference in a new issue