pythonnumpyopencvpython-imaging-library

How to average number of frames in OpenCV2


I found this very nice example, how to make an array of frames: https://stackoverflow.com/a/17394994/7489698

But I do not know how to use this in PIL correctly. To demonstrate it, I have sketched this commands:

import numpy as np
import cv2
from PIL import Image, ImageTk

nframes = 10
frame = cv2.imread('test.jpg')

(x,y,d)=frame.shape
frames = np.empty((nframes,x, y, d))

for k in range(nframes):
    frames[k,:,:,:] = frame

frame_avg=np.int8(np.mean(frames,0))
frame_avg.shape

cv2.imwrite('test_avg.jpg',frame_avg)

Image.fromarray(frame)
Image.fromarray(frame_avg)

The image test.jpg is read and 10 times stored in the array frames. The average thereof is stored in the file test_avg.jpg where it can be seen that it looks ok.

Converting the orignal frame to a PIL image is working, but the last line throws this error:

---> 20 Image.fromarray(frame_avg)

/usr/local/lib/python3.10/dist-packages/PIL/Image.py in fromarray(obj, mode)
   3313             typekey_shape, typestr = typekey
   3314             msg = f"Cannot handle this data type: {typekey_shape}, {typestr}"
-> 3315             raise TypeError(msg) from e
   3316     else:
   3317         rawmode = mode

TypeError: Cannot handle this data type: (1, 1, 3), |i1

What did I do wrong???

KR, Christof


Solution

  • The frame_avg array you created has a data type of int8 (|i1), which PIL's Image.fromarray does not support for RGB images. PIL expects uint8 (np.uint8) data for typical RGB images. That's why you are seeing the TypeError. Change the data type of frame_avg from int8 to uint8. And edit your code like this:

    import numpy as np
    import cv2
    from PIL import Image
    
    nframes = 10
    frame = cv2.imread('test.jpg')
    
    # Convert to RGB for PIL compatibility (OpenCV reads as BGR)
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    
    (x, y, d) = frame.shape
    frames = np.empty((nframes, x, y, d), dtype=np.uint8)
    
    for k in range(nframes):
        frames[k, :, :, :] = frame
    
    # Average and convert back to uint8
    frame_avg = np.mean(frames, axis=0).astype(np.uint8)
    
    # Save averaged image using OpenCV (convert back to BGR for OpenCV)
    cv2.imwrite('test_avg.jpg', cv2.cvtColor(frame_avg, cv2.COLOR_RGB2BGR))
    
    # Convert to PIL Image and display
    Image.fromarray(frame)       # should work
    Image.fromarray(frame_avg)   # now works too
    

    The following documentations and links may help you

    Which is best practice doing null checks or empty checks with Kotlin data classes?

    https://github.com/python-pillow/Pillow/issues/1190

    https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.fromarray

    If you are looking for some more OpenCV related projects, you can see here: https://www.pcbway.com/project/shareproject/How_to_use_AI_to_program_a_2DOF_ROBOT_Arduino_pyhton_opencv_chatgp_ps3eyecam_d3896679.html