pythonnumpymatplotlibvideo-processingmencoder

Plotting directly to movie with numpy and mencoder


So, this should be a comment to this thread, but it's apparently closed, so here it goes. I've been playing around quite successfully with matplotlib and numpy and mencoder, as has been suggested around here. I have since adopted Voki Codder buffer to stdin solution, which speeds the whole process up considerably. The thing is, I couldn't find any documentation on the -format="bgra" part of the command. This means that the bytes are from right to left blue green red alpha, right. Do they have to be uint32, or something else. The problem is I'm plotting colormaps of floats, so I'm trying to convert them to grayscale, but I'm getting lots of weird patterns which make me strongly believe I'm doing something wrong. I wrote this function to convert from floats to uint32 within a range. But the result is not why I expected, am I doing something terribly stupid?

def grayscale(x, min, max):
  return np.uint32((x-min)/(max-min)*0xffffff)

Solution

  • I think you're getting confused on what the uint32 represents. It's 4 bands of uint8 integers.

    If you have floating point data, and want to represent it in grayscale, you don't want to rescale it to a full 32 bit range, you want to rescale it to an 8-bit range, and repeat that for the red, green, and blue bands (and then presumably put in a constant alpha band).

    You could also just use a different byteorder. Y8 is just a single grayscale, 8-bit band, and Y16 is a single, grayscale 16-bit band. (Have a look at the output of mencoder -rawvideo format=help for a full (though somewhat confusing) listing.)

    Just to illustrate using numpy for view a 32-bit integer as four bands of 8-bit integers:

    import numpy as np
    height, width = 20,20
    
    # Make an array with 4 bands of uint8 integers
    image = np.zeros((height, width, 4), dtype=np.uint8)
    
    # Filling a single band (red) 
    b,g,r,a = image.T
    r.fill(255) 
    
    # Fill the image with yellow and leave alpha alone
    image[...,:3] = (255, 255, 0) 
    
    # Then when we want to view it as a single, 32-bit band:
    image32bit = image.reshape(-1).view(np.uint32).reshape(height, width)
    # (Note that this is a view. In other words,  we could change "b" above 
    #  and it would change "image32bit")
    

    In your case, however, you probably want to do something more like this:

    import numpy as np
    from videosink import VideoSink
    
    height, width = 20,20
    numframes = 1000
    data = np.random.random((height, width, numframes))
    
    # Rescale your data into 0-255, 8-bit integers 
    # (This could be done in-place if you need to conserve memory)
    d    ata_rescaled = 255.0 / (data.max() - data.min()) * (data - data.min())
    data_rescaled = data_rescaled.astype(np.uint8)
    
    # The key here is the "Y8" format. It's 8-bit grayscale.
    video = VideoSink((height,width), "test", rate=20, byteorder="Y8")
    
    # Iterate over last axis
    for frame in data.T:
        video.run(frame.T)
    video.close()