Metadata-Version: 2.4
Name: qualisr-lab
Version: 0.2.0
Summary: A toolkit for extracting IQA features, training regressors, and analyzing super-resolution image quality against human scores.
Author-email: Oleg Ryabinin <oleg.ryabinin@graphics.cs.msu.ru>, Evgeney Bogatyrev <evgeney.bogatyrev@graphics.cs.msu.ru>, Khaled Abud <khaled.abud@graphics.cs.msu.ru>, Dmitriy Vatolin <dmitriy@graphics.cs.msu.ru>
License-Expression: BSD-3-Clause
Project-URL: Homepage, https://github.com/sangwyn/QualiSR-Lab
Project-URL: Repository, https://github.com/sangwyn/QualiSR-Lab
Keywords: image-quality-assessment,super-resolution,reduced-reference,multimedia
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Image Processing
Requires-Python: <3.14,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
Requires-Dist: matplotlib==3.10.9
Requires-Dist: numpy==2.2.6
Requires-Dist: pandas==2.3.3
Requires-Dist: pillow==12.2.0
Requires-Dist: scikit-learn==1.7.2
Requires-Dist: scipy==1.18.0
Requires-Dist: tqdm==4.67.3
Requires-Dist: huggingface_hub==1.12.0
Provides-Extra: regressors
Requires-Dist: catboost==1.2.10; extra == "regressors"
Requires-Dist: xgboost==3.2.0; extra == "regressors"
Provides-Extra: features
Requires-Dist: bitsandbytes==0.49.2; extra == "features"
Requires-Dist: icecream==2.2.0; extra == "features"
Requires-Dist: opencv-python==4.13.0.92; extra == "features"
Requires-Dist: pyiqa==0.1.15.post2; extra == "features"
Requires-Dist: timm==1.0.26; extra == "features"
Requires-Dist: torch==2.11.0; extra == "features"
Requires-Dist: torchvision==0.26.0; extra == "features"
Requires-Dist: transformers==5.6.2; extra == "features"
Provides-Extra: notebook
Requires-Dist: ipykernel==7.2.0; extra == "notebook"
Requires-Dist: ipython==8.39.0; extra == "notebook"
Provides-Extra: dev
Requires-Dist: pytest==9.0.3; extra == "dev"
Requires-Dist: ruff==0.15.12; extra == "dev"
Provides-Extra: all
Requires-Dist: absl-py==2.4.0; extra == "all"
Requires-Dist: accelerate==1.13.0; extra == "all"
Requires-Dist: addict==2.4.0; extra == "all"
Requires-Dist: aiohappyeyeballs==2.6.1; extra == "all"
Requires-Dist: aiohttp==3.13.5; extra == "all"
Requires-Dist: aiosignal==1.4.0; extra == "all"
Requires-Dist: annotated-doc==0.0.4; extra == "all"
Requires-Dist: anyio==4.13.0; extra == "all"
Requires-Dist: asttokens==3.0.1; extra == "all"
Requires-Dist: async-timeout==5.0.1; extra == "all"
Requires-Dist: attrs==26.1.0; extra == "all"
Requires-Dist: beautifulsoup4==4.14.3; extra == "all"
Requires-Dist: bitsandbytes==0.49.2; extra == "all"
Requires-Dist: catboost==1.2.10; extra == "all"
Requires-Dist: certifi==2026.4.22; extra == "all"
Requires-Dist: cfgv==3.5.0; extra == "all"
Requires-Dist: charset-normalizer==3.4.7; extra == "all"
Requires-Dist: click==8.3.3; extra == "all"
Requires-Dist: colorama==0.4.6; extra == "all"
Requires-Dist: comm==0.2.3; extra == "all"
Requires-Dist: contourpy==1.3.2; extra == "all"
Requires-Dist: cuda-bindings==13.2.0; extra == "all"
Requires-Dist: cuda-pathfinder==1.5.3; extra == "all"
Requires-Dist: cuda-toolkit==13.0.2; extra == "all"
Requires-Dist: cycler==0.12.1; extra == "all"
Requires-Dist: datasets==4.8.4; extra == "all"
Requires-Dist: debugpy==1.8.20; extra == "all"
Requires-Dist: decorator==5.2.1; extra == "all"
Requires-Dist: dill==0.4.1; extra == "all"
Requires-Dist: distlib==0.4.0; extra == "all"
Requires-Dist: einops==0.8.2; extra == "all"
Requires-Dist: exceptiongroup==1.3.1; extra == "all"
Requires-Dist: executing==2.2.1; extra == "all"
Requires-Dist: facexlib==0.3.0; extra == "all"
Requires-Dist: filelock==3.29.0; extra == "all"
Requires-Dist: filterpy==1.4.5; extra == "all"
Requires-Dist: fonttools==4.62.1; extra == "all"
Requires-Dist: frozenlist==1.8.0; extra == "all"
Requires-Dist: fsspec==2026.2.0; extra == "all"
Requires-Dist: ftfy==6.3.1; extra == "all"
Requires-Dist: future==1.0.0; extra == "all"
Requires-Dist: gdown==6.0.0; extra == "all"
Requires-Dist: graphviz==0.21; extra == "all"
Requires-Dist: grpcio==1.80.0; extra == "all"
Requires-Dist: h11==0.16.0; extra == "all"
Requires-Dist: hf-xet==1.4.3; extra == "all"
Requires-Dist: httpcore==1.0.9; extra == "all"
Requires-Dist: httpx==0.28.1; extra == "all"
Requires-Dist: huggingface_hub==1.12.0; extra == "all"
Requires-Dist: icecream==2.2.0; extra == "all"
Requires-Dist: identify==2.6.19; extra == "all"
Requires-Dist: idna==3.13; extra == "all"
Requires-Dist: ImageIO==2.37.3; extra == "all"
Requires-Dist: iniconfig==2.3.0; extra == "all"
Requires-Dist: ipykernel==7.2.0; extra == "all"
Requires-Dist: ipython==8.39.0; extra == "all"
Requires-Dist: jedi==0.19.2; extra == "all"
Requires-Dist: Jinja2==3.1.6; extra == "all"
Requires-Dist: joblib==1.5.3; extra == "all"
Requires-Dist: jupyter_client==8.8.0; extra == "all"
Requires-Dist: jupyter_core==5.9.1; extra == "all"
Requires-Dist: kiwisolver==1.5.0; extra == "all"
Requires-Dist: lazy-loader==0.5; extra == "all"
Requires-Dist: llvmlite==0.47.0; extra == "all"
Requires-Dist: lmdb==2.2.0; extra == "all"
Requires-Dist: Markdown==3.10.2; extra == "all"
Requires-Dist: markdown-it-py==4.0.0; extra == "all"
Requires-Dist: MarkupSafe==3.0.3; extra == "all"
Requires-Dist: matplotlib==3.10.9; extra == "all"
Requires-Dist: matplotlib-inline==0.2.1; extra == "all"
Requires-Dist: mdurl==0.1.2; extra == "all"
Requires-Dist: mpmath==1.3.0; extra == "all"
Requires-Dist: multidict==6.7.1; extra == "all"
Requires-Dist: multiprocess==0.70.19; extra == "all"
Requires-Dist: narwhals==2.20.0; extra == "all"
Requires-Dist: nest-asyncio==1.6.0; extra == "all"
Requires-Dist: networkx==3.4.2; extra == "all"
Requires-Dist: nodeenv==1.10.0; extra == "all"
Requires-Dist: numba==0.65.1; extra == "all"
Requires-Dist: numpy==2.2.6; extra == "all"
Requires-Dist: nvidia-cublas==13.1.0.3; extra == "all"
Requires-Dist: nvidia-cuda-cupti==13.0.85; extra == "all"
Requires-Dist: nvidia-cuda-nvrtc==13.0.88; extra == "all"
Requires-Dist: nvidia-cuda-runtime==13.0.96; extra == "all"
Requires-Dist: nvidia-cudnn-cu13==9.19.0.56; extra == "all"
Requires-Dist: nvidia-cufft==12.0.0.61; extra == "all"
Requires-Dist: nvidia-cufile==1.15.1.6; extra == "all"
Requires-Dist: nvidia-curand==10.4.0.35; extra == "all"
Requires-Dist: nvidia-cusolver==12.0.4.66; extra == "all"
Requires-Dist: nvidia-cusparse==12.6.3.3; extra == "all"
Requires-Dist: nvidia-cusparselt-cu13==0.8.0; extra == "all"
Requires-Dist: nvidia-nccl-cu12==2.30.4; extra == "all"
Requires-Dist: nvidia-nccl-cu13==2.28.9; extra == "all"
Requires-Dist: nvidia-nvjitlink==13.0.88; extra == "all"
Requires-Dist: nvidia-nvshmem-cu13==3.4.5; extra == "all"
Requires-Dist: nvidia-nvtx==13.0.85; extra == "all"
Requires-Dist: openai-clip==1.0.1; extra == "all"
Requires-Dist: opencv-python==4.13.0.92; extra == "all"
Requires-Dist: opencv-python-headless==4.13.0.92; extra == "all"
Requires-Dist: packaging==26.2; extra == "all"
Requires-Dist: pandas==2.3.3; extra == "all"
Requires-Dist: parso==0.8.6; extra == "all"
Requires-Dist: pexpect==4.9.0; extra == "all"
Requires-Dist: pillow==12.2.0; extra == "all"
Requires-Dist: platformdirs==4.9.6; extra == "all"
Requires-Dist: plotly==6.7.0; extra == "all"
Requires-Dist: pluggy==1.6.0; extra == "all"
Requires-Dist: pre_commit==4.6.0; extra == "all"
Requires-Dist: prompt_toolkit==3.0.52; extra == "all"
Requires-Dist: propcache==0.4.1; extra == "all"
Requires-Dist: protobuf==7.34.1; extra == "all"
Requires-Dist: psutil==7.2.2; extra == "all"
Requires-Dist: ptyprocess==0.7.0; extra == "all"
Requires-Dist: pure_eval==0.2.3; extra == "all"
Requires-Dist: pyarrow==24.0.0; extra == "all"
Requires-Dist: Pygments==2.20.0; extra == "all"
Requires-Dist: pyiqa==0.1.15.post2; extra == "all"
Requires-Dist: pyparsing==3.3.2; extra == "all"
Requires-Dist: PySocks==1.7.1; extra == "all"
Requires-Dist: pytest==9.0.3; extra == "all"
Requires-Dist: python-dateutil==2.9.0.post0; extra == "all"
Requires-Dist: python-discovery==1.2.2; extra == "all"
Requires-Dist: pytz==2026.1.post1; extra == "all"
Requires-Dist: PyYAML==6.0.3; extra == "all"
Requires-Dist: pyzmq==27.1.0; extra == "all"
Requires-Dist: regex==2026.4.4; extra == "all"
Requires-Dist: requests==2.33.1; extra == "all"
Requires-Dist: rich==15.0.0; extra == "all"
Requires-Dist: ruff==0.15.12; extra == "all"
Requires-Dist: safetensors==0.7.0; extra == "all"
Requires-Dist: scikit-image==0.25.2; extra == "all"
Requires-Dist: scikit-learn==1.7.2; extra == "all"
Requires-Dist: scipy==1.18.0; extra == "all"
Requires-Dist: sentencepiece==0.2.1; extra == "all"
Requires-Dist: shellingham==1.5.4; extra == "all"
Requires-Dist: six==1.17.0; extra == "all"
Requires-Dist: soupsieve==2.8.3; extra == "all"
Requires-Dist: stack-data==0.6.3; extra == "all"
Requires-Dist: sympy==1.14.0; extra == "all"
Requires-Dist: tensorboard==2.20.0; extra == "all"
Requires-Dist: tensorboard-data-server==0.7.2; extra == "all"
Requires-Dist: threadpoolctl==3.6.0; extra == "all"
Requires-Dist: tifffile==2025.5.10; extra == "all"
Requires-Dist: timm==1.0.26; extra == "all"
Requires-Dist: tokenizers==0.22.2; extra == "all"
Requires-Dist: tomli==2.4.1; extra == "all"
Requires-Dist: torch==2.11.0; extra == "all"
Requires-Dist: torchvision==0.26.0; extra == "all"
Requires-Dist: tornado==6.5.5; extra == "all"
Requires-Dist: tqdm==4.67.3; extra == "all"
Requires-Dist: traitlets==5.14.3; extra == "all"
Requires-Dist: transformers==5.6.2; extra == "all"
Requires-Dist: triton==3.6.0; extra == "all"
Requires-Dist: typer==0.25.0; extra == "all"
Requires-Dist: typing_extensions==4.15.0; extra == "all"
Requires-Dist: tzdata==2026.2; extra == "all"
Requires-Dist: urllib3==2.6.3; extra == "all"
Requires-Dist: virtualenv==21.2.4; extra == "all"
Requires-Dist: wcwidth==0.6.0; extra == "all"
Requires-Dist: Werkzeug==3.1.8; extra == "all"
Requires-Dist: xgboost==3.2.0; extra == "all"
Requires-Dist: xxhash==3.7.0; extra == "all"
Requires-Dist: yapf==0.43.0; extra == "all"
Requires-Dist: yarl==1.23.0; extra == "all"
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist

<div align=center class="logo">
      <img src="https://raw.githubusercontent.com/sangwyn/QualiSR-Lab/main/logo.png" style="width:640px">
</div>

# QualiSR-Lab: Reduced-Reference IQA for SR

[Oleg Ryabinin](https://orcid.org/0009-0008-3153-4183)<sup>1,2</sup> | [Evgeney Bogatyrev](https://orcid.org/0000-0002-6173-3561)<sup>1,2,3</sup> | [Khaled Abud](https://orcid.org/0009-0009-7131-5839)<sup>1,2,3</sup> | [Dmitriy Vatolin](https://orcid.org/0000-0002-8893-9340)<sup>1,2,3</sup>

<sup>1</sup>Lomonosov Moscow State University, 119991, Moscow, Russia

<sup>2</sup>AI Center, Lomonosov Moscow State University

<sup>3</sup>MSU Institute for Artificial Intelligence, Lomonosov Moscow State University

---

## Overview

This project studies which features extracted from Low-Resolution (LR) and Super-Resolution (SR) images are most informative for Image Quality Assessment (IQA). Its purpose is to assist researchers in studying the best features for their upscaled image quality metrics by providing a pipeline to extract the features and build a comprehensive graphical summary on their contribution to IQA and correlation of the resulting metric with human scores.

The proposed pipeline is:

1. **Prepare labels and features**  
   Compute image features and attach normalized quality labels.

2. **Train regressors**  
   Fit regression models on the resulting tabular data to obtain a simple Reduced-Reference (RR) quality metric.

3. **Analyze feature importance and correlation**  
   Evaluate feature importance and compute PLCC/SRCC to identify the most informative features for SR quality assessment.

The sections below describe the required data format and the workflow.

![Pipeline overview](https://raw.githubusercontent.com/sangwyn/QualiSR-Lab/main/pipeline.png)

---

## Installation and quickstart

Python 3.12 or 3.13 is required. From the repository root:

```bash
python -m pip install -c requirements.txt -e ".[regressors]"
qualisr-run-regressors
```

This runs Random Forest, XGBoost, and CatBoost on the packaged labels and precomputed features for 120 SR images. No image download is needed. Results and plots are written to `plots/baseline@pca5/`; add `--no-plots` to skip plotting.

For image feature extraction, install the additional dependencies:

```bash
python -m pip install -c requirements.txt -e ".[features,regressors]"
```

The [PyPI package](https://pypi.org/project/qualisr-lab/) is also available:

```bash
python -m pip install "qualisr-lab[features,regressors]==0.2.0"
```

The Docker image includes regression dependencies and runs the bundled example without plots:

```bash
docker build -t qualisr-lab:0.2.0 .
mkdir -p plots
docker run --rm --mount type=bind,source="${PWD}/plots",target=/app/plots qualisr-lab:0.2.0
```

Create the local `plots/` directory first if needed. The mount preserves results after the container exits. For an interactive shell, append `bash` and add `-it` to `docker run`.

Docker applies `requirements.txt` as constraints, including transitive dependencies.
To build the complete pinned Linux/CUDA environment instead, use
`docker build --build-arg QUALISR_EXTRAS=all -t qualisr-lab:0.2.0-all .`.
The full environment is large and includes NVIDIA libraries; GPU execution also requires
the container runtime's GPU support.

For the complete pinned environment locally, including notebook and development dependencies:

```bash
python -m pip install -r requirements.txt -e .
# Alternatively, from PyPI:
python -m pip install "qualisr-lab[all]==0.2.0"
# Or with Conda from the repository root:
conda env create -f environment.yml
```

The `notebook` extra provides IPython and an IPython kernel; the `dev` extra provides
pytest and Ruff. LightGBM and the separate `shap` package are optional integrations
outside the pinned environment; install them separately if needed. XGBoost's native
SHAP computation remains available with the `regressors` extra.

The wheel includes the current pipeline config, experiment JSONs, and the five CSVs
used by the bundled example. Experiment configs still require their external datasets
and locally configured paths. Release preparation is documented in [RELEASE.md](https://github.com/sangwyn/QualiSR-Lab/blob/main/RELEASE.md).

See [dataset/readme.md](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md) for dataset download notes and the parser/sample interface for custom datasets.

---

## Run with image datasets

Download [QualiSR-Set120](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md#download), then run commands from the repository root. The unified runner loads the configured datasets once and uses consistent sample IDs across feature extraction, artifact statistics, and regression.

Before running the full pipeline, edit [`configs/pipeline.json`](https://github.com/sangwyn/QualiSR-Lab/blob/main/configs/pipeline.json):

- Set each dataset's `root` and `features_root`.
- Select the feature groups and metrics needed for the experiment. Reference embeddings, generic timm embeddings, and embedding differences are disabled by default.

```bash
qualisr-run-pipeline --config configs/pipeline.json
```

The runner does not download datasets or generate artifact masks. It writes feature CSVs under `features/`, PCA outputs under `features/pca/`, and regression outputs under `plots/` with the default paths. The default regressor inputs are NR metrics without Q-Align, RLFN-based FR metrics, and VGG/ResNet PCA-5 features.

Run individual stages with the same configuration:

```bash
qualisr-run-pipeline --config configs/pipeline.json --only-section references
qualisr-run-pipeline --config configs/pipeline.json --only-section features
qualisr-run-pipeline --config configs/pipeline.json --only-section pca statistics
qualisr-run-regressors --config configs/pipeline.json
```

An explicit dataset configuration requires the LR/SR image files. Omit `--config` for the image-free bundled example. For custom parsers, dataset roles, and path rules, see the [dataset guide](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md). For ablations, transfer studies, and grouped cross-validation, see the [experiment suite](https://github.com/sangwyn/QualiSR-Lab/blob/main/configs/experiments/readme.md).

---

## Python API

Run the bundled experiment without plots:

```python
from qualisr import run_regressor_experiment

result = run_regressor_experiment(make_plots=False)
print(result["results"])
```

Run the regression stage of a configured image dataset:

```python
from qualisr import run_pipeline

run_pipeline(
    config_path="configs/pipeline.json",
    only_section=["regressors"],
    no_plots=True,
)
```

---

## Full Reproducibility Run

To download the dataset and run feature extraction, PCA, artifact statistics, and regressor analysis end to end:

```bash
python -m pip install -r requirements.txt -e .
qualisr-run-pipeline --config configs/pipeline.json
```

You can also use BASH script:

```bash
bash reproduce_pipeline.sh
```

The script writes feature-group CSVs such as `features/fr.csv`, `features/nr.csv`, and `features/vgg.csv`, PCA outputs to `features/pca/`, and plots/results to `plots/`.

---

## Workflow

You may either launch the whole pipeline in a single command with your JSON config as in the previous section or do each step separately. The unified pipeline parses the configured `datasets` entries into one shared sample list; datasets may use a bundled parser, a parser function from a user Python file, or explicit labels/image directories. Multiple entries are combined in one run. See [dataset/readme.md](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md) for the complete contract.

The standalone commands below retain their directory-based arguments for focused use outside the unified pipeline.

### Step 0 (optional): Prepare reference images

Produce [RLFN](https://github.com/bytedance/RLFN) / [SPAN](https://github.com/zononhzy/SPAN) / bicubic images for LR + SR pairs (used to compute FR metrics).
The bundled `realtime_sr/` directory is a clone-only convenience asset; pip
installs do not include these scripts/checkpoints, so pass your own paths for
RLFN/SPAN when running outside the repository.

```bash
qualisr-make-reference \
  --lr-dir dataset/lr \
  --sr-dirs PASD=dataset/sr/PASD SUPIR=dataset/sr/SUPIR RealESRGAN=dataset/sr/RealESRGAN \
  --out-root dataset/ref \
  --refs bicubic rlfn span \
  --scale 4 \
  --rlfn-script realtime_sr/RLFN/inference-RLFN.py \
  --rlfn-ckpt realtime_sr/RLFN/rlfn-tuned-4x.pth \
  --span-script realtime_sr/SPAN/inference-SPAN.py \
  --span-ckpt realtime_sr/SPAN/span-tuned-4x.pth
```


### Step 1: Compute image features

Compute FR / NR / [VGG](https://arxiv.org/abs/1409.1556) / [ResNet](https://arxiv.org/abs/1512.03385) / [SigLIP](https://arxiv.org/abs/2303.15343) features for SR images and save them into a single CSV file. VGG, ResNet, and timm embeddings can also be extracted from one configured SR-resolution reference type.

SR methods are passed as `METHOD=DIR`.  
Reference image filenames are expected in the format:

```text
<sr_stem>@<sr_method>@<ref_name>.<ext>
```

```bash
qualisr-extract-features \
  --sr-dirs PASD=dataset/sr/PASD SUPIR=dataset/sr/SUPIR RealESRGAN=dataset/sr/RealESRGAN \
  --gt-dir dataset/hr \
  --lr-dir dataset/lr \
  --ref-dirs bicubic=dataset/ref/bicubic rlfn=dataset/ref/rlfn span=dataset/ref/span \
  --features fr,nr,vgg,resnet,siglip \
  --output features/image_features.csv \
  --device cuda
```

To extract the corresponding embeddings from one reference type, select it with
`--embedding-reference` and use the `ref-vgg`, `ref-resnet`, or `ref-timm`
feature names. For example:

```bash
qualisr-extract-features \
  --sr-dirs PASD=dataset/sr/PASD SUPIR=dataset/sr/SUPIR RealESRGAN=dataset/sr/RealESRGAN \
  --ref-dirs bicubic=dataset/ref/bicubic \
  --embedding-reference bicubic \
  --features ref-vgg,ref-resnet \
  --output features/reference_embeddings.csv \
  --device cuda
```

In the unified pipeline, configure the reference once as
`features.common.embedding_reference`. All enabled reference-embedding groups
use that same reference.

### Step 2: Apply PCA to high-dimensional features

Apply Principal Component Analysis (PCA) to high-dimensional feature blocks such as `vgg_*` and `resnet_*` in CSV files produced in Step 1.

```bash
qualisr-apply-pca \
  --input features/image_features.csv \
  --blocks vgg=vgg_ resnet=resnet_ \
  --n-components 5 10 25 50 75 \
  --test-size 0.2 \
  --split-seed 42 \
  --output-dir features/pca
```

For component-wise differences after PCA, independently fitted PCA coordinates
are not comparable. Use paired mode to fit one basis on the stacked SR and
reference training rows and transform both inputs:

```bash
qualisr-apply-pca \
  --input features/vgg.csv \
  --reference-input features/ref_vgg.csv \
  --blocks vgg=vgg_ \
  --reference-blocks vgg=ref_vgg_ \
  --n-components 5 \
  --output-dir features/pca \
  --output-template vgg_shared_pca{n}.csv \
  --reference-output-template ref_vgg_shared_pca{n}.csv
```

### Step 3 (optional): Compute embedding differences

Compute signed, element-wise `SR - reference` differences. Rows are matched by
`sample_id`, and block mappings explicitly identify the corresponding columns:

```bash
qualisr-embedding-difference \
  --reference-input features/pca/ref_vgg_shared_pca5.csv \
  --sr-input features/pca/vgg_shared_pca5.csv \
  --blocks vgg_diff=vgg_pca_,vgg_pca_ \
  --output features/vgg_diff_pca5.csv
```

The same command can operate on raw embeddings, for example with
`--blocks vgg_diff=ref_vgg_,vgg_`.

### Step 4: Compute artifact-mask statistics

Compute summary statistics for heatmaps stored as `.npy`, `.npy.gz`, or compatible compressed files.  
Input directories can be passed as `PREFIX=DIR` to ensure stable sample naming.

```bash
qualisr-compute-stats \
  --heatmap-dirs PASD=dataset/heatmaps/PASD SUPIR=dataset/heatmaps/SUPIR RealESRGAN=dataset/heatmaps/RealESRGAN \
  --output features/stats.csv \
  --percentiles 5 95 \
  --area-thresholds 0 0.5 0.75
```

### Step 5: Fit regressors and analyze results

Train regressors and produce summary on feature importances and correlations. The correlation plot can also include direct NR/FR metric baselines from feature CSV files.

```bash
qualisr-run-regressors --config configs/pipeline.json
```

Training and validation datasets, their split behavior, and their feature
roots are declared once in the top-level `datasets` list. See
[dataset/readme.md](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md#selecting-datasets).

You can also use [regressors.ipynb](regressors.ipynb) notebook for experimentsn; install `.[regressors,notebook]` to use it. It trains regressors, evaluates them, and visualizes:

- feature importances,
- PLCC/SRCC correlations,
- feature cross-correlation matrix,
- MOS/prediction scatter plot,
- comparisons across feature groups and model settings.

The first notebook cell describes the workflow for running experiments individually or in batches.

Example outputs:

![Feature importances](https://raw.githubusercontent.com/sangwyn/QualiSR-Lab/main/plots/example@pca5/importances/all_models_importances.png)
![Correlations](https://raw.githubusercontent.com/sangwyn/QualiSR-Lab/main/plots/example@pca5/correlations/correlations.png)

### Profiling

Add `--profile` to standalone feature extraction or statistics to write `<output_stem>_profile.csv`. Feature extraction also accepts `--profile-flops`, which reruns supported PyTorch model calls and implies profiling. FLOP values are estimates and may omit unsupported operations.

For regression, `--profile` writes runtime and prediction-cost estimates under the run's `profiling/` directory. Supply `--feature-profile-files` (or `profiling.feature_profile_files` in the regressor config) for combined feature and regressor estimates. Unified feature/statistics profiling is configured in the corresponding JSON sections.

---

## Feature Types

This section summarizes the feature groups used in the pipeline. For references and guidelines to adding custom features, address [features/readme.md](https://github.com/sangwyn/QualiSR-Lab/blob/main/features/readme.md).

### No-Reference (NR) metrics

NR metrics are widely used in SR-IQA because they do not require a perfect high-resolution reference image. Their main limitation is that they ignore information available in the input LR image, which may cause them to miss or even reward artifacts introduced by SR models.

Recommended NR metrics in this project, based on results from [VSRQAD](https://ieeexplore.ieee.org/document/11458719):

- [Q-Align](https://github.com/Q-Future/Q-Align)
- [MUSIQ](https://github.com/anse3832/MUSIQ)
- [ARNIQA](https://github.com/miccunifi/ARNIQA)
- [UNIQUE](https://github.com/zwx8981/UNIQUE)
- [PaQ2PiQ](https://github.com/baidut/paq2piq)

These metrics are computed through the [PyIQA](https://github.com/chaofengc/IQA-PyTorch) interface, so the list can be changed easily.

---

### Full-Reference (FR) metrics

FR metrics are not always ideal for SR-IQA because they assume access to a perfect reference image. Still, they provide useful information about fidelity.

When true GT images are unavailable, the project uses **pseudo-GT** references: images obtained by upscaling the LR input with methods that are faithful to the LR image and do not introduce strong hallucinated content.

Reference upscaling methods used here:

- bicubic interpolation
- [SPAN](https://github.com/zononhzy/SPAN)
- [RLFN](https://github.com/bytedance/RLFN)

Recommended FR metrics in this project, based on results from [VSRQAD](https://ieeexplore.ieee.org/document/11458719):

- [LPIPS-VGG](https://github.com/richzhang/perceptualsimilarity)
- [STLPIPS-VGG](https://github.com/abhijay9/ShiftTolerant-LPIPS)
- [PieAPP](https://github.com/prashnani/PerceptualImageError)
- [AHIQ](https://github.com/IIGROUP/AHIQ)
- [PSNR](https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio)
- [SSIM](https://ece.uwaterloo.ca/~z70wang/publications/ssim.html)

These metrics are also computed through [PyIQA](https://github.com/chaofengc/IQA-PyTorch).

---

### Pretrained encoder features (+ PCA)

Feature embeddings from pretrained encoders can capture semantic and perceptual information not covered by classical IQA metrics.

This project uses features extracted from:

- [VGG](https://arxiv.org/abs/1409.1556)
- [ResNet](https://arxiv.org/abs/1512.03385)
- [SigLIP](https://arxiv.org/abs/2303.15343)
- any encoders from [timm](https://github.com/huggingface/pytorch-image-models)

Because these embeddings are often high-dimensional, Principal Component Analysis (PCA) can be applied before training regressors.

---

### Artifact-mask statistics

Artifacts are common in modern deep-learning-based SR models. The working hypothesis of this project is:

> Artifact-related information provides useful signals for assessing generated image quality.

An artifact mask is a single-channel tensor with values in the range `[0, 1]`.  
Masks for SR images must be computed beforehand with a suitable method such as [Prominence-Aware Artifact Detector](https://arxiv.org/abs/2510.16752).

The project extracts the following summary statistics from artifact masks:

- min
- max
- mean
- median
- std
- percentiles
- thresholded artifact area

## License

Project code is released under the [BSD-3-Clause license](https://github.com/sangwyn/QualiSR-Lab/blob/main/LICENSE). Third-party code, checkpoints, and datasets have separate terms; see [third-party notices](https://github.com/sangwyn/QualiSR-Lab/blob/main/THIRD_PARTY_NOTICES.md) and the [dataset guide](https://github.com/sangwyn/QualiSR-Lab/blob/main/dataset/readme.md#license).
