How do I save animation to a video using VTKAVIWriter?

(Arvin) #1

I’m using the VTK Animation example Rotating Sphere and trying to save the animation to a video and was unsuccessful. My approach was to connect the vtkRenderer to vtkAVIWriter with vtkWindowToImageFilter

Below is the code I tested it with. This will output test.avi file that fails with ‘unable to open file’ on VLC Media player.

#include <vtkActor.h>
#include <vtkNamedColors.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkAVIWriter.h>
#include <vtkWindowToImageFilter.h>

class vtkTimerCallback2 : public vtkCommand
{
public:
  int timerId;
  static vtkTimerCallback2 *New()
  {
    vtkTimerCallback2 *cb = new vtkTimerCallback2;
    cb->TimerCount = 0;
    return cb;
  }
  virtual void Execute(vtkObject *caller, unsigned long eventId,
    void * vtkNotUsed(callData))
  {
    vtkRenderWindowInteractor *iren =
      dynamic_cast<vtkRenderWindowInteractor*>(caller);
    if (vtkCommand::TimerEvent == eventId)
    {
      ++this->TimerCount;
    }
    if (TimerCount < 36)
    {
      actor->RotateZ(5);
      iren->GetRenderWindow()->Render();
    }
    else
    {
      iren->DestroyTimer();
    }
  }

private:
  int TimerCount;

public:
  vtkActor* actor;
};

int main(int, char*[])
{
  vtkSmartPointer<vtkNamedColors> colors =
    vtkSmartPointer<vtkNamedColors>::New();

  // Create a sphere
  vtkSmartPointer<vtkSphereSource> sphereSource =
    vtkSmartPointer<vtkSphereSource>::New();
  sphereSource->SetCenter(0.0, 0.0, 0.0);
  sphereSource->SetRadius(1.0);
  sphereSource->SetThetaResolution(15);
  sphereSource->SetPhiResolution(15);
  sphereSource->Update();

  // Create a mapper and actor
  vtkSmartPointer<vtkPolyDataMapper> mapper =
    vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper->SetInputConnection(sphereSource->GetOutputPort());
  vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
  actor->SetMapper(mapper);
  actor->RotateX(90);
  actor->GetProperty()->SetRepresentationToWireframe();

  // Create a window to Image Filter
  vtkSmartPointer<vtkWindowToImageFilter> imageFilter =
     vtkSmartPointer<vtkWindowToImageFilter>::New();

  //Create a video Writer
  vtkSmartPointer<vtkAVIWriter> videoWriter =
     vtkSmartPointer<vtkAVIWriter>::New();

  // Create a renderer, render window, and interactor
  vtkSmartPointer<vtkRenderer> renderer =
    vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renderWindow =
    vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);
  renderWindow->SetSize(640, 480);

  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  renderWindowInteractor->SetRenderWindow(renderWindow);

  // Add the actor to the scene
  renderer->AddActor(actor);

  renderer->SetBackground(colors->GetColor3d("Seashell").GetData());
  // Render and interact
  renderWindow->Render();

  // Initialize must be called prior to creating timer events.
  renderWindowInteractor->Initialize();

  // Sign up to receive TimerEvent
  vtkSmartPointer<vtkTimerCallback2> cb =
    vtkSmartPointer<vtkTimerCallback2>::New();
  cb->actor = actor;

  renderWindowInteractor->AddObserver(vtkCommand::TimerEvent, cb);
  int timerId = renderWindowInteractor->CreateRepeatingTimer(100);
  cb->timerId = timerId;

  imageFilter->SetInput(renderWindow);
  imageFilter->SetInputBufferTypeToRGB();
  imageFilter->ReadFrontBufferOff();
  imageFilter->Update();

  videoWriter->SetInputConnection(imageFilter->GetOutputPort());
  videoWriter->SetFileName("test.avi");
  videoWriter->Start();
  // Start the interaction and timer
  renderWindowInteractor->Start();

  videoWriter->End();
 
  return EXIT_SUCCESS;
}

Thank you

(Andras Lasso) #2

Please elaborate.

If you post code, at least use the “preformatted text” option, but if it is long then consider uploading it to GitHub or as a gist and just post the link here.

(Andrew Wilson) #3

Here’s an example snip from a script I wrote recently to export an animation as an uncompressed AVI. I got hung up on having to call Modified on the screenshot filter because it doesn’t see the input as having updated. Hopefully this can help.

<... Includes and what not>
static int frameCount = 0;
vtkSmartPointer<vtkAVIWriter> aviWriter;
vtkSmartPointer<vtkRenderLargeImage> screenshotFilter;

static void ExecuteTimerCallback(vtkObject* caller, unsigned long eventId, void* clientData, void* callData)
{
	vtkRenderWindowInteractor* iren = static_cast<vtkRenderWindowInteractor*>(caller);
    // Only write 200 frames
	if (frameCount < 200)
	{
		<... Updated some things here>
		iren->Render();
		screenshotFilter->Modified();
		screenshotFilter->Update();
		aviWriter->Write();
		frameCount++;
	}
}

int main()
{
    <... Set some things up here>

    vtkRenderer> ren;
	ren->AddActor(actor);
	ren->AddActor2D(scalarBarActor);
	vtkNew<vtkRenderWindow> renWin;
	renWin->SetSize(2560, 1440);
	renWin->AddRenderer(ren);
	vtkNew<vtkRenderWindowInteractor> iren;
	iren->SetInteractorStyle(vtkInteractorStyleTrackballCamera::New());
	iren->SetRenderWindow(renWin);

	vtkNew<vtkCallbackCommand> callback;
	callback->SetCallback(ExecuteTimerCallback);
	iren->AddObserver(vtkCommand::TimerEvent, callback);

	screenshotFilter = vtkSmartPointer<vtkRenderLargeImage>::New();
	screenshotFilter->SetInput(ren);
	screenshotFilter->SetMagnification(1.0);
	screenshotFilter->Update();
	aviWriter = vtkSmartPointer<vtkAVIWriter>::New();
	aviWriter->SetFileName("Output/output.avi");
	aviWriter->SetInputData(screenshotFilter->GetOutput());
	aviWriter->SetQuality(2);
	aviWriter->SetRate(24);
	aviWriter->SetCompressorFourCC("DIB");
	aviWriter->Start();

	ren->ResetCamera();

	ren->SetBackground(0.6, 0.6, 0.3);
	renWin->Render();
	iren->CreateRepeatingTimer(10);
	iren->Start();

	aviWriter->End();

	return 1;
}