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

Avid's keyframe values different from PyAAF2 interpolation with extreme bezier handles #131

Open
austinwitherspoon opened this issue Mar 14, 2024 · 9 comments

Comments

@austinwitherspoon
Copy link

austinwitherspoon commented Mar 14, 2024

This is definitely an edge case - most of the test cases I've made have been pretty much identical between pyaaf2 and what is displayed in Avid! So great work!

We've been noticing occasional anomalies though, and I finally was able to reproduce one myself.

If I intentionally push the handles of a bezier curve too far, I can get pyaaf2 to spit out some incorrect values.

image

I've attached an example aaf file, and here is the code I ran on it:

import aaf2

VALUES_IN_AVID = {
    0: 0.0,
    1: 40.2,
    2: 79.92,
    3: 119.09,
    4: 157.63,
    5: 194.45,
    6: 232.43,
    7: 268.39,
    8: 303.14,
    9: 336.38,
    10: 367.72,
    11: 396.57,
    12: 422.01,
    13: 442.48,
    14: 454.99,
    15: 452.45,
    16: 409.92,
    17: 227.30,
    18: 107.82,
    19: 55.0,
    20: 26.25,
    21: 10.24,
    22: 2.3,
    23: 0.0,
}

file = "bezier_example.aaf"
fp = aaf2.open(file)

op_group = next(fp.content.compositionmobs()).slots[8].segment.components[1]
paramY = next(
    param for param in op_group.parameters if isinstance(param, aaf2.misc.VaryingValue) and param.name == "DVE_POS_Y_U"
)
for i in range(0, 24):
    avid_value = VALUES_IN_AVID[i]
    pyaaf2_value = paramY.value_at(i)
    diff = abs(pyaaf2_value - avid_value)
    print(
        i, f"PyAAF2: {pyaaf2_value:.3f}", f"Avid: {avid_value:.2f}", f"Diff: {diff:.3f}", "OK" if diff < 0.1 else "FAIL"
    )

That dictionary at the top is manually populated with values seen in the keyframe editor in Avid Media Composer.
The script will compare avid's value and pyaaf2's value and if there's a margin of error larger than .1, will print "FAIL"

With this AAF, I get the following output:

0 PyAAF2: 0.000 Avid: 0.00 Diff: 0.000 OK
1 PyAAF2: 40.149 Avid: 40.20 Diff: 0.051 OK
2 PyAAF2: 79.705 Avid: 79.92 Diff: 0.215 FAIL
3 PyAAF2: 118.582 Avid: 119.09 Diff: 0.508 FAIL
4 PyAAF2: 156.672 Avid: 157.63 Diff: 0.958 FAIL
5 PyAAF2: 193.838 Avid: 194.45 Diff: 0.612 FAIL
6 PyAAF2: 229.905 Avid: 232.43 Diff: 2.525 FAIL
7 PyAAF2: 264.645 Avid: 268.39 Diff: 3.745 FAIL
8 PyAAF2: 297.748 Avid: 303.14 Diff: 5.392 FAIL
9 PyAAF2: 328.781 Avid: 336.38 Diff: 7.599 FAIL
10 PyAAF2: 357.113 Avid: 367.72 Diff: 10.607 FAIL
11 PyAAF2: 381.771 Avid: 396.57 Diff: 14.799 FAIL
12 PyAAF2: 401.138 Avid: 422.01 Diff: 20.872 FAIL
13 PyAAF2: 412.239 Avid: 442.48 Diff: 30.241 FAIL
14 PyAAF2: 408.694 Avid: 454.99 Diff: 46.296 FAIL
15 PyAAF2: 373.544 Avid: 452.45 Diff: 78.906 FAIL
16 PyAAF2: 267.610 Avid: 409.92 Diff: 142.310 FAIL
17 PyAAF2: 150.411 Avid: 227.30 Diff: 76.889 FAIL
18 PyAAF2: 84.263 Avid: 107.82 Diff: 23.557 FAIL
19 PyAAF2: 45.857 Avid: 55.00 Diff: 9.143 FAIL
20 PyAAF2: 22.652 Avid: 26.25 Diff: 3.598 FAIL
21 PyAAF2: 9.030 Avid: 10.24 Diff: 1.210 FAIL
22 PyAAF2: 2.055 Avid: 2.30 Diff: 0.245 FAIL
23 PyAAF2: 0.000 Avid: 0.00 Diff: 0.000 OK

Again, this is definitely an edge case as far as I can tell, when you push the bezier handles too far! So it's totally understandable if this isn't a high priority.

@austinwitherspoon
Copy link
Author

bezier_example.aaf.zip

@markreidvfx
Copy link
Owner

Thanks for taking time looking at this! I really appreciate it :) I have some ideas what it might be. Any chance you would be able to convert your verified tests to unit tests? It would be super helpful when fixing this issue. We wouldn't want to break any cases you've verified.

@austinwitherspoon
Copy link
Author

Sure, I'll try to make some tests when I get into the office tomorrow!

@markreidvfx
Copy link
Owner

markreidvfx commented Mar 14, 2024

I think my suspicions are correct. These are the coordinates of the points and handles between 0-23

(0.0, 0.0)--(25.4089376, 1027.02703)   (10.2015177, 0.0)--(23.0, 0.0)

As we can see the first handles x coordinate is actually after the last keyframe. For this case I've been scaling the handle so its if doesn't go beyond that last keyframe. (This is typically what I've done in curve editors I've made in the past)
image

In this case my code is scaling them to.

 (25.4089376, 1027.02703) --> (23.0, 929.6579834176143)

If I remove the scaling, the results are much closer.

0 PyAAF2: 0.000 Avid: 0.00 Diff: 0.000 OK
1 PyAAF2: 40.198 Avid: 40.20 Diff: 0.002 OK
2 PyAAF2: 79.916 Avid: 79.92 Diff: 0.004 OK
3 PyAAF2: 119.088 Avid: 119.09 Diff: 0.002 OK
4 PyAAF2: 157.633 Avid: 157.63 Diff: 0.003 OK
5 PyAAF2: 195.454 Avid: 194.45 Diff: 1.004 FAIL
6 PyAAF2: 232.426 Avid: 232.43 Diff: 0.004 OK
7 PyAAF2: 268.390 Avid: 268.39 Diff: 0.000 OK
8 PyAAF2: 303.135 Avid: 303.14 Diff: 0.005 OK
9 PyAAF2: 336.377 Avid: 336.38 Diff: 0.003 OK
10 PyAAF2: 367.716 Avid: 367.72 Diff: 0.004 OK
11 PyAAF2: 396.566 Avid: 396.57 Diff: 0.004 OK
12 PyAAF2: 422.007 Avid: 422.01 Diff: 0.003 OK
13 PyAAF2: 442.480 Avid: 442.48 Diff: 0.000 OK
14 PyAAF2: 454.986 Avid: 454.99 Diff: 0.004 OK
15 PyAAF2: 452.447 Avid: 452.45 Diff: 0.003 OK
16 PyAAF2: 409.922 Avid: 409.92 Diff: 0.002 OK
17 PyAAF2: 227.303 Avid: 227.30 Diff: 0.003 OK
18 PyAAF2: 107.825 Avid: 107.82 Diff: 0.005 OK
19 PyAAF2: 55.004 Avid: 55.00 Diff: 0.004 OK
20 PyAAF2: 26.253 Avid: 26.25 Diff: 0.003 OK
21 PyAAF2: 10.241 Avid: 10.24 Diff: 0.001 OK
22 PyAAF2: 2.296 Avid: 2.30 Diff: 0.004 OK
23 PyAAF2: 0.000 Avid: 0.00 Diff: 0.000 OK

I think you transcribed frame 5 wrong, my avid says its 195.50 not 194.45

I'm not entirely sure what the implications of not sanitizing the handles in extreme cases is, but it doesn't appear media composer is doing any.

@austinwitherspoon
Copy link
Author

Out of curiosity, I decided to pull the handle significantly further than god ever intended:
image

Some part of me was expecting Avid to crash, but nope! Interesting how they decide to clamp it like that.

@austinwitherspoon
Copy link
Author

I'm not sure what's easier for you, if you copy over from my branch or if I do a merge request with a failing test?

Let me know if you'd like a pull request!

I've added two keyframe interpolation tests, one passing and one failing (this example) here:
https://github.com/austinwitherspoon/pyaaf2/tree/keyframe-interpolation-tests

@markreidvfx
Copy link
Owner

Thanks @austinwitherspoon. Sure, if your cool with it I'll take test from your branch and add the fix so it all be in one nice clean commit :)

@austinwitherspoon
Copy link
Author

Go ahead!

markreidvfx pushed a commit that referenced this issue Mar 24, 2024
Don't preform handle scaling and clamping
Add tests for keyframe interpolation
markreidvfx pushed a commit that referenced this issue Mar 24, 2024
Don't preform handle scaling and clamping
Add tests for keyframe interpolation
@markreidvfx
Copy link
Owner

markreidvfx commented Mar 25, 2024

your tests have been added to the dev branch.

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

2 participants