在OpenCV中重用抓取的模型



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

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

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而不检查是否提供了预训练模型。

您在使用cv2.GC_EVAL时有正确的思路,因此您只需要执行分割,而无需再次计算模型。 不幸的是,即使您使用此标志,这也是OpenCV源本身的限制。 如果在遇到GC_EVAL条件时查看实际的C++实现,它会在cv::grabcut方法结束时执行此操作。 请注意,Pythoncv2.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与自定义前景/背景模型,你可以做的是你必须自己修改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的官方存储库上打开了一个问题。 希望他们有时间时能解决这个问题:https://github.com/opencv/opencv/issues/9191

最新更新