Exemple #1
0
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)
Exemple #2
0
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
Exemple #3
0
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)
Exemple #4
0
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