【问题标题】:OpenCV - iterate over each blob in a binary image and use it as maskOpenCV - 迭代二进制图像中的每个 blob 并将其用作掩码
【发布时间】:2017-03-03 13:50:22
【问题描述】:

我有一个二值图像和一个相同大小的彩色图像。我需要迭代二进制图像的每个 blob(白色像素块)并将其用作掩码,并从彩色图像中找到该 blob 区域的平均颜色。

我试过了:

HierarchyIndex[] hierarchy;
Point[][] contours;
binaryImage.FindContours(out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxNone);

    using (Mat mask = Mat.Zeros(matColor.Size(), MatType.CV_8UC1))
        foreach (var bl in contours)
            if (Cv2.ContourArea(bl) > 5)
            {
                mask.DrawContour(bl, Scalar.White, -1);                                                    
                Rect rect = Cv2.BoundingRect(bl);
                Scalar mean = Cv2.Mean(colorImage[rect], mask[rect]);

                mask.DrawContour(bl, Scalar.Black, -1);
            }

这适用于没有孔的斑点。但是,在我的情况下,我有许多具有影响平均计算的大洞的 blob 区域。

我不知道如何使用层次结构信息解决它;或其他方法。

(我的代码适用于 OpenCVSharp,但欢迎使用任何其他包装器或语言回答。)

编辑:我添加了一个示例图像。交通标志部分是问题所在。

其实我想我已经用这个方法解决了这个问题:

using PLine = List<Point>;
using Shape = List<List<Point>>;
internal static IEnumerable<Tuple<PLine, Shape>> FindContoursWithHoles(this Mat mat)
        {
            Point[][] contours;
            HierarchyIndex[] hierarchy;
            mat.FindContours(out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone);

            Dictionary<int, bool> dic = new Dictionary<int, bool>();
            for (int i = 0; i < contours.Length; i++)
                if (hierarchy[i].Parent < 0)
                    dic[i] = true;

            bool ok = false;
            while (!ok)
            {
                ok = true;
                for (int i = 0; i < contours.Length; i++)
                    if (dic.ContainsKey(i))
                    {
                        bool isParent = dic[i];
                        var hi = hierarchy[i];
                        if (hi.Parent >= 0) dic[hi.Parent] = (!isParent);
                        if (hi.Child >= 0) dic[hi.Child] = (!isParent);
                        while (hi.Next >= 0)
                        {
                            dic[hi.Next] = isParent;
                            hi = hierarchy[hi.Next];
                            if (hi.Parent >= 0) dic[hi.Parent] = (!isParent);
                            if (hi.Child >= 0) dic[hi.Child] = (!isParent);
                        }
                        hi = hierarchy[i];
                        while (hi.Previous >= 0)
                        {
                            dic[hi.Previous] = isParent;
                            hi = hierarchy[hi.Previous];
                            if (hi.Parent >= 0) dic[hi.Parent] = (!isParent);
                            if (hi.Child >= 0) dic[hi.Child] = (!isParent);
                        }
                    }
                    else
                        ok = false;
            }
            foreach (int i in dic.Keys.Where(a => dic[a]))
            {
                PLine pl = contours[i].ToList();
                Shape childs = new Shape();
                var hiParent = hierarchy[i];
                if (hiParent.Child >= 0)
                {
                    childs.Add(contours[hiParent.Child].ToList());
                    var hi = hierarchy[hiParent.Child];
                    while (hi.Next >= 0)
                    {
                        childs.Add(contours[hi.Next].ToList());
                        hi = hierarchy[hi.Next];
                    }
                    hi = hierarchy[hiParent.Child];
                    while (hi.Previous >= 0)
                    {
                        childs.Add(contours[hi.Previous].ToList());
                        hi = hierarchy[hi.Previous];
                    }
                }

                yield return Tuple.Create(pl, childs);
            }
        }

通过将孔绘制为黑色,我们可以将每个斑点用作单个蒙版:

var blobContours = blobs.FindContoursWithHoles().ToList();
using (Mat mask = Mat.Zeros(mat0.Size(), MatType.CV_8UC1))
    for (int i = 0; i < blobContours.Count; i++)
    {
        var tu = blobContours[i];
        var bl = tu.Item1;
        if (Cv2.ContourArea(bl) > 100)
        {
            mask.DrawContour(bl, Scalar.White, -1);
            foreach (var child in tu.Item2)
                mask.DrawContour(child, Scalar.Black, -1);
            Rect rect = Cv2.BoundingRect(bl);
            Scalar mean = Cv2.Mean(mat0[rect], mask[rect]);
        }
    }

我认为应该有更简单的方法。

还有另一个问题。在某些情况下,标志的单个红色部分(这是一个单独的白色斑点)不是作为父外圆和子内圆,而是在外面有两个圆作为子圆的大父轮廓(即,另一个圆内的洞)孔,生成一个单独的 blob,它没有作为父对象找到)。是的,它在层次上是正确的,但对我没有帮助。我希望我能说清楚,对不起我的英语。

【问题讨论】:

  • 你能上传你正在使用的图像和面具吗?我不明白你提到的“漏洞”
  • 你能附上一张带孔的斑点的样本图像,计算不准确吗?
  • 在您的二进制图像中,您认为什么是 blob?
  • 我将每个白色像素岛视为一个斑点。对不起,如果文献中没有这样说。
  • 使用 cv::connectedComponents(嗯,它相当于 C#)

标签: opencv


【解决方案1】:

@Miki 非常感谢你。我能够使用 ConnectedComponents 实现我想要的。它简单快捷:

var cc = Cv2.ConnectedComponentsEx(binaryImage, PixelConnectivity.Connectivity8);
foreach (var bl in cc.Blobs)
    using (Mat mask = new Mat())
    {
        cc.FilterByBlob(binaryImage, mask, bl);
        Rect rect = bl.Rect;
        Scalar mean = Cv2.Mean(colorImage[rect], mask[rect]);
    }

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 2015-10-11
    • 2018-07-21
    • 2020-04-13
    • 2018-09-27
    • 2015-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-13
    相关资源
    最近更新 更多