7

I have a loop that modifies elements of a 2D numpy array water_depth with type float. The array contains water depth for each pixel and the range is usually between 0 to 1.5m. I would like to make a video out of this changing array: each iteration can be a frame in the video. I only found this link explaining a similar question and suggests using cv2 VideoWriter. The problem is that my numpy array is a float, not integer. Does this mean that I need to do some sort of pre-processing on my array in each iteration?

import numpy as np

water_depth = np.zeros((500,700), dtype=float)

for i in range(1000):
    random_locations = np.random.random_integers(200,450, size=(200, 2))
    for item in random_locations:
        water_depth[item[0], item[1]] += 0.1
        #add this array to the video
5
  • 3
    Do you actually have OpenCV installed to be able to use it? Because that is one method but not the only way. The easiest way is probably just to save each image individually, and then run them through whatever you like to create a video (for command line tools, the most popular is probably ffmpeg).
    – alkasm
    Commented Aug 19, 2018 at 5:42
  • Yes I installed opencv 64bit yesterday and it works fine. I am fine with any methods. I thought the advantage of using opencv would be its speed. Isn't saving arrays into images costly? If not then any method would be fine. Commented Aug 19, 2018 at 5:47
  • 3
    I guess but you could say the same about the video file itself, I mean, it holds all the information about the frames...anyways, if you'd like to make a video using cv2.VideoWriter(), then yes, you need to scale and convert your array. VideoWriter() needs RGB images (not single-channel grayscale images like your water_depth) and needs them to be 8-bit. You can scale them appropriately between 0 and 255 with cv2.normalize() and then you can merge that into a three-channel image with cv2.merge().
    – alkasm
    Commented Aug 19, 2018 at 6:06
  • I guess your comment answers my question. I used scipy.misc.toimage(water_depth, cmin=0.0, cmax=1.5).save('image\{0}.png'.format(i)) to save my array into image. scipy.misc.toimage takes care of the required pre-processing by just indicating the min and max of arrays. Commented Aug 19, 2018 at 6:12
  • 2
    If you don't want to go to the trouble of installing OpenCV, have a look at my answer here stackoverflow.com/a/46850365/2836621 where I show how to write a video just using ffmpeg. It is C++ rather than Python, but it's only a single line. Commented Aug 19, 2018 at 7:29

1 Answer 1

10

Note that working with OpenCV for video I/O purposes can sometimes be tricky. The library isn't built around supporting these kinds of operations, they're just included as a nicety. Typically, OpenCV will be built off ffmpeg support, and whether or not you have the same codecs to read/write videos as another person is somewhat arbitrary depending on your system. With that said, here's an example so you get the idea of exactly what preprocessing you might do:

import numpy as np
import cv2

# initialize water image
height = 500
width = 700
water_depth = np.zeros((height, width), dtype=float)

# initialize video writer
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
fps = 30
video_filename = 'output.avi'
out = cv2.VideoWriter(video_filename, fourcc, fps, (width, height))

# new frame after each addition of water
for i in range(10):
    random_locations = np.random.random_integers(200,450, size=(200, 2))
    for item in random_locations:
        water_depth[item[0], item[1]] += 0.1
        #add this array to the video
        gray = cv2.normalize(water_depth, None, 255, 0, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
        gray_3c = cv2.merge([gray, gray, gray])
        out.write(gray_3c)

# close out the video writer
out.release()

Note that I changed the number of iterations to 10 instead of 1000 just to make sure it worked. Here normalize(..., 255, 0, ...) scales your image so that the max value is 255 (white) and the min value is 0 (black). This means that when at first all your random points start dotting everything, they turn white. However once one point lands on top of another, that will be the brightest---twice as bright as all other points, so they'll immediately drop to be gray. If that's not what you want, you have to think if you'd have a maximum value that your image might be and assume that your images won't change brightness otherwise.

2
  • 1
    This answer is complete and solved my problem. Is it also possible to make a colour video: zeros as white and the rest lets say from light blue to dark blue? Commented Aug 19, 2018 at 6:28
  • 2
    @BehzadJamali, sure, you could do that---probably easiest in the HSV colorspace. You can check out my answers here and here to get some more familiarity with HSV.
    – alkasm
    Commented Aug 19, 2018 at 18:10

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.