Metadata-Version: 2.4
Name: ayase
Version: 0.1.15
Summary: Modular media quality metrics toolkit
Project-URL: Homepage, https://github.com/seruva19/ayase
Project-URL: Repository, https://github.com/seruva19/ayase
Project-URL: Issues, https://github.com/seruva19/ayase/issues
Project-URL: Changelog, https://github.com/seruva19/ayase/blob/main/CHANGELOG.md
Author: Ayase Contributors
License-Expression: MIT
License-File: LICENSE
Keywords: ai,computer-vision,dataset,image,metrics,ml,quality-assessment,video,vqa
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Requires-Dist: imageio-ffmpeg<1.0,>=0.4.9
Requires-Dist: imageio<3.0,>=2.33.0
Requires-Dist: numpy<3.0,>=1.24.0
Requires-Dist: opencv-python<5.0,>=4.9.0
Requires-Dist: pandas<3.0,>=2.1.0
Requires-Dist: pillow<13.0,>=10.2.0
Requires-Dist: pyarrow<23.0,>=15.0.0
Requires-Dist: pydantic-settings<3.0,>=2.1.0
Requires-Dist: pydantic<3.0,>=2.5.0
Requires-Dist: rich<15.0,>=13.7.0
Requires-Dist: tomli-w<2.0,>=1.0.0
Requires-Dist: tomli<3.0,>=2.0.1; python_version < '3.11'
Requires-Dist: tqdm<5.0,>=4.66.0
Requires-Dist: typer[all]<1.0,>=0.12.0
Requires-Dist: typing-extensions>=4.0.0; python_version < '3.11'
Provides-Extra: all
Requires-Dist: accelerate>=0.25.0; extra == 'all'
Requires-Dist: black>=24.0.0; extra == 'all'
Requires-Dist: decord>=0.6.0; extra == 'all'
Requires-Dist: diffusers>=0.25.0; extra == 'all'
Requires-Dist: einops>=0.7.0; extra == 'all'
Requires-Dist: huggingface-hub>=0.20.0; extra == 'all'
Requires-Dist: insightface>=0.7.0; extra == 'all'
Requires-Dist: invisible-watermark>=0.2.0; extra == 'all'
Requires-Dist: librosa>=0.10.0; extra == 'all'
Requires-Dist: lpips>=0.1.4; extra == 'all'
Requires-Dist: mediapipe<1.0,>=0.10.0; extra == 'all'
Requires-Dist: mypy>=1.8.0; extra == 'all'
Requires-Dist: nltk>=3.8.0; extra == 'all'
Requires-Dist: onnxruntime>=1.14.0; extra == 'all'
Requires-Dist: open-clip-torch>=2.24.0; extra == 'all'
Requires-Dist: paddleocr<3.0,>=2.7.0; extra == 'all'
Requires-Dist: paddlepaddle<3.0,>=2.5.0; extra == 'all'
Requires-Dist: pyiqa<1.0,>=0.1.13; extra == 'all'
Requires-Dist: pytest-cov>=4.1.0; extra == 'all'
Requires-Dist: pytest-sugar>=1.0.0; extra == 'all'
Requires-Dist: pytest>=7.4.0; extra == 'all'
Requires-Dist: python-levenshtein>=0.21.0; extra == 'all'
Requires-Dist: pyyaml>=6.0; extra == 'all'
Requires-Dist: requests>=2.31.0; extra == 'all'
Requires-Dist: ruff>=0.1.0; extra == 'all'
Requires-Dist: safetensors>=0.4.0; extra == 'all'
Requires-Dist: scikit-learn>=1.3.0; extra == 'all'
Requires-Dist: scikit-video>=1.1.11; extra == 'all'
Requires-Dist: scipy>=1.11.0; extra == 'all'
Requires-Dist: soundfile>=0.12.0; extra == 'all'
Requires-Dist: t2v-metrics>=1.0.0; extra == 'all'
Requires-Dist: textual>=0.52.0; extra == 'all'
Requires-Dist: timm>=0.9.12; extra == 'all'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'all'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'all'
Requires-Dist: transformers<5.0,>=4.36.0; extra == 'all'
Requires-Dist: transnetv2-pytorch>=1.0.0; extra == 'all'
Requires-Dist: ultralytics<9.0,>=8.0.0; extra == 'all'
Requires-Dist: umap-learn>=0.5.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=24.0.0; extra == 'dev'
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-sugar>=1.0.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: i-core
Provides-Extra: ml
Requires-Dist: accelerate>=0.25.0; extra == 'ml'
Requires-Dist: decord>=0.6.0; extra == 'ml'
Requires-Dist: diffusers>=0.25.0; extra == 'ml'
Requires-Dist: einops>=0.7.0; extra == 'ml'
Requires-Dist: huggingface-hub>=0.20.0; extra == 'ml'
Requires-Dist: lpips>=0.1.4; extra == 'ml'
Requires-Dist: nltk>=3.8.0; extra == 'ml'
Requires-Dist: open-clip-torch>=2.24.0; extra == 'ml'
Requires-Dist: paddleocr<3.0,>=2.7.0; extra == 'ml'
Requires-Dist: paddlepaddle<3.0,>=2.5.0; extra == 'ml'
Requires-Dist: pyiqa<1.0,>=0.1.13; extra == 'ml'
Requires-Dist: python-levenshtein>=0.21.0; extra == 'ml'
Requires-Dist: pyyaml>=6.0; extra == 'ml'
Requires-Dist: requests>=2.31.0; extra == 'ml'
Requires-Dist: safetensors>=0.4.0; extra == 'ml'
Requires-Dist: scikit-learn>=1.3.0; extra == 'ml'
Requires-Dist: scikit-video>=1.1.11; extra == 'ml'
Requires-Dist: scipy>=1.11.0; extra == 'ml'
Requires-Dist: timm>=0.9.12; extra == 'ml'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'ml'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'ml'
Requires-Dist: transformers<5.0,>=4.36.0; extra == 'ml'
Requires-Dist: ultralytics<9.0,>=8.0.0; extra == 'ml'
Provides-Extra: tui
Requires-Dist: textual>=0.52.0; extra == 'tui'
Provides-Extra: v-align
Requires-Dist: t2v-metrics>=1.0.0; extra == 'v-align'
Provides-Extra: v-audio
Requires-Dist: librosa>=0.10.0; extra == 'v-audio'
Requires-Dist: soundfile>=0.12.0; extra == 'v-audio'
Provides-Extra: v-face
Requires-Dist: mediapipe<1.0,>=0.10.0; extra == 'v-face'
Provides-Extra: v-identity
Requires-Dist: insightface>=0.7.0; extra == 'v-identity'
Requires-Dist: onnxruntime>=1.14.0; extra == 'v-identity'
Provides-Extra: v-iqa
Requires-Dist: pyiqa<1.0,>=0.1.13; extra == 'v-iqa'
Requires-Dist: scikit-video>=1.1.11; extra == 'v-iqa'
Requires-Dist: scipy>=1.11.0; extra == 'v-iqa'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-iqa'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'v-iqa'
Provides-Extra: v-ml
Requires-Dist: accelerate>=0.25.0; extra == 'v-ml'
Requires-Dist: decord>=0.6.0; extra == 'v-ml'
Requires-Dist: diffusers>=0.25.0; extra == 'v-ml'
Requires-Dist: einops>=0.7.0; extra == 'v-ml'
Requires-Dist: huggingface-hub>=0.20.0; extra == 'v-ml'
Requires-Dist: lpips>=0.1.4; extra == 'v-ml'
Requires-Dist: nltk>=3.8.0; extra == 'v-ml'
Requires-Dist: open-clip-torch>=2.24.0; extra == 'v-ml'
Requires-Dist: paddleocr<3.0,>=2.7.0; extra == 'v-ml'
Requires-Dist: paddlepaddle<3.0,>=2.5.0; extra == 'v-ml'
Requires-Dist: pyiqa<1.0,>=0.1.13; extra == 'v-ml'
Requires-Dist: python-levenshtein>=0.21.0; extra == 'v-ml'
Requires-Dist: pyyaml>=6.0; extra == 'v-ml'
Requires-Dist: requests>=2.31.0; extra == 'v-ml'
Requires-Dist: safetensors>=0.4.0; extra == 'v-ml'
Requires-Dist: scikit-learn>=1.3.0; extra == 'v-ml'
Requires-Dist: scikit-video>=1.1.11; extra == 'v-ml'
Requires-Dist: scipy>=1.11.0; extra == 'v-ml'
Requires-Dist: timm>=0.9.12; extra == 'v-ml'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-ml'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'v-ml'
Requires-Dist: transformers<5.0,>=4.36.0; extra == 'v-ml'
Requires-Dist: ultralytics<9.0,>=8.0.0; extra == 'v-ml'
Provides-Extra: v-motion
Requires-Dist: decord>=0.6.0; extra == 'v-motion'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-motion'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'v-motion'
Provides-Extra: v-nemo
Requires-Dist: huggingface-hub>=0.20.0; extra == 'v-nemo'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-nemo'
Requires-Dist: transformers<5.0,>=4.36.0; extra == 'v-nemo'
Provides-Extra: v-object
Requires-Dist: ultralytics<9.0,>=8.0.0; extra == 'v-object'
Provides-Extra: v-ocr
Requires-Dist: paddleocr<3.0,>=2.7.0; extra == 'v-ocr'
Requires-Dist: paddlepaddle<3.0,>=2.5.0; extra == 'v-ocr'
Requires-Dist: python-levenshtein>=0.21.0; extra == 'v-ocr'
Provides-Extra: v-perceptual
Requires-Dist: einops>=0.7.0; extra == 'v-perceptual'
Requires-Dist: lpips>=0.1.4; extra == 'v-perceptual'
Requires-Dist: open-clip-torch>=2.24.0; extra == 'v-perceptual'
Requires-Dist: timm>=0.9.12; extra == 'v-perceptual'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-perceptual'
Requires-Dist: torchvision<1.0,>=0.16.0; extra == 'v-perceptual'
Provides-Extra: v-scene
Requires-Dist: transnetv2-pytorch>=1.0.0; extra == 'v-scene'
Provides-Extra: v-text
Requires-Dist: pyyaml>=6.0; extra == 'v-text'
Requires-Dist: requests>=2.31.0; extra == 'v-text'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-text'
Requires-Dist: transformers<5.0,>=4.36.0; extra == 'v-text'
Provides-Extra: v-umap
Requires-Dist: open-clip-torch>=2.24.0; extra == 'v-umap'
Requires-Dist: scikit-learn>=1.3.0; extra == 'v-umap'
Requires-Dist: torch<3.0,>=2.1.0; extra == 'v-umap'
Requires-Dist: umap-learn>=0.5.0; extra == 'v-umap'
Provides-Extra: v-watermark
Requires-Dist: invisible-watermark>=0.2.0; extra == 'v-watermark'
Description-Content-Type: text/markdown

# Ayase

Modular media quality metrics toolkit.  

⚠️ Work in progress. Some features may not work as expected.

## Overview

- 246 quality metrics across visual, temporal, audio, perceptual, and safety categories.
- Modular pipeline - modules compute raw values, downstream apps decide what to do with them.
- CLI and Python API.
- Profile-based pipeline configuration.

## Installation

Core (no ML models, metadata/structural checks only):

```bash
pip install ayase
```

With ML-based quality metrics:

```bash
pip install ayase[ml]           # Everything
pip install ayase[v-perceptual] # CLIP, LPIPS, open-clip, timm
pip install ayase[v-iqa]        # PyIQA, BRISQUE, NIQE, scikit-video
pip install ayase[v-motion]     # RAFT optical flow, decord
pip install ayase[v-ocr]        # PaddleOCR text recognition
pip install ayase[v-face]       # MediaPipe face detection
pip install ayase[v-audio]      # librosa audio analysis
```

Development:

```bash
pip install ayase[dev]          # pytest, black, ruff, mypy
pip install ayase[all]          # Everything including dev + TUI
```

See [MODELS.md](MODELS.md) for the complete inventory of all pretrained weights used by every module. Models are downloaded and cached automatically on first use via HuggingFace Hub, Torch Hub, and PyIQA.

## Metrics

208 modules across image, video, and audio quality assessment. See [METRICS.md](METRICS.md) for the full reference with config parameters.

Categories: visual quality (NR/FR), perceptual similarity, motion, temporal consistency, audio, text alignment, HDR, safety, face, depth, codec.


## Quick Start

### CLI

```bash
# Scan a dataset and get a report
ayase scan ./my_dataset

# Scan with specific modules
ayase scan ./my_dataset --modules metadata,basic_quality,motion

# List all available modules
ayase modules list

# Check which modules can be loaded (dependencies installed)
ayase modules check

# Filter dataset by quality score
ayase filter ./my_dataset --min-score 70 --output ./filtered
```

### Python API (recommended)

```python
from ayase import AyasePipeline

ayase = AyasePipeline(modules=["basic"])
results = ayase.run("./my_dataset")

for path, sample in results.items():
    if sample.quality_metrics:
        print(f"{sample.path.name}: technical={sample.quality_metrics.technical_score}")

print(f"Total: {ayase.stats.total_samples}, Valid: {ayase.stats.valid_samples}")
ayase.export("report.json")
```

`AyasePipeline` accepts three ways to configure modules:

```python
# By module names
ayase = AyasePipeline(modules=["metadata", "basic_quality", "motion"])

# By profile dict
ayase = AyasePipeline(profile={
    "name": "my_check",
    "modules": ["basic", "aesthetic"],
    "module_config": {
        "aesthetic": {"model_name": "openai/clip-vit-large-patch14"},
    },
})

# By profile file
ayase = AyasePipeline(profile="my_profile.toml")

# With custom config
from ayase.config import AyaseConfig
ayase = AyasePipeline(config=AyaseConfig(general={"parallel_jobs": 16}), modules=["basic"])
```

### Low-level Pipeline API

```python
import asyncio
from pathlib import Path
from ayase.pipeline import Pipeline, ModuleRegistry
from ayase.scanner import scan_dataset

ModuleRegistry.discover_modules()
module_names = ["metadata", "basic_quality", "semantic_alignment"]
modules = [ModuleRegistry.get_module(n)() for n in module_names]

pipeline = Pipeline(modules)
pipeline.start()

samples = scan_dataset(Path("./my_dataset"), recursive=True)
for sample in samples:
    processed = asyncio.run(pipeline.process_sample(sample))

pipeline.stop()
pipeline.export_report("report.json", format="json")
```

### Profile-based pipelines

```python
from ayase import load_profile, instantiate_profile_modules

profile = load_profile("my_profile.toml")
modules = instantiate_profile_modules(profile)
# modules is a list of PipelineModule instances ready for Pipeline()
```

## Configuration

Create `ayase.toml` in your project root:

```toml
[general]
parallel_jobs = 8
cache_enabled = true

[quality]
enable_blur_detection = true
blur_threshold = 100.0

[pipeline]
dataset_path = "./my_dataset"
modules = ["metadata", "basic_quality", "motion"]
plugin_folders = ["plugins"]

[output]
default_format = "markdown"
artifacts_dir = "reports"
artifacts_format = "json"

[filter]
default_mode = "list"
min_score_threshold = 60
```

Ayase looks for config in: `./ayase.toml` -> `~/.config/ayase/config.toml` -> built-in defaults.

## Writing Plugins

Create a `.py` file in your `plugins/` folder:

```python
from ayase.models import Sample, ValidationIssue, ValidationSeverity
from ayase.pipeline import PipelineModule

class MyCustomCheck(PipelineModule):
    name = "my_check"
    description = "Custom quality check"
    default_config = {"threshold": 0.5}

    def process(self, sample: Sample) -> Sample:
        # Your logic here
        if some_score < self.config["threshold"]:
            sample.validation_issues.append(
                ValidationIssue(
                    severity=ValidationSeverity.WARNING,
                    message="Quality below threshold",
                )
            )
        return sample
```

Then run:

```bash
ayase scan ./data --modules metadata,my_check
```

## Development

```bash
git clone <repo-url>
cd ayase
pip install -e ".[dev]"

# Run tests
pytest

# Lint and format
ruff check src/ tests/
black src/ tests/

# Type check
mypy src/ayase
```

## License

MIT - see [LICENSE](LICENSE).
