从整体积分图中高效获取局部区域积分图的方法

碧海醫心
发布: 2025-11-16 11:07:16
原创
154人浏览过

从整体积分图中高效获取局部区域积分图的方法

本文详细介绍了如何从一个大型图像(如精灵图集)的积分图中,高效地提取出其中任意指定局部区域(如单个精灵)的积分图。核心方法包括精确切片和基于 numpy 广播机制的行/列减法调整,确保生成的局部积分图具有正确的零起始点,从而实现对子区域求和的快速计算,避免重新计算整个子区域的积分图。

引言:积分图及其应用

积分图(Integral Image),又称求和面积表(Summed-Area Table),是一种用于快速计算图像任意矩形区域内像素值之和的数据结构。其核心思想是,图像中任意一点 (x, y) 的积分图值 I(x, y) 等于原始图像中以 (0, 0) 为左上角、以 (x, y) 为右下角的矩形区域内所有像素值的总和。利用积分图,计算任意矩形区域 (x1, y1) 到 (x2, y2) 内的像素和,仅需四次查找和三次加减运算:I(x2, y2) - I(x1-1, y2) - I(x2, y1-1) + I(x1-1, y1-1)。这极大地提高了区域求和的效率,广泛应用于图像处理、计算机视觉(如Haar特征检测)等领域。

问题:从整体积分图中提取局部积分图

在实际应用中,我们可能拥有一个大型图像(例如,包含多个精灵的精灵图集)的完整积分图。现在,我们需要获取其中某个特定子区域(例如,图集中的一个独立精灵)的积分图。直接对子区域重新计算积分图虽然可行,但如果需要频繁提取,效率会较低。更优的方法是,能否利用已有的整体积分图,通过简单的数学运算和切片操作来“导出”子区域的积分图?

核心原理与实现步骤

答案是肯定的。从整体积分图中提取局部区域的积分图,可以通过以下步骤实现:

  1. 确定子区域的坐标范围: 首先,明确目标子区域在原始大图中的左上角 (y0, x0) 和右下角 (y1-1, x1-1) 像素坐标。这里我们使用半开区间表示法,即 [y0:y1, x0:x1] 代表从 y0 行到 y1-1 行,从 x0 列到 x1-1 列的区域。

  2. 从整体积分图中进行切片: 由于积分图通常会在第一行和第一列进行零填充,以简化边界条件的处理。因此,原始图像中 (y, x) 位置的像素值累加到积分图中的 (y+1, x+1) 位置。 如果我们目标子区域在原始大图中的范围是 [y0:y1, x0:x1],那么在整体积分图中对应的区域将是 [y0:y1+1, x0:x1+1]。 我们需要从整体积分图中切取出这个范围的子矩阵。关键在于,切片时必须包含目标区域的“前导”行和“前导”列(即在整体积分图中,子区域左侧和上方的行/列)。这些前导行和列在后续的调整步骤中至关重要。

  3. 调整切片值以重置原点: 切片得到的子矩阵,其值是基于整体积分图的 (0, 0) 原点累加的。为了使其成为一个独立的、以自身左上角为 (0, 0) 原点的积分图,我们需要进行调整。 调整方法是:将切片子矩阵的第一行和第一列的值从整个子矩阵中减去。具体来说:

    • 将子矩阵的第一行(除了第一个元素)减去其第一个元素。
    • 将子矩阵的第一列(除了第一个元素)减去其第一个元素。
    • 更高效的做法是,将子矩阵的第一行(作为一维数组)从整个子矩阵中减去。
    • 然后,将子矩阵的第一列(作为一维数组)从整个子矩阵中减去。 经过这两步减法操作后,切片子矩阵的第一行和第一列将变为零,从而有效地将子积分图的原点“重置”到其自身的左上角,使其符合标准积分图的定义。

Python 示例

下面通过一个具体的 Python 例子来演示上述步骤,使用 numpy 进行数组操作,并利用 opencv 的 cv.integral 函数来生成和验证积分图。

存了个图
存了个图

视频图片解析/字幕/剪辑,视频高清保存/图片源图提取

存了个图 17
查看详情 存了个图
import numpy as np
import cv2 as cv

# 1. 定义原始精灵图集和目标子区域
sprite_sheet = np.uint8([
    [1, 2, 1, 2],
    [3, 4, 3, 4],
    [1, 2, 1, 2],
    [3, 4, 3, 4],
])

# 定义子区域在sprite_sheet中的坐标 (y0, y1, x0, x1)
# 对应原始图像的 [y0:y1, x0:x1] 区域
# 例如,我们想提取 [[1, 2], [3, 4]] 这个子图
# 它在 sprite_sheet 中是 sprite_sheet[2:4, 2:4]
y0, y1, x0, x1 = 2, 4, 2, 4
sprite = sprite_sheet[y0:y1, x0:x1]

print("原始精灵图集:\n", sprite_sheet)
print("\n目标子区域 (sprite):\n", sprite)

# 2. 计算整个精灵图集的积分图
# cv.integral 会自动在第一行和第一列添加0填充
sheet_integral = cv.integral(sprite_sheet)
print("\n精灵图集积分图 (sheet_integral):\n", sheet_integral)

# 3. 验证:直接计算目标子区域的积分图 (作为我们期望的输出)
expected_sprite_integral = cv.integral(sprite)
print("\n期望的子区域积分图 (cv.integral(sprite)):\n", expected_sprite_integral)

# 4. 从整体积分图中切片
# 注意:切片范围是 [y0:y1+1, x0:x1+1],以包含子区域的“前导”行和列
# 这里的 y0, x0 是原始图像的起始索引,y1, x1 是结束索引+1
# sheet_integral 的索引比原始图像大1
# 所以,如果原始图像的子区域是 [y0:y1, x0:x1]
# 那么在 sheet_integral 中,对应的切片是 [y0:y1+1, x0:x1+1]
# 例如,y0=2, y1=4, x0=2, x1=4
# 切片范围是 sheet_integral[2:5, 2:5]
sliced_integral = sheet_integral[y0:y1+1, x0:x1+1].copy() # 使用 .copy() 避免视图修改原数据
print("\n从整体积分图切片得到的原始子矩阵 (sliced_integral):\n", sliced_integral)

# 5. 调整切片值以重置原点
# 使用 NumPy 的广播机制:
# 第一次减法:将第一行 (sliced_integral[0:1, :]) 从整个矩阵中减去
sliced_integral -= sliced_integral[0:1, :]
print("\n第一次调整后 (减去第一行): \n", sliced_integral)

# 第二次减法:将第一列 (sliced_integral[:, 0:1]) 从整个矩阵中减去
sliced_integral -= sliced_integral[:, 0:1]
print("\n第二次调整后 (减去第一列,最终结果):\n", sliced_integral)

# 6. 验证结果
print("\n最终提取的子区域积分图是否与期望值一致:", np.array_equal(sliced_integral, expected_sprite_integral))
登录后复制

运行结果示例:

原始精灵图集:
 [[1 2 1 2]
 [3 4 3 4]
 [1 2 1 2]
 [3 4 3 4]]

目标子区域 (sprite):
 [[1 2]
 [3 4]]

精灵图集积分图 (sheet_integral):
 [[ 0  0  0  0  0]
 [ 0  1  3  4  6]
 [ 0  4 10 14 20]
 [ 0  5 13 18 26]
 [ 0  8 20 28 40]]

期望的子区域积分图 (cv.integral(sprite)):
 [[ 0  0  0]
 [ 0  1  3]
 [ 0  4 10]]

从整体积分图切片得到的原始子矩阵 (sliced_integral):
 [[10 14 20]
 [13 18 26]
 [20 28 40]]

第一次调整后 (减去第一行): 
 [[ 0  0  0]
 [ 3  4  6]
 [10 14 20]]

第二次调整后 (减去第一列,最终结果):
 [[ 0  0  0]
 [ 0  1  3]
 [ 0  4 10]]

最终提取的子区域积分图是否与期望值一致: True
登录后复制

注意事项

  • 坐标系统: 务必理解原始图像坐标、积分图坐标以及切片范围之间的对应关系。cv.integral 函数生成的积分图通常比原始图像多一行一列(用于零填充),因此原始图像中的 (y, x) 对应积分图中的 (y+1, x+1)。
  • 切片范围: 切片 sheet_integral[y0:y1+1, x0:x1+1] 的关键在于 +1,它确保我们包含了子区域在积分图中的“前导”行和列,这对于后续的减法调整至关重要。
  • NumPy 广播: 在调整步骤中,sliced_integral -= sliced_integral[0:1, :] 和 sliced_integral -= sliced_integral[:, 0:1] 利用了 NumPy 的广播机制。[0:1, :] 选取的是第一行(保持二维结构),[:, 0:1] 选取的是第一列(保持二维结构),这样 NumPy 就能正确地将其广播到整个矩阵进行减法运算。
  • 数据类型: 积分图的值可能会比原始像素值大很多,因此需要使用足够大的数据类型(如 int32 或 int64)来存储,以避免溢出。cv.integral 默认会使用 int32。

总结

通过上述方法,我们可以高效地从一个大型图像的积分图中提取出任意指定局部区域的积分图,而无需重新计算。这种方法利用了积分图的性质和 NumPy 的高效数组操作,对于需要频繁处理图像子区域积分图的场景(如实时目标检测、特征提取等)具有重要的实用价值。理解并掌握这一技巧,能够显著优化图像处理算法的性能。

以上就是从整体积分图中高效获取局部区域积分图的方法的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号