Close

Soln #3: Anti-Spoofing Techniques for Attendance Registration

A project log for Multi-Domain Depth AI Usecases on the Edge

SLAM, ADAS-CAS, Sensor Fusion, Touch-less Attendance, Elderly Assist, Monocular Depth, Gesture & Security Cam with OpenVINO, Math & RPi

Anand UthamanAnand Uthaman 10/25/2021 at 06:330 Comments

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.

eye-wink.gif
                                                            Identifying open vs closed eye based on the count of white pixels

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.

eye_wink_histogram.gif
                                                                                   Histogram Spread during Left Eye Wink

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.

inverse_sigmoid_fitting.gif
                                                 Inverse Sigmoid Curve Fitting: Left and Right Eye Wink


# 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],[0],None,[256],[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

Discussions