网格最近邻 BFS 慢

Posted

技术标签:

【中文标题】网格最近邻 BFS 慢【英文标题】:Grid nearest neighbour BFS slow 【发布时间】:2021-10-17 19:20:47 【问题描述】:

我正在尝试对我的图像进行上采样。我以这种方式用相应的像素填充上采样版本。 伪代码: upsampled.getPixel(((int)(x * factorX), (int)(y * factorY))) = old.getPixel(x, y) 结果,我最终得到了未完全填充的位图,我尝试用它最近填充的邻居填充每个未填充的像素。 我使用此方法进行 nn 搜索并为每个未填充的像素调用它。在更改其值后,我不会将未填充的像素标记为已填充,因为它可能会产生一些奇怪的图案。问题是 - 它有效但非常慢。在 i7 9700k 上执行时间为 2500 x 3000 img 按因子 x = 1,5 和 y = 1,5 大约需要 10 秒。

template<typename T>
std::pair<int, int> cn::Utils::nearestNeighbour(const Bitmap <T> &bitmap, const std::pair<int, int> &point, int channel, const bool *filledArr) 
auto belongs = [](const cn::Bitmap<T> &bitmap, const std::pair<int, int> &point)
    return point.first >= 0 && point.first < bitmap.w && point.second >= 0 && point.second < bitmap.h;
;
if(!(belongs(bitmap, point)))
    throw std::out_of_range("This point does not belong to bitmap!");

auto hash = [](std::pair<int, int> const &pair)
    std::size_t h1 = std::hash<int>()(pair.first);
    std::size_t h2 = std::hash<int>()(pair.second);
    return h1 ^ h2;
;

//from where, point
std::queue<std::pair<int, int>> queue;
queue.push(point);

std::unordered_set<std::pair<int, int>, decltype(hash)> visited(10, hash);

while (!queue.empty())
    auto p = queue.front();
    queue.pop();
    visited.insert(p);
    if(belongs(bitmap, p))
        if(filledArr[bitmap.getDataIndex(p.first, p.second, channel)])
            return p.first, p.second;
        
        std::vector<std::pair<int,int>> neighbors(4);
        neighbors[0] = p.first - 1, p.second;
        neighbors[1] = p.first + 1, p.second;
        neighbors[2] = p.first, p.second - 1;
        neighbors[3] = p.first, p.second + 1;

        for(auto n : neighbors) 
            if (visited.find(n) == visited.end()) 
                queue.push(n);
            
        
    

return std::pair<int, int>(-1, -1);

bitmap.getDataIndex() 在 O(1) 时间内工作。这是它的实现

template<typename T>
int cn::Bitmap<T>::getDataIndex(int col, int row, int depth) const
    if(col >= this->w or col < 0 or row >= this->h or row < 0 or depth >= this->d or depth < 0)
        throw std::invalid_argument("cell does not belong to bitmap!");
    
    return depth * w * h + row * w + col;

我花了一段时间来调试它,但真的找不到让它这么慢的原因。 理论上,当按因子 x = 1,5, y = 1,5 缩放时,填充像素与未填充像素的距离不应超过 2 个像素,因此良好实施的 BFS 不会花费很长时间。

我也将这种编码用于位图,例如 3x3x3 图像

     * (each row and channel is in ascending order)

     * 00, 01, 02, | 09, 10, 11, | 18, 19, 20,
    c0 03, 04, 05, c112, 13, 14, c221, 22, 23,
     * 06, 07, 08, | 15, 16, 17, | 24, 25, 26,

【问题讨论】:

最近邻升级并不难。您只需遍历目标图像中的所有像素并执行target(x,y) = source(round(x/factor), round(y/factor)) 【参考方案1】:

填充像素与未填充像素的距离不应超过 2 个像素,因此良好实施的 BFS 不会花费很长时间。

当然,做一次不会花很长时间。但是您需要对输出图像中的几乎每个像素都执行此操作,并且多次执行不需要很长时间的操作仍然需要很长时间。

不要搜索设置的像素,而是使用您拥有的有关早期计算的信息来直接找到您正在寻找的值。

例如,在您的输出图像中,设置像素位于((int)(x * factorX), (int)(y * factorY)),对于整数xy。所以对于一个未设置的像素(a, b),可以通过((int)(round(a/factorX)*factorX), (int)(round(b/factorY)*factorY))找到最近的设置像素。

但是,您最好以更简单的方式直接对图像进行上采样:不要循环输入像素,而是循环输出像素,然后找到相应的输入像素。

【讨论】:

谢谢,这就是过度思考的意思;) 但是让我们假设我有一个问题中指定格式的网格并且无法访问源图像 - 那么呢?我有一个想法来实际使用转换矩阵来调整大小,如果它也支持旋转会更好。同样从这种格式中,我可以以相同的方式使用线性插值。

以上是关于网格最近邻 BFS 慢的主要内容,如果未能解决你的问题,请参考以下文章

K-近邻算法交叉验证,网格搜索

如何通过R-最近邻求解最近邻?

最近邻算法

k-d树的最近邻搜索算法

sklearn—无监督最近邻

近邻法总结