Any access control or attendance system needs to be fake-proof. It's possible to cheat the above system by showing a photo of a registered person. How can we differentiate a real human vs a photo?
We can treat "liveness detection" as a binary classification problem and train a CNN to distinguish real faces from fake ones. But this would be expensive on the edge. Alternatively, we can detect spoofing,
1. In 3D: Using light reflections on the face. Might be overkill on edge devices.
2. In 2D: We can do eyewink detection in 2D images. Feasible on edge devices.
To detect eye winks, it's most efficient to monitor the change in white pixel count around the eye region. But it's not as reliable as monitoring the EAR (Eye Aspect Ratio). If the Eye Aspect Ratio rises and falls periodically then it's a real human, otherwise fake. The rise and fall can be detected by fitting a sigmoid or inverse sigmoid curve.
Instead, we can always use Deep Learning or ML techniques to classify an eye image as open or closed. But it's advisable, in the interest of efficiency, to use a numerical solution when you code for edge devices. See how the spread of non-zero pixels in the histogram takes a sudden dip when an eye is closed.
We have used a parametric curve fit algorithm to fit a sigmoid or inverse sigmoid function at the tail end of the above curve to detect eye open or close events. The person is a real human if any such event occurred.
# Code to fit the inverse sigmoid curve to tail end of signal def sigmoid(x, L ,x0, k, b): y = L / (1 + np.exp(k*(x-x0)))+b return (y) def isCurveSigmoid(pixelCounts, count): try: xIndex = len(pixelCounts) p0 = [max(pixelCounts), np.median(xIndex),1,min(pixelCounts)] # this is an mandatory initial guess popt, pcov = curve_fit(sigmoid, list(range(xIndex)), pixelCounts, p0, method='lm', maxfev=5000) yVals = sigmoid(list(range(xIndex)), *popt) # May have to check for a value much less than Median to avoid false positives. if np.median(yVals[:10]) - np.median(yVals[-10:]) > 15: print('Event Triggered...') return True except Exception as err: print(traceback.format_exc()) return False def findCurveFit(eye, image, pixelCount, frame_count, numFrames = 50): triggerEvent = False if (len(image) == 0): return pixelCount, False # Convert to gray scale as histogram works well on 256 values. gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # calculate frequency of pixels in range 0-255 histg = cv2.calcHist([gray],,None,,[0,256]) # hack to know whether eye is closed or not. # more spread of pixels in a histogram signifies an opened eye activePixels = np.count_nonzero(histg) pixelCount.append(activePixels) if len(pixelCount) > numFrames and frame_count % 15 == 0: if isCurveSigmoid(pixelCount[-numFrames+10:], len(pixelCount)): print('Event Triggered...') pixelCount.clear() plt.clf() triggerEvent = True return pixelCount, triggerEvent