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
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)))
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
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
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
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
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
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
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