1.关键知识点
- 分割算法选择
- 背景融合-高斯模糊
- 遮罩层生成
1.1分割算法的选择
- GMM/Kmeans:对视频的第一帧做trainning,对其他帧只做预言不做trainning。
- .基于色彩的处理方式
- RGB与HSV空间
最终看哪个方法失效更好。对视频的第一帧做trainning,对其他帧只做预言不做trainning。
1.2知识点补充
1.2.1概括
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。这个模型中颜色的参数分别是:色调(H),饱和度(S),亮度(V)。
- 色调H:用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
- 饱和度S:取值范围为0.0~1.0;
- 亮度V:取值范围为0.0(黑色)~1.0(白色)。
RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Saturation Value)颜色模型是面向用户的。HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。
1.2.2HSV颜色分量范围
一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围,下面是通过实验计算的模糊范围(准确的范围在网上都没有给出)。
H: 0— 180
S: 0— 255
V: 0— 255
此处把部分红色归为紫色范围:
2.完整程序
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
Mat replace_and_blend(Mat& frame, Mat& mask);
Mat background_01;
Mat background_02;
int main()
{
background_01 = imread("F:\\opencv\\opencv\\sources\\samples\\data\\leuvenA.jpg");
background_02 = imread("F:\\opencv\\opencv\\sources\\samples\\data\\leuvenB.jpg");
VideoCapture capture;
capture.open("F:\\opencv\\opencv\\sources\\samples\\data\\Megamind.avi");
if (!capture.isOpened())
{
printf("could not find the video file...\n");
return -1;
}
namedWindow("input video", WINDOW_AUTOSIZE);
namedWindow("new video", WND_PROP_AUTOSIZE);
Mat frame,hsv,mask;
int count = 0;
while (capture.read(frame))
{
cvtColor(frame, hsv, COLOR_BGR2HSV);
inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);
//形态学操作
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(mask, mask, MORPH_CLOSE, k);
erode(mask, mask, k);
GaussianBlur(mask, mask, Size(3, 3), 0, 0);
Mat result = replace_and_blend(frame, mask);
imshow("mask", mask);
char c = waitKey(1);
if (c == 27)
{
break;
}
imshow("output result", result);
imshow("input video", frame);
}
waitKey(0);
return 0;
}
Mat replace_and_blend(Mat& frame, Mat& mask)
{
Mat result = Mat::zeros(frame.size(), frame.type());
int h = frame.rows;
int w = frame.cols;
int dims = frame.channels();
//replace and blend
int m = 0;
double wt = 0;
int r = 0, g = 0, b = 0;
int r1 = 0, g1 = 0, b1 = 0;
int r2 = 0, g2 = 0, b2 = 0;
for (int row = 0; row < h; row++)
{
uchar* current = frame.ptr<uchar>(row);
uchar* bgrow = background_01.ptr<uchar>(row);
uchar* maskrow = mask.ptr<uchar>(row);
uchar* targetrow = result.ptr<uchar>(row);
for (int col = 0;col<w;col++)
{
m = *maskrow++;
if (m == 255)//背景
{
*targetrow++ = *bgrow++;
*targetrow++ = *bgrow++;
*targetrow++ = *bgrow++;
current += 3;
}
else if (m == 0)//前景
{
*targetrow++ = *current++;
*targetrow++ = *current++;
*targetrow++ = *current++;
bgrow += 3;
}
else
{
b1 = *bgrow++;
g1 = *bgrow++;
r1 = *bgrow++;
b2 = *current++;
g2 = *current++;
r2 = *current++;
//混合权重
wt = m / 255.0;
//混合
b = b1 * wt + b2 * (1.0 - wt);
g = g1 * wt + g2 * (1.0 - wt);
r = r1 * wt + r2 * (1.0 - wt);
*targetrow++ = b;
*targetrow++ = g;
*targetrow++ = r;
}
}
}
return result;
}