Esempio n. 1
0
    def onStrokeAdded(self, stroke):
        "Tags 1's and 0's as letters (TextAnnotation)"
        closedDistRatio = 0.22
        circularityThresh_0 = 0.80
        circularityThresh_1 = 0.20
        strokeLen = max(GeomUtils.strokeLength(stroke), 1)
        normDist = max(3, strokeLen / 5)
        head, tail = stroke.Points[0], stroke.Points[-1]

        endDist = GeomUtils.pointDistance(head.X, head.Y, tail.X, tail.Y)
        #If the endpoints are 1/thresh apart, actually close the thing
        isClosedShape = GeomUtils.pointDistanceSquared(head.X, head.Y, tail.X, tail.Y) \
                        < (strokeLen * closedDistRatio) ** 2

        if isClosedShape: #Close the shape back up
            s_norm = GeomUtils.strokeNormalizeSpacing( Stroke(stroke.Points + [stroke.Points[0]]) , normDist ) 
        else:
            s_norm = GeomUtils.strokeNormalizeSpacing( stroke , normDist ) 
        curvatures = GeomUtils.strokeGetPointsCurvature(s_norm)
        circularity = GeomUtils.strokeCircularity( s_norm ) 

        if isClosedShape and circularity > circularityThresh_0:
            height = stroke.BoundTopLeft.Y - stroke.BoundBottomRight.Y
            oAnnotation = TextAnnotation("0", height)
            l_logger.debug("Annotating %s with %s" % ( stroke, oAnnotation))
            BoardSingleton().AnnotateStrokes( [stroke],  oAnnotation)
            l_logger.debug(" Afterward: %s.annotations is %s" % ( stroke, stroke.Annotations))

        elif len(stroke.Points) >= 2 \
            and max(curvatures) < 0.5 \
            and circularity < circularityThresh_1:
                if stroke.Points[0].X < stroke.Points[-1].X + strokeLen / 2.0 \
                and stroke.Points[0].X > stroke.Points[-1].X - strokeLen / 2.0:
                    height = stroke.BoundTopLeft.Y - stroke.BoundBottomRight.Y
                    oneAnnotation = TextAnnotation("1", height)
                    l_logger.debug("Annotating %s with %s" % ( stroke, oneAnnotation.text))
                    BoardSingleton().AnnotateStrokes( [stroke],  oneAnnotation)
                    l_logger.debug(" Afterward: %s.annotations is %s" % ( stroke, stroke.Annotations))
                elif stroke.Points[0].Y < stroke.Points[-1].Y + strokeLen / 2.0 \
                and stroke.Points[0].Y > stroke.Points[-1].Y - strokeLen / 2.0:
                    width = stroke.BoundBottomRight.X - stroke.BoundTopLeft.X 
                    dashAnnotation = TextAnnotation("-", width * 1.5) #Treat the dash's (boosted) width as its scale 
                    l_logger.debug("Annotating %s with %s" % ( stroke, dashAnnotation.text))
                    BoardSingleton().AnnotateStrokes( [stroke],  dashAnnotation)
        else:
            if not isClosedShape:
                l_logger.debug("0: Not a closed shape")
            if not (circularity > circularityThresh_0):
                l_logger.debug("0: Not circular enough: %s" % (circularity))
            if not len(stroke.Points) >= 2:
                l_logger.debug("1: Not enough points")
            if not (circularity < circularityThresh_1):
                l_logger.debug("1: Too circular")
            if not (max(curvatures) < 0.5):
                l_logger.debug("1: Max curvature too big %s" % max(curvatures))
            if not ( stroke.Points[0].X < stroke.Points[-1].X + strokeLen / 3 \
               and   stroke.Points[0].X > stroke.Points[-1].X - strokeLen / 3):
                l_logger.debug("1: Not vertical enough: \nX1 %s, \nX2 %s, \nLen %s" % (stroke.Points[0].X, stroke.Points[-1].X, strokeLen))
Esempio n. 2
0
def classifyArrowhead(board, stroke):
    """Following the class 1 semantics, this classifies arrowheads"""
    if _isArrowHead(stroke, arrowHeadMatcher):
        #                * (tip-point)
        #              o   o
        #             o      o
        #            o         o
        #          o            o
        
        #Get the endpoints/tip point as max curvature
        strokeNorm = GeomUtils.strokeNormalizeSpacing(stroke, numpoints = 7)
        curvatures = GeomUtils.strokeGetPointsCurvature(strokeNorm)
        ptIdx = curvatures.index(max(curvatures))
        tip = strokeNorm.Points[ptIdx] #Middle is the point of max curvature
        annotation = ArrowHeadAnnotation(stroke.Points[0], tip, stroke.Points[-1])
        return annotation
    return None
Esempio n. 3
0
def _isArrowHead(stroke, matcher):
    
    numPts = 11
    sNorm = GeomUtils.strokeNormalizeSpacing(stroke, numpoints = numPts)
    curvatures = GeomUtils.strokeGetPointsCurvature(sNorm)
    maxCurv = max(curvatures)
    maxCurvIdx = curvatures.index(maxCurv)
    #Make sure the max curvature is roughly in the middle of the stroke before even bothering
    #   with more complicated checks
    if maxCurvIdx > (numPts / 5.0) and maxCurvIdx < ( 4 * numPts / 5.0): 
        strkLen = GeomUtils.strokeLength(stroke)
        arrowHeadStroke = GeomUtils.strokeNormalizeSpacing(Stroke([sNorm.Points[0], sNorm.Points[maxCurvIdx], sNorm.Points[-1]]), numpoints = strkLen) #What would the approximated arrowhead look like?
        origStroke = GeomUtils.strokeNormalizeSpacing(stroke, numpoints = strkLen)
        approxAcc = GeomUtils.strokeDTWDist(sNorm, arrowHeadStroke)
        #logger.debug("Stroke approximates arrowhead with %s accuracy" % (approxAcc))

        return approxAcc < 500000
        #_isArrowHead_Template(stroke, matcher) or _isArrowHead_Template(Stroke(list(reversed(stroke.Points))), matcher)
    
    return False
Esempio n. 4
0
def strokeCurvatureHistogram(stroke, norm_len=None, gran=10):
    # points = GeomUtils.strokeNormalizeSpacing( stroke, numpoints=50).Points
    rad2deg = 57.295
    if norm_len == None:
        norm_len = len(stroke.Points)

    norm_stroke = GeomUtils.strokeNormalizeSpacing(stroke, numpoints=norm_len)

    # find the first 90 degree turn in the stroke
    curvatures = GeomUtils.strokeGetPointsCurvature(norm_stroke)

    for idx, ori in enumerate(curvatures):
        print "%s:\t|" % (idx),
        quantity = ori * rad2deg
        while quantity > 0:
            quantity -= gran
            print "X",
        print "\t\t%s" % (ori * rad2deg)
    print "_______________________________"
    print "Max:%s, Avg%s" % (max(curvatures), sum(curvatures) / float(len(curvatures)))
    print "_______________________________"
    def tagBox(self, stroke):

        endPointDistPct = 0.10 #How close (as % of length) the points have to be to each other
        boxApproxThresh = 50000 #The DTW distance between the stroke and how it best fits a box
        stkLen = GeomUtils.strokeLength(stroke)
        ep1, ep2 = stroke.Points[0], stroke.Points[-1]
        epDistSqr = GeomUtils.pointDistanceSquared(ep1.X, ep1.Y, ep2.X, ep2.Y)
        if  epDistSqr > (endPointDistPct * stkLen) ** 2:
            print "Endpoints aren't close enough to be a box"
            return
        overshoot = max(1, len(stroke.Points)/10)
        norm_stroke = GeomUtils.strokeSmooth(GeomUtils.strokeNormalizeSpacing(Stroke(stroke.Points + stroke.Points[0:overshoot]), numpoints = 70))
        #D.strokeCurvatureHistogram(norm_stroke)
        curvatures = GeomUtils.strokeGetPointsCurvature(norm_stroke)
        corners = set([])
        curvatures_cpy = list(curvatures)
        while len(corners) < 4:
            crnr_idx = curvatures_cpy.index(max(curvatures_cpy))
            crnr = curvatures_cpy[crnr_idx] * 57.295
            for nBor in range(crnr_idx -2, crnr_idx + 3):
                if nBor < len(curvatures_cpy) and nBor > 0:
                    curvatures_cpy[nBor] = 0
            if crnr > 0: #30 and crnr < 150:
                #Have a curvature, and we haven't already classified its immed neighbors as a corner
                corners.add(crnr_idx)
            else:
                break
        if len(corners) != 4:
            return
        else:
            c_list = [norm_stroke.Points[c_idx] for c_idx in sorted(list(corners))]
            cornerStroke = Stroke(c_list + c_list[:2])
            boxStroke = GeomUtils.strokeNormalizeSpacing(Stroke(c_list + [c_list[0]]))
            origStroke = GeomUtils.strokeNormalizeSpacing(Stroke(stroke.Points + [stroke.Points[0]]))
            approxAcc = GeomUtils.strokeDTWDist(boxStroke, origStroke)
            print "Box approximates original with %s accuracy" % (approxAcc)
            if approxAcc < boxApproxThresh:
                self.getBoard().AnnotateStrokes([stroke], BoxAnnotation(c_list))
Esempio n. 6
0
    def onStrokeAdded( self, stroke ):
        "Watches for Strokes that look like an arrow to Annotate"
        smoothedStroke = GeomUtils.strokeSmooth(stroke)
        ep1 = stroke.Points[0]
        ep2 = stroke.Points[-1]
        #ep1 = smoothedStroke.Points[0]
        #ep2 = smoothedStroke.Points[-1]
        isArrowHead = False
        #GeomUtils.ellipseAxisRatio(stroke)


        #Match single-stroke arrows
        #DISABLED
        logger.debug("**Warning: Single-stroke arrows disabled**")
        tip, tail = None, None
        tip, tail = _isSingleStrokeArrow(smoothedStroke)
        #if tip is None or tail is None:
            #revpts = list(smoothedStroke.Points)
            #revpts.reverse()
            #tip, tail = _isSingleStrokeArrow(Stroke(revpts))
        
        if  tip is not None and tail is not None:
            isArrowHead = False
            anno = ArrowAnnotation( tip, tail, headstroke= stroke, tailstroke = stroke )
            self.getBoard().AnnotateStrokes( [stroke],  anno)
        #/DISABLED
        else:
            if _isArrowHead(smoothedStroke, self.arrowHeadMatcher):
                logger.debug(" ARROWHEAD")
                #head = smoothedStroke
                head = stroke
                isArrowHead = True

                #                * (tip-point)
                #              o   o
                #             o      o
                #            o         o
                #          o            o
                
                #Get the endpoints/tip point as max curvature
                strokeNorm = GeomUtils.strokeNormalizeSpacing(smoothedStroke, numpoints = 7)
                curvatures = GeomUtils.strokeGetPointsCurvature(strokeNorm)
                ptIdx = curvatures.index(max(curvatures))
                tip = strokeNorm.Points[ptIdx] #Middle is the point of max curvature

                #Match it to any tails we have 
                matchedTails = self._matchHeadtoTail(head = stroke, point = tip)
                for headpoint, tail in matchedTails:
                    #Orient the tail correctly
                    if tail.Points[0] == headpoint:
                        endpoint = tail.Points[-1]
                        direction = 'head2tail'
                    elif tail.Points[-1] == headpoint:
                        endpoint = tail.Points[0]
                        direction = 'tail2head'

                    logger.debug("Stroke is head of arrow, drawn %s" % (direction))
                    anno = ArrowAnnotation(tip, endpoint, headstroke = stroke, tailstroke = tail, direction = direction)
                    self.getBoard().AnnotateStrokes([head, tail],anno)
        
        #Match it like a tail even if we think it's an arrowhead. Oh ambiguity!
        matchedHeads = self._matchHeadtoTail(tail = stroke, point = ep1)
        tail = stroke
        for tip, head in matchedHeads:
            logger.debug("Stroke is tail of arrow, drawn head2tail")
            anno = ArrowAnnotation(tip, ep2, headstroke = head, tailstroke = tail, direction='head2tail') #Arrow is from the back endpoint to the tip of the arrowhead
            self.getBoard().AnnotateStrokes([head, tail],anno)
            
        matchedHeads = self._matchHeadtoTail(tail = stroke, point = ep2)
        for tip, head in matchedHeads:
            logger.debug("Stroke is tail of arrow, drawn tail2head")
            anno = ArrowAnnotation(tip, ep1, headstroke = head, tailstroke =tail, direction='tail2head')
            self.getBoard().AnnotateStrokes([head, tail],anno)
        
        #Add this stroke to the pool for future evaluation
        sNorm = GeomUtils.strokeNormalizeSpacing(stroke, numpoints = max(GeomUtils.strokeLength(stroke) / 3, 1))
        self._endpoints.append( (ep1, stroke, sNorm) )
        self._endpoints.append( (ep2, stroke, sNorm) )
        if isArrowHead:
            self._arrowHeads.append( (tip, stroke, sNorm) )
Esempio n. 7
0
def _scoreStrokesForLetter(strokelist, letter):
    """Get the confidence score for a group of strokes matching a letter, normalized [0.0-1.0]"""
    retConfidence = 0.0
    #Recognition thresholds
    closedDistRatio = 0.22
    circularityThresh_0 = 0.80
    circularityThresh_1 = 0.20
    maxStraightCurvature = 0.6
    strokesBB = GeomUtils.strokelistBoundingBox(strokelist)

    if len(strokelist) == 0:
        return 0.0

    #The case of a single point
    if strokesBB[0].X == strokesBB[1].X and strokesBB[0].Y == strokesBB[1].Y:
        return 0.0

    #Recognize a zero
    if letter.upper() == "0":
        stroke = strokelist[0]
        strokeLen = max(GeomUtils.strokeLength(stroke), 1)
        normDist = max(3, strokeLen / 5) #granularity of point spacing -- at least 3
        head, tail = stroke.Points[0], stroke.Points[-1]

        endDist = GeomUtils.pointDistance(head.X, head.Y, tail.X, tail.Y)
        #If the endpoints are 1/thresh apart, actually close the thing
        isClosedShape = GeomUtils.pointDistanceSquared(head.X, head.Y, tail.X, tail.Y) \
                        < ( (strokeLen * closedDistRatio) ** 2 )

        if isClosedShape: #Close the shape back up
            s_norm = GeomUtils.strokeNormalizeSpacing( Stroke(stroke.Points + [stroke.Points[0]]) , normDist ) 
        else:
            s_norm = GeomUtils.strokeNormalizeSpacing( stroke , normDist ) 
        #curvatures = GeomUtils.strokeGetPointsCurvature(s_norm)
        circularity = GeomUtils.strokeCircularity( s_norm ) 

        if isClosedShape:
            retConfidence += 0.5
        if circularity > circularityThresh_0:
            retConfidence += 0.5
        return retConfidence
    #Recognize a one
    elif letter.upper() == "1":
        stroke = strokelist[0]
        strokeLen = max(GeomUtils.strokeLength(stroke), 1)
        normDist = max(3, strokeLen / 5) #granularity of point spacing -- at least 3
        s_norm = GeomUtils.strokeNormalizeSpacing( stroke , normDist ) 

        circularity = GeomUtils.strokeCircularity( s_norm ) 
        curvatures = GeomUtils.strokeGetPointsCurvature(s_norm)
        if max(curvatures) < maxStraightCurvature:
            retConfidence += 0.30
        if circularity < circularityThresh_1:
            retConfidence += 0.5
            if stroke.Points[0].X < stroke.Points[-1].X + strokeLen / 2.0 \
            and stroke.Points[0].X > stroke.Points[-1].X - strokeLen / 2.0:
                retConfidence += 0.2
        return retConfidence
    #Recognize a dash
    elif letter.upper() == "-":
        stroke = strokelist[0]
        strokeLen = max(GeomUtils.strokeLength(stroke), 1)
        normDist = max(3, strokeLen / 5) #granularity of point spacing -- at least 3
        s_norm = GeomUtils.strokeNormalizeSpacing( stroke , normDist ) 

        circularity = GeomUtils.strokeCircularity( s_norm ) 
        curvatures = GeomUtils.strokeGetPointsCurvature(s_norm)
        if max(curvatures) < maxStraightCurvature:
            retConfidence += 0.30
        if circularity < circularityThresh_1:
            retConfidence += 0.5
            if stroke.Points[0].Y < stroke.Points[-1].Y + strokeLen / 2.0 \
            and stroke.Points[0].Y > stroke.Points[-1].Y - strokeLen / 2.0:
                retConfidence += 0.2
        return retConfidence
    else:
        return 0.0