在贾志刚老师的带领下去做了用GMM的方法做证件照背景替换。
void IDPHOTO()
{
string image_path = "tx.png";
Mat src_image = imread(image_path);
if (!src_image.data)
{
cout << "could not load image.." << endl;
return ;
}
imshow("src_image", src_image);
int width = src_image.cols;
int height = src_image.rows;
int dims = src_image.channels();
//选择浮点数,因为要用到浮点运算
Mat samples(width * height, dims, CV_32FC1);
for (int row = 0; row < height; row++)
{
uchar* ptr_src = src_image.ptr<uchar>(row);
for (int col = 0; col < width * dims; col += dims)
{
int index = row * width + col / dims;
samples.at<float>(index, 0) = ptr_src[col + 0];
samples.at<float>(index, 1) = ptr_src[col + 1];
samples.at<float>(index, 2) = ptr_src[col + 2];
}
}
int cluster = 4;
int covmattype = EM::Types::COV_MAT_SPHERICAL;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);
Mat label;
//其实Kmeans和GMM-EM就这点代码差距
//kmeans(samples, cluster, label, criteria, 3, cv::KmeansFlags::KMEANS_PP_CENTERS);
Ptr<EM> em_model = EM::create();
em_model->setClustersNumber(cluster);
em_model->setCovarianceMatrixType(covmattype);
em_model->setTermCriteria(criteria);
em_model->trainEM(samples, noArray(), label, noArray());
Mat result(src_image.size(), src_image.type());
Mat mask(src_image.size(), CV_8UC1);
//获得坐标(2,2)像素的颜色分类
//其实这里可以多获取几个点,然后在判断背景颜色,以防取到的是噪声点(虽然概率及其低)
int colorid = label.at<int>(2 * width + 2, 0);
for (int row = 0; row < height; row++)
{
uchar* ptr_mask = mask.ptr<uchar>(row);
for (int col = 0; col < width; col++)
{
if (label.at<int>(row * width + col) == colorid)
ptr_mask[col] = 0;
else
ptr_mask[col] = 255;
}
}
imshow("mask", mask);
Mat kernel = getStructuringElement(cv::MorphShapes::MORPH_RECT, Size(5, 5), Point(-1, -1));
morphologyEx(mask, mask, cv::MorphTypes::MORPH_CLOSE, kernel, Point(-1, -1));
//erode(mask, mask, kernel);
imshow("erode-mask", mask);
GaussianBlur(mask, mask, Size(5, 5), 0, 0);
imshow("gau-mask", mask);
double w = 0.0;
Vec3b bgdcolor(255, 0, 0);
for (int row = 0; row < height; row++)
{
uchar* ptr_result = result.ptr<uchar>(row);
uchar* ptr_srcimage = src_image.ptr<uchar>(row);
uchar* ptr_mask = mask.ptr<uchar>(row);
for (int col = 0; col < width*3; col+=3)
{
if (ptr_mask[col / 3] ==0)
{
ptr_result[col + 0] = bgdcolor[0];
ptr_result[col + 1] = bgdcolor[1];
ptr_result[col + 2] = bgdcolor[2];
}
else if(ptr_mask[col / 3]==255)
{
ptr_result[col + 0] = ptr_srcimage[col + 0];
ptr_result[col + 1] = ptr_srcimage[col + 1];
ptr_result[col + 2] = ptr_srcimage[col + 2];
}
else
{
w = ptr_mask[col / 3] / 255.0;
ptr_result[col + 0] = ptr_srcimage[col + 0] * w + bgdcolor[0] * (1 - w);
ptr_result[col + 1] = ptr_srcimage[col + 1] * w + bgdcolor[1] * (1 - w);
ptr_result[col + 2] = ptr_srcimage[col + 2] * w + bgdcolor[2] * (1 - w);
}
}
}
imwrite("tx_photo.png", result);
imshow("result", result);
}
从得到效果来看,GMM的方法要比K-means的方法要差一些,而且我也努力的调整了GMM的参数以及换了不同的照片,最终效果都不怎么理想,生成的mask简直没法看,可能是要给一张颜色效果非常好的原图,GMM方法才能执行出好的效果,而且从速度方面来看,K-means的方法也要比GMM快很多。所以综合来看,K-means要比GMM好。