解决方案
数据文件:paw.txt。源代码:
from scipy import *
from operator import itemgetter
n = 5 # how many fingers are we looking for
d = loadtxt("paw.txt")
width, height = d.shape
# Create an array where every element is a sum of 2x2 squares.
fourSums = d[:-1,:-1] + d[1:,:-1] + d[1:,1:] + d[:-1,1:]
# Find positions of the fingers.
# Pair each sum with its position number (from 0 to width*height-1),
pairs = zip(arange(width*height), fourSums.flatten())
# Sort by descending sum value, filter overlapping squares
def drop_overlapping(pairs):
no_overlaps = []
def does_not_overlap(p1, p2):
i1, i2 = p1[0], p2[0]
r1, col1 = i1 / (width-1), i1 % (width-1)
r2, col2 = i2 / (width-1), i2 % (width-1)
return (max(abs(r1-r2),abs(col1-col2)) >= 2)
for p in pairs:
if all(map(lambda prev: does_not_overlap(p,prev), no_overlaps)):
no_overlaps.append(p)
return no_overlaps
pairs2 = drop_overlapping(sorted(pairs, key=itemgetter(1), reverse=True))
# Take the first n with the heighest values
positions = pairs2[:n]
# Print results
print d, "\n"
for i, val in positions:
row = i / (width-1)
column = i % (width-1)
print "sum = %f @ %d,%d (%d)" % (val, row, column, i)
print d[row:row+2,column:column+2], "\n"
输出不重叠方块。似乎选择的区域与您的示例中相同。
一些评论
棘手的部分是计算所有2x2平方的和。我猜你们都需要,所以可能会有重叠。我使用切片从原始2D数组中切割第一个/最后一个列和行,然后将它们全部重叠在一起并计算总和。
为了更好地理解它,对一个3x3数组进行成像:
>>> a = arange(9).reshape(3,3) ; a
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
然后你可以把它切片:
>>> a[:-1,:-1]
array([[0, 1],
[3, 4]])
>>> a[1:,:-1]
array([[3, 4],
[6, 7]])
>>> a[:-1,1:]
array([[1, 2],
[4, 5]])
>>> a[1:,1:]
array([[4, 5],
[7, 8]])
现在想象一下,你把它们一个叠在另一个上面,然后在相同的位置上求和。这些和将与左上角位置相同的2x2平方的和完全相同:
>>> sums = a[:-1,:-1] + a[1:,:-1] + a[:-1,1:] + a[1:,1:]; sums
array([[ 8, 12],
[20, 24]])
当你有2x2平方的和时,你可以用max来求最大值,或者排序,或者排序来求峰值。
为了记住峰值的位置,我将每个值(总和)与其在扁平数组中的序数位置(参见zip)耦合起来。然后在打印结果时再次计算行/列位置。
笔记
我允许2x2平方重叠。编辑后的版本过滤掉其中的一些,这样只有不重叠的正方形出现在结果中。
选择手指(一个想法)
另一个问题是如何从所有的峰中选择可能是手指的峰。我有个主意,可能有用也可能没用。我现在没有时间实现它,所以只是伪代码。
我注意到,如果前面的手指几乎保持在一个完美的圆圈上,后面的手指应该在这个圆圈里。此外,食指的间距也差不多相等。我们可以尝试使用这些启发式属性来检测手指。
伪代码:
select the top N finger candidates (not too many, 10 or 12)
consider all possible combinations of 5 out of N (use itertools.combinations)
for each combination of 5 fingers:
for each finger out of 5:
fit the best circle to the remaining 4
=> position of the center, radius
check if the selected finger is inside of the circle
check if the remaining four are evenly spread
(for example, consider angles from the center of the circle)
assign some cost (penalty) to this selection of 4 peaks + a rear finger
(consider, probably weighted:
circle fitting error,
if the rear finger is inside,
variance in the spreading of the front fingers,
total intensity of 5 peaks)
choose a combination of 4 peaks + a rear peak with the lowest penalty
这是一种蛮力方法。如果N相对较小,那么我认为它是可行的。对于N=12,有C_12^5 = 792种组合,乘以5种选择后指的方法,所以每个爪子要评估3960种情况。