【问题标题】:Reusing models from grabcut in OpenCV在 OpenCV 中重用来自 Grabcut 的模型
【发布时间】:2017-12-25 00:27:01
【问题描述】:

我使用 OpenCV 示例中的交互式 grabcut.py 来分割图像并保存前景和背景模型。然后我使用这些模型来分割更多相同类型的图像,因为我不想每次都重新训练模型。

运行抓取算法后,掩码全为零(全为背景),因此它不会分割任何内容。

from matplotlib import pyplot as plt
import numpy as np
import cv2

img = cv2.imread('usimg1.jpg')
mask = np.zeros(img.shape[:2], np.uint8)
bgdModel = np.load('bgdmodel.npy')
fgdModel = np.load('fgdmodel.npy')

cv2.grabCut(img, mask, None, bgdModel, fgdModel, 5, cv2.GC_EVAL)

mask = np.where((mask==2) | (mask==0), 0, 1).astype('uint8') 
img = img * mask[:, :, np.newaxis]

plt.imshow(img)
plt.show()

我尝试使用掩码或矩形初始化算法,但这会产生错误,因为模型不是空的(这是我真正想要的)。

我如何必须将预训练的模型传递给算法,这样每次我分割图像时它们就不会从头开始重新训练?

编辑 在 rayryeng 发表评论后,我实现了以下代码:

cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 0, cv2.GC_INIT_WITH_RECT)
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 2, cv2.GC_EVAL)

它似乎有效,但第一次调用现在改变了我的模型。在源代码中它调用learnGMMs 而不检查是否提供了预训练模型。

【问题讨论】:

  • Bgdmodel.npy 是你想要并重用的?
  • 是的,bgdmodel.npy 和 fgdmodel.npy
  • 这很奇怪...cv2.GC_EVAL 的目的是已经使用预训练模型。您甚至可以在源代码中看到这一点。 github.com/opencv/opencv/blob/master/modules/imgproc/src/…。当遇到GC_EVAL 时,它会在实际进行分割之前对掩码进行简单的错误检查,而无需实际进行学习。您应该能够使用cv2.GC_EVAL 并将前景掩码指定为None 应该可以正常工作。
  • 另外,grabcut的测试用例也很好奇:github.com/opencv/opencv/blob/…。您会看到他们首先调用 grabcut 进行 0 次迭代来学习模型,然后他们再次重用这些相同的模型来实际进行分割 - 2 次迭代。试试看?尝试先用 0 次迭代学习模型,然后使用带有GC_EVAL 的预计算模型进行最终分割。
  • @rayryeng 是的,它成功了,谢谢!

标签: python opencv image-processing image-segmentation


【解决方案1】:

您在使用cv2.GC_EVAL 的地方有正确的思路,因此您只需要执行分割而无需再次计算模型。 不幸的是,即使您使用此标志,这也是 OpenCV 源代码本身的限制。如果您查看actual C++ implementation when you encounter the GC_EVAL condition,它会执行此操作towards the end of the cv::grabcut method。请注意,Python cv2.grabCut 方法是 cv::grabcut 的包装器:

if( mode == GC_EVAL )
    checkMask( img, mask );

const double gamma = 50;
const double lambda = 9*gamma;
const double beta = calcBeta( img );

Mat leftW, upleftW, upW, uprightW;
calcNWeights( img, leftW, upleftW, upW, uprightW, beta, gamma );

for( int i = 0; i < iterCount; i++ )
{
    GCGraph<double> graph;
    assignGMMsComponents( img, mask, bgdGMM, fgdGMM, compIdxs );
    learnGMMs( img, mask, compIdxs, bgdGMM, fgdGMM );
    constructGCGraph(img, mask, bgdGMM, fgdGMM, lambda, leftW, upleftW, upW, uprightW, graph );
    estimateSegmentation( graph, mask );
}

您会看到GC_EVAL 在代码中只出现一次,用于检查输入的有效性。罪魁祸首是learnGMMs 函数。即使您指定了经过训练的模型,这些模型也会被重置,因为对 learnGMMs 的调用忽略了 GC_EVAL 标志,因此无论您指定什么标志作为输入,都会调用它。

受这篇文章的启发:OpenCV - GrabCut with custom foreground/background models,你可以做的是你必须自己修改 OpenCV 源代码,你可以在循环中放置一个 if 语句以在调用之前检查 GC_EVAL 标志learnGMMs:

if( mode == GC_EVAL )
    checkMask( img, mask );

const double gamma = 50;
const double lambda = 9*gamma;
const double beta = calcBeta( img );

Mat leftW, upleftW, upW, uprightW;
calcNWeights( img, leftW, upleftW, upW, uprightW, beta, gamma );

for( int i = 0; i < iterCount; i++ )
{
    GCGraph<double> graph;
    assignGMMsComponents( img, mask, bgdGMM, fgdGMM, compIdxs );
    if (mode != GC_EVAL) // New
        learnGMMs( img, mask, compIdxs, bgdGMM, fgdGMM );
    constructGCGraph(img, mask, bgdGMM, fgdGMM, lambda, leftW, upleftW, upW, uprightW, graph );
    estimateSegmentation( graph, mask );
}

这应该能够使用预先训练的模型,而不必在每次迭代中重新学习它们。进行更改后,您必须再次重新编译源代码,并且希望能够在使用 cv2.GC_EVAL 标志时使用您的预训练模型而无需清除它们。

对于未来,我在 OpenCV 的官方 repo 上打开了一个 issue。希望他们有空时能解决这个问题:https://github.com/opencv/opencv/issues/9191

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-11
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    • 2015-09-01
    • 2019-05-22
    • 1970-01-01
    • 2014-11-20
    相关资源
    最近更新 更多