Skip to content

Commit

Permalink
Image templating added
Browse files Browse the repository at this point in the history
  • Loading branch information
georgegach committed Oct 11, 2018
1 parent 7567f2f commit 09812e0
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 10 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ usage: run.py [-h] -i INPUT [-ow WIDTH] [-oh HEIGHT] [-e EXT [EXT ...]]
-nc, --no-caching Ignores .ff file cache if specified
-wt WINDOWTIME, --window-time WINDOWTIME
Duration of each frame in debug window
-t TEMPLATE, --template TEMPLATE
Template input image to set as the main face shape
rather than the total average
```


Expand Down
3 changes: 2 additions & 1 deletion run.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
parser.add_argument('-nw', '--no-warps', dest="noWarps", help="Hides warping stage if specified", action="store_true", default=False)
parser.add_argument('-nc', '--no-caching', dest="noCaching", help="Ignores .ff file cache if specified", action="store_true", default=False)
parser.add_argument('-wt', '--window-time', dest="windowTime", help="Duration of each frame in debug window", type=int, default=500)
parser.add_argument('-t', '--template', dest="template", help="Template input image to set as the main face shape rather than the total average", type=str, default=None)

options = parser.parse_args()
print(options)
ext = options.ext or ["*.jpg", "*.jpeg"]
Averager(width=options.width, height=options.height).run(path=options.input, ext=ext, window=options.window, showWarps=not options.noWarps, windowTime=options.windowTime, useCaching=not options.noCaching).save(name=options.output)
Averager(width=options.width, height=options.height).run(path=options.input, ext=ext, window=options.window, showWarps=not options.noWarps, windowTime=options.windowTime, useCaching=not options.noCaching, template=options.template).save(name=options.output)

print(f">>> Executed in {time.time()-start:.2f} seconds")
53 changes: 45 additions & 8 deletions src/faceAverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def __init__(self, width=600, height=800):
self.width = width
self.height = height
self.detective = Detective()
self.params = {
'eyeDistance' : 0.3,
'eyeRatioY' : 2.5
}


def loadImages(self, detections):
pbar = tqdm(range(len(detections)))
Expand All @@ -28,17 +33,36 @@ def loadImages(self, detections):
return [np.float32(im['img'])/255.0 for im in detections]


def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWarps=False, useCaching=True):

def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWarps=False, useCaching=True, template=None):
self.windowTime = windowTime
self.inputpath = path
self.images = self.detective.getImages(path, ext=ext).features(useCaching=useCaching).detections
self.images = self.detective.getImages(path, ext=ext, template=template).features(useCaching=useCaching).detections
w, h = self.width, self.height

allPoints = [im['shape'] for im in self.images]
images = self.loadImages(self.images)

# Eye corners
eyecornerDst = [ (np.int(0.38 * w ), np.int(h / 2.5)), (np.int(0.62 * w ), np.int(h / 2.5)) ]
# Place a given template in a correct position on canvas
if template != None:
imEyeDistX = allPoints[0][45][0] - allPoints[0][36][0]
scale = (w*self.params['eyeDistance']) / imEyeDistX
allPoints[0] = np.multiply(allPoints[0], scale).astype(np.int)
images[0] = (cv2.resize(images[0], (0,0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC).astype(np.float64) * 255).astype(np.uint8)
imEyeMid = ((allPoints[0][45][0] + allPoints[0][36][0])//2, (allPoints[0][45][1] + allPoints[0][36][1])//2)
x_start = np.int(w/2 - imEyeMid[0])
y_start = np.int(h/self.params['eyeRatioY'] - imEyeMid[1])
allPoints[0] += (x_start, y_start)
canvas = Image.fromarray(np.zeros((h, w, 3), images[0].dtype))
canvas.paste(Image.fromarray(images[0]), (x_start, y_start))
canvas = np.array(canvas).astype(np.float32)/255
canvas = cv2.medianBlur(canvas, 3)
images[0] = canvas
imEyeDistX = allPoints[0][45][0] - allPoints[0][36][0]
imEyeDistY = allPoints[0][45][1] - allPoints[0][36][1]
eyecornerDst = [ (np.int(w/2 - imEyeDistX/2), np.int(h / self.params['eyeRatioY'])), (np.int(w/2 + imEyeDistX/2), np.int(h / self.params['eyeRatioY']) + imEyeDistY), ]

else:
eyecornerDst = [ (np.int(w/2 - w*(self.params['eyeDistance']/2)), np.int(h / self.params['eyeRatioY'])), (np.int(w/2 + w*(self.params['eyeDistance']/2) ), np.int(h / self.params['eyeRatioY'])) ]

imagesNorm = []
pointsNorm = []
Expand All @@ -47,7 +71,15 @@ def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWa
boundaryPts = np.array([(0,0), (w/2,0), (w-1,0), (w-1,h/2), ( w-1, h-1 ), ( w/2, h-1 ), (0, h-1), (0,h/2) ])

# Initialize location of average points to 0s
pointsAvg = np.array([(0,0)]* ( len(allPoints[0]) + len(boundaryPts) ), np.float32())
if template != None:
points1 = allPoints[0]
tform = self.similarityTransform(eyecornerDst, eyecornerDst)
points2 = np.reshape(np.array(points1), (68,1,2))
points = cv2.transform(points2, tform)
pointsAvg = np.float32(np.reshape(points, (68, 2)))
pointsAvg = np.append(pointsAvg, boundaryPts, axis=0)
else:
pointsAvg = np.array([(0,0)]* ( len(allPoints[0]) + len(boundaryPts) ), np.float32())

n = len(allPoints[0])

Expand Down Expand Up @@ -81,7 +113,8 @@ def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWa
points = np.append(points, boundaryPts, axis=0)

# Calculate location of average landmark points.
pointsAvg = pointsAvg + points / numImages
if template == None:
pointsAvg = pointsAvg + points / numImages

pointsNorm.append(points)
imagesNorm.append(img)
Expand Down Expand Up @@ -132,6 +165,10 @@ def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWa

if window:
oldImg = cv2.cvtColor(imagesNorm[i], cv2.COLOR_BGR2RGB)

for j, point in enumerate(pointsNorm[i]):
cv2.putText(oldImg, str(j), (int(point[0]), int(point[1])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2)

resultImg = cv2.cvtColor(output / (i+1), cv2.COLOR_BGR2RGB)
theimg = np.hstack((resultImg, oldImg))
cv2.imshow('Face Average', theimg)
Expand All @@ -157,7 +194,7 @@ def run(self, path, ext=['*.jpg','*.jpeg'], window=False, windowTime=500, showWa
if window:
cv2.resizeWindow('Face Average', w, h)
cv2.imshow('Face Average', output)
cv2.waitKey(10000)
cv2.waitKey(self.windowTime*2)

self.result = output * 255

Expand Down
7 changes: 6 additions & 1 deletion src/faceFeaturesDetector.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ def __init__(self, predictor_path="src/shape_predictor_68_face_landmarks.dat"):
self.predictor = dlib.shape_predictor(predictor_path)


def getImages(self, path, ext=["*jpg"]):
def getImages(self, path, ext=["*jpg"], template=None):
self.files = []
for e in ext:
self.files.extend(glob.glob(os.path.join(path, e)))
if template != None:
index = [i for i, s in enumerate(self.files) if template in s]
assert index != [], "> Template '{t}' name was not found".format(t=template)
index = index[0]
self.files = [self.files[index]] + self.files[0:index] + self.files[index+1:]
return self

# returns image, d
Expand Down

0 comments on commit 09812e0

Please sign in to comment.