import sys import cv2 import numpy as np import math from os.path import exists, basename from common import image_resize, display, normalize_angle from json import load #clockwise from top left order = [ 0, 2, 3, 5, 4, 1 ] matchMethods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR', 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED'] def read_text (textPath) : holePunches = {} with open(textPath) as json: holePunches = load(json) return holePunches # # CALIBRATE # if len(sys.argv) < 2: print('Please provide path of normalized scan to calibrate to') exit(1) if len(sys.argv) < 3: print('Please provide path to output svg template') exit(2) normalImage = sys.argv[-2] if not exists(normalImage) : print('Normalized scan does not exist, please provide one that does') exit(2) normalText = normalImage + '.json' if not exists(normalText) : print('Corresponding normalized scan text does not exist, please generate one') exit(3) outputTmpl = sys.argv[-1] print(f'Calibrating to scan {basename(normalImage)}') registrationMark = cv2.imread('./registrationMark.png', 0) w, h = registrationMark.shape[:2] holePunches = read_text(normalText) original = cv2.imread(normalImage) img = original.copy() height, width = img.shape[:2] orientation = height > width marks = [] if not orientation : print(f'Scan is not in portrait mode, exiting...') exit(3) print(holePunches) def get_distance(ref, point): # print('ref: {} , point: {}'.format(ref, point)) x1, y1 = ref[0], ref[1] x2, y2 = point[0], point[1] return math.hypot(x2 - x1, y2 - y1) def group_points(points): groups = {} groupnum = 0 while len(points) > 1: groupnum += 1 key = str(groupnum) groups[key] = [] ref = points.pop(0) for i, point in enumerate(points): d = get_distance(ref, point) if d < 30: groups[key].append(points[i]) points[i] = None points = list(filter(lambda x: x is not None, points)) return list([[int(np.mean(list([x[0] for x in groups[arr]]))), int(np.mean(list([x[1] for x in groups[arr]])))] for arr in groups]) def find_closest (pt, pts) : return pts[min(range(len(pts)), key = lambda i: get_distance(pts[i], pt))] def find_in_half (half) : halfGray = cv2.cvtColor(half, cv2.COLOR_BGR2GRAY) res = cv2.matchTemplate(halfGray, registrationMark, cv2.TM_CCOEFF_NORMED) threshold = 0.7 loc = np.where( res >= threshold) for pt in zip(*loc[::-1]): cv2.rectangle(half, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2) return list(zip(*loc[::-1])) ttly = holePunches['0']['y']-round(height*0.05) ttlx = holePunches['0']['x'] topHalf = img[ttly:holePunches['1']['y']+round(height*0.1), ttlx:holePunches['2']['x']] topHalfPts = find_in_half(topHalf) thpts = group_points(topHalfPts) for pt in thpts : #print(f'{ttlx + pt[0]},{ttly + pt[1]}') marks.append((ttlx + pt[0] + round(w / 2), ttly + pt[1] + round(h / 2),)) print(f'Found {len(thpts)} points') #display(topHalf) btly = holePunches['4']['y']-round(height*0.1) btlx = holePunches['3']['x'] bottomHalf = img[btly:holePunches['5']['y']+round(height*0.05), btlx:holePunches['4']['x']] bottomHalfPts = find_in_half(bottomHalf) bhpts = group_points(bottomHalfPts) for pt in bhpts : #print(f'{btlx + pt[0]},{btly + pt[1]}') marks.append((btlx + pt[0] + round(w / 2), btly + pt[1] + round(h / 2), )) clean = original.copy() for pt in marks : print(pt) cv2.circle(clean, pt, 50, (0,0,255), -1) print(f'Found {len(bhpts)} points') if len(marks) != 16 : print(f'{len(marks)} != 16 marks') exit(1) print(find_closest((0,0,), marks)) print(find_closest((width,0,), marks)) print(find_closest((0,height,), marks)) print(find_closest((width,height,), marks)) display(clean)