怎么做一个好的图像切割
这个随笔的题目很奇怪,本来我是想写图像分割的,但是很容易和CV领域里的image segmentation弄混淆。那个领域无数人研究灌水烂了,我要说的是一个简单的数学问题,如何对一个图像做一个好的切割?
什么叫好的切割?
答:不重不漏!
为什么这样说?这其实是我在应用并行计算时发现的问题。你想把图像分成大小相同的两个部分(比如),分别对两个部分进行并行的操作。那么一个好的分割是你无论如何少不掉的。
在进行图形切割之前还需要注意一点的是,图像有两维,但是这个问题可以归结为定长一维线段的(多段)切割。读者简单想一想便可知。
方法
- 将线段的(将要切割的)几个部分编上编号,0,1,2... ,一共n个部分。线段长记为 L
- 计算如下值 L * index / n , L * (index + 1) / n
- 两个值之间的,就是编号为index的部分所占。
这种方法有什么问题?当 L 能被 n 整除时没有任何问题。比如L为16,n为4,数列$[ L * index / n ]_n$就为 0, 4, 8, 12。我们的切割水到渠成,第一部分0 ~ 4,但不包括4。第二部分... ... 第四部分12 ~ 16,但不包括16。
但当 L 不能被 n 整除呢?假如 n 为 3 我们的数列变成了:0, 5.33, 10.66。此时我们便要注意,因为我们经常需要把这些非整数作为参数传给函数的!假如你传给 int 型参数,小数部分就会被截掉。那么函数拿到的东西是这样的。0 ~ 5 但是不包括 5, 5 ~ 10 但不包括 10, 10 ~ 16 但不包括 16。
合理吗?很合理!即,如果你使用这种表示方式是一点问题都没有的。第一节长度为5,第二节长度为5,第三节长度为6, 5 + 5 + 6 = 16。做了一个好的分割。
但是,你经常会遇到一种等价的表达方式!
即 开始 + 长度 的表达方式。
如,第一节起始为0,长度为5;第二节起始为5,长度为5。问题就在于这个长度。
很容易想到的这个长度就是 L / n 。当它为整数的时候,没有任何问题。此时线段被均分为整数大小的子线段。
但是当 L / n 不为整数时,如我上面所说,它经常作为参数被传给 int 型的参数。此时就会有问题。
L / n = 16 / 3 = 5.33, int 5.33 = 5。
那么每一节的长度都是5,我们的截取就出现了问题:漏了一些点!
针对这种等价的表达方式,正确的做法是这样的:
我们的数列 $[ L * index / n ]_n$ 为 0, 5.33, 10.66
每一节的长度应该是这样来计算:即先取整再作差来获得正确结果!这样的话每一段的长度就为 5 - 0 = 5,10 - 5 = 5, 16 - 10 = 6。是无论如何不会漏的。
总结:
虽然这些东西可能非常简单甚至不需写出来,但是也是我的一点思索。记录在此,以防遇到同样的问题的时候重复思考。idea就是当线段不可能均等切割的时候找到真实的切割方式。