-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Fix 183: "t values issue that causes the animations to not be finished entirely" #698
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
Conversation
@@ -38,9 +38,9 @@ def handle_play_like_call(func): | |||
|
|||
def wrapper(self, scene, *args, **kwargs): | |||
allow_write = not config["skip_animations"] | |||
self.file_writer.end_animation(allow_write) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
close any open pipes
manim/renderer/cairo_renderer.py
Outdated
self.update_frame(scene, ignore_skipping=False) | ||
self.add_frame(self.camera.pixel_array) | ||
self.file_writer.end_animation(not config["skip_animations"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add the last frame - This fixes the bug!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in #183, we took the decision to add an extra frame at the end of the video, so it seems good to me.
However, Could you :
- Maybe add a comment saying that it adds an additional frame to the video (because, it's not very intuitive that 1seconde at 15fps will be .. 16 frames)
- And a video test (videos test compare meta data of the videos, including the number of frames).
EDIT : I didn't see, but tests are already failing because of this
EDIT2 : I didn't read neither you PR text, so I basically repeated eveything you said there sorry
@@ -38,9 +38,9 @@ def handle_play_like_call(func): | |||
|
|||
def wrapper(self, scene, *args, **kwargs): | |||
allow_write = not config["skip_animations"] | |||
self.file_writer.end_animation(allow_write) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion : Could you add ao comment saying this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you are asking here 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @huguesdevimeux means to add a comment saying "Close any open pipes"
I still intend to make the output video be 1s long, but it will still be 16 frames. I personally don't have a problem with that, I think the off-by-one error here is fine (and may in fact be expected).
I will add tests once more people have taken a look and agreed that this PR is in the right path. |
I agree with this approach. It makes sense to partition the time interval of one second into a series of half-open intervals [0, 1/30) + [1/30, 2/30) + ... + [29/30, 30/30) each showing one frame in order to produce a 30fps video, as well as an additional final frame in the end (which "closes" the interval, so to say). The docbuild failed with |
I agree with the approach too, it'll just require some tweaking to make the tests pass. |
I also agree with this additional frame strategy. Nice that you found a way to solve this! |
Just tested this script: class VideoTest(Scene):
def construct(self):
def func1(num):
text =Text(f"{num}",font='Arial').scale(3)
text.num= num
return text
text= func1(0)
def update_curve(d,dt):
d.num = d.num+1
d.become(func1(d.num))
self.add(text)
text.add_updater(update_curve)
self.play(ShowCreation(Square())) output from master: |
And regarding my previous post: |
Great find @kolibril13 this is exactly why I didn't want to implement the tests yet. Let's take this script: import manim as mn
mn.config.frame_rate = 5
mn.config.pixel_height = 150
mn.config.pixel_width = 150
def make_lbl(num):
text = mn.Text(f"{num}").scale(7)
text.num = num
return text
def update_lbl(lbl, dt):
lbl.num += 1
lbl.become(make_lbl(lbl.num))
class MyScene(mn.Scene):
def construct(self):
text = make_lbl(0).add_updater(update_lbl)
self.add(text)
sq = mn.Square(stroke_width=30).scale(3)
self.play(mn.ShowCreation(sq), run_time=1) (I think the syntax for the updaters is actually kind of terrible - it seems it should be a lot easier to do this. But anyhow...) on master this produces the following five frames:and on this PR branch:Notes
|
@kolibril13 the problem of starting at the number 2 will be fixed by #710 |
Depends on #710 |
Ok, after #710, there was one further bug to squash. Basically, So I moved the call to |
what |
I mean if any part of the codebase (other than |
Update: #710 is merged so I just need to fix the tests (this will involve changing some pre-existing tests) and adding new ones. |
Is there yet a way to re-generate all test data from the current reference scripts? |
What? Now I'm confused. |
Maybe it's a linux problem, because both Windows and Mac seem to work the same way? |
Ah no no no, I fell in the whole problem of this issue 😅 @naveen521kk and @PhilippImhof could you pretty please send me the output of the same script but not the last frame generated with |
Thank you @PhilippImhof I believe this is related to the fact that macos and windows added the last frame with a different duration than linux did. I do not like where this is going. |
Thanks all. I now believe the problem is how the numbers themselves are being rendered. See e.g. this comment. I think I'm just going to change the test to do the same thing but say with colors instead of |
Solved! Ready for final review. |
def update_lbl(lbl, dt): | ||
lbl.num += 1 | ||
lbl.become(make_lbl(lbl.num)) | ||
|
||
|
||
class LastFrame(mn.Scene): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment/issue : I'm worry that it won't test anything, as graphical unit tests are run with the equivalent of -s
flag ( every animation is skipped)
therfore, the last t value is always the run_time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how this tests the change either...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going through older issues/PRs again and recall @kolibril13 mentioning a way to test video which is VERY applicable here as a way to test this PR. #1035
I recommend we test in this manner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the record, in #1019 I Implemented such tests on t values, so no need to implement this here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @leotrs likely doesn't have time to update this PR, I could take over (and also look into @eulertour's suggestion). Would anyone mind if I commit directly onto this branch and bring it up-to-date with current master?
@@ -38,9 +38,9 @@ def handle_play_like_call(func): | |||
|
|||
def wrapper(self, scene, *args, **kwargs): | |||
allow_write = not config["skip_animations"] | |||
self.file_writer.end_animation(allow_write) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @huguesdevimeux means to add a comment saying "Close any open pipes"
I think it will cause huge conflicts with #1019 . (I'm the author of #1019, so I'm biaised). I think it makes more sense to merge #1019 before this, since #1019 is mostly clean up PR that add tests, and then to come back later to this. (Not because I don't want to fix merge conflicts on my PR, really not, this is a rational thought). |
Makes sense, I'll hold off on any such work on this PR until then and checkout/review that #1019. Once merged though, are there any other objections to moving this along? I suppose I could start a separate PR entirely. |
Please do.
…On Tue, Mar 9, 2021, 6:42 AM Jason Villanueva ***@***.***> wrote:
***@***.**** commented on this pull request.
As @leotrs <https://github.com/leotrs> likely doesn't have time to update
this PR, I could take over (and also look into @eulertour
<https://github.com/eulertour>'s suggestion
<https://github.com/ManimCommunity/manim/pull/698/files#r528143200>).
Would anyone mind if I commit directly onto this branch and bring it
up-to-date with current master?
------------------------------
In manim/renderer/cairo_renderer.py
<#698 (comment)>:
> @@ -38,9 +38,9 @@ def handle_play_like_call(func):
def wrapper(self, scene, *args, **kwargs):
allow_write = not config["skip_animations"]
+ self.file_writer.end_animation(allow_write)
I think @huguesdevimeux <https://github.com/huguesdevimeux> means to add
a comment saying "Close any open pipes"
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#698 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAILYAALCXYGG2JMUX64LEDTCX3SXANCNFSM4TSEKU6A>
.
|
Converting to draft since it has merge conflicts and due to inactivity. |
I would close this for now and we can take a fresh look at this on the new version and see what we are able to fix with the knowledge we aquired from this PR. I linked it to the original issue such that we do not loose this information.
|
Motivation
Manim doesn't render animations completely. For a video rendering library, this should be priority 1 right now. See #183 for @huguesdevimeux's excellent report. In short, the animation
generates a video whose last frame is this:
You can see that the square has not been rendered fully, there is still a bit of the animation left to play (see open top left corner).
After discussion in #183, the way to solve this seems to be to simply add the last frame manually at the end of the video. With the mess that is scene file writer, scene, renderer, caching, etc, it was not obvious to me how to do this correctly. Luckily, thanks to all the good work put it by the dev team recently, I was finally able to figure it out, I think.
Explanation for Changes
The way things currently work:
play
opens a new FFMPEG pipe.The way I'm proposing we do things:
play
closes a previous FFMPEG pipe, if one exists.After all frames are fed, one of two things can happen: (a)
play
is called with a new animation (in which case it would close the currently open pipe in step 1.), or (b) the video ends. If the video ends,Renderer.finish()
is called, which now takes care of writing the last frame (this is the bug fix! 🎉) and closing the open pipe, so no pipes are ever left unattended. This is basically what you see in the diff inrenderer/cairo_renderer.py
.There is only one sticking point: the first call to
play
will try to close an (unexisting) pipe, so I added a quick check for that inscene/scene_file_writer.py
Testing Status
The tests currently do not pass, because this fix will change some tests. However before further work I wanted to hear what everyone thinks. Mainly, this bug will make it so that a manim script with a single 1s animation will render a video that is longer than 1s. I believe this should be fixable by tweaking the last call to ffmpeg somehow, but I'm not sure how.
Further comments
I believe #618 should be addressed only after merging this (or else fixing #183)
This is a pretty big change and it will break user space so thoughts appreciated @ManimCommunity/core