我的目标是实现类似于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)" }
}
或者
如果我只有非凹多边形,我可以将我的搜索区域划分为4个不同的方向,只需让算法向下走,不必担心重复。但事实并非如此。
我是否会更好地使用扫描线算法,查看每个像素,沿边创建路径并使用它?如果我只对一小部分感兴趣并且可以从已知点向外生长,我不明白为什么我应该看一下整个图像。这也可能很快变得复杂。
答案 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 ”)。