Last updated
Was this helpful?
Last updated
Was this helpful?
🎯多重散射腔内的被动非线性光学映射 | 🎯积分球构建多重散射腔 | 🎯改变腔内的光散射增强非线性阶数 | 🎯使用Fashion MNIST图像集,面部关键点数据集和行人检测数据集评估算法
我们可以使用此算法将灰度图像转换为黑色和白色两种颜色。该算法将像素值四舍五入为两个极值中的最接近值(0 表示黑色,255 表示白色)。原始值与四舍五入值之间的差值(称为误差)将添加到相邻像素,分布如下:
因此,该行上的下一个像素的误差为 7/16,而下一行的像素的误差为 5/16,依此类推。处理完当前像素后,算法将转到下一个像素,该像素现在包含前一个像素的部分误差。优化此算法的一个关键问题是,并行处理像素可能是不可能的:每个像素的最终值都会受到对先前像素进行的计算的影响。这表明使用多个线程进行并行处理可能很困难或不可能。
让我们加载将要使用的库以及测试图像(一个 400×400 NumPy 的 uint8 数组)。
如果这不仅仅是一个示例,我们希望使用各种图像和尺寸对代码进行基准测试,以匹配我们期望遇到的各种输入。然而,为了简单起见,我们将坚持使用这张图片。
以下,我将从一个简单的实现开始,使其更快,减少内存使用量,然后再进行进一步优化。为清晰起见,省略了一些中间步骤和失败的实验。
最简单实现:
代码将临时结果存储在 16 位整数中,因为添加错误可能会使某些像素为负数或大于 255。这两种情况都不适合无符号 8 位整数。最后,我将结果转换为 8 位图像,这就是函数应该返回的内容。
用 numba.njit 修饰的代码看起来像 Python 代码,但是实际上在运行时编译成机器代码。这是快速、低水平的代码!
运行时间:2.3ms
一般来说,我们希望使循环的内部部分尽可能快。查看代码中的误差扩散部分,指令级并行性应该有助于加快代码的运行速度,因为每个计算都是独立的:
分支预测错误会导致它们变慢吗?稍微思考一下就会发现,这些分支非常容易预测,因为它们仅取决于像素位置。考虑一个 6×6 图像:根据图像中像素的位置,将采用不同的分支组合。
例如,区域 1 和 4 中的像素无法将误差扩散到前一列,因为没有前一列。因此不会采用相关分支(如果 y < last_y 且 x > 0)。
在较大的图像中,几乎所有像素都将位于区域 2 中,并采用完全相同的分支,因此 CPU 应该能够可靠地预测这些分支。因此,合理的假设是,即使在当前状态下,代码的扩散部分也以不错的速度运行。
相反,误差本身的计算似乎可能很慢:
首先,分支取决于像素值,因此 CPU 可能难以预测,而且不清楚编译器是否会生成无分支代码。其次,这其中涉及一些相对复杂的数学运算:对浮点数进行舍入似乎会很慢。
所有这些都可以简化为一个简单的检查:测量像素值是否小于或大于中间点。在 Python 中:new_value = 0 if old_value < 128 else 255。由于 new_value 在任何一种情况下都会获得一个值集,因此希望编译器将其转换为无分支代码,这样我们就不必担心分支预测错误的代价。
运行时间:830.7s
运行时间:909.6s
运行时间:602.7s