Optical recognition flexNExT blinkies detector

As you may know, I intend to extend my login software to include optical recognition of my flexNExT’s blinkies - as in, it identifies the NFC chip’s UID of course, but it doesn’t grant access unless it also recognizes the triangular pattern of the blinkies from an overhead camera exactly at the same time the reader is activated.

So I’ve been playing with my webcam at home to get the ball rolling and code some working code. If you’re interested in playing with this also, here’s the Python script, and what it produces on the screen:

#!/usr/bin/python3
"""DT flexNExT implant's blinkies optical detector
"""
import cv2
import numpy as np
import math



# flexNExT blinkies detection parameters
min_blinky_radius = 5 #pixels
max_blinky_radius = 30 #pixels
min_blinkies_distance = 20 #pixels
max_blinkies_distance = 100 #pixels
blinkies_size_diff_tolerance = 50 #%
blinkies_equilateral_triangle_tolerance = 20 #%
blur_radius = 5
hough_gradient_dp = 1.0
hough_gradient_param1 = 50
hough_gradient_param2 = 25



# Overlay options
overlay_flexnext_outline = False
overlay_blinky_outlines = True
overlay_triangle = True



# Various convenience variables
red = (0, 0, 255)

bstmin = (100 - blinkies_size_diff_tolerance) / 100
bstmax = (100 + blinkies_size_diff_tolerance) / 100

betmin = (100 - blinkies_equilateral_triangle_tolerance) / 100
betmax = (100 + blinkies_equilateral_triangle_tolerance) / 100



# Open the default video capture device
cap = cv2.VideoCapture(0)

while True:

  # Grab an image
  image = cap.read()[1]

  # Find circles in the image
  gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
  blurred = cv2.GaussianBlur(gray, (blur_radius, blur_radius), 0)

  circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT,
		hough_gradient_dp, min_blinkies_distance,
		minRadius = min_blinky_radius, maxRadius = max_blinky_radius,
		param1 = hough_gradient_param1, param2 = hough_gradient_param2)

  if circles is not None:

    # Convert the coordinates and radii of the circles to integers
    circles = np.round(circles[0, :]).astype("int")

    # Loop over possible first vertices of the blinkies' triangle
    for i1, (x1, y1, r1) in enumerate(circles):

      # If that circle is already used, skip it
      if not r1:
        continue

      # Loop over possible second vertices of the blinkies' triangle
      for i2, (x2, y2, r2) in enumerate(circles):

        # If that circle is already used or identical to the first circle,
        # skip it
        if not r2 or i2 == i1:
          continue

        # Distance between the first two possible vertices
        v1v2 = math.sqrt((x2-x1)**2 + (y2-y1)**2)

        # If that circle is too far or too dissimilar to the first one, skip it
        if v1v2 > max_blinkies_distance or not (r1 * bstmin < r2 < r1 * bstmax):
          continue

        # Loop over possible third vertices of the blinkies' triangle
        for i3, (x3, y3, r3) in enumerate(circles):

          # If that circle is already used, or identical to the first two,
          # skip it
          if not r3 or i3 == i1 or i3 == i2:
            continue

          # Distances between the third possible vertex and the two others
          v1v3 = math.sqrt((x3-x1)**2 + (y3-y1)**2)
          v2v3 = math.sqrt((x3-x2)**2 + (y3-y2)**2)
 
          # If that circle is too far or too dissimilar to the first two,
          # or the distances between the three vertices are too far apart
          # (i.e. the triangle isn't "equilateral enough"), skip it
          if v1v3 > max_blinkies_distance or  v2v3 > max_blinkies_distance or \
			not(r1 * bstmin < r3 < r1 * bstmax) or \
			not(r2 * bstmin < r3 < r2 * bstmax) or \
			not(v2v3 * betmin < v1v2 < v2v3 * betmax) or \
			not(v1v3 * betmin < v1v2 < v1v3 * betmax) or \
			not(v2v3 * betmin < v1v3 < v2v3 * betmax):
            continue

          # We found a flexNExT: mark the three circles as used
          circles[i1][2] = circles[i2][2] = circles[i3][2] = 0

          # Draw a circle around each blinky
          if overlay_blinky_outlines:
            rb = int((r1 + r2 + r3) / 3)
            cv2.circle(image, (x1, y1), rb, red, 2)
            cv2.circle(image, (x2, y2), rb, red, 2)
            cv2.circle(image, (x3, y3), rb, red, 2)

          # Draw a circle around the flexNExT
          if overlay_flexnext_outline:
            xfn = int((x1 + x2 + x3) / 3)
            yfn = int((y1 + y2 + y3) / 3)
            rfn = int((v1v2 + v2v3 + v1v3) / 3 * 1.1)
            cv2.circle(image, (xfn, yfn), rfn, red, 2)

          # Draw a triangle inside the three vertices
          if overlay_triangle:
            cv2.line(image, (x1, y1), (x2, y2), red, 2)
            cv2.line(image, (x2, y2), (x3, y3), red, 2)
            cv2.line(image, (x3, y3), (x1, y1), red, 2)


  # Show the image in a window
  cv2.imshow("flexNExT blinkies detector", image)

  # Press Q to quit
  if cv2.waitKey(1) == ord("q"):
      break



# Release the video capture device
cap.release()

# Destroy the window
cv2.destroyAllWindows()

You’ll need to install the Python OpenCV library. Other than that, it runs without any other library. Tested on Linux, but it ought to run on Windows also.

5 Likes

THAT is cool as fuck.

You are a clever cunt @anon3825968 :wink:

2 Likes

Thanks. But I didn’t do anything: all the clever bits are handled by OpenCV. There are some seriously intelligent people who coded that stuff. I’m just monkeying with it.

Well you are more clever than I am…

“Look everybody, here is a .gif that looks like what @anon3825968 did” :peanuts: :monkey:

giphy (29)

giphy (30)

2 Likes

Lorddd, this is mesmerizing to watch. Amazing work!!!

Edit: It just occurred to me that I may also be able to do this with my laptop once I have my FlexNExT. Thank you for the idea, Rosco!!!

Thanks, I just simply googled

“predator target .gif”

Pretty easy really :stuck_out_tongue_winking_eye:

There are some really intelligent people at google that did the heavy lifting…

2 Likes

You don’t need to wait for the flexNExT to try it: a simple piece of paper with 3 black marker dots will fool the code perfectly :slight_smile:

2 Likes