<commit_before>"""Image standardization.

This module implements the percentile landmark method described in [1]. Much of
the notation (variable names etc.) comes from this paper.

Learning:
    - Use landmark_scores() to get percentile scores for all images.
    - Use map_onto_scale() to map landmark scores to the standard scale.
    - Average mapped landmarks to get standard landmarks. TODO: This should be
      implemented here somehow.
    - Use write_std_cfg() to output configuration.

Standardizing:
    - Use read_std_cfg() to input configuration.
    - Use standardize() to standardize images according to configuration.
    - You can also use transform() to do this more free-form.

See also tools/standardize.py.

[1] Nyul et al. 2000: New variants of a method of MRI scale standardization.
"""

# TODO: Mention that the whole image must be included when standardizing.
# TODO: Replace scoreatpercentile() with np.percentile().

from __future__ import absolute_import, division, print_function
import collections

import numpy as np

import dwi.files
import dwi.util


def default_configuration():
    """Default standardization configuration."""
    return dict(
        pc=(0., 99.8),  # Min, max percentiles.
        landmarks=tuple(range(10, 100, 10)),  # Landmark percentiles.
        scale=(1, 4095),  # Min, max intensities on standard scale.
        )


def landmark_scores(img, pc1, pc2, landmarks, thresholding=True):
    """Get scores at histogram landmarks.

    Parameters
    ----------
    img : ndarray
        Model used for fitting.
    pc1, pc2 : number
        Minimum and maximum percentiles.
    landmarks : iterable of numbers
        Landmark percentiles.
    thresholding : bool, optional
        Whether to threshold by mean (default True). This includes only values
        higher than mean, which should help ignore the image background.

    Returns
    -------
    p1, p2 : float
        Minimum and maximum percentile scores.
    scores : tuple of floats
        Landmark percentile scores.
    """
    from scipy.stats import scoreatpercentile
    if thresholding:
        threshold = np.mean(img)
        img = img[img > threshold]
    p1 = scoreatpercentile(img, pc1)
    p2 = scoreatpercentile(img, pc2)
    scores = tuple(scoreatpercentile(img, i) for i in landmarks)
    return p1, p2, scores


def map_onto_scale(p1, p2, s1, s2, v):
    """Map value v from original scale [p1, p2] onto standard scale [s1, s2].

    Parameters
    ----------
    p1, p2 : number
        Minimum and maximum percentile scores.
    s1, s2 : number
        Minimum and maximum intensities on the standard scale.
    v : number
        Value to map.

    Returns
    -------
    r : float
        Mapped value.
    """
    assert p1 <= p2, (p1, p2)
    assert s1 <= s2, (s1, s2)
    if p1 == p2:
        assert s1 == s2, (s1, s2)
        return s1
    f = (v-p1) / (p2-p1)
    r = f * (s2-s1) + s1
    return r


def transform(img, p1, p2, scores, s1, s2, mapped_scores):
    """Transform image onto standard scale.

    Parameters
    ----------
    img : ndarray
        Image to transform.
    p1, p2 : number
        Minimum and maximum percentile scores.
    scores : iterable of numbers
        Landmark percentile scores.<commit_msg>assert<commit_after>"""Image standardization.

This module implements the percentile landmark method described in [1]. Much of
the notation (variable names etc.) comes from this paper.

Learning:
    - Use landmark_scores() to get percentile scores for all images.
    - Use map_onto_scale() to map landmark scores to the standard scale.
    - Average mapped landmarks to get standard landmarks. TODO: This should be
      implemented here somehow.
    - Use write_std_cfg() to output configuration.

Standardizing:
    - Use read_std_cfg() to input configuration.
    - Use standardize() to standardize images according to configuration.
    - You can also use transform() to do this more free-form.

See also tools/standardize.py.

[1] Nyul et al. 2000: New variants of a method of MRI scale standardization.
"""

# TODO: Mention that the whole image must be included when standardizing.
# TODO: Replace scoreatpercentile() with np.percentile().

from __future__ import absolute_import, division, print_function
import collections

import numpy as np

import dwi.files
import dwi.util


def default_configuration():
    """Default standardization configuration."""
    return dict(
        pc=(0., 99.8),  # Min, max percentiles.
        landmarks=tuple(range(10, 100, 10)),  # Landmark percentiles.
        scale=(1, 4095),  # Min, max intensities on standard scale.
        )


def landmark_scores(img, pc1, pc2, landmarks, thresholding=True):
    """Get scores at histogram landmarks.

    Parameters
    ----------
    img : ndarray
        Model used for fitting.
    pc1, pc2 : number
        Minimum and maximum percentiles.
    landmarks : iterable of numbers
        Landmark percentiles.
    thresholding : bool, optional
        Whether to threshold by mean (default True). This includes only values
        higher than mean, which should help ignore the image background.

    Returns
    -------
    p1, p2 : float
        Minimum and maximum percentile scores.
    scores : tuple of floats
        Landmark percentile scores.
    """
    from scipy.stats import scoreatpercentile
    if thresholding:
        threshold = np.mean(img)
        img = img[img > threshold]
    p1 = scoreatpercentile(img, pc1)
    p2 = scoreatpercentile(img, pc2)
    scores = tuple(scoreatpercentile(img, i) for i in landmarks)
    return p1, p2, scores


def map_onto_scale(p1, p2, s1, s2, v):
    """Map value v from original scale [p1, p2] onto standard scale [s1, s2].

    Parameters
    ----------
    p1, p2 : number
        Minimum and maximum percentile scores.
    s1, s2 : number
        Minimum and maximum intensities on the standard scale.
    v : number
        Value to map.

    Returns
    -------
    r : float
        Mapped value.
    """
    assert p1 <= p2, (p1, p2)
    assert s1 <= s2, (s1, s2)
    if p1 == p2:
        assert s1 == s2, (p1, p2, s1, s2)
        return s1
    f = (v-p1) / (p2-p1)
    r = f * (s2-s1) + s1
    return r


def transform(img, p1, p2, scores, s1, s2, mapped_scores):
    """Transform image onto standard scale.

    Parameters
    ----------
    img : ndarray
        Image to transform.
    p1, p2 : number
        Minimum and maximum percentile scores.
    scores : iterable of numbers
        Landmark percentile scores.
{'fraction_non_alphanumeric': 0.09197860962566845, 'fraction_numerical': 0.022612681436210848, 'mean_word_length': 2.532649757150567, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '305612', 'n_tokens_mistral': 1968, 'n_tokens_neox': 1855, 'n_words': 743}