omrchecker

repository·master·Indexed 22 days ago

https://github.com/udayraj123/omrchecker

A tool for Optical Mark Recognition (OMR) designed to match student roll numbers on exam scripts. It features feature-based alignment to correct scan rotation and translation, level adjustment for image preprocessing, and support for Right-To-Left (RTL) field types. The library includes a CLI via main.py and programmatic access through entry_point_for_args, utilizing a two-tier global and local thresholding strategy for bubble detection.

Tokens
7.7K
Snippets
22
Records
38
Agent score
78%

What's inside omrchecker

  1. Overview of OMRChecker Sample capabilities

    master

    The sample5 directory serves as a demonstration for several advanced OMRChecker workflows:

    • Document Scanning App Compatibility: Demonstrates how to run OMRChecker on images captured using popular mobile document scanning applications.
    • Shared Template Configuration: Shows how to use a single template.json file across multiple sub-folders (useful for processing multiple scan batches with the same layout).
    • Custom Marking Evaluation: Demonstrates how to use an evaluation.json file to implement custom marking logic that does not rely on streak-based marking.
  2. Use feature-based alignment to correct scan errors

    master

    Feature-based alignment is used to correct rotation and translation errors in scanned OMR scripts. When document scanners produce imperfect alignments, you can use a reference image to align the scans correctly.

    To implement this, use a reference image that contains distinct features. Avoid using images with many repeated patterns (like OMR bubbles), as these cause ambiguity during feature extraction. Instead, use forms with significant text or unique markings. For best results, generate the reference image from a vector PDF rather than a scanned blank to ensure a perfectly aligned baseline.

  3. Creating custom RTL fields for non-standard bubble counts

    master

    If you need a different number of choices or a specific sequence for RTL bubbles, you can define a custom field inline in your template.json. To do this, specify the bubbleValues array and set the direction to "horizontal" within the field block. This overrides the default left-to-right behavior.

    "MCQBlock_CUSTOM_RTL": {
        "bubbleValues": ["G", "F", "E", "D", "C", "B", "A"],
        "direction": "horizontal",
        "fieldLabels": ["q1..7"],
        "bubblesGap": 40,
        "labelsGap": 50,
        "origin": [100, 100]
    }
  4. Handle OMR layout shifts caused by paper type or printer settings

    master

    OMR layouts can shift significantly depending on the paper type (e.g., colored thick paper vs. xeroxed thin paper) or printer margin settings. This is particularly noticeable in OMR sheets with a large number of questions, where layout shifts accumulate toward the bottom of the sheet.

    Causes of Layout Shifts

    • Printer margin settings: Different printers may apply different margins to the same layout, causing horizontal or vertical elongation.
    • The Fan-out effect: Paper dimensions can change due to moisture absorption (humidity). Standard 80gsm office paper is particularly susceptible to shape changes.

    To ensure accurate OMR checking, do not use a single template for all print types. Instead:

    1. Scan different types of prints (e.g., original colored sheets vs. xeroxed copies) into separate folders.
    2. Use a separate template.json layout file for each specific folder to account for the unique dimensional characteristics of that print type.
  5. Using built-in RTL field types for OMR templates

    master

    For OMR sheets used in Right-To-Left (RTL) languages where answer bubbles are reversed (e.g., D, C, B, A), OMRChecker provides two built-in field types. You can use these in your template.json by setting the fieldType key.

    Available built-in RTL types:

    • QTYPE_MCQ4_RTL: 4-choice questions with reversed values ["D", "C", "B", "A"].
    • QTYPE_MCQ5_RTL: 5-choice questions with reversed values ["E", "D", "C", "B", "A"].
    "MCQBlock_RTL": {
        "fieldType": "QTYPE_MCQ4_RTL",
        "fieldLabels": ["q1..10"],
        "bubblesGap": 40,
        "labelsGap": 50,
        "origin": [100, 100]
    }
  6. Perform level adjustment for light shading

    master

    If OMR bubbles are not shaded dark enough for accurate detection, perform a level adjustment to enhance contrast.

    In the provided sample, the following adjustments were used to darken light shading and clean the background:

    • Set the black point to 70% to darken light shading.
    • Set the white point to 80% to remove light-grey backgrounds in columns.
  7. Configure Field Blocks using `FieldBlock`

    master

    A FieldBlock represents a structured area on the OMR sheet containing bubbles. It is automatically instantiated by the Template class when parsing fieldBlocks from the JSON.

    Key properties of a FieldBlock include:

    • name: The identifier for the block.
    • origin: The [x, y] starting coordinates.
    • direction: Either "vertical" or "horizontal".
    • fieldType: The type of field (e.g., integer, custom, etc.).
    • bubbleValues: The set of values the bubbles represent.
    • parsed_field_labels: The labels associated with the fields in this block.
  8. Understand the Template JSON structure

    master

    A Template object is initialized by reading a JSON file. The following keys are expected in the JSON object:

    • customLabels: Mapping of custom label names to their associated label strings.
    • fieldBlocks: Definitions of specific areas on the sheet (e.g., roll number, question answers).
    • outputColumns: An array defining the expected output columns.
    • preProcessors: Configuration for image pre-processing steps.
    • bubbleDimensions: Dimensions for the bubbles.
    • emptyValue: The value to represent an empty/unmarked field.
    • options: General template options.
    • pageDimensions: The width and height of the OMR sheet page.
  9. How OMRChecker processes directories and templates

    master

    OMRChecker uses a recursive directory traversal model to process OMR sheets. For every directory in the tree, the engine:

    1. Loads Configuration: Looks for a local config.json (defined by CONFIG_FILENAME) to override default tuning parameters.
    2. Loads Templates: Looks for a local template file (defined by TEMPLATE_FILENAME). If found, it initializes a Template object for that specific directory.
    3. Identifies Images: Scans for supported image formats (.png, .jpg, .jpeg, .pdf).
    4. Handles Exclusions: Automatically excludes files specified by pre-processors or the EvaluationConfig.
    5. Executes Processing:
      • If setLayout is True in args, it enters a layout visualization mode.
      • If setLayout is False, it performs full OMR extraction and grading.

    This allows different subdirectories to have different OMR layouts and tuning configurations within a single batch run.

  10. How the Processor extension framework works

    master

    The framework uses a plugin-style architecture based on Python's pkgutil and inspect modules.

    1. Discovery: ProcessorManager walks the directory tree of a specified package.
    2. Filtering: For every module found, the manager uses get_name_filter to find class definitions within that module.
    3. Validation: It checks if a class is a subclass of Processor but ensures it is not the Processor base class itself.
    4. Registration: Valid subclasses are added to the self.processors dictionary, making them available for instantiation by the application.
  11. Visualize template layouts with setLayout mode

    master

    When the setLayout flag is set to True in your args dictionary, OMRChecker enters a visualization mode instead of full processing. This mode is useful for debugging and verifying that your template correctly aligns with the physical OMR sheets.

    It will load each image, apply the template's pre-processors, and use InteractionUtils.show to display the template layout overlaid on the image.

    args = {
        "output_dir": "./output",
        "setLayout": True  # Enables layout visualization mode
    }
    entry_point("./input_dir", args)