我正在写一个JS接缝雕刻库。它工作得很好,我可以实时地非常干净地重新缩放一个1024x1024的图像,就像我可以拖动它一样快。看起来很棒!但是为了获得这样的性能,我需要预先计算大量的数据,这大约需要10秒。我正在努力消除这个瓶颈,并在这里寻找想法。
接缝雕刻作品通过去除能量最低的"弯弯曲曲";图像中的像素线。例如,如果你有一个10x4的图像,水平接缝可能看起来像这样:
........x.
.x.....x.x
x.xx..x...
....xx....
所以如果你将它的大小调整为10x3,你删除了所有的"X"像素。一般的想法是,接缝围绕着对你来说视觉上很重要的东西,所以不是普通的缩放,所有的东西都被压扁了,你主要是删除那些看起来像空白的东西,而图片中的重要元素不受影响。
计算能级、删除能级和重新计算的过程相当昂贵,所以我在node.js中预先计算并生成一个.seam
文件。
.seam文件中的每个接缝基本上是:起始位置,方向,方向,方向,方向,....所以对于上面的例子,你可以输入:
starting position: 2
seam direction: -1 1 0 1 0 -1 -1 -1 1
这是相当紧凑的,允许我根据设置为1024x1024的图像生成~60-120kb的.seam文件。
现在,为了获得快速渲染,我生成了一个2D网格,表示应该删除像素的顺序。所以:
(图一):
........1.
.1.....1.1
1.11..1...
....11....
包含1条信息,然后我们可以添加第二个接缝:
(图B):
2...2....2
.222.2.22.
......2...
合并后得到:
2...2...12
.122.2.1.1
1211..122.
....112...
为了完整性,我们可以添加接缝3 &4:
(图C &D):
33.3..3...
..3.33.333
4444444444
并合并为:
(图E):
2343243412
3122424141
1211331224
4434112333
你会注意到,在这个合并版本中,2s并不是全部连接在一起的,因为合并版本是基于原始像素位置的,而接缝是基于计算接缝时的像素位置的,对于第二个接缝来说,这是一个10x3px的图像。
这允许前端渲染器基本上只是循环遍历图像中的所有像素,并根据需要删除的像素数量对网格进行过滤。它在我的电脑上以100fps的速度运行,这意味着它非常适合在大多数设备上进行单次调整。耶!
现在我要解决的问题是:
从接缝到-1 1 0 1 0 -1 -1 -1 1
到预先计算的要删除像素的网格的解码步骤是slow。这样做的基本原因是,每当一个接缝被移除时,从那里开始的所有接缝都被移动。
我目前计算"移位"的方式是通过拼接一个缝的每个像素的1,048,576元素数组(1024x1024像素的图像,其中每个索引是x * height + y
水平接缝),存储原始像素的位置。.splice
运行一百万次,实在是太慢了…
这看起来像是一个奇怪的leetcode问题,因为也许有一个数据结构可以让我知道"在这个像素之上有多少像素已经被一个接缝排除在外了"。这样我就知道"标准化索引"了。但是…我想不出来,我能想到的任何东西都需要多次重写才能使它更快。
或者可能有更好的方法来编码接缝数据,但是使用接缝的每像素1-2位是非常有效的,而我能想到的任何其他方法都会使这些文件变得巨大。
感谢您花时间阅读这篇文章!
[edit and tl;dr]—如何有效地合并figures A-D
到figure E
?或者,从任何压缩格式中有效生成图E的任何想法
如果我理解正确,您当前的算法是:
while there are pixels in Image:
seam = get_seam(Image)
save(seam)
Image = remove_seam_from_image(Image, seam)
然后要构造一个数组,其中包含每个接缝的编号。
为此,您可以创建一个1024x1024的数组,其中每个值都是数组中该元素的索引(y*width+x
)。
修改后的算法给出你想要的
Let Indices have the dimensions of Image and be initialized to [0, len(Image)0
Let SeamNum have the dimensions of Image and be initialized to -1
seam_num = 0
while there are pixels in Image:
seam = get_seam(Image)
Image = remove_seam_from_image(Image, seam)
Indices = remove_seam_from_image_and_write_seam_num(Indices, seam, SeamNum, seam_num)
seam_num++
remove_seam_from_image_and_write_seam_num
在概念上与remove_seam_from_image
相同,除了当seam
从Indices
中移除每个像素时,它会将seam_num
写入SeamNum
中由Indices
中像素值指示的位置。
输出是您正在寻找的SeamNum
数组。