【问题标题】:Using OpenCV-Python, how to use Python lists and Numpy arrays together efficiently?使用 OpenCV-Python,如何有效地结合使用 Python 列表和 Numpy 数组?
【发布时间】:2019-09-18 14:59:56
【问题描述】:

我正在尝试重现我在艺术博物馆看到的实时视频狭缝扫描效果。

程序使用 opencv-python 保存来自网络摄像头的最后 480 帧的缓冲区,然后将每帧中的一行像素组合成单个图像。我编写了产生预期效果的代码,但运行速度比我想要的要慢一些。如何以最有效的方式同时使用 python 列表和 numpy 数组来优化算法?

请注意,填充缓冲区需要 10 秒左右才能看到视频效果。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

# originally tried to use numpy to store the buffer
#buffer = np.zeros(shape=(480,480,640,3),dtype=np.uint8) 
buffer = []
slices = []
f2 = np.zeros(shape=(480,640,3),dtype=np.uint8)

display = False
rows = np.arange(480)

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()
    frame = cv2.flip(frame, -1) # flipping the frame to get the scan lines to move downward

    # originally I tried to make the buffer like this but get a memory error    
    #buffer = np.roll(buffer, -1, axis=0)
    #buffer[-1] = frame

    # using a list of numpy arrays seems to be more efficient for storing the buffer   
    buffer.append(frame)
    if(len(buffer) > 480):
        buffer = buffer[1:]

        # using a list to combine a row of pixels from each frame
        # could this be optimized with numpy arrays?
        for i in range(len(buffer)):
            slices.append(buffer[i][i])

        # convert each row to a numpy array to store final frame for display
        f2[rows] = np.array(slices[::-1])
        slices.clear()
        cv2.imshow('frame', f2)

    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

我的另一个想法是重写代码以使用更少的缓冲帧并将每帧中的几行组合在一起,但如果可能的话,我想尝试使用全分辨率视频优化代码。

【问题讨论】:

  • 好吧,我可以提供一些优化,首先使用渲染选项,因为您的像素几乎总是只会改变一点,它会改善渲染。比使用多线程,因此下一帧将被计算为您呈现的最后一帧。您只能存储您使用的值的一半(只是一些空间优化)。
  • 感谢您的建议。我找不到任何关于渲染选项的信息。有没有办法只更新已更改的像素?你有那个链接吗?我正在努力实现多线程
  • 其实现在我也找不到了,也许你可以使用set_datadraw。您可以专门针对此主题提出另一个问题。
  • 我意识到由于网络摄像头的帧率,延迟效果很慢。如果我得到 30 fps 并填充 480 帧的缓冲区,这应该需要大约 16 秒,我重写了代码以便能够处理不同大小的缓冲区以获得不同的延迟效果。例如,240 帧缓冲区将每帧的两行像素组合在一起,或者 120 帧缓冲区将每帧的 4 行像素组合在一起。如果有人感兴趣,请告诉我,我会发布更多代码。

标签: python algorithm numpy opencv


【解决方案1】:

我实现了设置切片大小以加快速度。它使图像更阻塞,但没关系。我喜欢 8 的切片大小。为了好玩,我还实现了一些颜色映射。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

buffer = []
slices = []
height = 480
width = 640
sliceSize = 8

cap.set(4, height)
cap.set(3, width)
f2 = np.zeros(shape=(height,width,3),dtype=np.uint8)

display = False
rows = np.arange(int(height/2))

cv2.namedWindow('funnymirror', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('funnymirror', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

c = 0
cmap = 9
cycle = True

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()
    frame = cv2.flip(frame, 0) # flipping the frame to get the scan lines to move downward

    cframe = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)  # BGR color to gray level

    if cycle:
        c= (c +1) % 255
    cframe+= c
    cframe = cv2.applyColorMap(cframe, cmap)

    frame = cv2.addWeighted(frame, 0.7, cframe, 0.3, 0)

    # using a list of numpy arrays seems to be more efficient for storing the buffer   
    buffer.append(frame)
    if(len(buffer) > int(height/sliceSize)):
        buffer = buffer[1:]

        # using a list to combine a row(s) of pixels from each frame
        for i in range(int(height/sliceSize)):
            for j in range(sliceSize):
                slices = buffer[i][i*sliceSize+j]
                f2[i*sliceSize+j] = np.array(slices)

        f2 = cv2.flip(f2, -1)
        cv2.imshow('funnymirror', f2)


    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'): # press q to quit
        break
    # for testing purposes only change slice size on the fly
    elif key == 91: # press [ to reduces slices
        sliceSize = int(sliceSize/2)
        if sliceSize < 1:
            sliceSize = 1
        print(f'sliceSize: {sliceSize}')
    elif key == 93: # press ] to increase slices
        if sliceSize < 64:
            sliceSize*=2
        print(f'sliceSize: {sliceSize}')
    # press 1, 2, 3, or 4 to change color map
    elif key == 49: # press 1
        cmap = 2
    elif key == 50: # press 2
        cmap = 4
    elif key == 51: # press 3
        cmap = 9
    elif key == 52: #press 4
        cmap = 7
    elif key == 61: # press = to toggle cycling color map
        cycle= not cycle
        print(cycle)
    elif key!=255:
        print(key)

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

【讨论】:

    最近更新 更多