def get_temp_page(pages, page_number: int, rotate_dir: Rotation, temp_filename: str) -> Page: temp_path = "%s%s" % (utils.TEMP_DIR, temp_filename) pages[page_number - 1].save( temp_path, 'JPEG') # Save specified page out to temp so we can read it in again return Page.from_file(temp_path, rotate_dir)
def scan_page(list_id: str, rotate_dir: Rotation, args, page_number: int, ref_page: Page, ref_bounding_boxes: Dict[str, BoundingBox], list_dir: str, results_scans, results_stats, results_errors, previous_scans: dict, backup_writer): page = Page.from_file(utils.get_page_filename(list_id, page_number), rotate_dir) response_codes = utils.load_response_codes(list_id) # align page aligned_page = page.align_to(ref_page) if utils.__DEBUG__: aligned_page.show(title="aligned page") # confirm page has the correct list_id page_list_id = page.get_list_id(ref_bounding_boxes["list_id"]) if page_list_id != list_id: valid_id, page = handle_missing_page_id(aligned_page, page, list_id, ref_bounding_boxes["list_id"], page_number) if not valid_id: print( 'Error: Page {} has ID {}, but active ID is {}. Page {} has been skipped.' .format(page_number + 1, page_list_id, list_id, page_number + 1)) results_errors['skipped_pages'].append({page_number: page}) return results_scans, results_stats, results_errors # find the barcodes in the image and decode each of the barcodes # Barcode scanner needs the unthresholded image. barcodes = pyzbar.decode(page.raw_image) if len(barcodes) == 0: print('Error: Cannot find barcodes. Page {} has been skipped.'.format( page_number + 1)) results_errors['skipped_pages'].append({page_number: page}) return results_scans, results_stats, results_errors # loop over the detected barcodes voter_ids: Set = set() for barcode in barcodes: results_scans, results_stats, results_errors = scan_barcode( barcode, page, ref_bounding_boxes, list_dir, response_codes, args, results_scans, results_stats, results_errors, previous_scans, backup_writer, voter_ids) check_num_barcodes(page, list_dir, len(voter_ids), results_stats) if utils.__DEBUG__: page.show() return results_scans, results_stats, results_errors
def main() -> None: args = parse_args() list_id = args["list_id"] check_files_exist(list_id) list_dir: str = utils.get_list_dir(list_id) rotate_dir = utils.map_rotation(args["rotate_dir"]) ref_bounding_boxes = utils.load_ref_boxes(list_dir) ref_page = Page.from_file(list_dir + utils.CLEAN_IMAGE_FILENAME, rotate_dir) # init results object results_scans: list = [] # things to track for error reporting results_stats = {} results_stats['num_scanned_barcodes'] = 0 results_stats['num_missed_barcodes'] = 0 results_stats['num_error_barcodes'] = 0 results_stats['incorrect_scans'] = [] # stuff to build error PDF for human scanning results_errors: dict = {} results_errors['errors_for_human'] = [] results_errors['skipped_pages'] = [] # write out to CSV backup as process the list backup_filename, colnames = prep_backup_csv(list_dir, list_id) previous_scans = load_previous_scans(backup_filename, args) with open(backup_filename, mode='w') as backup_csv: backup_writer = csv.DictWriter(backup_csv, fieldnames=colnames) backup_writer.writeheader() num_pages = len( os.listdir("{}/{}".format(list_dir, utils.WALKLIST_DIR))) for page_number in range(args['start_page'], num_pages): print('===Scanning page {} of {} ==='.format( page_number + 1, num_pages)) results_scans, results_stats, results_errors = scan_page( list_id, rotate_dir, args, page_number, ref_page, ref_bounding_boxes, list_dir, results_scans, results_stats, results_errors, previous_scans, backup_writer) # output results output_results_csv(args['list_id'], list_dir, results_scans) # generate_error_pages(results_errors['errors_for_human'], results_errors['skipped_pages'], args['list_id']) # show list of skipped pages print('Skipped {} pages:'.format(len(results_errors['skipped_pages']))) for page in results_errors['skipped_pages']: print(page.keys()) # run test suite if set if args["test_file"]: test.run_test_suite(args['test_file'], results_scans) else: # print statistics show_statistics(results_stats, args)
def scan_barcode(barcode, page, ref_bounding_boxes, list_dir, response_codes, args, results_scans, results_stats, results_errors, previous_scans, backup_writer, voter_ids) -> Tuple[list, dict, dict]: barcode_info = extract_barcode_info(barcode, page) # skip if not a valid barcode if not barcode_info: return results_scans, results_stats, results_errors barcode_coords, voter_id = barcode_info # Check if the barcode has already been read, skip if so. if voter_id in voter_ids: return results_scans, results_stats, results_errors else: voter_ids.add(voter_id) # increment barcodes counter results_stats['num_scanned_barcodes'] += 1 # use the existing info if already scanned, unless in testing mode if voter_id in previous_scans and not args["test_file"]: print('Already scanned {}'.format(voter_id)) results_dict = previous_scans[voter_id] # new barcode to scan else: if utils.__DEBUG__: cv2.rectangle(page, barcode_coords.top_left.to_tuple(), barcode_coords.bottom_right.to_tuple(), (255, 0, 255), 3) page.show() # Get the corresponding response codes region response_bounding_box = get_response_for_barcode( barcode_coords, ref_bounding_boxes["response_codes"], page.size) # Figure out which ones are circled ref_response_codes = Page.from_file( list_dir + utils.RESPONSE_CODES_IMAGE_FILENAME, Rotation.NONE) circled_responses, has_error = get_circled_responses( response_bounding_box, response_codes, page, list_dir) has_error = has_error or error_check_responses(circled_responses) # if has an error at this point, add to the error tally if has_error: results_stats['num_error_barcodes'] += 1 # Do manual review if error or if flagged, unless in testing mode if (has_error or args["manual_review"]) and not args["test_file"]: verdict_right, circled_responses = manual_review( response_bounding_box, page, circled_responses, voter_id, response_codes) # if user verdict is false, add the voter_id to the list of incorrect scans if not verdict_right: results_stats['incorrect_scans'].append(voter_id) # if in testing mode, convert any None circled_responses to an empty list if args["test_file"] and circled_responses is None: circled_responses = [] # build results dict results_dict = build_results_dict(voter_id, circled_responses) # save results results_scans.append(results_dict) write_to_backup(results_dict, backup_writer) return results_scans, results_stats, results_errors