搜索区域边界 - 设置减去性能瓶颈

时间:2017-07-14 02:13:28

标签: swift algorithm set

我的目标是实现类似于Photoshop的魔棒的工具,以选择图片中具有相同颜色的连接区域。已知所需区域内的起点。我的第一个方法是查看每个单独的像素,如果颜色匹配,我记住一组中的这个位置,并开始查看周围的邻居。为了不多次执行相同的任务,我会跟踪我已经搜索过的地方。

set“objectsAlreadyLookedAt”开始快速增长,并且分析显示set.subtract方法占该函数花费时间的91%。我可以使用哪种数据结构来提高包含和插入的性能?

var objectsToLookAt = Set<CustomPoint>()    //All objects we still have to process
    var objectsAlreadyLookedAt = Set<CustomPoint>() // a list of pixels we already visited
    var objectMatch = Set<CustomPoint>() //pixels matched

    while(objectsToLookAt.count > 0){   //While we have points left to look at

        let pointToLookAt:CustomPoint = objectsToLookAt.popFirst()! //Randomly take one
        objectsAlreadyLookedAt.insert(pointToLookAt)    //Remember that we already visited this node

        offset  = pointToLookAt.y * width + pointToLookAt.x //Calculate the index for the data buffer

        if(pixelBuffer[offset]==needleColor){

            objectMatch.insert(pointToLookAt) //Remember match for later

            //Generate 8 surrounding candidates
            var neighboorPoints: Set<CustomPoint> = createSurroundingPoints(startPoint: pointToLookAt)

            //Remove all points we have already looked at from the set. BOTTLENECK!
            neighboorPoints.subtract(objectsAlreadyLookedAt)
            objectsToLookAt = objectsToLookAt.union(neighboorPoints)
         }
    }

...

//Hashable point class
class CustomPoint : Hashable, CustomStringConvertible{

    var x:Int
    var y:Int

    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }

    //In our case coordinates will never exeed 10k
    var hashValue: Int {
        return y + x*10000
    }

    static func ==(lhs: CustomPoint, rhs: CustomPoint) -> Bool {
        if(lhs.x == rhs.x && lhs.y == rhs.y){
            return true
        }
        return false
    }

 public var description: String { return "Point: x:\(x) y:\(y)" }
}

或者

  1. 如果我只有非凹多边形,我可以将我的搜索区域划分为4个不同的方向,只需让算法向下走,不必担心重复。但事实并非如此。

  2. 我是否会更好地使用扫描线算法,查看每个像素,沿边创建路径并使用它?如果我只对一小部分感兴趣并且可以从已知点向外生长,我不明白为什么我应该看一下整个图像。这也可能很快变得复杂。

2 个答案:

答案 0 :(得分:2)

总变化

如果您将使用两个结构 - 整个像素区域和边界集,那么可以大大加快算法的速度。像素可以处于4种状态:未选中,已接受,未接受,边框。

all pixels are unchecked
create oldBorderList
set startPixel is border
oldBorderList.add(startPixel)
while (oldBorderList.Length>0) {
    create newBorderList
    for each pixel in borderList{
        for every neighbour for pixel{
            if (neighbour is unchecked) {
                if{mainCondition for neighbour is true){
                    set neighbour to border
                    newBorderList.add(neighbour)
                }
                else {
                    set neighbour as unaccepted
                }
            }
        }
        set pixel to accepted
    }
    oldBorderList=newBorderList
}

当然,您应该添加超出字段限制的检查 当然,你知道如何快速遍历邻居吗?请随意使用https://codereview.stackexchange.com/a/8947/11012中的第3段算法。

较小的变化

如果你不喜欢整个想法,至少要注意,边框设置中的减少元素是完全没用的,对于每个下一个伟大的检查周期你都有完全新的边框设置。属于最后一个周期边界的像素,在这个周期中不属于它。只需在每个循环中创建一个新集合,并将其用于下一个循环。

答案 1 :(得分:0)

我建议使用词典而不是设置来检查某个 CustomPoint 对象是否已被处理。字典中的键是 x * 100000 + y (因为你说“在我们的情况下坐标永远不会产生10k ”)。