예제 #1
0
    def _matchHeadtoTail(self, head = None, tail = None, point = None):
        """Input head stroke or tail stroke. If head is specified, match it to a tail. If tail is specified, match it to a head.
           Parameter 'point' should be the tip if head is specified, the end-point if tail is specified.
           Returns a list of tuples: (tip, head_stroke) if tail is specified, (endpoint, tail_stroke) if head is specified."""
        retlist = []
        if point is None:
            return retlist

            
        if head is not None and tail is None: #Head is specified, find the tail
            tip = point
            ep1, ep2 = head.Points[0], head.Points[-1]
            headBreadth = GeomUtils.pointDistance(ep1.X, ep1.Y, ep2.X, ep2.Y)
            for endpoint, tailStroke in self._endpoints:
                if GeomUtils.strokeLength(head) < GeomUtils.strokeLength(tailStroke) \
                and _isPointWithHead(endpoint, head, tip): #Make sure the proportions aren't totally off
                    logger.debug("Head stroke has a tail close and within cone")
                    pointingLength = len(tailStroke.Points) / 5
                    #headToTail
                    if endpoint == tailStroke.Points[0]:
                        linept1, linept2 = tailStroke.Points[pointingLength], endpoint
                    elif endpoint== tailStroke.Points[-1]:
                        linept1, linept2 = tailStroke.Points[-pointingLength], endpoint
                    pointsTo = GeomUtils.linePointsTowards(linept1, linept2, tip, headBreadth)
                    if pointsTo:
                        retlist.append( (endpoint, tailStroke) )

        elif tail is not None and head is None: #Find the head
            endpoint = point
            pointingLength = len(tail.Points) / 5
            #headToTail
            if endpoint == tail.Points[0]:
                linept1, linept2 = tail.Points[pointingLength], endpoint
            elif endpoint== tail.Points[-1]:
                linept1, linept2 = tail.Points[-pointingLength], endpoint

            for tip, headStroke in self._arrowHeads:
                ep1, ep2 = headStroke.Points[0], headStroke.Points[-1]
                headBreadth = GeomUtils.pointDistance(ep1.X, ep1.Y, ep2.X, ep2.Y)
                if GeomUtils.strokeLength(headStroke) < GeomUtils.strokeLength(tail) \
                and _isPointWithHead(endpoint, headStroke, tip):
                    logger.debug("Tail stroke is close and within cone of an arrowhead")
                    pointsTo = GeomUtils.linePointsTowards(linept1, linept2, tip, headBreadth)
                    if pointsTo:
                        retlist.append( (tip, headStroke) )
        return retlist
예제 #2
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))
예제 #3
0
    def onAnnotationAdded( self, strokes, annotation ):
        "Checks to see if an equals sign has been added"
        
        # Find the midpoints         
        ul,br = GeomUtils.strokelistBoundingBox( strokes )
        midpointY = (ul.Y + br.Y) / 2
        midpointX = (ul.X + br.X) / 2
        strokeLen = GeomUtils.strokeLength(strokes[0])

        for a in self.possibleAnnotations:
            s = a.Strokes[0]
            prevStrokeLen = GeomUtils.strokeLength(s)

            # test the the two segments are of similar length
            lengthRange = 0.4
            if prevStrokeLen * (1-lengthRange) < strokeLen < prevStrokeLen * (1+lengthRange):
                pass # were the right length
            else: # not the right length, so lets start again
                continue

            ul,br = GeomUtils.strokelistBoundingBox( [s] )
            prevMidpointY = (ul.Y + br.Y) / 2
            prevMidpointX = (ul.X + br.X) / 2

            # Test that the two segments are close enough horizontally
            if GeomUtils.pointDistance(midpointX, 0, prevMidpointX, 0) < prevStrokeLen * 0.4:
                pass # there are close enough horizontally
            else: # we start again
                continue

            # Test that the two segments are close enough vertically
            if GeomUtils.pointDistance(0,midpointY, 0, prevMidpointY) < prevStrokeLen * 0.5:
                pass # there are close enough vertically
            else: # we start again
                continue

            # we found a match
            self.possibleAnnotations.remove(a)
            self.getBoard().AnnotateStrokes( strokes + [s],  EqualsAnnotation(1))
            return


        # no match was found, add to the list of possible
        self.possibleAnnotations.append(annotation)
        return
def linesPointAtEachother(linepair1, linepair2):
    ep1 = linepair1[1]
    ep2 = linepair2[1]
    pointsToRadius = max(15, 0.26 * GeomUtils.pointDistance(ep1.X, ep1.Y, ep2.X, ep2.Y) ) #Span out the valid radius at about 30 degrees
    l1_to_l2 = GeomUtils.linePointsTowards(linepair1[0], linepair1[1], linepair2[1], pointsToRadius)
    l2_to_l1 = GeomUtils.linePointsTowards(linepair2[0], linepair2[1], linepair1[1], pointsToRadius)
    ss_logger.debug("l1 points to l2: %s" % (l1_to_l2))
    ss_logger.debug("l2 points to l1: %s" % (l2_to_l1))
    return (l1_to_l2 and l2_to_l1)
    def linkStrokesTogether(self):
        def allPairs(xlist, ylist):
            for x in xlist:
                for y in ylist:
                    yield ( x, y )
        for w1, w1Dict in self.wallInfo.items():
            ep11, ep12 = w1.Points[0], w1.Points[-1]
            self.wallInfo[w1]['matches'] = {}

            for w2 , w2Dict in self.wallInfo.items():
                if w2 == w1 or w2 in self.wallInfo[w1]['matches']:
                    continue

                ep21, ep22 = w2.Points[0], w2.Points[-1]
                bestMatch = {'stroke': None, 'dist': None, 'from_pt': None, 'to_pt': None}
                for pair in allPairs([ep11, ep12], [ep21, ep22]):
                    dist = GeomUtils.pointDistance(pair[0].X, pair[0].Y, pair[1].X, pair[1].Y)
                    if bestMatch['dist'] is None or bestMatch['dist'] > dist:
                        bestMatch = {'dist': dist, 'from_pt': pair[0], 'to_pt': pair[1]}

                self.wallInfo[w1]['matches'][w2] = bestMatch
                self.wallInfo[w2]['matches'][w1] = dict(bestMatch)
            #endfor w2, w2Dict
        #endfor w1, w1Dict

        #Link together partial walls
        wallStack = list(self.wallInfo.keys())
        wallStrokes = {}
        curWall = None
        start = None
        while len(wallStack) > 0:
            if curWall == None:
                curWall = wallStack.pop()
            bestMatch = None
            bestDist = None
            for matchStk, matchDict in self.wallInfo[curWall]['matches'].items():
                if matchStk in wallStack and (bestMatch == None or matchDict['dist'] < bestDist):
                    bestDist = matchDict['dist']
                    bestMatch = matchStk

            if bestMatch != None:
                curWall = bestMatch
                wallStack.remove(bestMatch)
            


        for stk, stkDict in self.wallInfo.items():
            rtm_logger.debug( "%s: " % (stk))
            for match, mdict in stkDict['matches'].items():
                rtm_logger.debug( "    %s: %s " % (match, mdict['dist']))
예제 #6
0
    def _matchHeadtoTail(self, head = None, tail = None, point = None):
        """Input head stroke or tail stroke. If head is specified, match it to a tail. If tail is specified, match it to a head.
           Parameter 'point' should be the tip if head is specified, the end-point if tail is specified.
           Returns a list of tuples: (tip, head_stroke) if tail is specified, (endpoint, tail_stroke) if head is specified."""
        retlist = []
        if point is None:
            return retlist

            
        headStroke = head
        tailStroke = tail

        if headStroke is not None and tail is None: #Head is specified, find the tail
            tip = point
            ep1, ep2 = headStroke.Points[0], headStroke.Points[-1]
            headBreadth = GeomUtils.pointDistance(ep1.X, ep1.Y, ep2.X, ep2.Y)
            for endpoint, origStk, tailStroke in self._endpoints:
                pointingLength = len(tailStroke.Points) / 10
                if endpoint == tailStroke.Points[0]: #Treat as drawn head2tail
                    tailpoints = tailStroke.Points
                    linept1, linept2 = tailStroke.Points[pointingLength], endpoint
                elif endpoint== tailStroke.Points[-1]: #Treat as drawn tail2head
                    tailpoints = list(reversed(tailStroke.Points))
                    linept1, linept2 = tailStroke.Points[-(pointingLength+1)], endpoint

                headLen = GeomUtils.strokeLength(headStroke) 
                tailLen = GeomUtils.strokeLength(tailStroke)
                pointWithHead = _isPointWithHead(tailpoints, headStroke, tip)
                if headLen < tailLen * 2 \
                and pointWithHead:
                    logger.debug("Head stroke has a tail close and within cone")
                    #headToTail
                    pointsTo = GeomUtils.linePointsTowards(linept1, linept2, tip, headBreadth)
                    if pointsTo:
                        logger.debug("  Tail points to head")
                        retlist.append( (endpoint, origStk) )
                    else:
                        logger.debug("  Tail does NOT point to head")
                else:
                    if headLen < tailLen * 2:
                        logger.debug("  Head stroke scale is okay for this arrowhead")
                    else:
                        logger.debug("  Head stroke scale is BAD for this arrowhead")
                        logger.debug("  Head Len: %s, tail Len: %s" % (headLen, tailLen))
                    if not pointWithHead:
                        logger.debug("  Head stroke is NOT close or within cone of an arrowhead\n")
                    else:
                        logger.debug("  Head stroke is close and within cone of an arrowhead\n")

        elif tailStroke is not None and headStroke is None: #Find the head
            endpoint = point
            pointingLength = len(tailStroke.Points) / 10

            if endpoint == tailStroke.Points[0]: #Treat as drawn head2tail
                tailpoints = tailStroke.Points
                linept1, linept2 = tailStroke.Points[pointingLength], endpoint
            elif endpoint== tailStroke.Points[-1]: #Treat as drawn tail2head
                tailpoints = list(reversed(tailStroke.Points))
                linept1, linept2 = tailStroke.Points[-pointingLength], endpoint

            for tip, origStk, headStroke in self._arrowHeads:
                ep1, ep2 = headStroke.Points[0], headStroke.Points[-1]
                headBreadth = GeomUtils.pointDistance(ep1.X, ep1.Y, ep2.X, ep2.Y)
                headLen = GeomUtils.strokeLength(headStroke) 
                tailLen = GeomUtils.strokeLength(tailStroke)
                pointWithHead = _isPointWithHead(tailpoints, headStroke, tip)
                if headLen < tailLen * 2\
                and pointWithHead:
                    logger.debug("Tail stroke is close and within cone of an arrowhead")
                    pointsTo = GeomUtils.linePointsTowards(linept1, linept2, tip, headBreadth)
                    if pointsTo:
                        logger.debug("  Tail points to head")
                        retlist.append( (tip, origStk) )
                    else:
                        logger.debug("  Tail does NOT point to head")
                else:
                    if headLen < tailLen * 2:
                        logger.debug("  Tail stroke scale is okay for this arrowhead")
                    else:
                        logger.debug("  Tail stroke scale is BAD for this arrowhead")
                        logger.debug("  Head Len: %s, tail Len: %s" % (headLen, tailLen))

                    if not pointWithHead:
                        logger.debug("  Tail stroke is NOT close or within cone of an arrowhead\n")
                    else:
                        logger.debug("  Tail stroke is close and within cone of an arrowhead\n")
                        
        return retlist
예제 #7
0
    def onAnnotationAdded( self, strokes, annotation ):
        "Checks to see if an multiply sign has been added"

        # Find the midpoints         
        ul,br = GeomUtils.strokelistBoundingBox( strokes )
        midpointY = (ul.Y + br.Y) / 2
        midpointX = (ul.X + br.X) / 2
        strokeLen = GeomUtils.strokeLength(strokes[0])

        if annotation.isType(DirectedLine.BT_LineAnnotation):
            possibleAnnotations = self.possibleAnnotations_TB
            otherPossibleAnnotations = self.possibleAnnotations_BT
        elif annotation.isType(DirectedLine.TB_LineAnnotation):
            possibleAnnotations = self.possibleAnnotations_BT
            otherPossibleAnnotations = self.possibleAnnotations_TB

        print len(possibleAnnotations)

        for a in possibleAnnotations:
            s = a.Strokes[0]
            prevStrokeLen = GeomUtils.strokeLength(s)

            # test the the two segments are of similar length
            lengthRange = 0.4
            if prevStrokeLen * (1-lengthRange) < strokeLen < prevStrokeLen * (1+lengthRange):
                pass # were the right length
            else: # not the right length, so lets start again
                continue

            ul,br = GeomUtils.strokelistBoundingBox( [s] )
            prevMidpointY = (ul.Y + br.Y) / 2
            prevMidpointX = (ul.X + br.X) / 2

            # Test that the two segments are close enough horizontally
            if GeomUtils.pointDistance(midpointX, 0, prevMidpointX, 0) < prevStrokeLen * 0.25:
                pass # there are close enough horizontally
            else: # we start again
                continue

            # Test that the two segments are close enough vertically
            if GeomUtils.pointDistance(0,midpointY, 0, prevMidpointY) < prevStrokeLen * 0.25:
                pass # there are close enough vertically
            else: # we start again
                continue

            # we found a match
            possibleAnnotations.remove(a)

            annos = s.findAnnotations()

            annos += self.getBoard().FindAnnotations(strokelist = strokes)

            for i in annos:
                self.getBoard().RemoveAnnotation(i)

            ul,br = GeomUtils.strokelistBoundingBox( strokes + [s] )
            height = ul.Y - br.Y
            self.getBoard().AnnotateStrokes( strokes + [s],  MultAnnotation(height))
            return


        # no match was found, add to the list of possible
        otherPossibleAnnotations.append(annotation)
        return
예제 #8
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