OpenCV - 迭代二进制图像中的每个blob并将其用作掩码

时间:2017-03-03 13:50:23

标签: opencv

我有一个二进制图像和相同大小的彩色图像。我需要迭代二进制图像的每个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);
            }

适用于没有洞的斑点。然而在我的情况下,我有许多斑点区域有大洞,影响平均计算。

我无法弄清楚如何使用层次结构信息来解决它;或者用另一种方法。

(我的代码适用于OpenCVSharp,但在任何其他包装器或语言中的答案都很好。)

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

实际上我认为我用这种方法解决了这个问题:

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);
            }
        }

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

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,没有找到作为父母)。是的它在层次上是正确的,但对我没有帮助。我希望我能清楚自己,对不起我的英语。

1 个答案:

答案 0 :(得分:2)

@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]);
    }