End-to-end Optical Music Recognition (OMR) system for transcribing musical notation from images into structured MusicXML format.
—
Global state management system for intermediate processing results, enabling modular pipeline architecture and debugging capabilities. The layer system allows each processing stage to register intermediate results for use by subsequent stages.
Register processing layers for global access throughout the pipeline.
def register_layer(name: str, layer: ndarray) -> None:
"""
Register a processing layer with a given name.
Stores the layer data in global memory for access by other
processing stages. Prints a warning if layer name already exists.
Parameters:
- name (str): Unique identifier for the layer
- layer (ndarray): Numpy array containing the layer data
Raises:
AssertionError: If layer is not a numpy array
"""Retrieve registered processing layers by name.
def get_layer(name: str) -> ndarray:
"""
Retrieve a registered processing layer by name.
Parameters:
- name (str): Name of the layer to retrieve
Returns:
ndarray: The requested layer data
Raises:
KeyError: If no layer with the given name exists
"""Manage the lifecycle of registered layers.
def delete_layer(name: str) -> None:
"""
Delete a registered layer and free its memory.
Silently handles cases where the layer doesn't exist.
Parameters:
- name (str): Name of the layer to delete
"""
def list_layers() -> List[str]:
"""
List all currently registered layer names.
Returns:
List[str]: List of all registered layer names
"""
def show_access_count() -> None:
"""
Display access statistics for all registered layers.
Shows how many times each layer has been accessed,
useful for debugging and optimization.
"""The oemer pipeline uses standardized layer names for consistency:
"original_image" - Original input image (RGB)"staff_pred" - Staff line predictions from neural network"symbols_pred" - Combined symbol predictions"notehead_pred" - Note head predictions"stems_rests_pred" - Stems and rests predictions"clefs_keys_pred" - Clefs and accidentals predictions"staffs" - Array of Staff instances"zones" - Staff zone boundaries"notes" - Array of NoteHead instances"note_groups" - Array of NoteGroup instances"clefs" - Array of Clef instances"sfns" - Array of sharp/flat/natural instances"rests" - Array of Rest instances"barlines" - Array of Barline instances"note_id" - Note ID mapping layer"group_map" - Note group mapping layer"bboxes" - Bounding box registration layerfrom oemer.layers import register_layer, get_layer, delete_layer, list_layers
import numpy as np
import cv2
# Register an image layer
image = cv2.imread("sheet_music.jpg")
register_layer("original_image", image)
# Register prediction layers
staff_predictions = np.random.randint(0, 2, (1000, 1500), dtype=np.uint8)
register_layer("staff_pred", staff_predictions)
symbol_predictions = np.random.randint(0, 3, (1000, 1500), dtype=np.uint8)
register_layer("symbols_pred", symbol_predictions)
# List all registered layers
print("Registered layers:", list_layers())
# Retrieve layers for processing
original = get_layer("original_image")
staff = get_layer("staff_pred")
symbols = get_layer("symbols_pred")
print(f"Original image shape: {original.shape}")
print(f"Staff predictions shape: {staff.shape}")
print(f"Symbol predictions shape: {symbols.shape}")
# Clean up specific layer
delete_layer("symbols_pred")
print("Layers after deletion:", list_layers())from oemer.layers import register_layer, get_layer
from oemer.staffline_extraction import extract as staff_extract
from oemer.notehead_extraction import extract as note_extract
import numpy as np
def process_with_layers(image_path: str):
"""Example of layer-based processing pipeline."""
# 1. Load and register input image
image = cv2.imread(image_path)
register_layer("original_image", image)
# 2. Generate and register predictions (simplified)
# In real usage, these come from neural network inference
h, w = image.shape[:2]
staff_pred = np.random.randint(0, 2, (h, w), dtype=np.uint8)
symbols_pred = np.random.randint(0, 2, (h, w), dtype=np.uint8)
register_layer("staff_pred", staff_pred)
register_layer("symbols_pred", symbols_pred)
# 3. Extract stafflines (reads from layers automatically)
staffs, zones = staff_extract()
register_layer("staffs", np.array(staffs))
register_layer("zones", zones)
# 4. Extract noteheads (reads from layers automatically)
notes = note_extract()
register_layer("notes", np.array(notes))
# 5. Access results from any stage
extracted_staffs = get_layer("staffs")
extracted_notes = get_layer("notes")
print(f"Extracted {len(extracted_staffs)} staffs")
print(f"Extracted {len(extracted_notes)} notes")
return extracted_staffs, extracted_notes
# Run the pipeline
staffs, notes = process_with_layers("test_score.jpg")from oemer.layers import register_layer, delete_layer, list_layers, show_access_count
import numpy as np
def memory_efficient_processing():
"""Example of memory-efficient layer management."""
# Register large intermediate layers
large_layer1 = np.zeros((5000, 5000), dtype=np.float32)
large_layer2 = np.zeros((5000, 5000), dtype=np.float32)
register_layer("temp_layer1", large_layer1)
register_layer("temp_layer2", large_layer2)
print(f"Layers registered: {list_layers()}")
# Process data using the layers
data1 = get_layer("temp_layer1")
data2 = get_layer("temp_layer2")
# Combine and create final result
result = data1 + data2
register_layer("final_result", result)
# Clean up intermediate layers to free memory
delete_layer("temp_layer1")
delete_layer("temp_layer2")
print(f"Layers after cleanup: {list_layers()}")
# Show access statistics
show_access_count()
return get_layer("final_result")
result = memory_efficient_processing()from oemer.layers import register_layer, get_layer, list_layers
import matplotlib.pyplot as plt
import numpy as np
def debug_pipeline_stage(stage_name: str):
"""Debug a specific pipeline stage by examining its layers."""
print(f"\n=== Debugging {stage_name} ===")
print(f"Available layers: {list_layers()}")
# Check if expected layers exist
expected_layers = ["original_image", "staff_pred", "symbols_pred"]
for layer_name in expected_layers:
try:
layer = get_layer(layer_name)
print(f"✓ {layer_name}: shape={layer.shape}, dtype={layer.dtype}")
# Show basic statistics
if layer.dtype in [np.uint8, np.int32, np.float32]:
print(f" Range: [{np.min(layer)}, {np.max(layer)}]")
print(f" Non-zero pixels: {np.count_nonzero(layer)}")
except KeyError:
print(f"✗ {layer_name}: NOT FOUND")
# Visualize layers if available
try:
original = get_layer("original_image")
staff = get_layer("staff_pred")
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
axes[0].imshow(original)
axes[0].set_title("Original Image")
axes[0].axis('off')
axes[1].imshow(staff, cmap='gray')
axes[1].set_title("Staff Predictions")
axes[1].axis('off')
plt.tight_layout()
plt.savefig(f"debug_{stage_name}.png")
print(f"Saved debug visualization: debug_{stage_name}.png")
except KeyError as e:
print(f"Could not create visualization: {e}")
# Use in pipeline debugging
debug_pipeline_stage("after_inference")from oemer.layers import get_layer, register_layer
import numpy as np
def validate_layer_consistency():
"""Validate that all layers have consistent dimensions."""
try:
# Get core layers
original = get_layer("original_image")
staff = get_layer("staff_pred")
symbols = get_layer("symbols_pred")
# Check shape consistency
h, w = original.shape[:2]
if staff.shape != (h, w):
print(f"WARNING: Staff predictions shape {staff.shape} != image shape {(h, w)}")
if symbols.shape != (h, w):
print(f"WARNING: Symbol predictions shape {symbols.shape} != image shape {(h, w)}")
# Check data ranges
if staff.dtype == np.uint8 and (staff.min() < 0 or staff.max() > 255):
print(f"WARNING: Staff predictions out of uint8 range: [{staff.min()}, {staff.max()}]")
if symbols.dtype == np.uint8 and (symbols.min() < 0 or symbols.max() > 255):
print(f"WARNING: Symbol predictions out of uint8 range: [{symbols.min()}, {symbols.max()}]")
print("✓ Layer consistency validation passed")
except KeyError as e:
print(f"✗ Layer validation failed: Missing layer {e}")
except Exception as e:
print(f"✗ Layer validation error: {e}")
# Run validation during pipeline execution
validate_layer_consistency()temp_, debug_)show_access_count() to identify unused layersKeyError exceptions when retrieving layersThe layer management system provides a flexible foundation for the oemer pipeline, enabling modular development, easy debugging, and efficient memory usage.
Install with Tessl CLI
npx tessl i tessl/pypi-oemer