import cv2
import numpy as np
import itertools
import math

# original undistort function.  Took 15ms to run. New version below only takes 3ms.
def undistort_crop2(orig_img):
      
    #undistort and crop
    #cv2.undistort(src, cameraMatrix, distCoeffs[, dst[, newCameraMatrix]]) -> dst
    dst = cv2.undistort(orig_img, mtx, dist, None, newcameramtx)
    x,y,w,h = roi
    crop_frame = dst[y:y+h, x:x+w]    
    return crop_frame

# create maps for undistortion
def init_undistort():
    #cv2.initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, m1type[, map1[, map2]]) -> map1, map2
    frame_size=(640,480)
    map1, map2=cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, frame_size, cv2.CV_32FC1)
    return map1, map2
   
# this is a faster undistort_crop that only does remapping. Requires call to init_undistort first to
# to create the map1 and map2   
def undistort_crop(orig_img):
    #cv2.remap(src, map1, map2, interpolation[, dst[, borderMode[, borderValue]]]) -> dst   
    dst = cv2.remap(orig_img, map1, map2, cv2.INTER_LINEAR)
    x,y,w,h = roi
    crop_frame = dst[y:y+h, x:x+w]    
    return crop_frame
    
def add_blobs(crop_frame):
    #frame=cv2.GaussianBlur(crop_frame, (3, 3), 0)
    frame=crop_frame
    # Convert BGR to HSV
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    # define range of green color in HSV
    #lower_green = np.array([60,20,20])
    #upper_green = np.array([80,255,255])
    
    # Actually purple
    lower_green = np.array([115,50,10])
    upper_green = np.array([160,255,255])
    
    
    # Threshold the HSV image to get only blue colors
    mask = cv2.inRange(hsv, lower_green, upper_green)
    #mask = cv2.erode(mask, None, iterations=2)
    mask = cv2.dilate(mask, None, iterations=1)    
    # Bitwise-AND mask and original image
    #res = cv2.bitwise_and(frame,frame, mask= mask)
    detector = cv2.SimpleBlobDetector_create(params)
    # Detect blobs.
    reversemask=255-mask
    keypoints = detector.detect(reversemask)
    if keypoints:
        print "found blobs"
        if len(keypoints) > 4:
            keypoints.sort(key=(lambda s: s.size))
            keypoints=keypoints[0:3]
        if len(keypoints)==4:
            pts= np.array([keypoints[i].pt for i in range(4)])
            #x,y=zip(*pts)
            
            # Calculate distances between all combinations of points
            dis_vectors = [l - r for l, r in itertools.combinations(pts, 2)]
            dcalc=[np.linalg.norm(dis_vectors[i]) for i in range(6)]

            # find the closest point to all of them, that is the middle point
            mean_a=np.array([dcalc[i] for i in [0,1,2]]).sum()/4.0
            mean_b=np.array([dcalc[i] for i in [0,3,4]]).sum()/4.0
            mean_c=np.array([dcalc[i] for i in [1,3,5]]).sum()/4.0
            mean_d=np.array([dcalc[i] for i in [2,4,5]]).sum()/4.0
            middlepoint=np.argmin(np.array([mean_a, mean_b, mean_c, mean_d]))

            idx=np.argmax(dcalc) # find two furthest points, those are left and right sidepoints
            max_dist_val=np.max(dcalc)            
            #print max_dist_val
            if idx ==0:
                sidepts=[0,1]
            elif idx==1:
                sidepts=[0,2]        
            elif idx==2:
                sidepts=[0,3]  
            elif idx==3:
                sidepts=[1,2]                  
            elif idx==4:
                sidepts=[1,3]                  
            elif idx==5:
                sidepts=[2,3]            
                
            # the frontpoint is the remaining one.
            frontpoint=6-np.array(sidepts+[middlepoint]).sum()    
            
            # now determine which side point is the left one
            # http://stackoverflow.com/questions/1560492/how-to-tell-whether-a-point-is-to-the-right-or-left-side-of-a-line
            a=keypoints[middlepoint].pt
            b=keypoints[frontpoint].pt
            c=keypoints[sidepts[0]].pt
            if ((b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0])) < 0:
                leftpt=sidepts[0]
                rightpt=sidepts[1]
            else:
                leftpt=sidepts[1]
                rightpt=sidepts[0]
            
            
            # Calculate angle
            # frontpoint - midpoint
            offset_line=np.array(keypoints[rightpt].pt)-np.array(keypoints[leftpt].pt)
            theta=-1*math.atan2(offset_line[1], offset_line[0])
            
            im_with_midpoint = cv2.drawKeypoints(frame, [keypoints[middlepoint]], np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
            im_with_midpoint_frontpoint = cv2.drawKeypoints(im_with_midpoint, [keypoints[frontpoint]], np.array([]), (255,0,0), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
            keypoints_side=[keypoints[i] for i in [leftpt]]
            im_with_keypoints1 = cv2.drawKeypoints(im_with_midpoint_frontpoint, keypoints_side, np.array([]), (0,255,0), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
            keypoints_side=[keypoints[i] for i in [rightpt]]
            im_with_keypoints = cv2.drawKeypoints(im_with_keypoints1, keypoints_side, np.array([]), (255,255,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
            
            textstr="%0.4f dist. %i,%i center, %0.2f deg" % (max_dist_val, keypoints[middlepoint].pt[0], keypoints[middlepoint].pt[1], theta*180/math.pi)
            max_blob_dist=max_dist_val
            blob_center=keypoints[middlepoint].pt 
            keypoints[middlepoint].pt[1]
            font = cv2.FONT_HERSHEY_SIMPLEX
            cv2.putText(im_with_keypoints, textstr,(10,25), font, .8,(255,255,255),2,cv2.LINE_AA)
        # Draw detected blobs as red circles.
        # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
        #im_with_keypoints = cv2.drawKeypoints(frame, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
        else:
            im_with_keypoints=crop_frame    
            print "%i blobs" % (len(keypoints))
            max_blob_dist=None
            blob_center=None
            theta=None
    else:
        print "no blobs"
        im_with_keypoints=crop_frame
        max_blob_dist=None
        blob_center=None
        theta=None
    return im_with_keypoints, max_blob_dist, blob_center, theta #, keypoint_in_orders



# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
 
# Change thresholds
params.minThreshold = 0;
params.maxThreshold = 256;
 
# Filter by Area.
params.filterByArea = True
params.minArea = 30
 
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.1
 
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.5
 
# Filter by Inertia
params.filterByInertia =True
params.minInertiaRatio = 0.01

# load params to undistort images
calfile=np.load('camera_cal_data_2016_03_25_15_23.npz')
newcameramtx=calfile['newcameramtx']
roi=calfile['roi']
mtx=calfile['mtx']
dist=calfile['dist']

map1, map2=init_undistort()