OpenCV4学习笔记(57)——基于GrabCut图像分割算法实现背景替换与背景虚化效果

在上一篇笔记《OpenCV4学习笔记(56)》中,整理了关于在OpenCV中使用GrabCut图像分割算法的相关内容,那么本次笔记就以GrabCut算法为基础来实现对图像的背景替换和背景虚化效果。

实现对图像的背景替换和背景虚化效果的整体流程如下:
(1)对图像进行USM锐化(可参阅《OpenCV4学习笔记(16)》
,用于增强图像细节,以便于提取前景区域。
(2)手动选择ROI区域并执行GrabCut算法进行图像分割。
(3)提取ROI区域的二值图像,并进行形态学滤波。
(4)读取要进行替换的新背景图像并进行高斯模糊。
(5)将新背景图像和前景图像相混合,并进一步进行模糊操作。

代码演示如下:

	//基于grabCut图像分割算法的背景替换和背景虚化
	//读取图像并进行USM锐化,增强目标细节,便于提取前景
	Mat image= imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\master.jpg");
	resize(image, image, Size(500, 700));
	imshow("image", image);
	Mat gaussian_image, USM_image;
	GaussianBlur(image, gaussian_image, Size(), 20, 20);
	addWeighted(image, 1.5, gaussian_image, -0.5, 0, USM_image, CV_16S);
	convertScaleAbs(USM_image, USM_image);
	imshow("USM_image", USM_image);

	//选择目标前景区域
	Rect roi_rect = selectROI("USM_image", USM_image, false);

	//进行grabCut图像分割
	Mat mask = Mat::zeros(image.size(), CV_8UC1);
	Mat bgdModel, fgdModel;
	grabCut(USM_image, mask, roi_rect, bgdModel, fgdModel, 10, GC_INIT_WITH_RECT);

	//提取前景ROI区域二值图像
	Mat foreground = Mat::zeros(image.size(), image.type());
	Mat foreground_roi = Mat::zeros(image.size(), CV_8UC3);
	for (int row = 0; row < image.rows; row++)
	{
		for (int col = 0; col < image.cols; col++)
		{
			if (mask.at<uchar>(row, col) == 1 || mask.at<uchar>(row, col) == 3)
			{
				foreground_roi.at<Vec3b>(row, col) = Vec3b(255,255,255);
			}
		}
	}

	//将得到的前景ROI区域二值图像进行开运算消除细微干扰后,和锐化图像进行与(and)操作,得到锐化后的前景区域
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(foreground_roi, foreground_roi, MORPH_OPEN, kernel);
	bitwise_and(foreground_roi, USM_image, foreground);
	imshow("foreground", foreground);

	//读取要进行替换的背景图像
	Mat background = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\background.jpg");
	resize(background, background, image.size());
	Mat new_background = background.clone();
	for (int row = 0; row < background.rows; row++)
	{
		for (int col = 0; col < background.cols; col++)
		{
			if (foreground.at<Vec3b>(row, col) != Vec3b(0, 0, 0))
			{
				new_background.at<Vec3b>(row, col) = image.at<Vec3b>(row, col);
			}
		}
	}
	imshow("new_background", new_background);

	//背景虚化
	//对背景图像进行高斯模糊
	Mat gaus_background = Mat::zeros(background.size(),background.type());
	GaussianBlur(background, gaus_background, Size(), 2, 2);
	for (int row = 0; row < gaus_background.rows; row++)
	{
		for (int col = 0; col < gaus_background.cols; col++)
		{
			if (foreground.at<Vec3b>(row, col) != Vec3b(0, 0, 0))
			{
				//将锐化图像前景和虚化背景图像混合
				gaus_background.at<Vec3b>(row, col) = foreground.at<Vec3b>(row, col);
			}
		}
	}
	//对虚化图像再进行高斯模糊,抵消前景的边缘效应和锐化效果
	GaussianBlur(gaus_background, gaus_background, Size(3, 3), 1);
	imshow("gaus_background", gaus_background);

这里使用的演示图像是:
在这里插入图片描述
接着经过锐化后的效果图像:
在这里插入图片描述
随后进行GrabCut图像分割,得到掩膜mask,注意mask直接显示的话是全黑的,因为它只有从0~3这四个像素取值:
在这里插入图片描述
对掩膜mask放大来看像素值,下面分别是可能背景和可能前景、明显背景和可能背景的分割处,:
在这里插入图片描述
在这里插入图片描述
由掩膜得到的前景图像为:
在这里插入图片描述

最后得到的新背景图像和背景虚化图像如下:
在这里插入图片描述
在这里插入图片描述
到这里就实现了对于一幅图像的背景替换和背景虚化了,但是从细节效果上来看,这种直接替换的方式使得前景和背景的连接处过渡仍然不够自然,感觉有些许割裂感。如果能对图像的前景与背景之间进行过渡处理,那么能很好的提高视觉效果。

好了,本次笔记整理到此结束,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
分水岭算法是一种基于图论的图像分割算法,它的主要思想是将图像看做一个拓扑图,并将图像中的每个像素点看做一个节点,通过计算各个节点之间的权值,构建出一张权值图。然后通过分析该图的拓扑结构,将图像分成不同的区域。 以下是基于分水岭算法图像分割步骤: 1. 对图像进行预处理,包括去噪、灰度化、二值化等操作。 2. 计算图像中每个像素点的梯度值,以梯度值作为节点之间的权值,构建出一张权值图。 3. 对权值图进行分割,根据分水岭原理,将权值图看做一个地形图,其中局部极小值(梯度值最小的点)被看做是山谷,这些山谷之间的区域被看做是分水岭。 4. 从所有山谷的位置开始,填充它们周围的区域,直到所有区域都被填充完毕。最后得到的分割结果即为图像的各个区域。 下面是Python实现基于分水岭的图像分割算法的代码: ```python import cv2 import numpy as np # 读入图像 img = cv2.imread('image.jpg') # 预处理,去噪,灰度化,二值化 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) # 计算梯度 dilate = cv2.dilate(thresh, None, iterations=3) erode = cv2.erode(dilate, None, iterations=3) gradient = cv2.subtract(erode, dilate) # 计算分水岭 markers = cv2.connectedComponents(np.uint8(gradient))[1] markers = markers + 1 markers[thresh == 255] = 0 markers = cv2.watershed(img, markers) # 显示分割结果 img[markers == -1] = [0, 0, 255] cv2.imshow('Segmentation', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 其中,使用了OpenCV实现了图像预处理、梯度计算、分水岭分割等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值