Ejemplo n.º 1
0
    def real_find_front_landmarks(self, page):
        a = ask(
            """Enter the x coordinate of an upper left landmark;
if your template is not offset or tilted, you could use 150.  If there's no
such landmark, enter -1:
""", int, -1)
        b = ask(
            """Now enter the corresponding y coordinate;
if your template is not offset or tilted, you could use 75.  If there's no
such landmark, enter -1:
""", int, -1)
        c = ask(
            """Enter the x coordinate of an upper RIGHT landmark;
if your template is not offset or tilted, you could use 2050.  If there's no
such landmark, enter -1:
""", int, -1)
        d = ask(
            """Enter the corresponding y coordinate;
if your template is not offset or tilted, you could use 75.  If there's no
such landmark, enter -1:
""", int, -1)
        if -1 in (a, b, c, d):
            raise Ballot.BallotException("Could not find landmarks")

        # flunk ballots with more than
        # allowed_corner_black_inches of black in corner
        # to avoid dealing with severely skewed ballots

        errmsg = "Dark %s corner on %s"
        testlen = self.allowed_corner_black
        xs, ys = page.image.size

        #boxes to test
        ul = (0, 0, testlen, testlen)

        ur = (xs - testlen, 0, xs - 1, testlen)

        lr = (xs - testlen, ys - testlen, xs - 1, ys - 1)

        ll = (0, ys - testlen, testlen, ys - 1)

        for area, corner in ((ul, "upper left"), (ur, "upper right"),
                             (lr, "lower right"), (ll, "lower left")):
            avg_darkness = ask("What's the intensity at the " + corner,
                               IntIn(0, 255))
            if int(avg_darkness) < 16:
                raise Ballot.BallotException(errmsg % (corner, page.filename))

        xoff = a
        yoff = b

        shortdiff = d - b
        longdiff = c - a
        rot = -shortdiff / float(longdiff)
        if abs(rot) > const.allowed_tangent:
            raise Ballot.BallotException(
                "Tilt %f of %s exceeds %f" %
                (rot, page.filename, const.allowed_tangent))

        return rot, xoff, yoff, longdiff
Ejemplo n.º 2
0
def get_offsets_and_tangent_from_blocks(im, dpi, dash_sep_in_pixels):
    """ locate marks at top left, right of image
    
    return the x,y coordinates of the large timing marks
    at upper left and upper right, 
    as well as the tangent of the tilt angle between them.
    """
    found_left = False
    found_right = False
    iround = lambda x: int(round(x))
    adj = lambda f: int(round(const.dpi * f))
    croptop = adj(block_zone_upper_y)
    cropbottom = croptop + dpi
    leftstart = 0
    leftend = adj(block_zone_width_to_crop)
    rightstart = im.size[0] - adj(block_zone_width_to_crop)
    rightend = im.size[0] - 1
    vertical_dist_top_dashes = dash_sep_in_pixels
    vertical_dist_block_dashes = iround(dpi * .17)

    leftcrop = im.crop((leftstart, croptop, leftend, cropbottom))
    rightcrop = im.crop((rightstart, croptop, rightend, cropbottom))

    # look for black white black bar pattern and return y of pattern start
    scanx = adj(0.1)
    leftstarty = find_y_of_landmark_pattern(leftcrop, dpi, scanx, scanx * 2)
    if leftstarty == -1:
        raise Ballot.BallotException("Failed to find left landmark.")

    rightstarty = find_y_of_landmark_pattern(rightcrop, dpi, scanx, scanx * 2)
    if rightstarty == -1:
        raise Ballot.BallotException("Failed to find right landmark.")

    leftdashcentery = leftstarty + adj(v_offset_to_dash_center)
    rightdashcentery = rightstarty + adj(v_offset_to_dash_center)

    # now go leftward from scanx
    # along the center of the top dash until white or off edge
    leftstartx = 0
    scanx = adj(0.2)
    for n in range(scanx):
        pix = leftcrop.getpixel(((scanx - n), leftdashcentery))
        if pix[0] > 128:
            leftstartx = scanx - n
            break

    rightstartx = 0
    for n in range(scanx):
        pix = rightcrop.getpixel(((scanx - n), rightdashcentery))
        if pix[0] > 128:
            rightstartx = scanx - n
            break

    return (leftstartx, leftstarty + croptop, rightstart + rightstartx,
            rightstarty + croptop, (rightstarty - leftstarty) /
            (im.size[0] - adj(block_zone_width_to_crop)))
Ejemplo n.º 3
0
    def find_landmarks(self, page):
        """ retrieve landmarks for a sequoia ballot, set tang, xref, yref

        Landmarks for the sequoia ballot are the "dash blocks" at the
        upper left and upper right. These are retrieved by calling
        get_offsets_and_tangent_from_blocks.

        """
        iround = lambda x: int(round(x))
        adj = lambda f: int(round(const.dpi * f))
        dash_sep_in_pixels = adj(0.17)
        (a, b, c, d,
         tilt) = get_offsets_and_tangent_from_blocks(page.image, const.dpi,
                                                     dash_sep_in_pixels)

        # flunk ballots with more than
        # allowed_corner_black_inches of black in corner
        # to avoid dealing with severely skewed ballots

        errmsg = "Dark %s corner on %s"
        testlen = self.allowed_corner_black
        xs, ys = page.image.size

        #boxes to test
        ul = (0, 0, testlen, testlen)

        ur = (xs - testlen, 0, xs - 1, testlen)

        lr = (xs - testlen, ys - testlen, xs - 1, ys - 1)

        ll = (0, ys - testlen, testlen, ys - 1)

        for area, corner in ((ul, "upper left"), (ur, "upper right"),
                             (lr, "lower right"), (ll, "lower left")):
            cropped = page.image.crop(area)
            area_stat = ImageStat.Stat(cropped)
            if area_stat.mean[0] < 16:
                raise Ballot.BallotException(errmsg % (corner, page.filename))

        xoff = a
        yoff = b

        shortdiff = d - b
        longdiff = c - a
        rot = -shortdiff / float(longdiff)
        if abs(rot) > const.allowed_tangent:
            raise Ballot.BallotException(
                "Tilt %f of %s exceeds %f" %
                (rot, page.filename, const.allowed_tangent))
        self.log.debug("find landmarks returning %f,%d,%d, %d" %
                       (rot, xoff, yoff, longdiff))
        # Ballot.py defines a distance y2y to be used for scaling between
        # template and ballot images.  Because both our landmarks are at the
        # top, we will consistently use longdiff for scaling in sequoia.
        return rot, xoff, yoff, longdiff
Ejemplo n.º 4
0
    def get_layout_code(self, page):
        """ Determine the layout code(s) from the ulc barcode(s) """
        # barcode zones to search are from 1/3" to 1/6" to left of ulc
        # and from 1/8" above ulc down to 2 5/8" below ulc.

        qtr_inch, sixth_inch, eighth_inch = adj(.22), adj(.1667), adj(.125)
        third_inch = adj(0.33)
        point2inch = adj(0.2)
        point02inch = adj(0.02)
        # don't pass negative x,y into getbarcode
        if page.xoff < point2inch:
            raise Ballot.BallotException("bad xref %d" % (page.xoff, ))
        if page.yoff < eighth_inch:
            raise Ballot.BallotException("bad yref")
        # pass image, x,y,w,h

        if page.xoff >= third_inch:
            startx = max(0, page.xoff - third_inch)
            widthx = sixth_inch
        elif page.xoff >= point2inch:
            startx = max(0, page.xoff - point2inch)
            widthx = 2
        try:
            barcode = hart_barcode(
                page.image,
                startx,
                page.yoff - eighth_inch,
                widthx,
                eighth_inch + int(round(
                    (7. * const.dpi) / 3.))  # bar code 2 1/3"
            )
        except BarcodeException as e:
            self.log.info("%s %s" % (page.filename, e))
            barcode = "NOGOOD"
        if not good_barcode(barcode):
            # try getting bar code from ocr of region beneath
            self.log.debug("Barcode no good, trying to get barcode via OCR")
            zone = page.image.crop(
                (max(0, page.xoff - adj(.35)), page.yoff + adj(2.5),
                 max(1, page.xoff - adj(.1)), page.yoff + adj(4.3)))
            zone = zone.rotate(-90)  #make it left to right
            barcode = self.extensions.ocr_engine(zone)

            #remove OCR errors specific to text guranteed numeric
            for bad, good in (("\n", ""), (" ", ""), ("O", "0"), ("o", "0"),
                              ("l", "1"), ("I", "1"), ("B", "8"), ("Z", "2"),
                              ("]", "1"), ("[", "1"), (".", ""), (",", ""),
                              ("/", "1")):
                barcode = barcode.replace(bad, good)

            if not good_barcode(barcode):
                raise Ballot.BallotException("bad bar code")

        return barcode
Ejemplo n.º 5
0
    def get_layout_code(self, page):
        print "In get_layout_code"
        print """ Determine the layout code by getting it from the user

The layout code must be determined on a vendor specific basis;
it is usually a series of dashes or a bar code at a particular
location on the ballot.

Layout codes may appear on both sides of the ballot, or only
on the fronts.  If the codes appear only on the front, you can
file the back layout under a layout code generated from the
front's layout code.  """
        print "In get_layout_code. Note that DuplexBallot only calls this for"
        print "the first in a pair of images."
        print "Note that if there's no barcode that DuplexBallot will attempt"
        print "to swap the front and back pages and you will be asked again"
        print "and if you still say there is no barcode, an error will be"
        print "raised"
        barcode = ask(
            """Enter a number as the simulated barcode,
        or -1 if your ballot is missing a barcode""", IntIn(0, 100), -1)
        # If this is a back page, need different arguments
        # to timing marks call; so have failure on front test
        # trigger a back page test
        if barcode == -1:
            raise Ballot.BallotException(
                "No barcode on front page of duplex ballot")
        page.barcode = barcode
        return barcode
Ejemplo n.º 6
0
    def get_layout_code(self, page):
        """ Determine the layout code by getting it from the user

        The layout code must be determined on a vendor specific basis;
        it is usually a series of dashes or a bar code at a particular
        location on the ballot.

        Layout codes may appear on both sides of the ballot, or only
        on the fronts.  If the codes appear only on the front, you can
        file the back layout under a layout code generated from the
        front's layout code.
        """
        print "In get_layout_code"
        barcode = ask("""Enter a number as the simulated barcode,
        or -1 if your ballot is missing a barcode""", IntIn(0, 100), -1)
        # If this is a back page, need different arguments
        # to timing marks call; so have failure on front test
        # trigger a back page test
        if barcode == -1:
            raise Ballot.BallotException("No barcode found")
        page.barcode = barcode
        return barcode
Ejemplo n.º 7
0
    def find_landmarks(self, page):
        """ retrieve landmarks for Hart images, set tang, xref, yref

        Landmarks for the Hart Ballot will be the ulc, urc, lrc, llc 
        (x,y) pairs marking the four corners of the main surrounding box."""
        TOP = True
        BOT = False
        LEFT = True
        RIGHT = False
        log = logging.getLogger('')
        #log.info("Entering hart_ballot find_landmarks.")
        #tiltinfo, from upperleft clockwise:
        #[(x,y),(x,y),(x,y),(x,y)] or None
        tiltinfo = []
        left_starting_x_offset = 2 * const.dpi
        right_starting_x_offset = page.image.size[0] - int(2.5 * const.dpi)
        if right_starting_x_offset <= int(const.dpi * .34):
            raise Ballot.BallotException(
                "Image width of %d pixels at %d dpi unexpectedly narrow." %
                (page.image.size[0], const.dpi))

        hline = scan_strips_for_horiz_line_y(page.image, const.dpi,
                                             left_starting_x_offset,
                                             const.dpi / 2, const.dpi / 2, TOP)
        tiltinfo.append(
            follow_hline_to_corner(page.image, const.dpi,
                                   left_starting_x_offset, hline, LEFT))
        hline = scan_strips_for_horiz_line_y(page.image, const.dpi,
                                             right_starting_x_offset,
                                             const.dpi / 2, const.dpi / 2, TOP)
        tiltinfo.append(
            follow_hline_to_corner(page.image, const.dpi,
                                   right_starting_x_offset, hline, RIGHT))
        hline = scan_strips_for_horiz_line_y(page.image, const.dpi,
                                             right_starting_x_offset,
                                             const.dpi / 2, const.dpi / 2, BOT)
        tiltinfo.append(
            follow_hline_to_corner(page.image, const.dpi,
                                   right_starting_x_offset, hline, RIGHT))
        hline = scan_strips_for_horiz_line_y(page.image, const.dpi,
                                             left_starting_x_offset,
                                             const.dpi / 2, const.dpi / 2, BOT)
        tiltinfo.append(
            follow_hline_to_corner(page.image, const.dpi,
                                   left_starting_x_offset, hline, LEFT))
        # removing PILB call
        #tiltinfo = page.image.gethartlandmarks(const.dpi, 0)
        if tiltinfo is None or tiltinfo[0][0] == 0 or tiltinfo[1][0] == 0:
            page.blank = True  #needs to ensure it is a page somehow
            self.log.info("Nonballot page at %s " % (page, ))
            return 0.0, 0, 0, 0

        # flunk ballots with more than
        # allowed_corner_black_inches of black in corner
        # to avoid dealing with severely skewed ballots

        errmsg = "Dark %s corner on %s"
        testlen = self.allowed_corner_black
        xs, ys = page.image.size

        #boxes to test
        ul = (0, 0, testlen, testlen)

        ur = (xs - testlen, 0, xs - 1, testlen)

        lr = (xs - testlen, ys - testlen, xs - 1, ys - 1)

        ll = (0, ys - testlen, testlen, ys - 1)

        for area, corner in ((ul, "upper left"), (ur, "upper right"),
                             (lr, "lower right"), (ll, "lower left")):
            if ImageStat.Stat(page.image.crop(area)).mean[0] < 16:
                raise Ballot.BallotException(errmsg % (corner, page.filename))

        xoff = tiltinfo[0][0]
        yoff = tiltinfo[0][1]

        shortdiff = tiltinfo[3][0] - tiltinfo[0][0]
        longdiff = tiltinfo[3][1] - tiltinfo[0][1]
        hypot = math.sqrt(shortdiff * shortdiff + longdiff * longdiff)
        if longdiff != 0:
            rot = shortdiff / float(longdiff)
        else:
            rot = 0
        if abs(rot) > const.allowed_tangent:
            raise Ballot.BallotException(
                "Tilt %f of %s exceeds %f" %
                (rot, page.filename, const.allowed_tangent))
        page.tiltinfo = tiltinfo
        return rot, xoff, yoff, hypot
Ejemplo n.º 8
0
    def find_landmarks(self, page):
        """ retrieve landmarks for a demo template, set tang, xref, yref

        Landmarks for the demo ballot are normally at 1/2" down and
        1" in from the top left and top right corners.

        The "image" you are using as a template may be offset or 
        tilted, in which case that information will be recorded
        so it may be taken into account when future images are
        examined.
        """
        print "In find_landmarks"
        a = ask("""Enter the x coordinate of an upper left landmark;
if your template is not offset or tilted, you could use 150.  If there's no
such landmark, enter -1:
""", int, -1)
        b = ask("""Now enter the corresponding y coordinate;
if your template is not offset or tilted, you could use 75.  If there's no
such landmark, enter -1:
""", int, -1)
        c = ask("""Enter the x coordinate of an upper RIGHT landmark;
if your template is not offset or tilted, you could use 2050.  If there's no
such landmark, enter -1:
""", int, -1)
        d = ask("""Enter the corresponding y coordinate;
if your template is not offset or tilted, you could use 75.  If there's no
such landmark, enter -1:
""", int, -1)
        if -1 in (a, b, c, d):
            raise Ballot.BallotException("Could not find landmarks")

        # flunk ballots with more than 
        # allowed_corner_black_inches of black in corner
        # to avoid dealing with severely skewed ballots

        errmsg = "Dark %s corner on %s"
        testlen = self.allowed_corner_black
        xs, ys = page.image.size

        #boxes to test
        ul = (0,             0,           
              testlen,       testlen)
 
        ur = (xs - testlen,  0,           
              xs - 1,        testlen)

        lr = (xs - testlen,  ys - testlen,
              xs - 1,        ys - 1)
 
        ll = (0,             ys - testlen,
              testlen,       ys - 1)

        for area, corner in ((ul, "upper left"),
                             (ur, "upper right"),
                             (lr, "lower right"),
                             (ll, "lower left")):
            avg_darkness = ask(
                "What's the intensity at the " + corner,
                IntIn(0, 255)
            )
            if int(avg_darkness) < 16:
                raise Ballot.BallotException(errmsg % (corner, page.filename))

        xoff = a
        yoff = b

        shortdiff = d - b
        longdiff = c - a
        rot = -shortdiff/float(longdiff)
        if abs(rot) > const.allowed_tangent:
            raise Ballot.BallotException(
                "Tilt %f of %s exceeds %f" % (rot, page.filename, const.allowed_tangent)
            )

        return rot, xoff, yoff, longdiff 
Ejemplo n.º 9
0
    def find_landmarks(self, page):
        """ retrieve landmarks for Saguache images, set tang, xref, yref

        Landmarks for the Saguache Ballot will be the 
        (x, y) pairs at the center of the two upper plus in a circle
        registration marks.

        They are searched for in the upper left and upper right
        square inches of the image.
        
        The coordinates of the pair at upper left are returned, along
        with a rotation value calculated from the two pairs.

        Ballots would be rejected at this stage if there is excessive
        black in any corner, potentially indicating a scanning problem.

        Current error handling will generally log and terminate 
        on first BallotException.
        """
        crop = page.image.crop((0, 0, const.dpi, const.dpi))
        (a, b) = find_plus_target(crop, const.dpi)
        crop = page.image.crop(
            (page.image.size[0] - const.dpi, 0, page.image.size[0], const.dpi))
        (c, d) = find_plus_target(crop, const.dpi)
        if a == -1 or b == -1 or c == -1 or d == -1:
            raise Ballot.BallotException("Could not find landmarks")
        # adjust c to ballot coordinates from crop coordinates
        c += (page.image.size[0] - const.dpi)

        # flunk ballots with more than
        # allowed_corner_black_inches of black in corner
        # to avoid dealing with severely skewed ballots

        errmsg = "Dark %s corner on %s"
        testlen = self.allowed_corner_black
        xs, ys = page.image.size

        #boxes to test
        ul = (0, 0, testlen, testlen)

        ur = (xs - testlen, 0, xs - 1, testlen)

        lr = (xs - testlen, ys - testlen, xs - 1, ys - 1)

        ll = (0, ys - testlen, testlen, ys - 1)

        for area, corner in ((ul, "upper left"), (ur, "upper right"),
                             (lr, "lower right"), (ll, "lower left")):
            if ImageStat.Stat(page.image.crop(area)).mean[0] < 16:
                raise Ballot.BallotException(errmsg % (corner, page.filename))

        xoff = a
        yoff = b

        shortdiff = d - b
        longdiff = c - a
        rot = -shortdiff / float(longdiff)
        if abs(rot) > const.allowed_tangent:
            raise Ballot.BallotException(
                "Tilt %f of %s exceeds %f" %
                (rot, page.filename, const.allowed_tangent))

        return rot, xoff, yoff