我有一个骨架为二进制像素,例如:

我想使用Open CV找到此骨架端点的坐标(在这种情况下,有四个)。

效率非常重要,因为我正在通过视频源实时分析其中的许多功能,并且需要同时执行许多其他操作。

(请注意,上面的屏幕快照具有调整大小的伪像,但很抱歉,它是我正在使用的8连骨架。)

最佳答案

给定您个人资料中问题和答案的标记,我将假设您想要C++实现。骨架化对象时,该对象应具有1像素的厚度。因此,我建议的一件事是找到图像中非零的那些像素,然后在围绕该像素的8连接邻域中进行搜索,并对那些非零的像素进行计数。如果该计数仅为2,则该候选对象是骨架端点。请注意,我还将忽略边框,因此我们不会超出范围。如果计数为1,这是一个嘈杂的孤立像素,因此我们应该忽略它。如果大于或等于3,则意味着您正在框架内的某个点处检查框架的一部分,或者在多条线连接在一起的点处进行检查,因此这也不应该是端点。

老实说,除了检查所有骨架像素是否符合此条件外,我想不出任何其他算法。因此,复杂度将为O(mn),其中mn是图像的行和列。对于图像中的每个像素,8像素邻域检查将花费固定的时间,并且对于您检查的所有骨架像素而言,这都是相同的。但是,这肯定是次线性的,因为图像中的大多数像素将为0,因此大多数情况下不会进行8像素邻域检查。

因此,假设您的图像存储在称为cv::Matim结构中,它是单 channel (灰度)图像,并且类型为uchar,这是我要尝试的方法。我还将存储骨架端点在std::vector类型中的坐标。每次检测到骨架点时,我们都会一次向 vector 添加两个整数-我们检测到终点的行和列。

// Declare variable to count neighbourhood pixels
int count;

// To store a pixel intensity
uchar pix;

// To store the ending co-ordinates
std::vector<int> coords;

// For each pixel in our image...
for (int i = 1; i < im.rows-1; i++) {
    for (int j = 1; j < im.cols-1; j++) {

        // See what the pixel is at this location
        pix = im.at<uchar>(i,j);

        // If not a skeleton point, skip
        if (pix == 0)
            continue;

        // Reset counter
        count = 0;

        // For each pixel in the neighbourhood
        // centered at this skeleton location...
        for (int y = -1; y <= 1; y++) {
            for (int x = -1; x <= 1; x++) {

                // Get the pixel in the neighbourhood
                pix = im.at<uchar>(i+y,j+x);

                // Count if non-zero
                if (pix != 0)
                    count++;
            }
        }

        // If count is exactly 2, add co-ordinates to vector
        if (count == 2) {
            coords.push_back(i);
            coords.push_back(j);
        }
    }
}

如果要在完成时显示坐标,只需检查此 vector 中的每对元素:
for (int i = 0; i < coords.size() / 2; i++)
    cout << "(" << coords.at(2*i) << "," coords.at(2*i+1) << ")\n";

为了完整起见,这也是一个Python实现。我正在使用numpy的一些功能来简化此操作。假设您的图像存储在也是灰度图像的img中,并导入OpenCV库和numpy(即import cv2import numpy as np),则这是等效的代码:
# Find row and column locations that are non-zero
(rows,cols) = np.nonzero(img)

# Initialize empty list of co-ordinates
skel_coords = []

# For each non-zero pixel...
for (r,c) in zip(rows,cols):

    # Extract an 8-connected neighbourhood
    (col_neigh,row_neigh) = np.meshgrid(np.array([c-1,c,c+1]), np.array([r-1,r,r+1]))

    # Cast to int to index into image
    col_neigh = col_neigh.astype('int')
    row_neigh = row_neigh.astype('int')

    # Convert into a single 1D array and check for non-zero locations
    pix_neighbourhood = img[row_neigh,col_neigh].ravel() != 0

    # If the number of non-zero locations equals 2, add this to
    # our list of co-ordinates
    if np.sum(pix_neighbourhood) == 2:
        skel_coords.append((r,c))

要显示端点的坐标,可以执行以下操作:
print "".join(["(" + str(r) + "," + str(c) + ")\n" for (r,c) in skel_coords])

次要说明:此代码未经测试。我没有在这台机器上安装C++ OpenCV,所以希望我写的东西能用。如果无法编译,您当然可以将我所做的转换成正确的语法。祝你好运!

关于python - 如何在OpenCV中找到二进制骨架图像的端点?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26537313/

10-11 21:40