예제 #1
0
    def detect(self,frame,user_roi,visualize=False):
        u_r = user_roi
        if self.window_should_open:
            self.open_window((frame.img.shape[1],frame.img.shape[0]))
        if self.window_should_close:
            self.close_window()

        if self._window:
            debug_img = np.zeros(frame.img.shape,frame.img.dtype)


        #get the user_roi
        img = frame.img
        r_img = img[u_r.view]
        #        bias_field = preproc.EstimateBias(r_img)
        # r_img = preproc.Unbias(r_img, bias_field)
        r_img = preproc.GaussBlur(r_img)
        r_img = preproc.RobustRescale(r_img)
        frame.img[u_r.view] = r_img
        gray_img = cv2.cvtColor(r_img,cv2.COLOR_BGR2GRAY)


        # coarse pupil detection

        if self.coarse_detection.value:
            integral = cv2.integral(gray_img)
            integral =  np.array(integral,dtype=c_float)
            x,y,w,response = eye_filter(integral,self.coarse_filter_min,self.coarse_filter_max)
            p_r = Roi(gray_img.shape)
            if w>0:
                p_r.set((y,x,y+w,x+w))
            else:
                p_r.set((0,0,-1,-1))
        else:
            p_r = Roi(gray_img.shape)
            p_r.set((0,0,None,None))
            w = img.shape[0]/2

        coarse_pupil_width = w/2.
        padding = coarse_pupil_width/4.
        pupil_img = gray_img[p_r.view]



        # binary thresholding of pupil dark areas
        hist = cv2.calcHist([pupil_img],[0],None,[256],[0,256]) #(images, channels, mask, histSize, ranges[, hist[, accumulate]])
        bins = np.arange(hist.shape[0])
        spikes = bins[hist[:,0]>40] # every intensity seen in more than 40 pixels
        if spikes.shape[0] >0:
            lowest_spike = spikes.min()
            highest_spike = spikes.max()
        else:
            lowest_spike = 200
            highest_spike = 255

        offset = self.intensity_range.value
        spectral_offset = 5
        if visualize:
            # display the histogram
            sx,sy = 100,1
            colors = ((0,0,255),(255,0,0),(255,255,0),(255,255,255))
            h,w,chan = img.shape
            hist *= 1./hist.max()  # normalize for display

            for i,h in zip(bins,hist[:,0]):
                c = colors[1]
                cv2.line(img,(w,int(i*sy)),(w-int(h*sx),int(i*sy)),c)
            cv2.line(img,(w,int(lowest_spike*sy)),(int(w-.5*sx),int(lowest_spike*sy)),colors[0])
            cv2.line(img,(w,int((lowest_spike+offset)*sy)),(int(w-.5*sx),int((lowest_spike+offset)*sy)),colors[2])
            cv2.line(img,(w,int((highest_spike)*sy)),(int(w-.5*sx),int((highest_spike)*sy)),colors[0])
            cv2.line(img,(w,int((highest_spike- spectral_offset )*sy)),(int(w-.5*sx),int((highest_spike - spectral_offset)*sy)),colors[3])

        # create dark and spectral glint masks
        self.bin_thresh.value = lowest_spike
        binary_img = bin_thresholding(pupil_img,image_upper=lowest_spike + offset)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
        cv2.dilate(binary_img, kernel,binary_img, iterations=2)
        spec_mask = bin_thresholding(pupil_img, image_upper=highest_spike - spectral_offset)
        cv2.erode(spec_mask, kernel,spec_mask, iterations=1)

        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))

        #open operation to remove eye lashes
        pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, kernel)

        if self.blur > 1:
            pupil_img = cv2.medianBlur(pupil_img,self.blur.value)

        edges = cv2.Canny(pupil_img,
                            self.canny_thresh,
                            self.canny_thresh*self.canny_ratio,
                            apertureSize= self.canny_aperture)


        # remove edges in areas not dark enough and where the glint is (spectral refelction from IR leds)
        edges = cv2.min(edges, spec_mask)
        edges = cv2.min(edges,binary_img)

        overlay =  img[u_r.view][p_r.view]
        if visualize:
            b,g,r = overlay[:,:,0],overlay[:,:,1],overlay[:,:,2]
            g[:] = cv2.max(g,edges)
            b[:] = cv2.max(b,binary_img)
            b[:] = cv2.min(b,spec_mask)

            # draw a frame around the automatic pupil ROI in overlay.
            overlay[::2,0] = 255 #yeay numpy broadcasting
            overlay[::2,-1]= 255
            overlay[0,::2] = 255
            overlay[-1,::2]= 255
            # draw a frame around the area we require the pupil center to be.
            overlay[padding:-padding:4,padding] = 255
            overlay[padding:-padding:4,-padding]= 255
            overlay[padding,padding:-padding:4] = 255
            overlay[-padding,padding:-padding:4]= 255

        if visualize:
            c = (100.,frame.img.shape[0]-100.)
            e_max = ((c),(self.pupil_max.value,self.pupil_max.value),0)
            e_recent = ((c),(self.target_size.value,self.target_size.value),0)
            e_min = ((c),(self.pupil_min.value,self.pupil_min.value),0)
            cv2.ellipse(frame.img,e_min,(0,0,255),1)
            cv2.ellipse(frame.img,e_recent,(0,255,0),1)
            cv2.ellipse(frame.img,e_max,(0,0,255),1)

        #get raw edge pix for later
        raw_edges = cv2.findNonZero(edges)

        def ellipse_true_support(e,raw_edges):
            a,b = e[1][0]/2.,e[1][1]/2. # major minor radii of candidate ellipse
            ellipse_circumference = np.pi*abs(3*(a+b)-np.sqrt(10*a*b+3*(a**2+b**2)))
            distances = dist_pts_ellipse(e,raw_edges)
            support_pixels = raw_edges[distances<=1.3]
            # support_ratio = support_pixel.shape[0]/ellipse_circumference
            return support_pixels,ellipse_circumference

        # if we had a good ellipse before ,let see if it is still a good first guess:
        if self.strong_prior:
            e = p_r.sub_vector(u_r.sub_vector(self.strong_prior[0])),self.strong_prior[1],self.strong_prior[2]

            self.strong_prior = None
            if raw_edges is not None:
                support_pixels,ellipse_circumference = ellipse_true_support(e,raw_edges)
                support_ratio =  support_pixels.shape[0]/ellipse_circumference
                if support_ratio >= self.strong_perimeter_ratio_range[0]:
                    refit_e = cv2.fitEllipse(support_pixels)
                    if self._window:
                        cv2.ellipse(debug_img,e,(255,100,100),thickness=4)
                        cv2.ellipse(debug_img,refit_e,(0,0,255),thickness=1)
                    e = refit_e
                    self.strong_prior = u_r.add_vector(p_r.add_vector(e[0])),e[1],e[2]
                    goodness = min(1.,support_ratio)
                    pupil_ellipse = {}
                    pupil_ellipse['confidence'] = goodness
                    pupil_ellipse['ellipse'] = e
                    pupil_ellipse['roi_center'] = e[0]
                    pupil_ellipse['major'] = max(e[1])
                    pupil_ellipse['minor'] = min(e[1])
                    pupil_ellipse['apparent_pupil_size'] = max(e[1])
                    pupil_ellipse['axes'] = e[1]
                    pupil_ellipse['angle'] = e[2]
                    e_img_center =u_r.add_vector(p_r.add_vector(e[0]))
                    norm_center = normalize(e_img_center,(frame.img.shape[1], frame.img.shape[0]),flip_y=True)
                    pupil_ellipse['norm_pupil'] = norm_center
                    pupil_ellipse['center'] = e_img_center
                    pupil_ellipse['timestamp'] = frame.timestamp

                    self.target_size.value = max(e[1])

                    self.confidence.value = goodness
                    self.confidence_hist.append(goodness)
                    self.confidence_hist[:-200]=[]
                    if self._window:
                        #draw a little animation of confidence
                        cv2.putText(debug_img, 'good',(410,debug_img.shape[0]-100), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
                        cv2.putText(debug_img, 'threshold',(410,debug_img.shape[0]-int(self.final_perimeter_ratio_range[0]*100)), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
                        cv2.putText(debug_img, 'no detection',(410,debug_img.shape[0]-10), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
                        lines = np.array([[[2*x,debug_img.shape[0]-int(100*y)],[2*x,debug_img.shape[0]]] for x,y in enumerate(self.confidence_hist)])
                        cv2.polylines(debug_img,lines,isClosed=False,color=(255,100,100))
                        self.gl_display_in_window(debug_img)
                    return pupil_ellipse





        # from edges to contours
        contours, hierarchy = cv2.findContours(edges,
                                            mode=cv2.RETR_LIST,
                                            method=cv2.CHAIN_APPROX_NONE,offset=(0,0)) #TC89_KCOS
        # contours is a list containing array([[[108, 290]],[[111, 290]]], dtype=int32) shape=(number of points,1,dimension(2) )

        ### first we want to filter out the bad stuff
        # to short
        good_contours = [c for c in contours if c.shape[0]>self.min_contour_size.value]
        # now we learn things about each contour through looking at the curvature.
        # For this we need to simplyfy the contour so that pt to pt angles become more meaningfull
        aprox_contours = [cv2.approxPolyDP(c,epsilon=1.5,closed=False) for c in good_contours]

        if self._window:
            x_shift = coarse_pupil_width*2
            color = zip(range(0,250,15),range(0,255,15)[::-1],range(230,250))
        split_contours = []
        for c in aprox_contours:
            curvature = GetAnglesPolyline(c)
            # we split whenever there is a real kink (abs(curvature)<right angle) or a change in the genreal direction
            kink_idx = find_kink_and_dir_change(curvature,80)
            segs = split_at_corner_index(c,kink_idx)

            #TODO: split at shart inward turns
            for s in segs:
                if s.shape[0]>2:
                    split_contours.append(s)
                    if self._window:
                        c = color.pop(0)
                        color.append(c)
                        s = s.copy()
                        s[:,:,0] += debug_img.shape[1]-coarse_pupil_width*2
                        # s[:,:,0] += x_shift
                        # x_shift += 5
                        cv2.polylines(debug_img,[s],isClosed=False,color=map(lambda x: x,c),thickness = 1,lineType=4)#cv2.CV_AA

        split_contours.sort(key=lambda x:-x.shape[0])
        # print [x.shape[0]for x in split_contours]
        if len(split_contours) == 0:
            # not a single usefull segment found -> no pupil found
            self.confidence.value = 0
            self.confidence_hist.append(0)
            if self._window:
                self.gl_display_in_window(debug_img)
            return {'timestamp':frame.timestamp,'norm_pupil':None}


        # removing stubs makes combinatorial search feasable
        split_contours = [c for c in split_contours if c.shape[0]>3]

        def ellipse_filter(e):
            in_center = padding < e[0][1] < pupil_img.shape[0]-padding and padding < e[0][0] < pupil_img.shape[1]-padding
            if in_center:
                is_round = min(e[1])/max(e[1]) >= self.min_ratio
                if is_round:
                    right_size = self.pupil_min.value <= max(e[1]) <= self.pupil_max.value
                    if right_size:
                        return True
            return False

        def ellipse_on_blue(e):
            center_on_dark = binary_img[e[0][1],e[0][0]]
            return bool(center_on_dark)

        def ellipse_support_ratio(e,contours):
            a,b = e[1][0]/2.,e[1][1]/2. # major minor radii of candidate ellipse
            ellipse_area =  np.pi*a*b
            ellipse_circumference = np.pi*abs(3*(a+b)-np.sqrt(10*a*b+3*(a**2+b**2)))
            actual_area = cv2.contourArea(cv2.convexHull(np.concatenate(contours)))
            actual_contour_length = sum([cv2.arcLength(c,closed=False) for c in contours])
            area_ratio = actual_area / ellipse_area
            perimeter_ratio = actual_contour_length / ellipse_circumference #we assume here that the contour lies close to the ellipse boundary
            return perimeter_ratio,area_ratio


        def final_fitting(c,edges):
            #use the real edge pixels to fit, not the aproximated contours
            support_mask = np.zeros(edges.shape,edges.dtype)
            cv2.polylines(support_mask,c,isClosed=False,color=(255,255,255),thickness=2)
            # #draw into the suport mast with thickness 2
            new_edges = cv2.min(edges, support_mask)
            new_contours = cv2.findNonZero(new_edges)
            if self._window:
                new_edges[new_edges!=0] = 255
                overlay[:,:,1] = cv2.max(overlay[:,:,1], new_edges)
                overlay[:,:,2] = cv2.max(overlay[:,:,2], new_edges)
                overlay[:,:,2] = cv2.max(overlay[:,:,2], new_edges)
            new_e = cv2.fitEllipse(new_contours)
            return new_e,new_contours


        # finding poential candidates for ellipse seeds that describe the pupil.
        strong_seed_contours = []
        weak_seed_contours = []
        for idx, c in enumerate(split_contours):
            if c.shape[0] >=5:
                e = cv2.fitEllipse(c)
                # is this ellipse a plausible canditate for a pupil?
                if ellipse_filter(e):
                    distances = dist_pts_ellipse(e,c)
                    fit_variance = np.sum(distances**2)/float(distances.shape[0])
                    if fit_variance <= self.inital_ellipse_fit_threshhold:
                        # how much ellipse is supported by this contour?
                        perimeter_ratio,area_ratio = ellipse_support_ratio(e,[c])
                        # logger.debug('Ellipse no %s with perimeter_ratio: %s , area_ratio: %s'%(idx,perimeter_ratio,area_ratio))
                        if self.strong_perimeter_ratio_range[0]<= perimeter_ratio <= self.strong_perimeter_ratio_range[1] and self.strong_area_ratio_range[0]<= area_ratio <= self.strong_area_ratio_range[1]:
                            strong_seed_contours.append(idx)
                            if self._window:
                                cv2.polylines(debug_img,[c],isClosed=False,color=(255,100,100),thickness=4)
                                e = (e[0][0]+debug_img.shape[1]-coarse_pupil_width*4,e[0][1]),e[1],e[2]
                                cv2.ellipse(debug_img,e,color=(255,100,100),thickness=3)
                        else:
                            weak_seed_contours.append(idx)
                            if self._window:
                                cv2.polylines(debug_img,[c],isClosed=False,color=(255,0,0),thickness=2)
                                e = (e[0][0]+debug_img.shape[1]-coarse_pupil_width*4,e[0][1]),e[1],e[2]
                                cv2.ellipse(debug_img,e,color=(255,0,0))

        sc = np.array(split_contours)


        if strong_seed_contours:
            seed_idx = strong_seed_contours
        elif weak_seed_contours:
            seed_idx = weak_seed_contours

        if not (strong_seed_contours or weak_seed_contours):
            if self._window:
                self.gl_display_in_window(debug_img)
            self.confidence.value = 0
            self.confidence_hist.append(0)
            return {'timestamp':frame.timestamp,'norm_pupil':None}

        # if self._window:
        #     cv2.polylines(debug_img,[split_contours[i] for i in seed_idx],isClosed=False,color=(255,255,100),thickness=3)

        def ellipse_eval(contours):
            c = np.concatenate(contours)
            e = cv2.fitEllipse(c)
            d = dist_pts_ellipse(e,c)
            fit_variance = np.sum(d**2)/float(d.shape[0])
            return fit_variance <= self.inital_ellipse_fit_threshhold


        solutions = pruning_quick_combine(split_contours,ellipse_eval,seed_idx,max_evals=1000,max_depth=5)
        solutions = filter_subsets(solutions)
        ratings = []


        for s in solutions:
            e = cv2.fitEllipse(np.concatenate(sc[s]))
            if self._window:
                cv2.ellipse(debug_img,e,(0,150,100))
            support_pixels,ellipse_circumference = ellipse_true_support(e,raw_edges)
            support_ratio =  support_pixels.shape[0]/ellipse_circumference
            # TODO: refine the selection of final canditate
            if support_ratio >=self.final_perimeter_ratio_range[0] and ellipse_filter(e):
                ratings.append(support_pixels.shape[0])
                if support_ratio >=self.strong_perimeter_ratio_range[0]:
                    self.strong_prior = u_r.add_vector(p_r.add_vector(e[0])),e[1],e[2]
                    if self._window:
                        cv2.ellipse(debug_img,e,(0,255,255),thickness = 2)
            else:
                #not a valid solution, bad rating
                ratings.append(-1)


        # selected ellipse
        if max(ratings) == -1:
            #no good final ellipse found
            if self._window:
                self.gl_display_in_window(debug_img)
            self.confidence.value = 0
            self.confidence_hist.append(0)
            return {'timestamp':frame.timestamp,'norm_pupil':None}

        best = solutions[ratings.index(max(ratings))]
        e = cv2.fitEllipse(np.concatenate(sc[best]))

        #final calculation of goodness of fit
        support_pixels,ellipse_circumference = ellipse_true_support(e,raw_edges)
        support_ratio =  support_pixels.shape[0]/ellipse_circumference
        goodness = min(1.,support_ratio)

        #final fitting and return of result
        new_e,final_edges = final_fitting(sc[best],edges)
        size_dif = abs(1 - max(e[1])/max(new_e[1]))
        if ellipse_filter(new_e) and size_dif < .3:
            if self._window:
                cv2.ellipse(debug_img,new_e,(0,255,0))
            e = new_e


        pupil_ellipse = {}
        pupil_ellipse['confidence'] = goodness
        pupil_ellipse['ellipse'] = e
        pupil_ellipse['pos_in_roi'] = e[0]
        pupil_ellipse['major'] = max(e[1])
        pupil_ellipse['apparent_pupil_size'] = max(e[1])
        pupil_ellipse['minor'] = min(e[1])
        pupil_ellipse['axes'] = e[1]
        pupil_ellipse['angle'] = e[2]
        e_img_center =u_r.add_vector(p_r.add_vector(e[0]))
        norm_center = normalize(e_img_center,(frame.img.shape[1], frame.img.shape[0]),flip_y=True)
        pupil_ellipse['norm_pupil'] = norm_center
        pupil_ellipse['center'] = e_img_center
        pupil_ellipse['timestamp'] = frame.timestamp

        self.target_size.value = max(e[1])

        self.confidence.value = goodness
        self.confidence_hist.append(goodness)
        self.confidence_hist[:-200]=[]
        if self._window:
            #draw a little animation of confidence
            cv2.putText(debug_img, 'good',(410,debug_img.shape[0]-100), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
            cv2.putText(debug_img, 'threshold',(410,debug_img.shape[0]-int(self.final_perimeter_ratio_range[0]*100)), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
            cv2.putText(debug_img, 'no detection',(410,debug_img.shape[0]-10), cv2.FONT_HERSHEY_SIMPLEX,0.3,(255,100,100))
            lines = np.array([[[2*x,debug_img.shape[0]-int(100*y)],[2*x,debug_img.shape[0]]] for x,y in enumerate(self.confidence_hist)])
            cv2.polylines(debug_img,lines,isClosed=False,color=(255,100,100))
            self.gl_display_in_window(debug_img)
        return pupil_ellipse
예제 #2
0
    def detect(self, frame, u_roi, visualize=False):
        #get the user_roi
        img = frame.img
        r_img = img[u_roi.lY:u_roi.uY, u_roi.lX:u_roi.uX]
        gray_img = grayscale(r_img)
        # coarse pupil detection
        integral = cv2.integral(gray_img)
        integral = np.array(integral, dtype=c_float)
        x, y, w, response = eye_filter(integral, 100, 400)
        p_roi = Roi(gray_img.shape)
        if w > 0:
            p_roi.set((y, x, y + w, x + w))
        else:
            p_roi.set((0, 0, -1, -1))
        coarse_pupil_center = x + w / 2., y + w / 2.
        coarse_pupil_width = w / 2.
        padding = coarse_pupil_width / 4.
        pupil_img = gray_img[p_roi.lY:p_roi.uY, p_roi.lX:p_roi.uX]
        # binary thresholding of pupil dark areas
        hist = cv2.calcHist([pupil_img], [0], None, [256], [
            0, 256
        ])  #(images, channels, mask, histSize, ranges[, hist[, accumulate]])
        bins = np.arange(hist.shape[0])
        spikes = bins[hist[:, 0] >
                      40]  # every intensity seen in more than 40 pixels
        if spikes.shape[0] > 0:
            lowest_spike = spikes.min()
            highest_spike = spikes.max()
        else:
            lowest_spike = 200
            highest_spike = 255

        offset = self.intensity_range.value
        spectral_offset = 5
        if visualize:
            # display the histogram
            sx, sy = 100, 1
            colors = ((0, 0, 255), (255, 0, 0), (255, 255, 0), (255, 255, 255))
            h, w, chan = img.shape
            hist *= 1. / hist.max()  # normalize for display

            for i, h in zip(bins, hist[:, 0]):
                c = colors[1]
                cv2.line(img, (w, int(i * sy)), (w - int(h * sx), int(i * sy)),
                         c)
            cv2.line(img, (w, int(lowest_spike * sy)),
                     (int(w - .5 * sx), int(lowest_spike * sy)), colors[0])
            cv2.line(img, (w, int((lowest_spike + offset) * sy)),
                     (int(w - .5 * sx), int(
                         (lowest_spike + offset) * sy)), colors[2])
            cv2.line(img, (w, int((highest_spike) * sy)),
                     (int(w - .5 * sx), int((highest_spike) * sy)), colors[0])
            cv2.line(
                img, (w, int((highest_spike - spectral_offset) * sy)),
                (int(w - .5 * sx), int(
                    (highest_spike - spectral_offset) * sy)), colors[3])

        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))

        #open operation to remove eye lashes
        pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, kernel)

        # PARAMS = {}
        # blob_detector = cv2.SimpleBlobDetector(**PARAMS)
        # kps =  blob_detector.detect(pupil_img)

        # blur = cv2.GaussianBlur(pupil_img,(5,5),0)
        blur = pupil_img
        # ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
        ret3, th3 = cv2.threshold(blur, lowest_spike + offset, 255,
                                  cv2.THRESH_BINARY)
        # ret3,th3 = cv2.threshold(th3,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
        th3 = cv2.Laplacian(th3, cv2.CV_64F)

        edges = cv2.Canny(pupil_img,
                          self.canny_thresh.value,
                          self.canny_thresh.value * self.canny_ratio.value,
                          apertureSize=self.canny_aperture.value)

        r_img[p_roi.lY:p_roi.uY, p_roi.lX:p_roi.uX, 1] = th3
        r_img[p_roi.lY:p_roi.uY, p_roi.lX:p_roi.uX, 2] = edges
        # for kp in kps:
        #     print kp.pt
        #     cv2.circle(r_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX],tuple(map(int,kp.pt,)),10,(255,255,255))

        no_result = {}
        no_result['timestamp'] = frame.timestamp
        no_result['norm_pupil'] = None
        return no_result
예제 #3
0
    def detect(self, frame, u_roi, visualize=False):

        if self.window_should_open:
            self.open_window()
        if self.window_should_close:
            self.close_window()

        #get the user_roi
        img = frame.img
        r_img = img[u_roi.lY:u_roi.uY, u_roi.lX:u_roi.uX]
        gray_img = grayscale(r_img)

        # coarse pupil detection
        integral = cv2.integral(gray_img)
        integral = np.array(integral, dtype=c_float)
        x, y, w, response = eye_filter(integral, self.coarse_filter_min,
                                       self.coarse_filter_max)
        p_roi = Roi(gray_img.shape)
        if w > 0:
            p_roi.set((y, x, y + w, x + w))
        else:
            p_roi.set((0, 0, -1, -1))
        coarse_pupil_center = x + w / 2., y + w / 2.
        coarse_pupil_width = w / 2.
        padding = coarse_pupil_width / 4.
        pupil_img = gray_img[p_roi.lY:p_roi.uY, p_roi.lX:p_roi.uX]

        # binary thresholding of pupil dark areas
        hist = cv2.calcHist([pupil_img], [0], None, [256], [
            0, 256
        ])  #(images, channels, mask, histSize, ranges[, hist[, accumulate]])
        bins = np.arange(hist.shape[0])
        spikes = bins[hist[:, 0] >
                      40]  # every intensity seen in more than 40 pixels
        if spikes.shape[0] > 0:
            lowest_spike = spikes.min()
            highest_spike = spikes.max()
        else:
            lowest_spike = 200
            highest_spike = 255

        offset = self.intensity_range.value
        spectral_offset = 5
        if visualize:
            # display the histogram
            sx, sy = 100, 1
            colors = ((0, 0, 255), (255, 0, 0), (255, 255, 0), (255, 255, 255))
            h, w, chan = img.shape
            hist *= 1. / hist.max()  # normalize for display

            for i, h in zip(bins, hist[:, 0]):
                c = colors[1]
                cv2.line(img, (w, int(i * sy)), (w - int(h * sx), int(i * sy)),
                         c)
            cv2.line(img, (w, int(lowest_spike * sy)),
                     (int(w - .5 * sx), int(lowest_spike * sy)), colors[0])
            cv2.line(img, (w, int((lowest_spike + offset) * sy)),
                     (int(w - .5 * sx), int(
                         (lowest_spike + offset) * sy)), colors[2])
            cv2.line(img, (w, int((highest_spike) * sy)),
                     (int(w - .5 * sx), int((highest_spike) * sy)), colors[0])
            cv2.line(
                img, (w, int((highest_spike - spectral_offset) * sy)),
                (int(w - .5 * sx), int(
                    (highest_spike - spectral_offset) * sy)), colors[3])

        # create dark and spectral glint masks
        self.bin_thresh.value = lowest_spike
        binary_img = bin_thresholding(pupil_img,
                                      image_upper=lowest_spike + offset)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
        cv2.dilate(binary_img, kernel, binary_img, iterations=2)
        spec_mask = bin_thresholding(pupil_img,
                                     image_upper=highest_spike -
                                     spectral_offset)
        cv2.erode(spec_mask, kernel, spec_mask, iterations=1)

        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))

        #open operation to remove eye lashes
        pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, kernel)

        if self.blur.value > 1:
            pupil_img = cv2.medianBlur(pupil_img, self.blur.value)

        edges = cv2.Canny(pupil_img,
                          self.canny_thresh.value,
                          self.canny_thresh.value * self.canny_ratio.value,
                          apertureSize=self.canny_aperture.value)

        # edges = cv2.adaptiveThreshold(pupil_img,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, self.canny_aperture.value, 7)

        # remove edges in areas not dark enough and where the glint is (spectral refelction from IR leds)
        edges = cv2.min(edges, spec_mask)
        edges = cv2.min(edges, binary_img)

        if visualize:
            overlay = img[u_roi.lY:u_roi.uY,
                          u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,
                                             p_roi.lX:p_roi.uX]
            chn_img = grayscale(overlay)
            overlay[:, :, 2] = cv2.max(chn_img, edges)  #b channel
            overlay[:, :, 0] = cv2.max(chn_img, binary_img)  #g channel
            overlay[:, :, 1] = cv2.min(chn_img, spec_mask)  #b channel

            pupil_img = frame.img[u_roi.lY:u_roi.uY,
                                  u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,
                                                     p_roi.lX:p_roi.uX]
            # draw a frame around the automatic pupil ROI in overlay...
            pupil_img[::2, 0] = 255, 255, 255
            pupil_img[::2, -1] = 255, 255, 255
            pupil_img[0, ::2] = 255, 255, 255
            pupil_img[-1, ::2] = 255, 255, 255

            pupil_img[::2, padding] = 255, 255, 255
            pupil_img[::2, -padding] = 255, 255, 255
            pupil_img[padding, ::2] = 255, 255, 255
            pupil_img[-padding, ::2] = 255, 255, 255

            frame.img[u_roi.lY:u_roi.uY,
                      u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,
                                         p_roi.lX:p_roi.uX] = pupil_img

        # from edges to contours
        contours, hierarchy = cv2.findContours(edges,
                                               mode=cv2.RETR_LIST,
                                               method=cv2.CHAIN_APPROX_NONE,
                                               offset=(0, 0))  #TC89_KCOS
        # contours is a list containing array([[[108, 290]],[[111, 290]]], dtype=int32) shape=(number of points,1,dimension(2) )

        ### first we want to filter out the bad stuff
        # to short
        good_contours = [
            c for c in contours if c.shape[0] > self.min_contour_size
        ]
        # now we learn things about each contour though looking at the curvature. For this we need to simplyfy the contour
        arprox_contours = [
            cv2.approxPolyDP(c, epsilon=1.5, closed=False)
            for c in good_contours
        ]
        # cv2.drawContours(pupil_img,good_contours,-1,(255,255,0))
        # cv2.drawContours(pupil_img,arprox_contours,-1,(0,0,255))

        if self._window:
            debug_img = np.zeros(img.shape, img.dtype)

        x_shift = coarse_pupil_width * 2  #just vor display
        color = zip(range(0, 250, 30),
                    range(0, 255, 30)[::-1], range(230, 250))
        split_contours = []
        for c in arprox_contours:
            curvature = GetAnglesPolyline(c)
            # print curvature
            # we split whenever there is a real kink (abs(curvature)<right angle) or a change in the genreal direction
            kink_idx = find_kink_and_dir_change(curvature, 100)
            # kinks,k_index = convexity_defect(c,curvature)
            # print "kink_idx", kink_idx
            segs = split_at_corner_index(c, kink_idx)
            # print len(segs)
            # segs.sort(key=lambda e:-len(e))
            for s in segs:
                split_contours.append(s)
                if self._window:
                    c = color.pop(0)
                    color.append(c)
                    # if s.shape[0] >=5:
                    #     cv2.polylines(debug_img,[s],isClosed=False,color=c)
                    s = s.copy()
                    s[:, :, 1] += coarse_pupil_width * 2
                    cv2.polylines(debug_img, [s], isClosed=False, color=c)
                    s[:, :, 0] += x_shift
                    x_shift += 5
                    cv2.polylines(debug_img, [s], isClosed=False, color=c)
        # return {'timestamp':frame.timestamp,'norm_pupil':None}

        #these segments may now be smaller, we need to get rid of those not long enough for ellipse fitting
        good_contours = [c for c in split_contours if c.shape[0] >= 5]
        # cv2.polylines(img,good_contours,isClosed=False,color=(255,255,0))

        shape = edges.shape
        ellipses = ((cv2.fitEllipse(c), c) for c in good_contours)
        ellipses = ((e, c) for e, c in ellipses
                    if (padding < e[0][1] < shape[0] -
                        padding and padding < e[0][0] < shape[1] - padding)
                    )  # center is close to roi center
        ellipses = ((e, c) for e, c in ellipses
                    if binary_img[e[0][1],
                                  e[0][0]])  # center is on a dark pixel
        ellipses = [(e, c) for e, c in ellipses
                    if is_round(e, self.target_ratio)]  # roundness test
        result = []
        for e, c in ellipses:
            size_dif = size_deviation(e, self.target_size.value)
            pupil_ellipse = {}
            pupil_ellipse['contour'] = c
            a, b = e[1][0] / 2., e[1][
                1] / 2.  # majar minor radii of candidate ellipse
            pupil_ellipse['circumference'] = np.pi * abs(
                3 * (a + b) - np.sqrt(10 * a * b + 3 * (a**2 + b**2)))
            # pupil_ellipse['convex_hull'] = cv2.convexHull(pupil_ellipse['contour'])
            pupil_ellipse['contour_area'] = cv2.contourArea(cv2.convexHull(c))
            pupil_ellipse['ellipse_area'] = np.pi * a * b
            # print abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area'])
            if abs(pupil_ellipse['contour_area'] -
                   pupil_ellipse['ellipse_area']) < 10:
                pupil_ellipse['goodness'] = abs(
                    pupil_ellipse['contour_area'] -
                    pupil_ellipse['ellipse_area']
                ) / 10  #perfect match we'll take this one
            else:
                pupil_ellipse['goodness'] = size_dif
            if visualize:
                pass
                # cv2.drawContours(pupil_img,[cv2.convexHull(c)],-1,(size_dif,size_dif,255))
                # cv2.drawContours(pupil_img,[c],-1,(size_dif,size_dif,255))
            pupil_ellipse['pupil_center'] = e[0]  # compensate for roi offsets
            pupil_ellipse['center'] = u_roi.add_vector(p_roi.add_vector(
                e[0]))  # compensate for roi offsets
            pupil_ellipse['angle'] = e[-1]
            pupil_ellipse['axes'] = e[1]
            pupil_ellipse['major'] = max(e[1])
            pupil_ellipse['minor'] = min(e[1])
            pupil_ellipse[
                'ratio'] = pupil_ellipse['minor'] / pupil_ellipse['major']
            pupil_ellipse['norm_pupil'] = normalize(
                pupil_ellipse['center'], (img.shape[1], img.shape[0]),
                flip_y=True)
            pupil_ellipse['timestamp'] = frame.timestamp
            result.append(pupil_ellipse)

        #### adding support
        if result:
            result.sort(key=lambda e: e['goodness'])
            # for now we assume that this contour is part of the pupil
            the_one = result[0]
            # (center, size, angle) = cv2.fitEllipse(the_one['contour'])
            # print "itself"
            distances = dist_pts_ellipse(cv2.fitEllipse(the_one['contour']),
                                         the_one['contour'])
            # print np.average(distances)
            # print np.sum(distances)/float(distances.shape[0])
            # print "other"
            # if self._window:
            # cv2.polylines(debug_img,[result[-1]['contour']],isClosed=False,color=(255,255,255),thickness=3)
            with_another = np.concatenate(
                (result[-1]['contour'], the_one['contour']))
            distances = dist_pts_ellipse(cv2.fitEllipse(with_another),
                                         with_another)
            # if 1.5 > np.sum(distances)/float(distances.shape[0]):
            #     if self._window:
            #         cv2.polylines(debug_img,[result[-1]['contour']],isClosed=False,color=(255,255,255),thickness=3)

            perimeter_ratio = cv2.arcLength(
                the_one["contour"], closed=False) / the_one['circumference']
            if perimeter_ratio > .9:
                size_thresh = 0
                eccentricity_thresh = 0
            elif perimeter_ratio > .5:
                size_thresh = the_one['major'] / (5.)
                eccentricity_thresh = the_one['major'] / 2.
                self.should_sleep = True
            else:
                size_thresh = the_one['major'] / (3.)
                eccentricity_thresh = the_one['major'] / 2.
                self.should_sleep = True
            if self._window:
                center = np.uint16(np.around(the_one['pupil_center']))
                cv2.circle(debug_img, tuple(center), int(eccentricity_thresh),
                           (0, 255, 0), 1)

            if self._window:
                cv2.polylines(debug_img, [the_one["contour"]],
                              isClosed=False,
                              color=(255, 0, 0),
                              thickness=2)
                s = the_one["contour"].copy()
                s[:, :, 0] += coarse_pupil_width * 2
                cv2.polylines(debug_img, [s],
                              isClosed=False,
                              color=(255, 0, 0),
                              thickness=2)
            # but are there other segments that could be used for support?
            new_support = [
                the_one['contour'],
            ]
            if len(result) > 1:
                the_one = result[0]
                target_axes = the_one['axes'][0]
                # target_mean_curv = np.mean(curvature(the_one['contour'])
                for e in result:

                    # with_another = np.concatenate((e['contour'],the_one['contour']))
                    # with_another = np.concatenate([r['contour'] for r in result])
                    with_another = e['contour']
                    distances = dist_pts_ellipse(cv2.fitEllipse(with_another),
                                                 with_another)
                    # print np.std(distances)
                    thick = int(np.std(distances))
                    if 1.5 > np.average(distances) or 1:
                        if self._window:
                            # print thick
                            thick = min(20, thick)
                            cv2.polylines(debug_img, [e['contour']],
                                          isClosed=False,
                                          color=(255, 255, 255),
                                          thickness=thick)

                    if self._window:
                        cv2.polylines(debug_img, [e["contour"]],
                                      isClosed=False,
                                      color=(0, 100, 100))
                    center_dist = cv2.arcLength(np.array(
                        [the_one["pupil_center"], e['pupil_center']],
                        dtype=np.int32),
                                                closed=False)
                    size_dif = abs(the_one['major'] - e['major'])

                    # #lets make sure the countour is not behind the_one/'s coutour
                    # center_point = np.uint16(np.around(the_one['pupil_center']))
                    # other_center_point = np.uint16(np.around(e['pupil_center']))

                    # mid_point =  the_one["contour"][the_one["contour"].shape[0]/2][0]
                    # other_mid_point =  e["contour"][e["contour"].shape[0]/2][0]

                    # #reflect around mid_point
                    # p = center_point - mid_point
                    # p = np.array((-p[1],-p[0]))
                    # mir_center_point = p + mid_point
                    # dist_mid = cv2.arcLength(np.array([mid_point,other_mid_point]),closed=False)
                    # dist_center = cv2.arcLength(np.array([center_point,other_mid_point]),closed=False)
                    # if self._window:
                    #     cv2.circle(debug_img,tuple(center_point),3,(0,255,0),2)
                    #     cv2.circle(debug_img,tuple(other_center_point),2,(0,0,255),1)
                    #     # cv2.circle(debug_img,tuple(mir_center_point),3,(0,255,0),2)
                    #     # cv2.circle(debug_img,tuple(mid_point),2,(0,255,0),1)
                    #     # cv2.circle(debug_img,tuple(other_mid_point),2,(0,0,255),1)
                    #     cv2.polylines(debug_img,[np.array([center_point,other_mid_point]),np.array([mid_point,other_mid_point])],isClosed=False,color=(0,255,0))

                    if center_dist < eccentricity_thresh:
                        # print dist_mid-dist_center
                        # if dist_mid > dist_center-20:

                        if size_dif < size_thresh:

                            new_support.append(e["contour"])
                            if self._window:
                                cv2.polylines(debug_img, [s],
                                              isClosed=False,
                                              color=(255, 0, 0),
                                              thickness=1)
                                s = e["contour"].copy()
                                s[:, :, 0] += coarse_pupil_width * 2
                                cv2.polylines(debug_img, [s],
                                              isClosed=False,
                                              color=(255, 255, 0),
                                              thickness=1)

                        else:
                            if self._window:
                                s = e["contour"].copy()
                                s[:, :, 0] += coarse_pupil_width * 2
                                cv2.polylines(debug_img, [s],
                                              isClosed=False,
                                              color=(0, 0, 255),
                                              thickness=1)
                    else:
                        if self._window:
                            cv2.polylines(debug_img, [s],
                                          isClosed=False,
                                          color=(0, 255, 255),
                                          thickness=1)

                    # new_support = np.concatenate(new_support)

            self.goodness.value = the_one['goodness']

            ###here we should AND original mask, selected contours with 2px thinkness (and 2px fitted ellipse -is the last one a good idea??)
            support_mask = np.zeros(edges.shape, edges.dtype)
            cv2.polylines(support_mask,
                          new_support,
                          isClosed=False,
                          color=(255, 255, 255),
                          thickness=2)
            # #draw into the suport mast with thickness 2
            new_edges = cv2.min(edges, support_mask)
            new_contours = cv2.findNonZero(new_edges)
            if self._window:
                debug_img[0:support_mask.shape[0], 0:support_mask.shape[1],
                          2] = new_edges

            ###### do the ellipse fit and filter think again
            ellipses = ((cv2.fitEllipse(c), c) for c in [new_contours])
            ellipses = ((e, c) for e, c in ellipses
                        if (padding < e[0][1] < shape[0] -
                            padding and padding < e[0][0] < shape[1] - padding)
                        )  # center is close to roi center
            ellipses = ((e, c) for e, c in ellipses
                        if binary_img[e[0][1],
                                      e[0][0]])  # center is on a dark pixel
            ellipses = [(size_deviation(e, self.target_size.value), e, c)
                        for e, c in ellipses
                        if is_round(e, self.target_ratio)]  # roundness test
            for size_dif, e, c in ellipses:
                pupil_ellipse = {}
                pupil_ellipse['contour'] = c
                a, b = e[1][0] / 2., e[1][
                    1] / 2.  # majar minor radii of candidate ellipse
                # pupil_ellipse['circumference'] = np.pi*abs(3*(a+b)-np.sqrt(10*a*b+3*(a**2+b**2)))
                # pupil_ellipse['convex_hull'] = cv2.convexHull(pupil_ellipse['contour'])
                pupil_ellipse['contour_area'] = cv2.contourArea(
                    cv2.convexHull(c))
                pupil_ellipse['ellipse_area'] = np.pi * a * b
                # print abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area'])
                if abs(pupil_ellipse['contour_area'] -
                       pupil_ellipse['ellipse_area']) < 10:
                    pupil_ellipse[
                        'goodness'] = 0  #perfect match we'll take this one
                else:
                    pupil_ellipse['goodness'] = size_dif
                if visualize:
                    pass
                    # cv2.drawContours(pupil_img,[cv2.convexHull(c)],-1,(size_dif,size_dif,255))
                    # cv2.drawContours(pupil_img,[c],-1,(size_dif,size_dif,255))
                pupil_ellipse['center'] = u_roi.add_vector(
                    p_roi.add_vector(e[0]))  # compensate for roi offsets
                pupil_ellipse['angle'] = e[-1]
                pupil_ellipse['axes'] = e[1]
                pupil_ellipse['major'] = max(e[1])
                pupil_ellipse['minor'] = min(e[1])
                pupil_ellipse[
                    'ratio'] = pupil_ellipse['minor'] / pupil_ellipse['major']
                pupil_ellipse['norm_pupil'] = normalize(
                    pupil_ellipse['center'], (img.shape[1], img.shape[0]),
                    flip_y=True)
                pupil_ellipse['timestamp'] = frame.timestamp
                result = [
                    pupil_ellipse,
                ]
            # the_new_one = result[0]

            #done - if the new ellipse is good, we just overwrote the old result

        if self._window:
            self.gl_display_in_window(debug_img)
            if self.should_sleep:
                # sleep(3)
                self.should_sleep = False
        if result:
            # update the target size
            if result[0]['goodness'] >= 3:  # perfect match!
                self.target_size.value = result[0]['major']
            else:
                self.target_size.value = self.target_size.value + .2 * (
                    result[0]['major'] - self.target_size.value)
                result.sort(
                    key=lambda e: abs(e['major'] - self.target_size.value))
            if visualize:
                pass
            return result[0]

        else:
            self.goodness.value = 100
            no_result = {}
            no_result['timestamp'] = frame.timestamp
            no_result['norm_pupil'] = None
            return no_result
예제 #4
0
def eye(g_pool):
    """
    this process needs a docstring
    """
    # # callback functions
    def on_resize(w, h):
        atb.TwWindowSize(w, h)
        adjust_gl_view(w, h)

    def on_key(key, pressed):
        if not atb.TwEventKeyboardGLFW(key, pressed):
            if pressed:
                if key == GLFW_KEY_ESC:
                    on_close()

    def on_char(char, pressed):
        if not atb.TwEventCharGLFW(char, pressed):
            pass

    def on_button(button, pressed):
        if not atb.TwEventMouseButtonGLFW(button, pressed):
            if bar.draw_roi.value:
                if pressed:
                    pos = glfwGetMousePos()
                    pos = normalize(pos, glfwGetWindowSize())
                    pos = denormalize(pos, (img.shape[1], img.shape[0]))  # pos in img pixels
                    r.setStart(pos)
                    bar.draw_roi.value = 1
                else:
                    bar.draw_roi.value = 0

    def on_pos(x, y):
        if atb.TwMouseMotion(x, y):
            pass
        if bar.draw_roi.value == 1:
            pos = glfwGetMousePos()
            pos = normalize(pos, glfwGetWindowSize())
            pos = denormalize(pos, (img.shape[1], img.shape[0]))  # pos in img pixels
            r.setEnd(pos)

    def on_scroll(pos):
        if not atb.TwMouseWheel(pos):
            pass

    def on_close():
        g_pool.quit.value = True
        print "EYE Process closing from window"

    # initialize capture, check if it works
    cap = autoCreateCapture(g_pool.eye_src, g_pool.eye_size)
    if cap is None:
        print "EYE: Error could not create Capture"
        return
    s, img = cap.read_RGB()
    if not s:
        print "EYE: Error could not get image"
        return
    height, width = img.shape[:2]

    # pupil object
    pupil = Temp()
    pupil.norm_coords = (0.0, 0.0)
    pupil.image_coords = (0.0, 0.0)
    pupil.ellipse = None
    pupil.gaze_coords = (0.0, 0.0)

    try:
        pupil.pt_cloud = np.load("cal_pt_cloud.npy")
        map_pupil = get_map_from_cloud(pupil.pt_cloud, g_pool.world_size)  ###world video size here
    except:
        pupil.pt_cloud = None

        def map_pupil(vector):
            return vector

    r = Roi(img.shape)
    p_r = Roi(img.shape)

    # local object
    l_pool = Temp()
    l_pool.calib_running = False
    l_pool.record_running = False
    l_pool.record_positions = []
    l_pool.record_path = None
    l_pool.writer = None
    l_pool.region_r = 20

    atb.init()
    bar = Bar(
        "Eye",
        g_pool,
        dict(
            label="Controls",
            help="eye detection controls",
            color=(50, 50, 50),
            alpha=100,
            text="light",
            position=(10, 10),
            refresh=0.1,
            size=(200, 300),
        ),
    )

    # add 4vl2 camera controls to a seperate ATB bar
    if cap.controls is not None:
        c_bar = atb.Bar(
            name="Camera_Controls",
            label=cap.name,
            help="UVC Camera Controls",
            color=(50, 50, 50),
            alpha=100,
            text="light",
            position=(220, 10),
            refresh=2.0,
            size=(200, 200),
        )

        # c_bar.add_var("auto_refresher",vtype=atb.TW_TYPE_BOOL8,getter=cap.uvc_refresh_all,setter=None,readonly=True)
        # c_bar.define(definition='visible=0', varname="auto_refresher")

        sorted_controls = [c for c in cap.controls.itervalues()]
        sorted_controls.sort(key=lambda c: c.order)

        for control in sorted_controls:
            name = control.atb_name
            if control.type == "bool":
                c_bar.add_var(name, vtype=atb.TW_TYPE_BOOL8, getter=control.get_val, setter=control.set_val)
            elif control.type == "int":
                c_bar.add_var(name, vtype=atb.TW_TYPE_INT32, getter=control.get_val, setter=control.set_val)
                c_bar.define(definition="min=" + str(control.min), varname=name)
                c_bar.define(definition="max=" + str(control.max), varname=name)
                c_bar.define(definition="step=" + str(control.step), varname=name)
            elif control.type == "menu":
                if control.menu is None:
                    vtype = None
                else:
                    vtype = atb.enum(name, control.menu)
                c_bar.add_var(name, vtype=vtype, getter=control.get_val, setter=control.set_val)
                if control.menu is None:
                    c_bar.define(definition="min=" + str(control.min), varname=name)
                    c_bar.define(definition="max=" + str(control.max), varname=name)
                    c_bar.define(definition="step=" + str(control.step), varname=name)
            else:
                pass
            if control.flags == "inactive":
                pass
                # c_bar.define(definition='readonly=1',varname=control.name)

        c_bar.add_button("refresh", cap.update_from_device)
        c_bar.add_button("load defaults", cap.load_defaults)

    else:
        c_bar = None

    # Initialize glfw
    glfwInit()
    glfwOpenWindow(width, height, 0, 0, 0, 8, 0, 0, GLFW_WINDOW)
    glfwSetWindowTitle("Eye")
    glfwSetWindowPos(800, 0)
    if isinstance(g_pool.eye_src, str):
        glfwSwapInterval(0)  # turn of v-sync when using video as src for benchmarking

    # register callbacks
    glfwSetWindowSizeCallback(on_resize)
    glfwSetWindowCloseCallback(on_close)
    glfwSetKeyCallback(on_key)
    glfwSetCharCallback(on_char)
    glfwSetMouseButtonCallback(on_button)
    glfwSetMousePosCallback(on_pos)
    glfwSetMouseWheelCallback(on_scroll)

    # gl_state settings
    import OpenGL.GL as gl

    gl.glEnable(gl.GL_POINT_SMOOTH)
    gl.glPointSize(20)
    gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
    gl.glEnable(gl.GL_BLEND)
    del gl

    # event loop
    while glfwGetWindowParam(GLFW_OPENED) and not g_pool.quit.value:
        bar.update_fps()
        s, img = cap.read_RGB()
        sleep(bar.sleep.value)  # for debugging only

        ###IMAGE PROCESSING
        gray_img = grayscale(img[r.lY : r.uY, r.lX : r.uX])

        integral = cv2.integral(gray_img)
        integral = np.array(integral, dtype=c_float)
        x, y, w = eye_filter(integral)
        if w > 0:
            p_r.set((y, x, y + w, x + w))
        else:
            p_r.set((0, 0, -1, -1))

        # create view into the gray_img with the bounds of the rough pupil estimation
        pupil_img = gray_img[p_r.lY : p_r.uY, p_r.lX : p_r.uX]

        # pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)),iterations=2)
        if True:
            hist = cv2.calcHist(
                [pupil_img], [0], None, [256], [0, 256]
            )  # (images, channels, mask, histSize, ranges[, hist[, accumulate]])
            bins = np.arange(hist.shape[0])
            spikes = bins[hist[:, 0] > 40]  # every color seen in more than 40 pixels
            if spikes.shape[0] > 0:
                lowest_spike = spikes.min()
            offset = 40

            ##display the histogram
            sx, sy = 100, 1
            colors = ((255, 0, 0), (0, 0, 255), (0, 255, 255))
            h, w, chan = img.shape
            # normalize
            hist *= 1.0 / hist.max()
            for i, h in zip(bins, hist[:, 0]):
                c = colors[1]
                cv2.line(img, (w, int(i * sy)), (w - int(h * sx), int(i * sy)), c)
            cv2.line(img, (w, int(lowest_spike * sy)), (int(w - 0.5 * sx), int(lowest_spike * sy)), colors[0])
            cv2.line(
                img,
                (w, int((lowest_spike + offset) * sy)),
                (int(w - 0.5 * sx), int((lowest_spike + offset) * sy)),
                colors[2],
            )

        # # k-means on the histogram finds peaks but thats no good for us...
        # term_crit = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
        # compactness, bestLabels, centers = cv2.kmeans(data=hist, K=2, criteria=term_crit, attempts=10, flags=cv2.KMEANS_RANDOM_CENTERS)
        # cv2.line(img,(0,1),(int(compactness),1),(0,0,0))
        # good_cluster = np.argmax(centers)
        # # A = hist[bestLabels.ravel() == good_cluster]
        # # B = hist[bestLabels.ravel() != good_cluster]
        # bins = np.arange(hist.shape[0])
        # good_bins = bins[bestLabels.ravel() == good_cluster]
        # good_bins_mean = good_bins.sum()/good_bins.shape[0]
        # good_bins_min = good_bins.min()

        # h,w,chan = img.shape
        # for h, i, label in zip(hist[:,0],range(hist.shape[0]), bestLabels.ravel()):
        #     c = colors[label]
        #     cv2.line(img,(w,int(i*sy)),(w-int(h*sx),int(i*sy)),c)

        else:
            # direct k-means on the image is best but expensive
            Z = pupil_img[:: w / 30 + 1, :: w / 30 + 1].reshape((-1, 1))
            Z = np.float32(Z)
            # define criteria, number of clusters(K) and apply kmeans()
            criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 2.0)
            K = 5
            ret, label, center = cv2.kmeans(Z, K, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
            offset = 0
            center.sort(axis=0)
            lowest_spike = int(center[1])
            # # Now convert back into uint8, and make original image
            # center = np.uint8(center)
            # res = center[label.flatten()]
            # binary_img = res.reshape((pupil_img.shape))
            # binary_img = bin_thresholding(binary_img,image_upper=res.min()+1)
            # bar.bin_thresh.value = res.min()+1

        bar.bin_thresh.value = lowest_spike
        binary_img = bin_thresholding(pupil_img, image_upper=lowest_spike + offset)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
        cv2.dilate(binary_img, kernel, binary_img, iterations=2)
        spec_mask = bin_thresholding(pupil_img, image_upper=250)
        cv2.erode(spec_mask, kernel, spec_mask, iterations=1)

        if bar.blur.value > 1:
            pupil_img = cv2.medianBlur(pupil_img, bar.blur.value)

        # create contours using Canny edge dectetion
        contours = cv2.Canny(
            pupil_img,
            bar.canny_thresh.value,
            bar.canny_thresh.value * bar.canny_ratio.value,
            apertureSize=bar.canny_aperture.value,
        )

        # remove contours in areas not dark enough and where the glint (spectral refelction from IR leds)
        contours = cv2.min(contours, spec_mask)
        contours = cv2.min(contours, binary_img)

        # Ellipse fitting from countours
        result = fit_ellipse(
            img[r.lY : r.uY, r.lX : r.uX][p_r.lY : p_r.uY, p_r.lX : p_r.uX],
            contours,
            binary_img,
            target_size=bar.pupil_size.value,
            size_tolerance=bar.pupil_size_tolerance.value,
        )

        # # Vizualizations
        overlay = cv2.cvtColor(pupil_img, cv2.COLOR_GRAY2RGB)  # create an RGB view onto the gray pupil ROI
        overlay[:, :, 0] = cv2.max(pupil_img, contours)  # green channel
        overlay[:, :, 2] = cv2.max(pupil_img, binary_img)  # blue channel
        overlay[:, :, 1] = cv2.min(pupil_img, spec_mask)  # red channel

        # draw a blue dotted frame around the automatic pupil ROI in overlay...
        overlay[::2, 0] = 0, 0, 255
        overlay[::2, -1] = 0, 0, 255
        overlay[0, ::2] = 0, 0, 255
        overlay[-1, ::2] = 0, 0, 255

        # and a solid (white) frame around the user defined ROI
        gray_img[:, 0] = 255
        gray_img[:, -1] = 255
        gray_img[0, :] = 255
        gray_img[-1, :] = 255

        if bar.display.value == 0:
            img = img
        elif bar.display.value == 1:
            img[r.lY : r.uY, r.lX : r.uX] = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB)
        elif bar.display.value == 2:
            img[r.lY : r.uY, r.lX : r.uX] = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB)
            img[r.lY : r.uY, r.lX : r.uX][p_r.lY : p_r.uY, p_r.lX : p_r.uX] = overlay
        elif bar.display.value == 3:
            img = cv2.cvtColor(pupil_img, cv2.COLOR_GRAY2RGB)
        else:
            pass

        if result is not None:
            pupil.ellipse, others = result
            pupil.image_coords = r.add_vector(p_r.add_vector(pupil.ellipse["center"]))
            # update pupil size,angle and ratio for the ellipse filter algorithm
            bar.pupil_size.value = bar.pupil_size.value + 0.5 * (pupil.ellipse["major"] - bar.pupil_size.value)
            bar.pupil_ratio.value = bar.pupil_ratio.value + 0.7 * (pupil.ellipse["ratio"] - bar.pupil_ratio.value)
            bar.pupil_angle.value = bar.pupil_angle.value + 1.0 * (pupil.ellipse["angle"] - bar.pupil_angle.value)

            # if pupil found tighten the size tolerance
            bar.pupil_size_tolerance.value -= 1
            bar.pupil_size_tolerance.value = max(10, min(50, bar.pupil_size_tolerance.value))

            # clamp pupil size
            bar.pupil_size.value = max(20, min(300, bar.pupil_size.value))

            # normalize
            pupil.norm_coords = normalize(pupil.image_coords, (img.shape[1], img.shape[0]), flip_y=True)

            # from pupil to gaze
            pupil.gaze_coords = map_pupil(pupil.norm_coords)
            g_pool.gaze_x.value, g_pool.gaze_y.value = pupil.gaze_coords

        else:
            pupil.ellipse = None
            g_pool.gaze_x.value, g_pool.gaze_y.value = 0.0, 0.0
            pupil.gaze_coords = None  # whithout this line the last know pupil position is recorded if none is found

            bar.pupil_size_tolerance.value += 1

        ###CALIBRATION###
        # Initialize Calibration (setup variables and lists)
        if g_pool.calibrate.value and not l_pool.calib_running:
            l_pool.calib_running = True
            pupil.pt_cloud = []

        # While Calibrating...
        if l_pool.calib_running and ((g_pool.ref_x.value != 0) or (g_pool.ref_y.value != 0)) and pupil.ellipse:
            pupil.pt_cloud.append([pupil.norm_coords[0], pupil.norm_coords[1], g_pool.ref_x.value, g_pool.ref_y.value])

        # Calculate mapping coefs
        if not g_pool.calibrate.value and l_pool.calib_running:
            l_pool.calib_running = 0
            if pupil.pt_cloud:  # some data was actually collected
                print "Calibrating with", len(pupil.pt_cloud), "collected data points."
                map_pupil = get_map_from_cloud(np.array(pupil.pt_cloud), g_pool.world_size, verbose=True)
                np.save("cal_pt_cloud.npy", np.array(pupil.pt_cloud))

        ###RECORDING###
        # Setup variables and lists for recording
        if g_pool.pos_record.value and not l_pool.record_running:
            l_pool.record_path = g_pool.eye_rx.recv()
            print "l_pool.record_path: ", l_pool.record_path

            video_path = path.join(l_pool.record_path, "eye.avi")
            # FFV1 -- good speed lossless big file
            # DIVX -- good speed good compression medium file
            if bar.record_eye.value:
                l_pool.writer = cv2.VideoWriter(
                    video_path, cv2.cv.CV_FOURCC(*"DIVX"), bar.fps.value, (img.shape[1], img.shape[0])
                )
            l_pool.record_positions = []
            l_pool.record_running = True

        # While recording...
        if l_pool.record_running:
            if pupil.gaze_coords is not None:
                l_pool.record_positions.append(
                    [
                        pupil.gaze_coords[0],
                        pupil.gaze_coords[1],
                        pupil.norm_coords[0],
                        pupil.norm_coords[1],
                        bar.dt,
                        g_pool.frame_count_record.value,
                    ]
                )
            if l_pool.writer is not None:
                l_pool.writer.write(cv2.cvtColor(img, cv2.cv.COLOR_BGR2RGB))

        # Done Recording: Save values and flip switch to off for recording
        if not g_pool.pos_record.value and l_pool.record_running:
            positions_path = path.join(l_pool.record_path, "gaze_positions.npy")
            cal_pt_cloud_path = path.join(l_pool.record_path, "cal_pt_cloud.npy")
            np.save(positions_path, np.asarray(l_pool.record_positions))
            try:
                np.save(cal_pt_cloud_path, np.asarray(pupil.pt_cloud))
            except:
                print "Warning: No calibration data associated with this recording."
            l_pool.writer = None
            l_pool.record_running = False

        ### GL-drawing
        clear_gl_screen()
        draw_gl_texture(img)

        if bar.draw_pupil and pupil.ellipse:
            pts = cv2.ellipse2Poly(
                (int(pupil.image_coords[0]), int(pupil.image_coords[1])),
                (int(pupil.ellipse["axes"][0] / 2), int(pupil.ellipse["axes"][1] / 2)),
                int(pupil.ellipse["angle"]),
                0,
                360,
                15,
            )
            draw_gl_polyline(pts, (1.0, 0, 0, 0.5))
            draw_gl_point_norm(pupil.norm_coords, (1.0, 0.0, 0.0, 0.5))

        atb.draw()
        glfwSwapBuffers()

    # end while running
    print "EYE Process closed"
    r.save()
    bar.save()
    atb.terminate()
    glfwCloseWindow()
    glfwTerminate()
예제 #5
0
    def detect(self,frame,u_roi,visualize=False):
        #get the user_roi
        img = frame.img
        r_img = img[u_roi.lY:u_roi.uY,u_roi.lX:u_roi.uX]
        gray_img = grayscale(r_img)
        # coarse pupil detection
        integral = cv2.integral(gray_img)
        integral =  np.array(integral,dtype=c_float)
        x,y,w,response = eye_filter(integral,100,400)
        p_roi = Roi(gray_img.shape)
        if w>0:
            p_roi.set((y,x,y+w,x+w))
        else:
            p_roi.set((0,0,-1,-1))
        coarse_pupil_center = x+w/2.,y+w/2.
        coarse_pupil_width = w/2.
        padding = coarse_pupil_width/4.
        pupil_img = gray_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX]
        # binary thresholding of pupil dark areas
        hist = cv2.calcHist([pupil_img],[0],None,[256],[0,256]) #(images, channels, mask, histSize, ranges[, hist[, accumulate]])
        bins = np.arange(hist.shape[0])
        spikes = bins[hist[:,0]>40] # every intensity seen in more than 40 pixels
        if spikes.shape[0] >0:
            lowest_spike = spikes.min()
            highest_spike = spikes.max()
        else:
            lowest_spike = 200
            highest_spike = 255


        offset = self.intensity_range.value
        spectral_offset = 5
        if visualize:
            # display the histogram
            sx,sy = 100,1
            colors = ((0,0,255),(255,0,0),(255,255,0),(255,255,255))
            h,w,chan = img.shape
            hist *= 1./hist.max()  # normalize for display

            for i,h in zip(bins,hist[:,0]):
                c = colors[1]
                cv2.line(img,(w,int(i*sy)),(w-int(h*sx),int(i*sy)),c)
            cv2.line(img,(w,int(lowest_spike*sy)),(int(w-.5*sx),int(lowest_spike*sy)),colors[0])
            cv2.line(img,(w,int((lowest_spike+offset)*sy)),(int(w-.5*sx),int((lowest_spike+offset)*sy)),colors[2])
            cv2.line(img,(w,int((highest_spike)*sy)),(int(w-.5*sx),int((highest_spike)*sy)),colors[0])
            cv2.line(img,(w,int((highest_spike- spectral_offset )*sy)),(int(w-.5*sx),int((highest_spike - spectral_offset)*sy)),colors[3])


        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))

        #open operation to remove eye lashes
        pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, kernel)


        # PARAMS = {}
        # blob_detector = cv2.SimpleBlobDetector(**PARAMS)
        # kps =  blob_detector.detect(pupil_img)

        # blur = cv2.GaussianBlur(pupil_img,(5,5),0)
        blur = pupil_img
        # ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
        ret3,th3 = cv2.threshold(blur,lowest_spike+offset,255,cv2.THRESH_BINARY)
        # ret3,th3 = cv2.threshold(th3,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
        th3 = cv2.Laplacian(th3,cv2.CV_64F)

        edges = cv2.Canny(pupil_img,
                            self.canny_thresh.value,
                            self.canny_thresh.value*self.canny_ratio.value,
                            apertureSize= self.canny_aperture.value)

        r_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX,1] = th3
        r_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX,2] = edges
        # for kp in kps:
        #     print kp.pt
        #     cv2.circle(r_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX],tuple(map(int,kp.pt,)),10,(255,255,255))

        no_result = {}
        no_result['timestamp'] = frame.timestamp
        no_result['norm_pupil'] = None
        return no_result
예제 #6
0
    def detect(self,frame,u_roi,visualize=False):

        if self.window_should_open:
            self.open_window()
        if self.window_should_close:
            self.close_window()



        #get the user_roi
        img = frame.img
        r_img = img[u_roi.lY:u_roi.uY,u_roi.lX:u_roi.uX]
        gray_img = grayscale(r_img)


        # coarse pupil detection
        integral = cv2.integral(gray_img)
        integral =  np.array(integral,dtype=c_float)
        x,y,w,response = eye_filter(integral,self.coarse_filter_min,self.coarse_filter_max)
        p_roi = Roi(gray_img.shape)
        if w>0:
            p_roi.set((y,x,y+w,x+w))
        else:
            p_roi.set((0,0,-1,-1))
        coarse_pupil_center = x+w/2.,y+w/2.
        coarse_pupil_width = w/2.
        padding = coarse_pupil_width/4.
        pupil_img = gray_img[p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX]



        # binary thresholding of pupil dark areas
        hist = cv2.calcHist([pupil_img],[0],None,[256],[0,256]) #(images, channels, mask, histSize, ranges[, hist[, accumulate]])
        bins = np.arange(hist.shape[0])
        spikes = bins[hist[:,0]>40] # every intensity seen in more than 40 pixels
        if spikes.shape[0] >0:
            lowest_spike = spikes.min()
            highest_spike = spikes.max()
        else:
            lowest_spike = 200
            highest_spike = 255

        offset = self.intensity_range.value
        spectral_offset = 5
        if visualize:
            # display the histogram
            sx,sy = 100,1
            colors = ((0,0,255),(255,0,0),(255,255,0),(255,255,255))
            h,w,chan = img.shape
            hist *= 1./hist.max()  # normalize for display

            for i,h in zip(bins,hist[:,0]):
                c = colors[1]
                cv2.line(img,(w,int(i*sy)),(w-int(h*sx),int(i*sy)),c)
            cv2.line(img,(w,int(lowest_spike*sy)),(int(w-.5*sx),int(lowest_spike*sy)),colors[0])
            cv2.line(img,(w,int((lowest_spike+offset)*sy)),(int(w-.5*sx),int((lowest_spike+offset)*sy)),colors[2])
            cv2.line(img,(w,int((highest_spike)*sy)),(int(w-.5*sx),int((highest_spike)*sy)),colors[0])
            cv2.line(img,(w,int((highest_spike- spectral_offset )*sy)),(int(w-.5*sx),int((highest_spike - spectral_offset)*sy)),colors[3])

        # create dark and spectral glint masks
        self.bin_thresh.value = lowest_spike
        binary_img = bin_thresholding(pupil_img,image_upper=lowest_spike + offset)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
        cv2.dilate(binary_img, kernel,binary_img, iterations=2)
        spec_mask = bin_thresholding(pupil_img, image_upper=highest_spike - spectral_offset)
        cv2.erode(spec_mask, kernel,spec_mask, iterations=1)

        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))

        #open operation to remove eye lashes
        pupil_img = cv2.morphologyEx(pupil_img, cv2.MORPH_OPEN, kernel)

        if self.blur.value >1:
            pupil_img = cv2.medianBlur(pupil_img,self.blur.value)

        edges = cv2.Canny(pupil_img,
                            self.canny_thresh.value,
                            self.canny_thresh.value*self.canny_ratio.value,
                            apertureSize= self.canny_aperture.value)


        # edges = cv2.adaptiveThreshold(pupil_img,255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, self.canny_aperture.value, 7)

        # remove edges in areas not dark enough and where the glint is (spectral refelction from IR leds)
        edges = cv2.min(edges, spec_mask)
        edges = cv2.min(edges,binary_img)

        if visualize:
            overlay =  img[u_roi.lY:u_roi.uY,u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX]
            chn_img = grayscale(overlay)
            overlay[:,:,2] = cv2.max(chn_img,edges) #b channel
            overlay[:,:,0] = cv2.max(chn_img,binary_img) #g channel
            overlay[:,:,1] = cv2.min(chn_img,spec_mask) #b channel

            pupil_img = frame.img[u_roi.lY:u_roi.uY,u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX]
            # draw a frame around the automatic pupil ROI in overlay...
            pupil_img[::2,0] = 255,255,255
            pupil_img[::2,-1]= 255,255,255
            pupil_img[0,::2] = 255,255,255
            pupil_img[-1,::2]= 255,255,255

            pupil_img[::2,padding] = 255,255,255
            pupil_img[::2,-padding]= 255,255,255
            pupil_img[padding,::2] = 255,255,255
            pupil_img[-padding,::2]= 255,255,255

            frame.img[u_roi.lY:u_roi.uY,u_roi.lX:u_roi.uX][p_roi.lY:p_roi.uY,p_roi.lX:p_roi.uX] = pupil_img


        # from edges to contours
        contours, hierarchy = cv2.findContours(edges,
                                            mode=cv2.RETR_LIST,
                                            method=cv2.CHAIN_APPROX_NONE,offset=(0,0)) #TC89_KCOS
        # contours is a list containing array([[[108, 290]],[[111, 290]]], dtype=int32) shape=(number of points,1,dimension(2) )


        ### first we want to filter out the bad stuff
        # to short
        good_contours = [c for c in contours if c.shape[0]>self.min_contour_size]
        # now we learn things about each contour though looking at the curvature. For this we need to simplyfy the contour
        arprox_contours = [cv2.approxPolyDP(c,epsilon=1.5,closed=False) for c in good_contours]
        # cv2.drawContours(pupil_img,good_contours,-1,(255,255,0))
        # cv2.drawContours(pupil_img,arprox_contours,-1,(0,0,255))

        if self._window:
            debug_img = np.zeros(img.shape,img.dtype)

        x_shift = coarse_pupil_width*2 #just vor display
        color = zip(range(0,250,30),range(0,255,30)[::-1],range(230,250))
        split_contours = []
        for c in arprox_contours:
            curvature = GetAnglesPolyline(c)
            # print curvature
            # we split whenever there is a real kink (abs(curvature)<right angle) or a change in the genreal direction
            kink_idx = find_kink_and_dir_change(curvature,100)
            # kinks,k_index = convexity_defect(c,curvature)
            # print "kink_idx", kink_idx
            segs = split_at_corner_index(c,kink_idx)
            # print len(segs)
            # segs.sort(key=lambda e:-len(e))
            for s in segs:
                split_contours.append(s)
                if self._window:
                    c = color.pop(0)
                    color.append(c)
                    # if s.shape[0] >=5:
                    #     cv2.polylines(debug_img,[s],isClosed=False,color=c)
                    s = s.copy()
                    s[:,:,1] +=  coarse_pupil_width*2
                    cv2.polylines(debug_img,[s],isClosed=False,color=c)
                    s[:,:,0] += x_shift
                    x_shift += 5
                    cv2.polylines(debug_img,[s],isClosed=False,color=c)
        # return {'timestamp':frame.timestamp,'norm_pupil':None}

        #these segments may now be smaller, we need to get rid of those not long enough for ellipse fitting
        good_contours = [c for c in split_contours if c.shape[0]>=5]
        # cv2.polylines(img,good_contours,isClosed=False,color=(255,255,0))

        shape = edges.shape
        ellipses = ((cv2.fitEllipse(c),c) for c in good_contours)
        ellipses = ((e,c) for e,c in ellipses if (padding < e[0][1] < shape[0]-padding and padding< e[0][0] < shape[1]-padding)) # center is close to roi center
        ellipses = ((e,c) for e,c in ellipses if binary_img[e[0][1],e[0][0]]) # center is on a dark pixel
        ellipses = [(e,c) for e,c in ellipses if is_round(e,self.target_ratio)] # roundness test
        result = []
        for e,c in ellipses:
            size_dif = size_deviation(e,self.target_size.value)
            pupil_ellipse = {}
            pupil_ellipse['contour'] = c
            a,b = e[1][0]/2.,e[1][1]/2. # majar minor radii of candidate ellipse
            pupil_ellipse['circumference'] = np.pi*abs(3*(a+b)-np.sqrt(10*a*b+3*(a**2+b**2)))
            # pupil_ellipse['convex_hull'] = cv2.convexHull(pupil_ellipse['contour'])
            pupil_ellipse['contour_area'] = cv2.contourArea(cv2.convexHull(c))
            pupil_ellipse['ellipse_area'] = np.pi*a*b
            # print abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area'])
            if abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area']) <10:
                pupil_ellipse['goodness'] = abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area'])/10 #perfect match we'll take this one
            else:
                pupil_ellipse['goodness'] = size_dif
            if visualize:
                    pass
                    # cv2.drawContours(pupil_img,[cv2.convexHull(c)],-1,(size_dif,size_dif,255))
                    # cv2.drawContours(pupil_img,[c],-1,(size_dif,size_dif,255))
            pupil_ellipse['pupil_center'] = e[0] # compensate for roi offsets
            pupil_ellipse['center'] = u_roi.add_vector(p_roi.add_vector(e[0])) # compensate for roi offsets
            pupil_ellipse['angle'] = e[-1]
            pupil_ellipse['axes'] = e[1]
            pupil_ellipse['major'] = max(e[1])
            pupil_ellipse['minor'] = min(e[1])
            pupil_ellipse['ratio'] = pupil_ellipse['minor']/pupil_ellipse['major']
            pupil_ellipse['norm_pupil'] = normalize(pupil_ellipse['center'], (img.shape[1], img.shape[0]),flip_y=True )
            pupil_ellipse['timestamp'] = frame.timestamp
            result.append(pupil_ellipse)


        #### adding support
        if result:
            result.sort(key=lambda e: e['goodness'])
            # for now we assume that this contour is part of the pupil
            the_one = result[0]
            # (center, size, angle) = cv2.fitEllipse(the_one['contour'])
            # print "itself"
            distances =  dist_pts_ellipse(cv2.fitEllipse(the_one['contour']),the_one['contour'])
            # print np.average(distances)
            # print np.sum(distances)/float(distances.shape[0])
            # print "other"
            # if self._window:
                # cv2.polylines(debug_img,[result[-1]['contour']],isClosed=False,color=(255,255,255),thickness=3)
            with_another = np.concatenate((result[-1]['contour'],the_one['contour']))
            distances =  dist_pts_ellipse(cv2.fitEllipse(with_another),with_another)
            # if 1.5 > np.sum(distances)/float(distances.shape[0]):
            #     if self._window:
            #         cv2.polylines(debug_img,[result[-1]['contour']],isClosed=False,color=(255,255,255),thickness=3)

            perimeter_ratio =  cv2.arcLength(the_one["contour"],closed=False)/the_one['circumference']
            if perimeter_ratio > .9:
                size_thresh = 0
                eccentricity_thresh = 0
            elif perimeter_ratio > .5:
                size_thresh = the_one['major']/(5.)
                eccentricity_thresh = the_one['major']/2.
                self.should_sleep = True
            else:
                size_thresh = the_one['major']/(3.)
                eccentricity_thresh = the_one['major']/2.
                self.should_sleep = True
            if self._window:
                center = np.uint16(np.around(the_one['pupil_center']))
                cv2.circle(debug_img,tuple(center),int(eccentricity_thresh),(0,255,0),1)

            if self._window:
                cv2.polylines(debug_img,[the_one["contour"]],isClosed=False,color=(255,0,0),thickness=2)
                s = the_one["contour"].copy()
                s[:,:,0] +=coarse_pupil_width*2
                cv2.polylines(debug_img,[s],isClosed=False,color=(255,0,0),thickness=2)
            # but are there other segments that could be used for support?
            new_support = [the_one['contour'],]
            if len(result)>1:
                the_one = result[0]
                target_axes = the_one['axes'][0]
                # target_mean_curv = np.mean(curvature(the_one['contour'])
                for e in result:

                    # with_another = np.concatenate((e['contour'],the_one['contour']))
                    # with_another = np.concatenate([r['contour'] for r in result])
                    with_another = e['contour']
                    distances =  dist_pts_ellipse(cv2.fitEllipse(with_another),with_another)
                    # print np.std(distances)
                    thick =  int(np.std(distances))
                    if 1.5 > np.average(distances) or 1:
                        if self._window:
                            # print thick
                            thick = min(20,thick)
                            cv2.polylines(debug_img,[e['contour']],isClosed=False,color=(255,255,255),thickness=thick)

                    if self._window:
                        cv2.polylines(debug_img,[e["contour"]],isClosed=False,color=(0,100,100))
                    center_dist = cv2.arcLength(np.array([the_one["pupil_center"],e['pupil_center']],dtype=np.int32),closed=False)
                    size_dif = abs(the_one['major']-e['major'])

                    # #lets make sure the countour is not behind the_one/'s coutour
                    # center_point = np.uint16(np.around(the_one['pupil_center']))
                    # other_center_point = np.uint16(np.around(e['pupil_center']))

                    # mid_point =  the_one["contour"][the_one["contour"].shape[0]/2][0]
                    # other_mid_point =  e["contour"][e["contour"].shape[0]/2][0]

                    # #reflect around mid_point
                    # p = center_point - mid_point
                    # p = np.array((-p[1],-p[0]))
                    # mir_center_point = p + mid_point
                    # dist_mid = cv2.arcLength(np.array([mid_point,other_mid_point]),closed=False)
                    # dist_center = cv2.arcLength(np.array([center_point,other_mid_point]),closed=False)
                    # if self._window:
                    #     cv2.circle(debug_img,tuple(center_point),3,(0,255,0),2)
                    #     cv2.circle(debug_img,tuple(other_center_point),2,(0,0,255),1)
                    #     # cv2.circle(debug_img,tuple(mir_center_point),3,(0,255,0),2)
                    #     # cv2.circle(debug_img,tuple(mid_point),2,(0,255,0),1)
                    #     # cv2.circle(debug_img,tuple(other_mid_point),2,(0,0,255),1)
                    #     cv2.polylines(debug_img,[np.array([center_point,other_mid_point]),np.array([mid_point,other_mid_point])],isClosed=False,color=(0,255,0))


                    if center_dist < eccentricity_thresh:
                    # print dist_mid-dist_center
                    # if dist_mid > dist_center-20:

                        if  size_dif < size_thresh:


                            new_support.append(e["contour"])
                            if self._window:
                                cv2.polylines(debug_img,[s],isClosed=False,color=(255,0,0),thickness=1)
                                s = e["contour"].copy()
                                s[:,:,0] +=coarse_pupil_width*2
                                cv2.polylines(debug_img,[s],isClosed=False,color=(255,255,0),thickness=1)

                        else:
                            if self._window:
                                s = e["contour"].copy()
                                s[:,:,0] +=coarse_pupil_width*2
                                cv2.polylines(debug_img,[s],isClosed=False,color=(0,0,255),thickness=1)
                    else:
                        if self._window:
                            cv2.polylines(debug_img,[s],isClosed=False,color=(0,255,255),thickness=1)

                    # new_support = np.concatenate(new_support)

            self.goodness.value = the_one['goodness']

            ###here we should AND original mask, selected contours with 2px thinkness (and 2px fitted ellipse -is the last one a good idea??)
            support_mask = np.zeros(edges.shape,edges.dtype)
            cv2.polylines(support_mask,new_support,isClosed=False,color=(255,255,255),thickness=2)
            # #draw into the suport mast with thickness 2
            new_edges = cv2.min(edges, support_mask)
            new_contours = cv2.findNonZero(new_edges)
            if self._window:
                debug_img[0:support_mask.shape[0],0:support_mask.shape[1],2] = new_edges


            ###### do the ellipse fit and filter think again
            ellipses = ((cv2.fitEllipse(c),c) for c in [new_contours])
            ellipses = ((e,c) for e,c in ellipses if (padding < e[0][1] < shape[0]-padding and padding< e[0][0] < shape[1]-padding)) # center is close to roi center
            ellipses = ((e,c) for e,c in ellipses if binary_img[e[0][1],e[0][0]]) # center is on a dark pixel
            ellipses = [(size_deviation(e,self.target_size.value),e,c) for e,c in ellipses if is_round(e,self.target_ratio)] # roundness test
            for size_dif,e,c in ellipses:
                pupil_ellipse = {}
                pupil_ellipse['contour'] = c
                a,b = e[1][0]/2.,e[1][1]/2. # majar minor radii of candidate ellipse
                # pupil_ellipse['circumference'] = np.pi*abs(3*(a+b)-np.sqrt(10*a*b+3*(a**2+b**2)))
                # pupil_ellipse['convex_hull'] = cv2.convexHull(pupil_ellipse['contour'])
                pupil_ellipse['contour_area'] = cv2.contourArea(cv2.convexHull(c))
                pupil_ellipse['ellipse_area'] = np.pi*a*b
                # print abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area'])
                if abs(pupil_ellipse['contour_area']-pupil_ellipse['ellipse_area']) <10:
                    pupil_ellipse['goodness'] = 0 #perfect match we'll take this one
                else:
                    pupil_ellipse['goodness'] = size_dif
                if visualize:
                        pass
                        # cv2.drawContours(pupil_img,[cv2.convexHull(c)],-1,(size_dif,size_dif,255))
                        # cv2.drawContours(pupil_img,[c],-1,(size_dif,size_dif,255))
                pupil_ellipse['center'] = u_roi.add_vector(p_roi.add_vector(e[0])) # compensate for roi offsets
                pupil_ellipse['angle'] = e[-1]
                pupil_ellipse['axes'] = e[1]
                pupil_ellipse['major'] = max(e[1])
                pupil_ellipse['minor'] = min(e[1])
                pupil_ellipse['ratio'] = pupil_ellipse['minor']/pupil_ellipse['major']
                pupil_ellipse['norm_pupil'] = normalize(pupil_ellipse['center'], (img.shape[1], img.shape[0]),flip_y=True )
                pupil_ellipse['timestamp'] = frame.timestamp
                result = [pupil_ellipse,]
            # the_new_one = result[0]

            #done - if the new ellipse is good, we just overwrote the old result

        if self._window:
            self.gl_display_in_window(debug_img)
            if self.should_sleep:
                # sleep(3)
                self.should_sleep = False
        if result:
            # update the target size
            if result[0]['goodness'] >=3: # perfect match!
                self.target_size.value = result[0]['major']
            else:
                self.target_size.value  = self.target_size.value +  .2 * (result[0]['major']-self.target_size.value)
                result.sort(key=lambda e: abs(e['major']-self.target_size.value))
            if visualize:
                pass
            return result[0]

        else:
            self.goodness.value = 100
            no_result = {}
            no_result['timestamp'] = frame.timestamp
            no_result['norm_pupil'] = None
            return no_result