无法使用opencv加载大图像
Can not load large image with opencv
我尝试使用 opencv 加载大小为 100.000 * 15.000 像素且文件大小为 4.305 kb 的图像。如果我使用以下代码加载此图像:
cv::Mat reallyBigImage = cv::imread("reallyBigImage.png");
我收到以下错误:
我有一台 32 GB RAM 的电脑,用 64 位编译这个程序。对于这种尺寸的图像,这应该足够了。这么大的图片可以整体加载到opencv吗?
这是我的程序在汇编程序中中断的地方。
箭头指示具体位置。此片段来自 memcpy.asm.
CopyUp:
cmp r8, 128
jbe XmmCopySmall
bt __favor, __FAVOR_ENFSTRG ; check for ENFSTRG (enhanced fast strings)
jnc XmmCopyUp ; If Enhanced Fast String not available, use XMM
; use Enhanced Fast Strings
; but first align the destination dst to 16 byte alignment
mov rax, r11 ; return original destination pointer
mov r11, rdi ; save rdi in r11
mov rdi, rcx ; move destination pointer to rdi
mov rcx, r8 ; move length to rcx
mov r8, rsi ; save rsi in r8
mov rsi, r10 ; move source pointer to rsi
-->rep movsb ; copy source to destination buffer
mov rsi, r8 ; restore rsi
mov rdi, r11 ; restore rdi
ret
该图像是 6 GB 原始数据(100,000 * 15,000 * 4 字节)。通常 Windows 将进程的最大堆大小限制为远低于该大小。但即使你增加了这个限制,你也会遇到内存碎片(无法写入单个 6GB 连续内存块),OpenCV 的分配器可能无法处理。
这就是您在将图像数据写入内存时遇到访问冲突错误的原因。它正在尝试写入不是 allowed/supposed 写入的内存位置。
您要么必须将图像拆分成多个 ROIs/sub-images 以避免碎片化,要么编写您自己的分配器,以较小的块在内存中分配图像。
如果你处理的是大图,我可以向你推荐libvips吗?它可用于 Linux、macOS 和 Windows - 免费,来自 here。
如果您有一张 100,000 x 15,000 PNG
的图像,并且您想要从一个点 (100,100) 开始提取一个 2000x1000 的区域,您可以这样做:
time vips crop test.png excerpt.png 2000 1000 100 100 --vips-leak
示例输出
memory: high-water mark 192.29 MB
real 0m1.771s
user 0m0.826s
sys 0m0.082s
如您所见,它仅使用了 200MB 的 RAM,并花费了大约 1 秒的时间。 time
和 --vips-leak
仅用于演示目的 - 您可以正常省略它们。
运行 一些本地测试,这可以用 OpenCV 3.1 重现,但不能用 3.2 或 3.3。这表明这是一个错误(以及您首先遇到访问冲突的事实)。
具体来说issue #6317.
这是在 OpenCV 3.2 发布之前修复的,这将符合上述观察结果。因此,最好的选择是升级您的 OpenCV 副本。
我尝试使用 opencv 加载大小为 100.000 * 15.000 像素且文件大小为 4.305 kb 的图像。如果我使用以下代码加载此图像:
cv::Mat reallyBigImage = cv::imread("reallyBigImage.png");
我收到以下错误:
我有一台 32 GB RAM 的电脑,用 64 位编译这个程序。对于这种尺寸的图像,这应该足够了。这么大的图片可以整体加载到opencv吗?
这是我的程序在汇编程序中中断的地方。 箭头指示具体位置。此片段来自 memcpy.asm.
CopyUp:
cmp r8, 128
jbe XmmCopySmall
bt __favor, __FAVOR_ENFSTRG ; check for ENFSTRG (enhanced fast strings)
jnc XmmCopyUp ; If Enhanced Fast String not available, use XMM
; use Enhanced Fast Strings
; but first align the destination dst to 16 byte alignment
mov rax, r11 ; return original destination pointer
mov r11, rdi ; save rdi in r11
mov rdi, rcx ; move destination pointer to rdi
mov rcx, r8 ; move length to rcx
mov r8, rsi ; save rsi in r8
mov rsi, r10 ; move source pointer to rsi
-->rep movsb ; copy source to destination buffer
mov rsi, r8 ; restore rsi
mov rdi, r11 ; restore rdi
ret
该图像是 6 GB 原始数据(100,000 * 15,000 * 4 字节)。通常 Windows 将进程的最大堆大小限制为远低于该大小。但即使你增加了这个限制,你也会遇到内存碎片(无法写入单个 6GB 连续内存块),OpenCV 的分配器可能无法处理。
这就是您在将图像数据写入内存时遇到访问冲突错误的原因。它正在尝试写入不是 allowed/supposed 写入的内存位置。
您要么必须将图像拆分成多个 ROIs/sub-images 以避免碎片化,要么编写您自己的分配器,以较小的块在内存中分配图像。
如果你处理的是大图,我可以向你推荐libvips吗?它可用于 Linux、macOS 和 Windows - 免费,来自 here。
如果您有一张 100,000 x 15,000 PNG
的图像,并且您想要从一个点 (100,100) 开始提取一个 2000x1000 的区域,您可以这样做:
time vips crop test.png excerpt.png 2000 1000 100 100 --vips-leak
示例输出
memory: high-water mark 192.29 MB
real 0m1.771s
user 0m0.826s
sys 0m0.082s
如您所见,它仅使用了 200MB 的 RAM,并花费了大约 1 秒的时间。 time
和 --vips-leak
仅用于演示目的 - 您可以正常省略它们。
运行 一些本地测试,这可以用 OpenCV 3.1 重现,但不能用 3.2 或 3.3。这表明这是一个错误(以及您首先遇到访问冲突的事实)。
具体来说issue #6317.
这是在 OpenCV 3.2 发布之前修复的,这将符合上述观察结果。因此,最好的选择是升级您的 OpenCV 副本。