Converting PNG files into a movie

Posted:


Pymedia is a python wrapper around the ffmpeg C audio/video library. It is sometimes useful to compose a video from a series of graphic files or to decompose a video into a series of graphic files. Pymedia is one tool to do this.

There is a sample program on the pymedia site that claims to create a video from a series of separate graphic files. However, it contains a bug. Besides, it may not be as clearly written as it could be. Here’s my rewrite of that code.

I have hard-coded in the function getFiles() that is a routine to find all the graphic files I want in the movie as sort them. If you reuse this script, you will need to write your own version of this.

import sys, os, time
import pymedia.video.vcodec as vcodec
import pygame

def makeVideo(files, outFile, outCodec='mpeg1video'):
  pygame.init()

  fw = open( outFile, 'wb' )
  if (fw == None) :
      print("Cannot open file " + outFile)
      return

  if outCodec == 'mpeg1video' :
      bitrate= 2700000
  else:
      bitrate= 9800000

  start = time.time()
  enc = None

  frame  = 1
  for fpath in files :
      img = pygame.image.load( fpath )

      # Init once, but need details from an image
      if enc == None :
          params= {
              'type': 0,
              'gop_size': 12,
              'frame_rate_base': 30, # 125,
              'max_b_frames': 0,
              'height': img.get_height(),
              'width': img.get_width(),
              'frame_rate': 90, # 2997,
              'deinterlace': 0,
              'bitrate': bitrate,
              'id': vcodec.getCodecID( outCodec )
              }
          enc = vcodec.Encoder( params )

      # Create VFrame
      bmpFrame= vcodec.VFrame( vcodec.formats.PIX_FMT_RGB24, 
                               img.get_size(), 
                               # Covert image to 24bit RGB 
                              (pygame.image.tostring(img, "RGB"), None, None)      
                             )
      # Convert to YUV, then codec
      d = enc.encode(bmpFrame.convert(vcodec.formats.PIX_FMT_YUV420P))
      fw.write(d.data)
      frame += 1

      if frame % 10 == 0 :
          sys.stdout.write("Progress: %05d/%05d\r" % (frame, len(files))) 
          sys.stdout.flush()

  fw.close()
  pygame.quit()

  print '\nDone\n%d frames written in %.2f secs( %.2f fps )' \
      % (frame, 
         time.time() - start, 
         float(frame) / (time.time()- start)
         )

def getFiles () :
    # Change this function to get files in the order you want them
    files = []
    base_dir = 'M:/backups/hosted_sites/taskboy/public_html/spy'
    for f in os.listdir(base_dir) :
        if f.startswith("20") and f.endswith("png") :
            files.append(os.path.join(base_dir, f))
    files.sort()
    return files

if __name__== '__main__':
    files = getFiles()
    makeVideo(files, "out.mpg")

The core of this code is in the makeVideo() function. Pygame is used to load the graphic files into a format that pymedia’s VFrame object can accept. That object is converted into a YUV format suitable for MPEG encoding.

You can control the speed of the images by setting the frame_rate in the params dictionary. Lower numbers are slower than bigger numbers. I leave as an exercise for the reader a refinement that allows for more fine-grained control of the MPEG encoding.