import sys import cv2 import numpy as np import math def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA): dim = None (h, w) = image.shape[:2] if width is None and height is None: return image if width is None: r = height / float(h) dim = (int(w * r), height) else: r = width / float(w) dim = (width, int(h * r)) resized = cv2.resize(image, dim, interpolation = inter) return resized def display (image) : resized = image_resize(image, 800, 800) cv2.imshow('img', resized) while True: key = cv2.waitKey(0) if key == 27: cv2.destroyAllWindows() break exit(0) def get_center (contour) : M = cv2.moments(contour) cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) return cX, cY def draw_line (image, hps, a, b) : print(f'{a} -> {b}') lA = (hps[a-1]['x'], hps[a-1]['y']) lB = (hps[b-1]['x'], hps[b-1]['y']) cv2.line(image, lA, lB, [0, 255, 0], 10) return (lA, lB) def horiz_angle (line) : deltaY = line[1][1] - line[0][1] #P2_y - P1_y deltaX = line[1][0] - line[0][0] #P2_x - P1_x angleInDegrees = math.degrees(math.atan2(deltaY, deltaX)) print(angleInDegrees) return angleInDegrees def verts_angle (line) : deltaY = line[1][1] - line[0][1] #P2_y - P1_y deltaX = line[1][0] - line[0][0] #P2_x - P1_x angleInDegrees = - (math.atan2(deltaX, deltaY) * 180 / math.pi) print(angleInDegrees) return angleInDegrees if len(sys.argv) < 2: print('Please provide path to image for analysis') exit(1) if len(sys.argv) < 3: print('Please provide path to template file to create') exit(2) scanImage = sys.argv[-2] templateFile = sys.argv[-1] print(f'Analyzing {scanImage} and creating {templateFile}') orig = cv2.imread(scanImage) img = orig.copy() height, width = img.shape[:2] orientation = height > width pageDim = (11, 8.5) if not orientation : pageDim = (8.5, 11) pageRatio = pageDim[1] / pageDim[0] left=-1 right=-1 top=-1 bottom=-1 if orientation : left = width * 0.2 right = width * 0.8 else : top = height * 0.2 bottom = height * 0.8 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.medianBlur(gray, 31) ret, thresh = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY) canny = cv2.Canny(thresh, 75, 200) contours, hierarchy = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) contourList = [] areaList = [] for contour in contours: approx = cv2.approxPolyDP(contour, 0.03 * cv2.arcLength(contour, True), True) if cv2.isContourConvex(approx) : cX, cY = get_center(contour) if (orientation and ( cX < left or cX > right) ) or ( not orientation and ( cY < top or cY > bottom)) : area = cv2.contourArea(contour) areaList.append(area) contourList.append(contour) maxArea=0 maxIndex=0 #reduce to lambda for i in range(len(areaList)) : area = areaList[i] if area > maxArea: maxArea = area maxIndex = i count = 0 holePunches = [] centersStr = [] areaRange = 0 topLeft = None minDist = 1000000 # pretty good # add position constraint while count < 6 : areaRange+=1 for i in range(len(areaList)) : area = areaList[i] if area == maxArea or area * ((100 + areaRange) / 100) > maxArea : cX, cY = get_center(contourList[i]) strC = f'{cX},{cY}' if strC in centersStr : continue centersStr.append(strC) print(f'{cX},{cY}') #cv2.circle(img, (cX, cY), 40, (255, 0, 0), -1) hp = { 'x' : cX, 'y' : cY, 'contour' : contourList[i], 'dist' : math.dist((cX, cY), (0, 0)), 'order': -1 } if hp['dist'] < minDist : minDist = hp['dist'] topLeft = hp holePunches.append(hp) count+=1 for hp in holePunches : hp['dist'] = math.dist( (topLeft['x'], topLeft['y']), (hp['x'], hp['y']) ) holePunches = sorted(holePunches, key = lambda hp: hp['dist']) i = 0 for hp in holePunches : hp['order'] = i cv2.putText(img, str(i + 1), (hp['x'], hp['y']), cv2.FONT_HERSHEY_SIMPLEX, 20, (0, 0, 255), 5, cv2.LINE_AA, False) i+=1 verts = [] horiz = [] #across top horiz.append(horiz_angle(draw_line(img, holePunches, 1, 3))) #across bottom horiz.append(horiz_angle(draw_line(img, holePunches, 4, 6))) #middle horiz.append(horiz_angle(draw_line(img, holePunches, 2, 5))) #top left verts.append(verts_angle(draw_line(img, holePunches, 1, 2))) #long left verts.append(verts_angle(draw_line(img, holePunches, 1, 4))) #top right verts.append(verts_angle(draw_line(img, holePunches, 3, 5))) #long right verts.append(verts_angle(draw_line(img, holePunches, 3, 6))) #bottom left verts.append(verts_angle(draw_line(img, holePunches, 2, 4))) #bottom right verts.append(verts_angle(draw_line(img, holePunches, 5, 6))) #for v in verts : #print(v) #for h in horiz : # print(h) # horiz_angle(h) print(f'Found hole punches within {areaRange}% of largest') #print(holePunches) #cv2.drawContours(img, list(map(lambda hp : hp['contour'], holePunches)), -1, (0, 255, 0), 20) #cv2.circle(img, (topLeft['x'], topLeft['y']), 50, (0, 0, 255), -1) display(img)