def extract_VOP(self, page, rotatefunc, scale, choice): """Extract statistics for a single oval or writein from the ballot. """ iround = lambda x: int(round(x)) adj = lambda f: int(round(const.dpi * f)) x, y = choice.coords() x = int(x) y = int(y) # NO horizontal margins in crop - grabbing region between marks! # const.margin_width_inches not used # hotspot_x_offset_inches IS used scaled_page_offset_x = page.xoff / scale scaled_page_offset_y = page.yoff / scale self.log.debug("Incoming coords (%d,%d), \ page offsets (%d,%d) template offsets (%d,%d)" % (x, y, page.xoff, page.yoff, scaled_page_offset_x, scaled_page_offset_y)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of transform: (%d,%d)" % (x, y)) x, y = rotatefunc(x, y, scale) cropx = x cropy = y cropy -= adj(.1) # NO horizontal margins in crop - grabbing region between marks! croplist = (cropx + self.hotspot_x_offset_inches, cropy - page.margin_height, min( cropx + self.hotspot_x_offset_inches + page.target_width, page.image.size[0] - 1), min(cropy + page.margin_height + page.target_height, page.image.size[1] - 1)) crop = page.image.crop(croplist) cropstat = ImageStat.Stat(crop) stats = Ballot.IStats(cropstats(crop, cropx, cropy)) #can be in separate func? voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = False if voted: # extension is overriden with local function for this ballot type writein = IsWriteIn(crop, stats, choice) if writein: x1 = min(self.writein_xoff + cropx, cropx) x2 = max(self.writein_xoff + cropx, cropx) y1 = min(self.writein_yoff + cropy, cropy + adj(.2)) y2 = max(self.writein_yoff + cropy, cropy + adj(.2)) crop = page.image.crop((x1, y1 - page.margin_height, min(x2, page.image.size[0] - 1), min(y2 + page.margin_height, page.image.size[1] - 1))) return cropx, cropy, stats, crop, voted, writein, ambiguous
def do(color, box, v, a): i = Image.new("RGB", (100, 100), "#fff") d = ImageDraw.Draw(i) d.rectangle((20, 20, 80, 80), fill="#000") d.rectangle(carve, fill="#fff") d.rectangle(box, fill=("#" + color*3)) s = Ballot.IStats(i.cropstats(100, 5, 20, 20, 60, 60, 1)) vp, ap = Ballot.IsVoted(i, s, None) assert vp == v and ap == a
def extract_VOP(self, page, rotatefunc, scale, choice): """Extract a single oval, or writein box, from the specified ballot""" iround = lambda x: int(round(x)) x, y = choice.coords() printed_oval_height = adj(const.target_height_inches) #BEGIN SHARABLE scaled_page_offset_x = page.xoff / scale scaled_page_offset_y = page.yoff / scale self.log.debug( "Incoming coords (%d,%d), \ page offsets (%d,%d) scaled page offsets (%d,%d), template offsets (%d,%d)" % (x, y, page.xoff, page.yoff, scaled_page_offset_x, scaled_page_offset_y, page.template.xoff, page.template.yoff)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of translation: (%d,%d)" % (x, y)) x, y = rotatefunc(x, y, scale) self.log.debug("Result of rotation: (%d,%d)" % (x, y)) # Below is using the pure python cropstats: cropx, cropy = x, y #not adjusted like in PILB cropstats crop = page.image.crop( (cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + page.target_width, page.image.size[0] - 1), min(cropy + page.margin_height + page.target_height, page.image.size[1] - 1))) stats = Ballot.IStats(cropstats(crop, x, y)) voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = self.extensions.IsWriteIn(crop, stats, choice) if writein: crop = page.image.crop( (cropx - page.margin_width + self.writein_xoff, cropy - page.margin_height + self.writein_yoff, min(cropx + page.margin_width + self.writein_width, page.image.size[0] - 1), min(cropy + page.margin_height + self.writein_height, page.image.size[1] - 1))) return cropx, cropy, stats, crop, voted, writein, ambiguous
def extract_VOP(self, page, rotatefunc, scale, choice): """Extract a single oval, or writein box, from the specified ballot""" iround = lambda x: int(round(x)) adj = lambda a: int(round(const.dpi * a)) x, y = choice.coords() margin_width = page.margin_width margin_height = page.margin_height printed_oval_height = adj(0.097) #BEGIN SHARABLE scaled_page_offset_x = page.xoff / scale scaled_page_offset_y = page.yoff / scale self.log.debug("Incoming coords (%d,%d), \ page offsets (%d,%d) template offsets (%d,%d)" % (x, y, page.xoff, page.yoff, scaled_page_offset_x, scaled_page_offset_y)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of transform: (%d,%d)" % (x, y)) x, y = rotatefunc(x, y, scale) #END SHARABLE cropx, cropy = x, y #not adjusted like in PILB cropstats crop = page.image.crop( (cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + page.target_width, page.image.size[0] - 1), min(cropy + page.margin_height + page.target_height, page.image.size[1] - 1))) # check strip at center to look for either filled or empty oval; # recenter vertically stripe = crop.crop( ((crop.size[0] / 2), 0, (crop.size[0] / 2) + 1, crop.size[1] - 1)) before_oval = 0 after_oval = 0 oval = 0 stripedata = list(stripe.getdata()) for num, p in enumerate(stripedata): if p[0] > 245: before_oval += 1 else: try: if ((stripedata[before_oval + printed_oval_height - 2][0] < 245) or (stripedata[before_oval + printed_oval_height - 1][0] < 245) or (stripedata[before_oval + printed_oval_height][0] < 245) or (stripedata[before_oval + printed_oval_height + 1][0] < 245) or (stripedata[before_oval + printed_oval_height + 2][0] < 245)): oval_start = num oval_end = num + printed_oval_height after_oval = stripe.size[1] - (oval_start + printed_oval_height) break except IndexError: break #print cropy,before_oval,oval,after_oval afterlessbefore = int(round((after_oval - before_oval) / 2)) if abs(afterlessbefore) > 2: cropy -= afterlessbefore #print "Adjusted",cropy crop = page.image.crop( (cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + page.target_width, page.image.size[0] - 1), min(cropy + page.margin_height + page.target_height, page.image.size[1] - 1))) stats = Ballot.IStats(cropstats(crop, x, y)) voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = self.extensions.IsWriteIn(crop, stats, choice) if writein: crop = page.image.crop( (cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + self.writein_xoff, page.image.size[0] - 1), min(cropy + page.margin_height + self.writein_yoff, page.image.size[1] - 1))) return cropx, cropy, stats, crop, voted, writein, ambiguous
def extract_VOP(self, page, rotatefunc, scale, choice): """Extract a single oval, or writein box, from the specified ballot""" iround = lambda x: int(round(x)) x, y = choice.coords() printed_oval_height = adj(const.target_height_inches) #BEGIN SHARABLE scaled_page_offset_x = page.xoff / scale scaled_page_offset_y = page.yoff / scale self.log.debug( "Incoming coords (%d,%d), \ page offsets (%d,%d) scaled page offsets (%d,%d), template offsets (%d,%d)" % (x, y, page.xoff, page.yoff, scaled_page_offset_x, scaled_page_offset_y, page.template.xoff, page.template.yoff)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of translation: (%d,%d)" % (x, y)) x, y = rotatefunc(x, y, scale) self.log.debug("Result of rotation: (%d,%d)" % (x, y)) # Below is using the pure python cropstats: cropx, cropy = x, y #not adjusted like in PILB cropstats crop = page.image.crop( (cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + page.target_width, page.image.size[0] - 1), min(cropy + page.margin_height + page.target_height, page.image.size[1] - 1))) # Commenting out material below as regardless of adding or subtracting # based upon it, it makes things worse in some situations (?) # The rotation is working well to capture the correct area. """ # check strip at center to look for either filled or empty oval; # recenter vertically stripe = crop.crop(((crop.size[0]/2),0,(crop.size[0]/2)+1,crop.size[1]-1)) before_oval = 0 after_oval = 0 oval = 0 dark_threshold = 192 stripedata = list(stripe.getdata()) for num,p in enumerate(stripedata): if p[0] > dark_threshold: before_oval += 1 else: try: if ((stripedata[before_oval+printed_oval_height-2][0] < dark_threshold) or (stripedata[before_oval+printed_oval_height-1][0] < dark_threshold) or (stripedata[before_oval+printed_oval_height][0] < dark_threshold) or (stripedata[before_oval+printed_oval_height+1][0] < dark_threshold) or (stripedata[before_oval+printed_oval_height+2][0] < dark_threshold)): oval_start = num ov_end = num + printed_oval_height after_oval = stripe.size[1] - (oval_start+printed_oval_height) break except IndexError: break afterlessbefore = int(round((after_oval - before_oval)/2)) if abs(afterlessbefore)>2: cropy += afterlessbefore self.log.debug("Result of afterlessbefore %d: (%d,%d)" % ( afterlessbefore,x,cropy)) crop = page.image.crop(( cropx - page.margin_width, cropy - page.margin_height, min(cropx + page.margin_width + page.target_width, page.image.size[0]-1), min(cropy + page.margin_height + page.target_height, page.image.size[1]-1) )) """ stats = Ballot.IStats(cropstats(crop, x, y)) voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = self.extensions.IsWriteIn(crop, stats, choice) if writein: crop = page.image.crop( (cropx - page.margin_width + self.writein_xoff, cropy - page.margin_height + self.writein_yoff, min(cropx + page.margin_width + self.writein_width, page.image.size[0] - 1), min(cropy + page.margin_height + self.writein_height, page.image.size[1] - 1))) return cropx, cropy, stats, crop, voted, writein, ambiguous
def extract_VOP(self, page, rotatefunc, scale, choice): """Extract a single oval, or writein box, from the specified ballot. We'll tell you the coordinates, you tell us the stats. The information gathered should enable the IsVoted function to make a reasonable decision about whether the area was voted, but the data is also available to anyone else wanting to see the raw statistics to make their own decision. """ print "In extract_VOP" adj = lambda f: int(round(const.dpi * f)) iround = lambda x: int(round(x)) # choice coords should be the upper left hand corner # of the bounding box of the printed vote target adj = lambda f: int(round(const.dpi * f)) x, y = choice.coords() x = int(x) y = int(y) margin_width = page.margin_width margin_height = page.margin_height scaled_page_offset_x = page.xoff/scale scaled_page_offset_y = page.yoff/scale self.log.debug("Incoming coords (%d,%d), \ page offsets (%d,%d) template offsets (%d,%d)" % ( x,y, page.xoff,page.yoff, scaled_page_offset_x,scaled_page_offset_y)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of transform: (%d,%d)" % (x,y)) x, y = rotatefunc(x, y, scale) ow, oh = page.target_width,page.target_height print """At %d dpi, on a scale of 0 to 255, tell us the average intensity from (%d, %d) for width %d height %d, given an offset from the specified x of %d """ % (const.dpi, x, y, ow, oh, self.vote_target_horiz_offset) intensity = ask("Intensity", IntIn(0, 255)) lowest = ask("Lowest count", IntIn(0, 1000)) low = ask("Low count", IntIn(0, 1000)) high = ask("High count", IntIn(0, 1000)) highest = ask("Highest count", IntIn(0, 1000)) suspicious = ask("Value of suspicious", int) ari, agi, abi = intensity, intensity, intensity lowestr, lowestg, lowestb = lowest, lowest, lowest lowr, lowg, lowb = low, low, low highestr, highestg, highestb = highest, highest, highest highr, highg, highb = high, high, high stats = Ballot.IStats( (ari, lowestr, lowr, highr, highestr, agi, lowestg, lowg, highg, highestg, abi, lowestb, lowb, highb, highestb, x, y, 0) ) #can be in separate func? cropx = stats.adjusted.x cropy = stats.adjusted.y crop = page.image.crop(( cropx - margin_width, cropy - margin_height, cropx + margin_width + ow, cropy + margin_height + oh )) #can be in separate func? voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = False if voted: writein = self.extensions.IsWriteIn(crop, stats, choice) if writein: print "Gather information about the write-in at", print cropx - margin_width, cropy - margin_height, print cropx + self.writein_xoff + margin_width, print cropy + self.writein_yoff + margin_height return cropx, cropy, stats, crop, voted, writein, ambiguous
def extract_VOP(self, page, rotatefunc, scale, choice): """Extract a single oval, or writein box, from the specified ballot. Note that cropstats is C code in Imaging-1.1.7, most of which fine-tunes the location of a Hart ballot vote box. The fine-tuning is left OFF in this call to cropstats. The "suspicious" value returned from cropstats is toggled if there is any dark pixel in the central region of the oval; this may be more appropriate to rectangular vote ops as in Hart. The IStats class in Ballot provides more convenient access to the list of information returned from cropstats. The determination of whether a vote target was actually voted, or whether a vote target represents a write-in, is currently performed by the default functions set into the Ballot Extensions object. These functions may be overriden. XXX Example! """ adj = lambda f: int(round(const.dpi * f)) iround = lambda x: int(round(x)) x, y = choice.coords() x = int(x) y = int(y) margin_width = page.margin_width margin_height = page.margin_height #BEGIN SHARABLE scaled_page_offset_x = page.xoff / scale scaled_page_offset_y = page.yoff / scale self.log.debug("Incoming coords (%d,%d), \ page offsets (%d,%d) template offsets (%d,%d)" % (x, y, page.xoff, page.yoff, scaled_page_offset_x, scaled_page_offset_y)) # adjust x and y for the shift of landmark between template and ballot x = iround(x + scaled_page_offset_x - page.template.xoff) y = iround(y + scaled_page_offset_y - page.template.yoff) self.log.debug("Result of transform: (%d,%d)" % (x, y)) x, y = rotatefunc(x, y, scale) #END SHARABLE ow = page.target_width oh = page.target_height stats = Ballot.IStats( page.image.cropstats( const.dpi, self.vote_target_horiz_offset, x, y, ow, oh, 0 # NOTE, this turns OFF fine adjustment, tuned to Hart in Imaging )) #can be in separate func? cropx = stats.adjusted.x cropy = stats.adjusted.y crop = page.image.crop( (cropx - margin_width, cropy - margin_height, cropx + margin_width + ow, cropy + margin_height + oh)) #can be in separate func? voted, ambiguous = self.extensions.IsVoted(crop, stats, choice) writein = False if voted: writein = self.extensions.IsWriteIn(crop, stats, choice) if writein: crop = page.image.crop( (cropx - margin_width, cropy - margin_height, min(cropx + self.writein_xoff + margin_width, page.image.size[0] - 1), min(cropy + self.writein_yoff + margin_height, page.image.size[1] - 1))) return cropx, cropy, stats, crop, voted, writein, ambiguous