-
Notifications
You must be signed in to change notification settings - Fork 7
Focus Points
#Getting Polygons
def findPolys(img):
"""Filters the img, finds the contours, and returns the polys."""
img = img.copy()
try:
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY); #Convert Image captured from Image Input to GrayScale
except:
gray = img
print "Already Gray."
edges = cv2.Canny(gray, 100 , 200, 3); #Apply Canny edge detection on the gray image
find = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Find contours with hierarchy
heirarchy = find[2][0] #the last index is the heirarchy, it is contained within a one value array
contours = find[1] #The second index is the contour list
#Return approximate polygons
polys = []
global DTOL
for c in contours:
temp = cv2.approxPolyDP(np.array(c),DTOL,True)
polys.append(temp[:,0])
return polys, heirarchy
#Retrieve Focus Points Focus points are the center of this app's scanning capabilities. They consist of a square box which contain one or two, filled or unfilled convex geometric shapes. They are listed in the "out" and "depth" outputs of this method, and the length of each out corresponds to the polygon shape of the innermost border.
#Code copied and modified from https://github.com/bharathp666/opencv_qr
def findFocusPoints(polys, heirarchy):
"""Returns Border Poly. Checks that the QR info matches the border found."""
focus, done = [], []
for i in range(len(polys)):
k, obj = i, []
if i not in done: #Check that through navigation you haven't been here before
done.append(i)
while heirarchy[k][2] != -1: #Navigate the heirarchy
k = heirarchy[k][2]
done.append(k)
obj.append(k)
if heirarchy[k][2] != -1: obj.append(k) #Add the last one
#Check if there are enough polys to count as a potential focus point
obj = [polys[x] for x in obj]
if len(obj) >= 5:
focus.append(obj[0:len(obj)]) #Get the last 5 elements and add them as a focus point
continue
#Filter the focus points for their innermost border
out,depth = [],[]
for f in focus:
i = findInnerBorder(f) #Checks the shapes and looks for a square, returns innermost border
if i>0: #If found, append to out
out.append(f[i-2]) #Fix this 2
depth.append(len(f)-i)
#Return the borders
return out, depth
#Classification ##Get Reference Point The reference point has a greater depth than all other points. As such, it can be detected using a simple max/index function:
def getRef(fp,depth):
i = depth.index(np.max(depth))
return centroid(fp[i])
##Get Inner Border The inner border is the deepest square polygon within a focus point. One below the inner border is the shape of the polygon itself, and one below that is the region containing the "fill" if the inner region is unfilled. This method also checks that all qualifications are met to be a valid focus point.
def findInnerBorder(cnts):
"""Checks shape of each contour from last to -5 and finds the first 'square.' Returns 0 if none exists."""
for x in range(len(cnts)):
cnt = cnts[-x]
if cv2.isContourConvex(cnt) and len(cnt)==4 and allSameLength(cnt):
return -x+3 #Unsure why 3 is needed here...
else: return 0
##Corners
Corners are specific focus points defining the outside border of the scan. They are defined as having square shapes, and being filled. There must be four within an image to qualify a valid scan, one of which must be a reference, which is deeper than the others but may have other polygon shapes(needs to be implemented). They are sorted counter-clockwise from the reference point.
def findCorners(fp):
"""Classifies squares and selects the four most likely to be corners"""
#Find all focus points of length four and calculate all of their angles to other fours
fours = list(filter(lambda z: len(z)==4, fp))
cents = map(lambda z: tuple(centroid(z)), fours)
angles = lambda x: [angle(x,y,z) for y in cents for z in cents if x != y and y != z and x != z]
#Classify corners as having 2 right angles
out = []
for c in cents:
if len(filter(lambda z: np.absolute(z-90.0)<ATOL, angles(c)))>=2 and c not in out:
out.append(c)
#Return their centroids
if len(out)!=4: raise Exception("Corners not Detected!")
else: print out
return out
def sortCorners(corners):
"""Sort edges by distance."""
cent = centroid(corners) #Get the centroid of the four corners
polar = map(lambda z: np.angle(complex(*tuple(np.array(z)-np.array(cent))),deg=True),corners) #Get the polar angle from centroid
sort = sorted(zip(corners,polar), key=lambda x: x[1]) #Sort by the polar coords
keys = [x for x,y in sort] #Return just the keys
return keys