人脸检测并打码的关键,首先需要定位人脸区域,再修改人脸区域像素灰度值。
一 定义马赛克函数
定义生成马赛克函数 Generate_Mosaic,对图形 Mat& src 做操作,将需要操作的块(faces)存入数组 vector<Rect>& faces 中(<Rect>表示用矩形区域描述)。
src:代表马赛克效果的图片; faces:显示马赛克的区域; Rect:矩形区域(x,y,w,h)
马赛克函数思想过程:定义马赛克大小为10像素,int step = 10。for (int t = 0; t < faces.size(); t++) 对每一张脸进行马赛克操作。再确定 faces 所在区域(x, y, w, h),然后针对 faces 所在进行马赛克处理。10像素的矩形遍历人脸矩形框区域像素,再将矩形框细分成若干个小方块,依次修改每个方块的像素,相同方块赋予相同灰度值。
src.at<Vec3b> (k, m)[c] (<Vec3b>原图的颜色, 位置(k, m),[C]颜色值)
bool Generate_Mosaic(Mat& src, vector<Rect>& faces)
{if (faces.empty())return false;int step = 10;//步长for (int t = 0; t < faces.size(); t++){int x = faces[t].tl().x; //人脸矩形框起点x坐标int y = faces[t].tl().y;//人脸矩形框起点y坐标int width = faces[t].width;//人脸矩形框宽int height = faces[t].height;//人脸矩形框高//仅对人脸区域进行像素修改。遍历人脸矩形框区域像素,并对其进行修改for (int i = y; i < (y + height); i += step){for (int j = x; j < (x + width); j += step){//将人脸矩形框再细分为若干个小方块,依次对每个方块修改像素(相同方块赋予相同灰度值)for (int k = i; k < (step + i); k++){for (int m = j; m < (step + j); m++){//对矩形区域像素值进行修改,rgb三通道for (int c = 0; c < 3; c++){src.at<Vec3b>(k, m)[c] = src.at<Vec3b>(i, j)[c];}}}}}}return true;
}
二 单张图片做打码处理
定义单张图片打码函数 Picture_Demo,将图片 Mat src 传入函数。首先加载人脸检测配置文件。在 OpenCV 中,人脸检测用的是 harr 或 LBP 特征,分类算法用的是 adaboost 算法。这种算法需要提前训练大量的图片,非常耗时,因此 OpenCV 已经训练好了,把训练结果存放在一些 xml 文件里面。然后创建人脸检测器并存储人脸检测的结果,人脸检测主要用到的是 CascadeClassifier 这个类,以及该类下的 detectMultiScale 函数。
detector.detectMultiScale(src, faces, 1.1, 5),src 检测的图片,faces 存储的结果,1.1 缩放尺寸
bool Picture_Demo(Mat src)
{//人脸检测配置文件string harr_file = "haarcascade_frontalface_default.xml";//创建人脸检测器CascadeClassifier detector;detector.load(harr_file);//人脸检测,存储人脸检测的结果vector<Rect>faces;detector.detectMultiScale(src, faces, 1.1, 5);if (!Generate_Mosaic(src, faces))return false;imshow("test", src);waitKey(0);return true;
}
三 视频打码操作
视频主要涉及视频的读取以及视频的保存
//把视频转图像操作
void VideoToImg()
{VideoCapture cap = VideoCapture("视频路径"); //创建一个对象cap,打开一个视频,若参数设为0则为打开默认摄像头if(!cap.isOpened()){cout << "打开失败。。。" << endl;return;}
// 如果成功开打,视频的每一帧都在对象cap中Mat img; //定义图片int i = 1;while (true) //用流的方式将图片导出来{cap >> img; //拿出一帧if (img.empty())break;imshow("img", img); //显示图片string URL = "data/img" + to_string(i) + ".jpg"; //定义图片存储路径 data/img0.jpgimwrite(URL, img); //把图片写入文件waitKey(30); //延迟30msi++;}return;
}
打开摄像头,获取每一帧图片并显示在窗口上。
//打开摄像头
void VideoToImg()
{VideoCapture cap = VideoCapture(0); //创建一个对象cap,打开一个视频,若参数设为0则为打开默认摄像头if (!cap.isOpened()){cout << "打开失败。。。" << endl;return;}// 如果成功开打,视频的每一帧都在对象cap中Mat img; //定义图片while (true) //用流的方式将图片导出来{cap >> img; //拿出一帧if (img.empty())break;imshow("img", img); //显示图片char userKey = waitKey(10);if (userKey == 27) //如果userkey为esc键,退出循环break;}return;
}
将摄像头捕获的视频分成每一帧,将每帧保存为一张图片,再使用马赛克函数完成打码操作。
其中加载人脸检测配置文件、创建人脸检测器及保存人脸检测结果与单张图片做马赛克相同。
flip(frame, frame, 1) 用filp切割视频,一帧一帧的拿出来
bool Video_Demo()
{//人脸检测配置文件string harr_file = "haarcascade_frontalface_default.xml";//创建人脸检测器CascadeClassifier detector;detector.load(harr_file);VideoCapture cap;cap.open(0);if (!cap.isOpened()){cout << "can not open the camera!" << endl;}Mat frame;while (cap.read(frame)){flip(frame, frame, 1); //函数的方式取出帧//人脸检测vector<Rect>faces;detector.detectMultiScale(frame, faces, 1.1, 5);if (Generate_Mosaic(frame, faces)){imshow("Demo", frame);}char key = waitKey(10);if (key == 27)break;}cap.release();return true;
}
四 源码
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;bool Generate_Mosaic(Mat& src, vector<Rect>& faces)
{if (faces.empty())return false;int step = 10;//步长for (int t = 0; t < faces.size(); t++){int x = faces[t].tl().x; //人脸矩形框起点x坐标int y = faces[t].tl().y;//人脸矩形框起点y坐标int width = faces[t].width;//人脸矩形框宽int height = faces[t].height;//人脸矩形框高//仅对人脸区域进行像素修改。遍历人脸矩形框区域像素,并对其进行修改for (int i = y; i < (y + height); i += step){for (int j = x; j < (x + width); j += step){//将人脸矩形框再细分为若干个小方块,依次对每个方块修改像素(相同方块赋予相同灰度值)for (int k = i; k < (step + i); k++){for (int m = j; m < (step + j); m++){//对矩形区域像素值进行修改,rgb三通道for (int c = 0; c < 3; c++){src.at<Vec3b>(k, m)[c] = src.at<Vec3b>(i, j)[c];}}}}}}return true;
}/*
void TestMosaic(Mat& src)
{vector<Rect> faces;faces.push_back(Rect(100, 100, 200, 200));faces.push_back(Rect(300, 300, 200, 200));Generate_Mosaic(src, faces);
}
*///对单张图片打马赛克
bool Picture_Demo(Mat src)
{//人脸检测配置文件string harr_file = "haarcascade_frontalface_default.xml";//创建人脸检测器CascadeClassifier detector;detector.load(harr_file);//人脸检测,存储人脸检测的结果vector<Rect>faces;detector.detectMultiScale(src, faces, 1.1, 5);if (!Generate_Mosaic(src, faces))return false;imshow("test", src);waitKey(0);return true;
}//把视频转图像操作
void VideoToImg()
{VideoCapture cap = VideoCapture("视频路径"); //创建一个对象cap,打开一个视频,若参数设为0则为打开默认摄像头if(!cap.isOpened()){cout << "打开失败。。。" << endl;return;}
// 如果成功开打,视频的每一帧都在对象cap中Mat img; //定义图片int i = 1;while (true) //用流的方式将图片导出来{cap >> img; //拿出一帧if (img.empty())break;imshow("img", img); //显示图片string URL = "data/img" + to_string(i) + ".jpg"; //定义图片存储路径 data/img0.jpgimwrite(URL, img); //把图片写入文件waitKey(30); //延迟30msi++;}return;
}
/*
//打开摄像头,并在窗口上显示每一帧图片
void VideoToImg()
{VideoCapture cap = VideoCapture(0); //创建一个对象cap,打开一个视频,若参数设为0则为打开默认摄像头if (!cap.isOpened()){cout << "打开失败。。。" << endl;return;}// 如果成功开打,视频的每一帧都在对象cap中Mat img; //定义图片while (true) //用流的方式将图片导出来{cap >> img; //拿出一帧if (img.empty())break;imshow("img", img); //显示图片char userKey = waitKey(10);if (userKey == 27) //如果userkey为esc键,退出循环break;}return;
}
*///对视频打马赛克
bool Video_Demo()
{//人脸检测配置文件string harr_file = "haarcascade_frontalface_default.xml";//创建人脸检测器CascadeClassifier detector;detector.load(harr_file);VideoCapture cap;cap.open(0);if (!cap.isOpened()){cout << "can not open the camera!" << endl;}Mat frame;while (cap.read(frame)){flip(frame, frame, 1);//人脸检测vector<Rect>faces;detector.detectMultiScale(frame, faces, 1.1, 5);if (Generate_Mosaic(frame, faces)){imshow("Demo", frame);}char key = waitKey(10);if (key == 27)break;}cap.release();return true;
}int main()
{Mat src = imread("图片路径");if (src.empty()){cout << "No Image!" << endl;system("pause");return -1;}//TestMosaic(src);Picture_Demo(src);//Video_Demo();system("pause");return 0;
}
五 运行效果