A Python library to read, import, export and modify pagesets from different AAC (Augmentative and Alternative Communication) providers. NB: Node/JS version at https://github.com/willwade/AACProcessors-nodejs and probably more coverage now. Currently supports:
- Grid 3 (
.gridset) - CoughDrop (OpenBoard) (
.obf,.obz) - TouchChat (
.touchChat) - Snap Core First (
.spb)
And unusual formats:
- Dot Processor (.dot)
- OPML (.opml)
- Apple Panels
- Screenshot Processor (optional)
- Extract text content from AAC pagesets
- Load AAC pagesets into a common tree structure
- Support for multiple AAC software formats
- Translation support for extracted text
- Consistent API across different AAC formats
- Convert between different AAC formats
- Analyze vocabulary usage and structure
pip install aac-processorsfrom aac_processors import GridsetProcessor, CoughDropProcessor, TouchChatProcessor, SnapProcessor
# Example with Grid 3
processor = GridsetProcessor()
texts = processor.extract_texts("path/to/your.gridset")
print(f"Found {len(texts)} text items")
# Load into tree structure
tree = processor.load_into_tree("path/to/your.gridset")
print(f"Found {len(tree.pages)} pages")
# Translate texts
translations = {
"Hello": "Hola",
"Goodbye": "Adiós"
}
translated_file = processor.process_texts("path/to/your.gridset", translations, "path/to/output.gridset")Want to try AACProcessors without installing? Click below to run our interactive demo:
This interactive notebook will:
- Install AACProcessors
- Download example AAC files
- Demonstrate key features:
- Viewing file structures
- Extracting texts
- Analyzing vocabulary
- Converting between formats
from aac_processors import GridsetProcessor, CoughDropProcessor
# Convert Grid3 to CoughDrop OBZ
grid_processor = GridsetProcessor()
cough_processor = CoughDropProcessor()
# Load Grid3 file into common tree structure
tree = grid_processor.load_into_tree("path/to/your.gridset")
# Export tree to CoughDrop OBZ format
cough_processor.export_tree(tree, "output.obz")
# Import from OBZ
imported_tree = cough_processor.load_into_tree("path/to/board.obz")The library includes two ways to view AAC board structures:
- Using the command-line viewer:
# Using the standalone viewer script
python demo_viewer.py path/to/your/board.gridset
# Or using the package module directly
python -m aac_processors.viewer path/to/your/board.gridset
# Programmatically:
from aac_processors import viewer, GridsetProcessor
# Load and print a board structure
processor = GridsetProcessor()
tree = processor.load_into_tree("path/to/your.gridset")
viewer.print_tree(tree)
# Or use the auto-detection feature
viewer.main() # Will prompt for file path- Programmatically:
from aac_processors import viewer, GridsetProcessor
from aac_processors.tree_structure import ButtonType
# Load and print a board structure
processor = GridsetProcessor()
tree = processor.load_into_tree("path/to/your.gridset")
viewer.print_tree(tree)
# Or use the auto-detection feature
viewer.main() # Will prompt for file pathThe viewer will show:
- Complete board structure with pages and buttons
- Button types (🗣️ Speech, 🔀 Navigation, ⚡ Action)
- Grid layout and button positions
- Navigation analysis (dead ends, orphaned pages)
- Circular references in navigation
from aac_processors import GridsetProcessor
from collections import Counter
import re
def analyze_vocabulary(file_path):
processor = GridsetProcessor()
tree = processor.load_into_tree(file_path)
# Collect all text from buttons
words = []
for page in tree.pages.values():
for button in page.buttons:
if button.label:
# Split into words and clean
button_words = re.findall(r'\w+', button.label.lower())
words.extend(button_words)
if button.message:
message_words = re.findall(r'\w+', button.message.lower())
words.extend(message_words)
# Count word frequencies
word_counts = Counter(words)
# Print statistics
print(f"Total unique words: {len(word_counts)}")
print("\nMost common words:")
for word, count in word_counts.most_common(10):
print(f" {word}: {count}")
return word_counts
# Example usage
vocabulary = analyze_vocabulary("path/to/your.gridset")See demo.py for a complete example of how to use the library.
python demo.pyOutput:
=== Viewing File Structure ===
Viewing structure of SimpleTest.gridset:
=== AAC Board Structure ===
Root Page:
📄 Start (4x4 grid)
Row 0:
[Empty] (0, 0)
🔀 quick chat (0, 1)
└─ Says: quick chat
└─ Goes to: quick chat
🔀 don't like (0, 2)
└─ Says: don't like
└─ Goes to: Don't like
🔀 something different (0, 3)
└─ Says: something different
└─ Goes to: something different
Row 1:
[Empty] (1, 0)
🔀 something's wrong (1, 1)
└─ Says: something's wrong
└─ Goes to: something's wrong
🔀 I want (1, 2)
└─ Says: I want
└─ Goes to: I want
🔀 Comment (1, 3)
└─ Says: Comment
└─ Goes to: Comment
Row 2:
[Empty] (2, 0)
🔀 About me (2, 1)
└─ Says: About me
└─ Goes to: About me
└─ Target Page:
📄 About me (5x4 grid)
Row 0:
🗣️ [No Label] (0, 0)
🗣️ Back (0, 1)
└─ Says: Back
🔀 Family (0, 2)
└─ Says: Family
└─ Goes to: Family
└─ Target Page:
📄 Family (5x7 grid)
Row 0:
[Empty] (0, 0)
🗣️ Back (0, 1)
└─ Says: Back
[Empty] (0, 2)
🗣️ Younger Sister (0, 3)
└─ Says: Younger Sister
[Empty] (0, 4)
[Empty] (0, 5)
[Empty] (0, 6)
Row 1:
[Empty] (1, 0)
🗣️ Dad (1, 1)
└─ Says: Dad
[Empty] (1, 2)
[Empty] (1, 3)
[Empty] (1, 4)
[Empty] (1, 5)
[Empty] (1, 6)
Row 2:
[Empty] (2, 0)
[Empty] (2, 1)
[Empty] (2, 2)
🔀 Cousins (2, 3)
└─ Says: Cousins
└─ Goes to: Cousins
[Empty] (2, 4)
[Empty] (2, 5)
[Empty] (2, 6)
Row 3:
[Empty] (3, 0)
🗣️ Mum (3, 1)
└─ Says: Mum
[Empty] (3, 2)
[Empty] (3, 3)
[Empty] (3, 4)
[Empty] (3, 5)
[Empty] (3, 6)
Row 4:
[Empty] (4, 0)
[Empty] (4, 1)
[Empty] (4, 2)
🔀 Aunts and Uncles (4, 3)
└─ Says: Aunts and Uncles
└─ Goes to: Aunts and Uncles
[Empty] (4, 4)
[Empty] (4, 5)
[Empty] (4, 6)
🗣️ Vegetarian (0, 3)
└─ Says: Vegetarian
Row 1:
[Empty] (1, 0)
🗣️ Name (1, 1)
└─ Says: Name
🔀 Pets (1, 2)
└─ Says: Pets
└─ Goes to: Pets
[Empty] (1, 3)
Row 2:
[Empty] (2, 0)
🗣️ Age (2, 1)
└─ Says: Age
🔀 Places I have been (2, 2)
└─ Says: Places I have been
└─ Goes to: Places I have been
[Empty] (2, 3)
Row 3:
[Empty] (3, 0)
🗣️ My birthday (3, 1)
└─ Says: My birthday
🗣️ like to look (3, 2)
└─ Says: like to look
[Empty] (3, 3)
Row 4:
[Empty] (4, 0)
[Empty] (4, 1)
[Empty] (4, 2)
[Empty] (4, 3)
🔀 I'm asking a question (2, 2)
└─ Says: I'm asking a question
└─ Goes to: questions
🔀 I feel (2, 3)
└─ Says: I feel
└─ Goes to: Feelings
└─ Target Page:
📄 Feelings (6x4 grid)
Row 0:
🗣️ [No Label] (0, 0)
🗣️ Back (0, 1)
└─ Says: Back
🗣️ angry. (0, 2)
└─ Says: angry.
[Empty] (0, 3)
Row 1:
[Empty] (1, 0)
🗣️ happy. (1, 1)
└─ Says: happy.
🗣️ excited. (1, 2)
└─ Says: excited.
[Empty] (1, 3)
Row 2:
[Empty] (2, 0)
🗣️ sad. (2, 1)
└─ Says: sad.
🗣️ fantastic. (2, 2)
└─ Says: fantastic.
[Empty] (2, 3)
Row 3:
[Empty] (3, 0)
🗣️ tired. (3, 1)
└─ Says: tired.
[Empty] (3, 2)
[Empty] (3, 3)
Row 4:
[Empty] (4, 0)
[Empty] (4, 1)
[Empty] (4, 2)
[Empty] (4, 3)
Row 5:
[Empty] (5, 0)
[Empty] (5, 1)
[Empty] (5, 2)
[Empty] (5, 3)
Row 3:
[Empty] (3, 0)
🔀 like (3, 1)
└─ Says: like
└─ Goes to: Like
🔀 Places to go (3, 2)
└─ Says: Places to go
└─ Goes to: let's go
🔀 Alphabet (3, 3)
└─ Says: Alphabet
└─ Goes to: Alphabet
=== Navigation Analysis ===
Total Pages: 6
Dead End Pages (no way back):
- Family
- Feelings
Orphaned Pages (no way to reach):
- Drinks
- About me 2
=== Extracting Texts ===
Extracted 43 texts from SimpleTest.gridset
Sample texts: ["something's wrong", 'Age', 'Back to home page', 'I feel', 'Pets']
=== Loading Tree Structure ===
Loaded 6 pages from SimpleTest.gridset
Page Drinks: Drinks
Grid size: (5, 4)
Buttons: 7
Page Family: Family
Grid size: (5, 7)
Buttons: 8- Full support for reading grid layouts
- Text extraction from buttons and pages
- Translation support
- Support for both single board (
.obf) and board set (.obz) formats - Extraction of button labels and messages
- Translation capabilities
- Support for TouchChat page sets
- Button text extraction
- Page structure loading
- Support for Snap Core First board sets
- Text extraction from buttons and pages
- Basic translation support
The screenshot processor is an optional dependency that requires additional dependencies. Install it with:
pip install aac-processors[screenshot]from aac_processors import ScreenshotProcessor
# Create processor
processor = ScreenshotProcessor()
# Load and print a board structure from screenshot
tree = processor.load_into_tree(
"path/to/screenshot.png",
grid_rows=6, # Optional: specify grid dimensions if known
grid_cols=4 # e.g. 6x4 for TouchChat24, 6x10 for TouchChat60
)
viewer.print_tree(tree)
# Extract text from screenshot
texts = processor.extract_texts("path/to/screenshot.png")
print(texts)
# Initialize processor with debug images enabled (optional)
processor = ScreenshotProcessor(save_debug_images=True)
# Get detailed page info including colors and grid layout
page = processor.create_page_from_screenshot(
"path/to/screenshot.png",
grid_rows=6, # Number of rows if known
grid_cols=4, # Number of columns if known
ignore_rows=1, # Skip top N rows (e.g. for menu bars)
)
print(f"Grid size: {page.grid_size}")
for btn in page.buttons:
print(f"Button at {btn.position}: {btn.label} (color: {btn.style.body_color})")Debug Images: When save_debug_images=True is set, the processor creates two visualization files alongside the input image:
input.png.debug.png: Shows the detected grid cells in greeninput.png.text_debug.png: Shows detected text regions in blue with text overlay
The package provides a command-line interface (CLI) for viewing and converting AAC files. After installation, you can use it in two ways:
Simply run without arguments to enter interactive mode:
aac-processorsThis will guide you through:
- Selecting an AAC file (with tab completion)
- Choosing to view its structure or convert it
- If converting, selecting the target format and output path
For direct command-line usage:
- View an AAC file structure:
aac-processors view input.gridset- Convert between formats:
# Basic conversion (auto-generates output filename)
aac-processors convert input.gridset --to coughdrop
# Specify custom output path
aac-processors convert input.obf --to grid --output custom_name.gridsetThe base class that all format-specific processors inherit from.
class FileProcessor:
def extract_texts(self, file_path: str) -> List[str]:
"""Extract all text content from the file"""
def load_into_tree(self, file_path: str) -> AACTree:
"""Load the file into a common tree structure"""
def process_texts(
self,
file_path: str,
translations: Optional[Dict[str, str]] = None,
output_path: Optional[str] = None
) -> Union[List[str], str, None]:
"""Process and translate texts in the file.
Returns:
- List[str] if extracting texts (translations=None)
- str if translating (path to translated file)
- None if error occurs
"""
def set_source_file(self, file_path: str) -> None:
"""Set the source file path for processing"""
def cleanup_temp_files(self) -> None:
"""Clean up any temporary files created during processing"""Represents the common structure for all AAC formats.
class AACTree:
pages: Dict[str, AACPage] # Dictionary of pages by ID
root_id: Optional[str] # ID of the root page
def add_page(self, page: AACPage) -> None:
"""Add a page to the tree"""
def get_page(self, page_id: str) -> Optional[AACPage]:
"""Get a page by ID"""Represents a single page in an AAC system.
class AACPage:
id: str # Unique identifier for the page
name: str # Display name of the page
grid_size: Tuple[int, int] # (rows, columns)
buttons: List[AACButton] # List of buttons on the pageRepresents a button in an AAC system.
class AACButton:
id: str # Unique identifier for the button
label: str # Display text
type: ButtonType # SPEAK, NAVIGATE, ACTION, etc.
position: Tuple[int, int] # Grid position (row, col)
vocalization: Optional[str] # Text to speak
target_page_id: Optional[str] # For navigation buttonsContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
To run the tests:
python -m pytestCurrent test coverage: 52%
uv pip install -e ".[dev,screenshot]"This project is licensed under the AGPLv3 License - see the LICENSE file for details.
- OpenAAC
- The AAC community for their feedback and support