高效处理超大图像一次存结果复用及OpenCV拼接性能优化咨询
需求背景
我正在重构OpenCV Stitcher API的stitching_detailed.py文件,程序支持用户自主选择待拼接的图像数量。我的使用场景是拼接单张最高可达300MP的历史高分辨率TIF扫描件,运行过程中很容易触发cv::OutOfMemoryError错误,因此内存管理是核心需求,同时需要在计算耗时和内存占用之间取得最优平衡。
工作流特征
我的拼接工作流在不同阶段需要用到不同分辨率的图像:
- 第一阶段用中分辨率图像做预估
- 第二阶段用低分辨率图像做其他预估
- 最终阶段用全分辨率图像完成拼接
当前的几种实现方案各有优劣,我做了如下性能测试:
性能测试基准代码
import time from functools import wraps import tracemalloc import cv2 import numpy as np def performance_test(func): @wraps(func) def wrapper(*args, **kwargs): tracemalloc.start() start = time.time() result = func(*args, **kwargs) current, peak = tracemalloc.get_traced_memory() print(f"Peak was {peak / 10**6} MB") end = time.time() print(f"Time was {end - start} s") tracemalloc.stop() return result return wrapper
测试用虚拟图像生成代码
img_names = ["dummy1.png", "dummy2.png", "dummy3.png"] image = np.zeros((10000, 10000, 3), np.uint8) image[:] = (0, 0, 255) for img_name in img_names: cv2.imwrite(img_name, image)
现有实现方案对比
方案1:按需加载(低内存,高耗时)
用到对应分辨率图像时才读取原图生成,内存占用最低,但需要反复读取原图,耗时最高:
def process_medium_resolution_images(img_names): for big_image in img_names: high = cv2.imread(big_image) medium = cv2.resize(high, (5000, 5000)) print(medium.shape) def process_low_resolution_images(img_names): for big_image in img_names: high = cv2.imread(big_image) low = cv2.resize(high, (1000, 1000)) print(low.shape) def process_high_resolution_images(img_names): for big_image in img_names: high = cv2.imread(big_image) print(high.shape) @performance_test def main(img_names): process_medium_resolution_images(img_names) process_low_resolution_images(img_names) process_high_resolution_images(img_names) main(img_names) # 测试结果:内存峰值675.15MB,耗时8.50s
方案2:全量缓存(低耗时,高内存)
一次性读取所有原图并生成所有分辨率的图像存入内存,速度最快,但内存占用远超阈值,不符合使用需求:
def img_reading_and_processing(img_names): high_imgs, medium_imgs, low_imgs = [], [], [] for big_image in img_names: high = cv2.imread(big_image) high_imgs.append(high) medium = cv2.resize(high, (5000, 5000)) medium_imgs.append(medium) low = cv2.resize(high, (1000, 1000)) low_imgs.append(low) return high_imgs, medium_imgs, low_imgs def process_imgs(imgs): for img in imgs: print(img.shape) @performance_test def main2(img_names): high_imgs, medium_imgs, low_imgs = img_reading_and_processing(img_names) process_imgs(medium_imgs) process_imgs(low_imgs) process_imgs(high_imgs) main2(img_names) # 测试结果:内存峰值1134.16MB,耗时2.83s
方案3:中间结果落盘(折中)
参考社区建议,将生成的中、低分辨率图像写入磁盘,后续阶段直接读取中间文件,内存占用和耗时都处于折中水平:
def img_reading_and_processing(img_names): for idx, big_image in enumerate(img_names): high = cv2.imread(big_image) medium = cv2.resize(high, (5000, 5000)) low = cv2.resize(high, (1000, 1000)) cv2.imwrite(f"medium{idx}.png", medium) cv2.imwrite(f"low{idx}.png", low) def process_medium_resolution_images2(img_names): for idx, img in enumerate(img_names): medium = cv2.imread(f"medium{idx}.png") print(medium.shape) def process_low_resolution_images2(img_names): for idx, img in enumerate(img_names): low = cv2.imread(f"low{idx}.png") print(low.shape) @performance_test def main3(img_names): process_medium_resolution_images2(img_names) process_low_resolution_images2(img_names) process_high_resolution_images(img_names) main3(img_names) # 测试结果:内存峰值678.15MB,耗时6.77s
诉求
期望找到能结合两种方案优势的实现方法,在内存不超阈值的前提下尽可能降低计算耗时。
内容的提问来源于stack exchange,提问作者Lukas Weber
相关产品推荐
相关产品推荐

