在 opencv-java 中优化 GrabCut 的性能

2022-09-03 02:49:39

最近,我得到了一个项目,我必须从给定的图像中提取面部(面部+头发)。

我通过以下方式解决此问题。

  1. 我正在从给定图像中提取人脸位置。[我得到一个矩形]
  2. 我正在提取该矩形并将其放置在与输入图像尺寸相同的另一个图像中。[face_image]
  3. 我正在步骤2 face_image应用grabCut算法。

当face_image包含平滑的背景时,算法grabCut它运行良好,但是当face_image的背景复杂时,算法grabCut也会在处理后的图像中提取背景的某些部分。

以下是我得到的结果的快照。

results of each step

这是我的 grabCut 代码:

public void extractFace(Mat image, String fileNameWithCompletePath, 
                       int xOne, int xTwo, int yOne, int yTwo) throws CvException {

    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

    Rect rectangle = new Rect(xOne, yOne, xTwo, yTwo);
    Mat result = new Mat();
    Mat bgdModel = new Mat();
    Mat fgdModel = new Mat();
    Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3));
    Imgproc.grabCut(image, result, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_RECT);
    Core.compare(result, source, result, Core.CMP_EQ);
    Mat foreground = new Mat(image.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
    image.copyTo(foreground, result);
    Imgcodecs.imwrite(fileNameWithCompletePath, foreground);
}

如何提高 grabCut 算法的性能,使其仅检测给定图像中的人脸和头发?


答案 1

你应该能够通过“帮助”一点关于前景和背景的知识来做到这一点。有一个python教程,展示了如何通过选择前景和背景来手动完成此操作。grabCut

若要自动执行此操作,需要找到以编程方式检测前景和背景。前景主要由头发和皮肤组成,因此您需要检测它们。

皮肤 - 有几篇关于如何做到这一点的论文和博客。其中一些非常简单这个OpenCV教程也可能有所帮助。我发现了普通的色调/饱和度,让我走得很远。

头发 - 这更棘手,但绝对可行。你可以发,只使用皮肤和背景,如果这证明是太多的工作。

背景 - 您应该能够使用 range() 在图像中查找紫色、绿色和蓝色的内容。您可以肯定地知道,这些东西不是皮肤或头发,因此是背景的一部分。

使用阈值创建最有可能是皮肤、头发和背景的区域的蒙版。然后,您可以将它们用作 和(或皮肤和头发面膜)而不是 。bgdModelfgdModelMat()

对不起,这是如此高的水平。我希望它有帮助!


答案 2

另一种方法,既然你已经检测到了人脸,就是简单地选择一个更好的初始蒙版来初始化GrabCut - 例如,通过使用椭圆形而不是矩形。

  1. 检测人脸矩形(就像您已经在做的那样)
  2. 创建蒙版:

    a) 创建与输入图像大小相同的新黑色图像

    b) 绘制一个白色填充椭圆,其高度、宽度、顶部和左侧位置与面矩形相同

  3. 调用 GrabCut 而不是 :GC_INIT_WITH_MASKGC_INIT_WITH_RECT

    Imgproc.grabCut(image, mask, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_MASK);
    

这使用更好的模型初始化前景,因为面比矩形更椭圆形,因此它应该包含较少的背景。