Skip to main content

Process and convert between different AAC file formats

Project description

AACProcessors

PyPI version Python Versions Tests Coverage License: AGPL v3

A Python library to read, import, export and modify pagesets from different AAC (Augmentative and Alternative Communication) providers. 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)

Features

  • 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

Installation

pip install aac-processors

Quick Start

from 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")

Try it now!

Want to try AACProcessors without installing? Click below to run our interactive demo:

Open In Colab

This interactive notebook will:

  • Install AACProcessors
  • Download example AAC files
  • Demonstrate key features:
    • Viewing file structures
    • Extracting texts
    • Analyzing vocabulary
    • Converting between formats

Usage Examples

Converting Between Formats (OBZ Import/Export)

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")

Viewing File Structure

The library includes two ways to view AAC board structures:

  1. 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
  1. 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 path

The 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

Vocabulary Analysis

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")

Demo

See demo.py for a complete example of how to use the library.

python demo.py

Output:

=== 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

Supported Formats

Grid 3 (.gridset)

  • Full support for reading grid layouts
  • Text extraction from buttons and pages
  • Translation support

CoughDrop (.obf, .obz)

  • Support for both single board (.obf) and board set (.obz) formats
  • Extraction of button labels and messages
  • Translation capabilities

TouchChat (.touchChat)

  • Support for TouchChat page sets
  • Button text extraction
  • Page structure loading

Snap Core First (.spb)

  • Support for Snap Core First board sets
  • Text extraction from buttons and pages
  • Basic translation support

Screenshot Processor (optional)

The screenshot processor is an optional dependency that requires additional dependencies. Install it with:

pip install aac-processors[screenshot]

screenshot Usage

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 green
  • input.png.text_debug.png: Shows detected text regions in blue with text overlay

Command Line Interface

The package provides a command-line interface (CLI) for viewing and converting AAC files. After installation, you can use it in two ways:

Interactive Mode

Simply run without arguments to enter interactive mode:

aac-processors

This will guide you through:

  1. Selecting an AAC file (with tab completion)
  2. Choosing to view its structure or convert it
  3. If converting, selecting the target format and output path

Command Line Mode

For direct command-line usage:

  1. View an AAC file structure:
aac-processors view input.gridset
  1. 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.gridset

API Documentation

Base Classes

FileProcessor

The 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"""

Common Data Structures

AACTree

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"""

AACPage

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 page

AACButton

Represents 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 buttons

Contributing

Contributions 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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Testing

To run the tests:

python -m pytest

Current test coverage: 52%

Dev

uv pip install -e ".[dev,screenshot]"

License

This project is licensed under the AGPLv3 License - see the LICENSE file for details.

Acknowledgments

  • OpenAAC
  • The AAC community for their feedback and support

Contributors

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aac_processors-0.1.8.tar.gz (22.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aac_processors-0.1.8-py3-none-any.whl (73.9 kB view details)

Uploaded Python 3

File details

Details for the file aac_processors-0.1.8.tar.gz.

File metadata

  • Download URL: aac_processors-0.1.8.tar.gz
  • Upload date:
  • Size: 22.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for aac_processors-0.1.8.tar.gz
Algorithm Hash digest
SHA256 a9f848433db34667b0e7160896bcb41d42d29c8b11f48964253bc67691b955e6
MD5 9a80dc6d76253543c90f80e0297c7539
BLAKE2b-256 559483d2cdebcdaa4f30eb6c7c107656f3de2ce754474e1aeab6783c124037e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for aac_processors-0.1.8.tar.gz:

Publisher: publish.yml on willwade/AACProcessors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aac_processors-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: aac_processors-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 73.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for aac_processors-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 60b8f1e48c132ecff069af9a157e90d3d3f383a224846dcf9358536384701067
MD5 8cc40ccafc9cc3e82c74980607762eaf
BLAKE2b-256 57a9cd3c550ba306144399b93730c15c2a2235acec74db6ad19bb481cdd49eb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for aac_processors-0.1.8-py3-none-any.whl:

Publisher: publish.yml on willwade/AACProcessors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page