Plotting directly to movie with numpy and mencoder
Asked Answered
T

1

6

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)
Terryterrye answered 12/8, 2011 at 15:12 Comment(0)
C
5

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()
Cambium answered 12/8, 2011 at 19:7 Comment(2)
Cool, so the best would be to do something like def grayscale(x,min,max): t = int_max*(x-min)/(max-min) return np.uint8(t)*0x1 + np.uint8(t)*0x100 + np.uint8(x)*0x10000 + 0x0f000000, where int_max would be the maximum value of 8-bit ints.Terryterrye
Great stuff Joe, I'm running the test right now, but I think I've got it now, the byteorder will probably do the trick! Thanks a ton! Alex.Terryterrye

© 2022 - 2024 — McMap. All rights reserved.