基于VS2017+OpenCV3.4.1的PC端摄像头卡通化效果案例步骤详解

4 篇文章 0 订阅
4 篇文章 0 订阅

教材:《深入理解OpenCV 实用计算机视觉项目解析》
https://pan.baidu.com/s/16YPsbWmcys31CBXPCR4b3Q
提取码:o8dk
案例源码:https://github.com/MasteringOpenCV/code

关于opencv_contrib3.4.1
可以自己编译,具体参考我的文章。
win7x64+vs2017+opencv3.4.1+opencv_contrib3.4.1安装配置过程
https://blog.csdn.net/LTyyCFY/article/details/90035645

中间会出现一些小问题啥的。慢慢找怎么解决吧
问题1:LNK1112: 模块计算机类型“X64”与目标计算机类型“x86”冲突
vs里面有Debug和Release两种配置。网上的解决方案基本同小异,这里说的问题是将配置管理器的平台和目标计算机(目标计算机在链接器->高级里)、视图->其他窗口->属性管理器修改后,VC++目录里的包含目录、库目录和链接器->输入->附加依赖项需要重新添加
我是将解决方案配置管理器和目标计算机修改后,在改VC++和附加依赖项
在这里插入图片描述

问题2:无法查找或打开PDB文件
参考:https://www.cnblogs.com/wxl845235800/p/7206767.html

搭建框架

#include<opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
    //可简单调用cv::VideoCapture对象的open()方法(它是访问摄像设备的OpenCV方法)来访问计算机的摄像头或摄像机。
	//将默认的摄像机编号0传递给此函数。一些计算机有多个摄像机或将0作为默认摄像机编号使程序不能运行
	//解决这类问题的通常做法是将用户指定摄像机编号作为命令行参数,比如:若想指定摄像机编号为1、2或-1,这种方法就比较恰当。
	int cameraNumber = 0;
	if (argc > 1) {
		cameraNumber = atoi(argv[1]);
	}
	VideoCapture camera;
	camera.open(cameraNumber);
	if (!camera.isOpened()) {
		cerr << "ERROR: Could not access the camera!" << endl;
		exit(1);
	}
	// 为了让程序在高分辨率摄像机上运行得更快,可用vc::VideoCaptrue::set()将摄像机的分辨率设为640x480.
    // 由于摄像机的模式、驱动,或操作系统不同,OpenCV可能无法改变某些摄像机属性。
    //camera.set()会返回一个bool值,可以打印出来看是否设置成功
	camera.set(CV_CAP_PROP_FRAME_WIDTH, 640);
	camera.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
	while (true) {
		Mat cameraFrame;
		camera >> cameraFrame;
		if (cameraFrame.empty()) {
			cerr << "ERROR: Couldn't grab a frame." << endl;
			exit(1);
		}
		imshow("cartoonfier", cameraFrame);
		char keypress = waitKey(20);// 如果你想看任何东西,这是必要的!
		if (keypress == 27) {  //Escape key
			// 退出程序!
			break;
		}
	} //end while
	return 0;
}

运行,会打开摄像头
在这里插入图片描述

生成黑白素描

  1. 灰度化,修改while(true)为
while (true) {
		Mat cameraFrame;
		camera >> cameraFrame;
		if (cameraFrame.empty()) {
			cerr << "ERROR: Couldn't grab a frame." << endl;
			exit(1);
		}
		// 将OpenCV默认的GBR格式转换为灰度格式
		Mat srcGray;
		cvtColor(cameraFrame, srcGray, CV_BGR2GRAY);
		imshow("cartoonfier", srcGray);
		char keypress = waitKey(20);// 如果你想看任何东西,这是必要的!
		if (keypress == 27) {  //Escape key
			// 退出程序!
			break;
		}
	} //end while

在这里插入图片描述
2. 在开始检测边缘之前,使用良好的中值滤波去除像素噪声.同时还可让边缘锐化。在imshow上方增加两行代码

Mat srcGray;
cvtColor(cameraFrame, srcGray, CV_BGR2GRAY);
const int MEDIAN_BLUE_FILTER_SIZE = 7;
medianBlur(srcGray, srcGray, MEDIAN_BLUE_FILTER_SIZE);
imshow("cartoonfier", srcGray)

在这里插入图片描述
可以看到有明显的变化
3. Laplacian滤波器所提取的边缘最接近手工素描,并且它与Canny边缘检测一样,可得到清晰的素描效果。但Canny边缘检测更容易受视频帧中随机噪声影响,从而使得素描边缘在不同帧之间经常有剧烈变化。
imshow上方增加三行代码,并修改imshow的展示图像

Mat srcGray;
cvtColor(cameraFrame, srcGray, CV_BGR2GRAY);
const int MEDIAN_BLUE_FILTER_SIZE = 7;
medianBlur(srcGray, srcGray, MEDIAN_BLUE_FILTER_SIZE);
Mat edges;
const int LAPLACIAN_FILTER_SIZE = 5;
Laplacian(srcGray, edges, CV_8U, LAPLACIAN_FILTER_SIZE);
imshow("cartoonfier", edges)

在这里插入图片描述
4. Laplacian边缘滤波器能生成不同亮度的边缘,为了使边缘看起来更像素描,可采用二值化阈值来使边缘只有白色或黑色。
imshow上方增加三行代码,并修改imshow的展示图像

Mat srcGray;
cvtColor(cameraFrame, srcGray, CV_BGR2GRAY);
const int MEDIAN_BLUE_FILTER_SIZE = 7;
medianBlur(srcGray, srcGray, MEDIAN_BLUE_FILTER_SIZE);
Mat edges;
const int LAPLACIAN_FILTER_SIZE = 5;
Laplacian(srcGray, edges, CV_8U, LAPLACIAN_FILTER_SIZE);
Mat mask;
const int EDGES_THRESHOLD = 80;
threshold(edges, mask, EDGES_THRESHOLD, 255, THRESH_BINARY_INV);
imshow("cartoonfier", mask)

在这里插入图片描述

生成彩色图像

  1. 强大的双边滤波器可平滑平坦区域,同时保持边缘锐化,因此,它作为一个自动的卡通化或图像滤波很不错,其缺点是效率低。一个重要的技巧是在低分辨率下使用双边滤波器,这会得到与高分辨率下相识的结果,但运行速度更快。可将分辨率减少为原图像的四分之一。
    imshow上方增加六行代码,并修改imshow的展示图像
Size size = cameraFrame.size();
Size smallSize;
smallSize.width = size.width / 2;
smallSize.height = size.height / 2;
Mat smallImg = Mat(smallSize, CV_8UC3);
resize(cameraFrame, smallImg, smallSize, 0, 0, INTER_LINEAR);
imshow("cartoonfier", smallImg);

可以看到画面变小了
在这里插入图片描述
2. 可通过多个小型双边滤波器来代替一个大型双边滤波器,从而在较短时间内得到很好的卡通效果。截断滤波器会使用滤波器的主要部分,而不会浪费时间在小部分上,滤波器效率提高几倍。控制双边滤波器可使用的4个参数分别是:色彩强度、位置强度、大小、重复技术。/bilateralFilter()函数不能覆盖它的输入值(这称为“就地处理”),因此需要一个临时的Mat变量,该变量在一个滤波器中作为输入变量而在另一个滤波器中作为输入变量
imshow上方增加十行代码

Mat tmp = Mat(smallSize, CV_8UC3);
int repetitions = 7;        // Repetitions for strong cartoon effect.
for (int i = 0; i < repetitions; i++) {
        int ksize = 9;           // Filter size. Has a large effect on speed.
        double sigmaColor = 9;  // Filter color strength.
        double sigmaSpace = 7;  // Positional strength. Effects speed.
        bilateralFilter(smallImg, tmp, ksize, sigmaColor, sigmaSpace);
        bilateralFilter(tmp, smallImg, ksize, sigmaColor, sigmaSpace);
		}
tmp.release();//释放tmp
imshow("cartoonfier", smallImg);

在这里插入图片描述
3. 注意,该处理过程使用的是缩小后的图像,因此,在处理后需将图像恢复到原来的大小
imshow上方增加两行代码,并修改imshow的展示图像

Mat bigImg;
resize(smallImg, bigImg, size, 0, 0, INTER_LINEAR);
imshow("cartoonfier", bigImg);

这里展示的是放大后的彩色图像

生成卡通图片

将素描叠加到彩色图像上即可生成卡通图像。
imshow上方增加代码如下,修改imshow的展示图像

//创建一个空白的输出图像,我们将在上面绘制
Mat displayedFrame(cameraFrame.size(), CV_8UC3);
//为了将边缘掩码(即素描)叠加到由双边滤波器所产生的图画上,需将空白图像的的所有元素全置为0(即目标变量对应的图像全置为黑色)
displayedFrame.setTo(0);
//然后将彩色图像的像素复制到空白图像t中,与“素描“掩码对应的彩色图像边缘的像素不会被复制。
cameraFrame.copyTo(displayedFrame, mask);
imshow("cartoonfier", displayedFrame);

在这里插入图片描述

完整.cpp文件源码

#include<opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char *argv[])
{
	
	int cameraNumber = 0;
	if (argc > 1) {
		cameraNumber = atoi(argv[1]);
	}
	VideoCapture camera;
	camera.open(cameraNumber);
	if (!camera.isOpened()) {
		cerr << "ERROR: Could not access the camera!" << endl;
		exit(1);
	}
	camera.set(CAP_PROP_FRAME_WIDTH, 640);
	camera.set(CAP_PROP_FRAME_HEIGHT, 480);
	while (true) {
		Mat cameraFrame;
		camera >> cameraFrame;
		if (cameraFrame.empty()) {
			cerr << "ERROR: Couldn't grab a frame." << endl;
			exit(1);
		}
#if 1
		Mat srcGray;
		cvtColor(cameraFrame, srcGray, CV_BGR2GRAY);
		
		const int MEDIAN_BLUE_FILTER_SIZE = 7;
		medianBlur(srcGray, srcGray, MEDIAN_BLUE_FILTER_SIZE);
		
		Mat edges;
		const int LAPLACIAN_FILTER_SIZE = 5;
		Laplacian(srcGray, edges, CV_8U, LAPLACIAN_FILTER_SIZE);

		Mat mask;
		const int EDGES_THRESHOLD = 80;
		threshold(edges, mask, EDGES_THRESHOLD, 255, THRESH_BINARY_INV);
#endif	
#if 1
		Size size = cameraFrame.size();
		Size smallSize;
		smallSize.width = size.width / 2;
		smallSize.height = size.height / 2;
		Mat smallImg = Mat(smallSize, CV_8UC3);
		resize(cameraFrame, smallImg, smallSize, 0, 0, INTER_LINEAR);
#endif
#if 1
		Mat tmp = Mat(smallSize, CV_8UC3);
		int repetitions = 7;        // Repetitions for strong cartoon effect.
		for (int i = 0; i < repetitions; i++) {
			int ksize = 9;           // Filter size. Has a large effect on speed.
			double sigmaColor = 9;  // Filter color strength.
			double sigmaSpace = 7;  // Positional strength. Effects speed.
			bilateralFilter(smallImg, tmp, ksize, sigmaColor, sigmaSpace);
			bilateralFilter(tmp, smallImg, ksize, sigmaColor, sigmaSpace);
		}
		tmp.release();
#endif
#if 1
		//注意,该处理过程使用的是缩小后的图像,因此,在处理后需将图像恢复到原来的大小,然后叠加前面得到的边缘掩码。
		resize(smallImg, cameraFrame, size, 0, 0, INTER_LINEAR);

		//创建一个空白的输出图像,我们将在上面绘制
		Mat displayedFrame(cameraFrame.size(), CV_8UC3);
		//为了将边缘掩码(即素描)叠加到由双边滤波器所产生的图画上,需将空白图像的的所有元素全置为0(即目标变量对应的图像全置为黑色)
		displayedFrame.setTo(0);

		//然后将源图像的像素复制到变量dst中,与“素描“掩码对应的源图像边缘的像素不会被复制。
		cameraFrame.copyTo(displayedFrame, mask);
#endif
		imshow("cartoonfier", displayedFrame);
		char keypress = waitKey(20);// 如果你想看任何东西,这是必要的!
		if (keypress == 27) {  //Escape key,前面定义了一个宏
			// 退出程序!
			break;
		}
	} //end while
	return 0;
}

很奇怪,之前我用别人编译好的opencv_contrib3.4.1倒是不卡顿
参见: https://blog.csdn.net/qq_36163358/article/details/86593634
后来我自己编译了opencv_contrib3.4.1,然后在彩色图像那里疯狂卡,经过一步步展示图像,发现在如下代码段执行后卡顿

Mat tmp = Mat(smallSize, CV_8UC3);
int repetitions = 7;        // Repetitions for strong cartoon effect.
for (int i = 0; i < repetitions; i++) {
        int ksize = 9;           // Filter size. Has a large effect on speed.
        double sigmaColor = 9;  // Filter color strength.
        double sigmaSpace = 7;  // Positional strength. Effects speed.
        bilateralFilter(smallImg, tmp, ksize, sigmaColor, sigmaSpace);
        bilateralFilter(tmp, smallImg, ksize, sigmaColor, sigmaSpace);
		}

具体原因暂时不明
实验:将repetitions 置为7,循环体执行一次,发现卡顿明显小了很多,应该和bilateralFilter调用有关,也可能是bilateralFilter的参数没设置好
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值