将图像读取到预先分配的 numpy 数组

问题描述 投票:0回答:2

我正在 Python 中进行一些fast图像处理(使用 Numpy/Scipy + OpenCV)。 有几千张形状完全相同的图像 - 一旦我读到第一张图像,我就确切地知道所有其他图像会是什么样子。

问题是从磁盘读取每个下一个图像会导致分配新内存(这很慢)。有没有办法通过将每个下一个图像直接读取到已经存在的内存中来避免这种情况(ndarray

)?我知道 
cv2.imdecode 在 C++ 中可以接受指向预分配 Mat 的指针,但它似乎没有 Python 绑定(唯一的选择是返回一个全新的数组)。

我需要这个来进行多重处理 - 我想将图像读入共享内存,然后在工作进程中对它们进行一些繁重的工作。现在,我被迫将

cv2.imread 分配和返回的数组中的数据复制到共享内存中,这又需要时间。我希望能够直接在那里写信。

python numpy opencv
2个回答
0
投票
下面的代码适用于视频帧,而不是图像文件序列,但在没有任何其他答案的情况下,我想无论如何我都会将其发布在这里。

import argparse from contextlib import contextmanager from multiprocessing import JoinableQueue, Process from multiprocessing.shared_memory import SharedMemory from pathlib import Path import cv2 import numpy as np @contextmanager def video_capture_context(videopath: str): video_capture = cv2.VideoCapture(videopath, cv2.CAP_ANY) try: yield video_capture finally: video_capture.release() def _vid_reader( videopath: Path, shared_memory_name: str, output_queue: JoinableQueue, ): with video_capture_context(str(videopath)) as video_capture: fps = float(video_capture.get(cv2.CAP_PROP_FPS)) x = cv2.CAP_PROP_FRAME_WIDTH y = cv2.CAP_PROP_FRAME_HEIGHT frame_no = 0 frame_time = 0.0 shared_memory = None shared_array = None sucess = True while sucess: # first frame is read without shared memory sucess, frame = video_capture.read(shared_array) if sucess: if frame is not None and shared_memory is None: # create the shared memory _after_ reading the first frame # so we know how much shared memory we need shared_memory = SharedMemory(name=shared_memory_name, create=True, size=frame.nbytes) shared_array = np.ndarray(frame.shape, dtype=frame.dtype, buffer=shared_memory.buf) shared_array[:] = frame[:] output_queue.put((frame_no, frame_time, frame.shape, frame.dtype)) # wait for the queue to be read from output_queue.join() # move on to next frame frame_no += 1 frame_time = frame_no / fps # put the sentinel into the queue output_queue.put(None) # wait for the sentinel to be read # otherwise shared memory of the last frame will close too soon output_queue.join() shared_memory.close() shared_memory.unlink() def _vid_saver( shared_memory_name: str, output_queue: JoinableQueue, ): shared_memory = None shared_array = None # get things in the queue until the sentinel while item := output_queue.get(): frame_no, frame_time, shape, dtype = item if not shared_memory: shared_memory = SharedMemory(name=shared_memory_name, create=False) shared_array = np.ndarray(shape, dtype=dtype, buffer=shared_memory.buf) print(frame_no) print(shared_memory.buf) print(shared_array.sum()) output_queue.task_done() shared_memory.close() # say we've processed the sentinel output_queue.task_done() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("filename") args = parser.parse_args() queue_reader = JoinableQueue(1) p_reader = Process( target=_vid_reader, args=(Path(args.filename), "frame", queue_reader), ) p_printer = Process(target=_vid_saver, args=("frame", queue_reader)) p_reader.start() p_printer.start() p_printer.join() p_reader.join()
我认为没有 

cv2.imread()

 的等效项,但也许可以将图像预处理为视频格式,以便在上面的代码中使用。

注意:因为只有单个图像大小的共享数组,所以没有任何方法可以同时在两个进程中操作图像。可以在少量共享内存实例之间切换是可以进行的进一步改进。


-1
投票
height, width = (50, 50) image = np.zeros((height, width)) id(image) # outputs: 140411457307552 image[:, :] = np.ones((height, width)) id(image) # outputs: 140411457307552 image = np.ones((height, width)) id(image) # outputs -> 140411437723280 # when reading from disk (assuming your images are 50x50 pixels) image[:, :] = cv2.imread("/home/.../your_im_50x50.png")

通过解决每个图像的尺寸,Python 将尝试将给定的数组存储到现有的数组中。这会导致内存分配到预分配的内存区域。如果数组没有相同的形状,则会引发 ValueError。 当仅提及变量名时,会创建对数组的新引用,从而在内存中产生一个新对象(cf ids)

© www.soinside.com 2019 - 2024. All rights reserved.