Introduction

Generating an OMR form in Aspose.OMR FOSS for Python means building a tree of element configs rooted at TemplateConfig, then handing that tree to OmrEngine.Generate(). The engine renders a page image plus a .omr descriptor that a later recognition pass reads back. This post walks through the element configs that make up that tree, and the alternative text-markup DSL that builds the same tree without constructing Python objects directly.

The library imports as import omr, is MIT licensed, and depends only on numpy, opencv-python, and Pillow.


What’s Included

The Template Tree

TemplateConfig is the document root; it contains one or more PageConfig objects, one per physical page. Every element below a page is a subclass of BaseConfig, carrying at minimum a Name and a Type discriminator (ConfigType).

from omr.generation.config import TemplateConfig
from omr.generation.config.elements.parents import PageConfig

tpl = TemplateConfig()
tpl.Name = "quiz"
page = PageConfig()
page.Children = []
tpl.Children = [page]

Question Elements

ChoiceBoxConfig defines a single question with a fixed set of option bubbles (each a ChoiceBoxAnswerConfig) and supports multi-select; FormValidationLogic, set once per TemplateProcessor, can raise if a question with Multiselect disabled receives more than one mark. AnswerSheetConfig lays out a numbered matrix of answer bubbles for exam-style sheets, with a configurable BubbleType and BubbleSize. CheckBoxConfig is a labeled group of blank boxes accepting any mark, with a tunable fill threshold.

from omr.generation.config.elements import AnswerSheetConfig

sheet = AnswerSheetConfig()
sheet.Name = "answers"
sheet.AnswersCount = 4    # bubbles per question (e.g. A-D)
sheet.ElementsCount = 20  # number of questions

Grids and Composite Grids

GridConfig renders a digit matrix — a fixed number of sections, each with 0–9 option bubbles — merged into a single value, commonly used for ID numbers. CompositeGridConfig is the more flexible variant: a custom symbol set per column, with marks across columns merging into one value.

Layout Containers

BlockConfig is a bordered, padded box; ContainerConfig arranges child blocks into columns or a page footer; PositionedBlockConfig places children at absolute XPosition/YPosition coordinates instead of flowing them. BlockConfig and GlobalPageSettings also carry an Overflow property (OverflowAction subclasses Clip/NoClip/Wrap) and LongWordHandling, but these enums parse and validate correctly without yet being honored by this release’s render engine — overflow behavior is not yet configurable, and is tracked on the upstream roadmap.

The Markup DSL

The same tree can be written as plain text instead of Python objects. ?choicebox=... opens a choice-box question, the following lines list its options, and &choicebox closes it.

import json
from omr.api import OmrEngine

markup = [
    "?choicebox=Rate it",
    "(1) Poor (2) Fair (3) Good",
    "&choicebox",
]
engine = OmrEngine()
result = engine.Generate(markup)
desc = json.loads(result.Template)

Quick Start

git clone https://github.com/aspose-omr-foss/Aspose.OMR-FOSS-for-Python.git
cd Aspose.OMR-FOSS-for-Python
pip install -e .
from omr.api import OmrEngine, FormValidationLogic

engine = OmrEngine()
gen = engine.Generate("final-exam.txt")          # or a code-first TemplateConfig
gen.Save("out", "final-exam")                    # -> out/final-exam.png + out/final-exam.omr
processor = engine.GetTemplateProcessor("out/final-exam.omr", FormValidationLogic.Ignore)
result = processor.Recognize("scan.png")         # -100 = auto-calibrated threshold
print(result.GetCsv())                           # also GetJson() / GetXml()

Open Source & Licensing

Aspose.OMR FOSS for Python is MIT licensed and hosted on GitHub. Commercial use is permitted under the same terms as any MIT-licensed project. For enterprise features and production support, see Aspose.OMR — Enterprise Product Family.


Getting Started