Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow single point test "curves" #5

Open
YodaEmbedding opened this issue Apr 1, 2024 · 0 comments
Open

Allow single point test "curves" #5

YodaEmbedding opened this issue Apr 1, 2024 · 0 comments

Comments

@YodaEmbedding
Copy link
Contributor

YodaEmbedding commented Apr 1, 2024

Request

Given:

import bjontegaard as bd


rate_anchor = [0.1, 0.2, 0.4, 0.8]
psnr_anchor = [30.0, 35.0, 40.0, 45.0]
rate_test = [0.18]
psnr_test = [36.0]

kwargs = dict(
    method="akima",
    require_matching_points=False,
    min_overlap=0.0,
)

bd_rate = bd.bd_rate(rate_anchor, psnr_anchor, rate_test, psnr_test, **kwargs)
bd_psnr = bd.bd_psnr(rate_anchor, psnr_anchor, rate_test, psnr_test, **kwargs)

print(f"BD-Rate={bd_rate:.2f}% | BD-PSNR={bd_psnr:.2f} dB")

Expected output:

BD-Rate=-21.65% | BD-PSNR=1.76 dB

Actual output:

bjontegaard/bjontegaard_delta.py:49: UserWarning: Curves do not overlap. BD cannot be calculated.
  warnings.warn("Curves do not overlap. BD cannot be calculated.")
BD-Rate=nan% | BD-PSNR=nan dB

Rationale

Consider a single test point $(R, D)$. Let its corresponding "fake" test curve be defined as $\{ (R, D), (R + \epsilon, D + \epsilon) \}$. As $\epsilon \to 0$, BD rate just becomes a distance measure between the test point and the interpolated point on the anchor curve with the same value of PSNR.

$$ \begin{align} \Delta R &= \frac{1}{\epsilon} \int_{D}^{D + \epsilon} \left[ R_{\text{test}}(y) - R_{\text{anchor}}(y) \right] \, dy \\ & \overset{\epsilon \to 0}{\longrightarrow} R_{\text{test}}(D) - R_{\text{anchor}}(D) \end{align} $$

Disclaimer: I didn't really check that the equation above makes sense. Also, pretend that $R \to \log R$, if needed.

As a numerical example, consider the following method which fakes a test "curve" using a small epsilon deviation about the single test point:

import bjontegaard as bd


def calculate_bd(rate_anchor, psnr_anchor, rate_test, psnr_test, eps=1e-6, **kwargs):
    if len(rate_test) == 1:
        # Fake a "curve" using a small % deviation of epsilon.
        [rate_test] = rate_test
        [psnr_test] = psnr_test
        rate_test = [rate_test, rate_test * (1 + eps)]
        psnr_test = [psnr_test, psnr_test * (1 + eps)]

    bd_rate = bd.bd_rate(rate_anchor, psnr_anchor, rate_test, psnr_test, **kwargs)
    bd_psnr = bd.bd_psnr(rate_anchor, psnr_anchor, rate_test, psnr_test, **kwargs)

    return bd_rate, bd_psnr


rate_anchor = [0.1, 0.2, 0.4, 0.8]
psnr_anchor = [30.0, 35.0, 40.0, 45.0]
rate_test = [0.18]
psnr_test = [36.0]


for i in range(10):
    eps = 10 ** (-i)
    bd_rate, bd_psnr = calculate_bd(
        rate_anchor,
        psnr_anchor,
        rate_test,
        psnr_test,
        eps=eps,
        method="akima",
        require_matching_points=False,
        min_overlap=0.0,
    )
    print(f"eps={eps:.0e} | BD-Rate={bd_rate:.2f}% | BD-PSNR={bd_psnr:.2f} dB")

Output:

eps=1e+00 | BD-Rate=-54.21% | BD-PSNR=17.26 dB
eps=1e-01 | BD-Rate=-35.97% | BD-PSNR=3.22 dB
eps=1e-02 | BD-Rate=-23.20% | BD-PSNR=1.90 dB
eps=1e-03 | BD-Rate=-21.81% | BD-PSNR=1.77 dB
eps=1e-04 | BD-Rate=-21.67% | BD-PSNR=1.76 dB
eps=1e-05 | BD-Rate=-21.65% | BD-PSNR=1.76 dB
eps=1e-06 | BD-Rate=-21.65% | BD-PSNR=1.76 dB
eps=1e-07 | BD-Rate=-21.65% | BD-PSNR=1.76 dB
eps=1e-08 | BD-Rate=-21.65% | BD-PSNR=1.76 dB
eps=1e-09 | BD-Rate=-21.65% | BD-PSNR=1.76 dB

Clearly, in the limit, the single point "curve" converges to a well-defined value of BD-Rate and BD-PSNR.


Other

I suppose a similar argument might also apply to single-point anchor "curves".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant