隐形斗篷
原文地址:https://www.learnOpenCV.com/invisibility-cloak-using-color-detection-and-segmentation-with-opencv/
开源代码:https://github.com/spmallick/learnopencv/tree/master/InvisibilityCloak
如果你是像我这样的哈利波特粉丝,你会知道隐形斗篷是什么。 是! 这是哈利波特用来隐形的斗篷。 当然,我们都知道隐形斗篷不是真的 - 这都是图形技巧。在这篇文章中,我们将学习如何使用OpenCV中的简单计算机视觉技术创建我们自己的“隐形衣”。 使用C ++和Python代码。
我们可以使用一种称为颜色检测和分割的图像处理技术来创造这种神奇的体验。 好消息是,你不需要成为霍格沃茨的一员! 你需要的只是一块红色的布料,请点击这篇文章。
看看下面的视频,我正在尝试自己的隐形衣!
它是如何工作的 ?
该算法原则上与绿色筛选非常相似。 但与我们删除背景的绿色筛选不同,在此应用程序中,我们删除了前景!
我们使用红色布作为我们的斗篷。 红色为什么? 为什么不绿色? 当然,我们可以用绿色,不是红色的魔术师的颜色? 除了笑话,绿色或蓝色等颜色也可以通过一些调整来正常工作。使用什么颜色是按照场景来的,使用绿幕时场景中就不能摆放绿色的东西,如果要摆放带有绿叶的花草可能会使用红幕布,有时候也会使用蓝幕,跟场景内的物体颜色差异越大越好。
主要原理
首先保存一幅场景图片,再使用特定颜色幕布遮盖身体,按照像素颜色替换成刚开始保存的场景图片中的像素。
当然,使用这种方法整个过程中摄像机不能移动!不然合成后身后的像素会看起来怪怪的。
基本思路如下:
- 开始拍摄视频,捕获并存储背景框架。
- 使用颜色检测算法检测红色布料。
- 通过生成蒙版将红色布块分割出来。
- 将红色幕布的像素位置填充背景图对应的位置的像素,生成最终的增强输出以创建神奇效果。
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iOStream>
using namespace std;
using namespace cv;
const char* keys = "{ video | | Path to the input video file. Skip this argument to capture frames from a camera.}";
int main(int argc, char** argv) {
// Parse command line arguments
CommandLineparser parser(argc, argv, keys);
// Create a VideoCapture object and open the input file
VideoCapture cap;
if (parser.has("video")) {
cap.open(parser.get<String>("video"));
}
else
cap.open(0);
// Check if camera opened successfully
if (!cap.isOpened()) {
cout << "ERROR opening video stream or file" << endl;
return -1;
}
Mat background;
for (int i = 0; i<60; i++)
{
cap >> background;
}
//图像的反转,0代表x轴旋转,任意正数代表y轴旋转,任意负数代表x和y轴同时旋转。
flip(background, background, 1);
while (1)
{
Mat frame;
// Capture frame-by-frame
cap >> frame;
// If the frame is empty, break immediately
if (frame.empty())
break;
Mat hsv;
flip(frame, frame, 1);
cvtColor(frame, hsv, COLOR_BGR2HSV);
Mat mask1, mask2;
inRange(hsv, Scalar(0, 120, 70), Scalar(10, 255, 255), mask1);
inRange(hsv, Scalar(170, 120, 70), Scalar(180, 255, 255), mask2);
mask1 = mask1 + mask2;
Mat kernel = Mat::ones(3, 3, CV_32F);
morphologyEx(mask1, mask1, cv::MORPH_OPEN, kernel);//形态学操作,开运算
morphologyEx(mask1, mask1, cv::MORPH_DILATE, kernel);//腐蚀操作
//在这里mask1, mask2互为逆运算,即mask1代表有红色的位置,mask2代表没有红色幕布的位置
bitwise_not(mask1, mask2);//bitwise_not对图像(灰度图像或彩色图像均可)每个像素值进行二进制“非”操作,mask做为输出
Mat res1, res2, final_output;
bitwise_and(frame, frame, res1, mask2);//与运算,res1作为输出,没有红色幕布的位置正常保留
bitwise_and(background, background, res2, mask1);//有红色幕布的位置,使用背景像素填补
addWeighted(res1, 1, res2, 1, 0, final_output);
imshow("Magic !!!", final_output);
// display the resulting frame
//imshow( "Frame", frame );
// Press ESC on keyboard to exit
char c = (char)waitKey(25);
if (c == 27)
break;
// Also relese all the mat created in the code to avoid memory leakage.
frame.release(), hsv.release(), mask1.release(), mask2.release(), res1.release(), res2.release(), final_output.release();
}
// When everything done, release the video capture object
cap.release();
// Closes all the frames
destroyAllwindows();
return 0;
}
步骤1:捕获并存储背景框架
如上所述,关键思想是用背景像素替换对应于布料的当前帧像素,以产生隐形斗篷的效果。 为此,我们需要存储背景框架。
问题1、为什么使用'for循环'捕获背景图像?
由于背景是静态的,我们不能简单地使用单帧吗? 当然,与多帧图像相比,捕获的图像有点暗。 这是因为相机刚刚开始捕捉帧,因此它的参数还不稳定。 因此,使用for循环捕获静态背景的多个图像就可以了。对多个帧进行平均也可以降低噪声
步骤2:红色检测
由于我们使用红色布料将其转换为隐形斗篷,因此我们将专注于检测框架中的红色。 我们有一个RGB(红 - 绿 - 蓝)图像,很容易只是阈值R通道并获得我们的掩码。 事实证明,由于RGB值对照明非常敏感,因此无法有效工作。 因此,即使斗篷是红色的,也可能存在一些区域,由于阴影,相应像素的红色通道值非常低。正确的方法是将图像的颜色空间从RGB转换为HSV(色调 - 饱和度 - 值)
问题2、什么是HSV色彩空间?
HSV颜色空间使用三个值表示颜色:
Hue:此通道编码颜色信息。即色相,就是我们平时所说的红、绿,如果你分的更细的话可能还会有洋红、草绿等等;在HSV模型中,用度数来描述色相,其中红色对应0度,绿色对应120度,蓝色对应240度。色调可以被认为是0度对应于红色的角度,120度对应于绿色,240度对应于蓝色。
饱和度:此通道编码颜色的强度/纯度。色彩的深浅度(0-100%) ,对于一种颜色比如红色,我们可以用浅红——大红——深红——红得发紫等等语言来描述它,对应在画水彩的时候即一种颜料加上不同分量的水形成不同的饱和度。例如,粉红色的饱和度低于红色。
Value:即色调,纯度,色彩的亮度(0-100%) ,此通道编码颜色的亮度。图像的着色和光泽组件显示在此通道中。
与基于原色定义的RGB不同,HSV的定义方式类似于人类感知颜色的方式。对于我们的应用,使用HSV颜色空间的主要优点是颜色/色调/波长仅由Hue组件表示。
OpenCV中的HSV颜色体系
与上述HSV颜色系统不同的是,如果直接使用OpenCV中cvtColor函数,并设置参数为CV_BGR2HSV,那么所得的H、S、V值范围分别是[0,180),[0,255),[0,255),而非[0,360],[0,1],[0,1];这时我们可以查下面的表格来确定颜色的大致区间。
要了解详细的HSV请移步博客:OpenCV学习笔记——HSV颜色空间超极详解&inRange函数用法及实战
在上面的代码中,我们首先捕获实时帧,将图像从RGB转换为HSV颜色空间,然后定义特定范围的H-S-V值以检测红色。
这里要补充一个函数:inRange():
我们可以利用inRange函数,通过调节图像色相(H)、饱和度(S)、亮度(V)区间选择我们需要的图像区域。
CV_EXPORTS_W void inRange(InputArray src, InputArray lowerb,
InputArray upperb, OutputArray dst);
检查图像src是否在另外两个数组元素值(一般是Scalar类型表示颜色区间)之间。这里的src通常也就是矩阵Mat。(以图像颜色分割为例)
请注意:该函数输出的dst是一幅二值化之后的图像。
如果一幅灰度图像的某个像素的灰度值在指定的高、低阈值范围之内,则在dst图像中令该像素值为255,否则令其为0,这样就生成了一幅二值化的输出图像,对于三通道彩色图像,每个通道的像素值都必须在规定的阈值范围内!
inRange函数只返回二进制掩码,其中白色像素(255)表示落入上限和下限范围的像素,黑色像素(0)不表示。色调值实际上分布在一个圆(范围在0-360度之间),但在OpenCV中,以适应8位值,范围从0到180。红色表示0-30以及150-180值。我们使用0-10和170-180的范围来避免将皮肤检测为红色。使用120-255的高范围饱和度是因为我们的布料应该是高度饱和的红色。较低的值范围是70,因此我们也可以检测布料皱纹中的红色。
mask1 = mask1 + mask2
使用上面的行,我们组合了为红色范围生成的蒙版。它基本上是按像素进行OR运算。这是运算符重载+的一个简单示例。现在您已了解如何进行颜色检测,您可以更改H-S-V范围并使用其他一些单色布代替红色。事实上,绿色布料比红色布料效果更好,因为绿色布料离人体肤色最远。
步骤3:分割出检测到的红色布料
在上一步中,我们生成了一个掩码,以确定帧中与检测到的颜色对应的区域。 我们改进这个面具,然后用它来分割框架中的布料。
步骤4:生成最终的增强输出以创建神奇的效果。
最后,我们将检测到的红色区域的像素值替换为静态背景的相应像素值,最后生成增强输出,创建神奇效果,将我们的布料转换为隐形斗篷。 为此,我们首先使用bitwise_and操作创建一个图像,其像素值对应于检测到的区域,等于静态背景的像素值,然后将输出添加到图像(res1),我们从中分割出红色 布。
改进:
要想抠像效果好,必须要选择一种好的颜色模型,这决定了在度量红色和非红色的时候背景和前景的数值差异程度。
其实我们常用的RGB颜色空间模型在进行颜色距离度量时其并不优秀。原博文作者使用了HSV颜色模型。此外还有LAB颜色模型,或者YCrCb,应该会获得更好的效果。当然这个问题很复杂,各种颜色空间模型中也有很多经验值,这很难说哪一种是最优秀的。
相关阅读
DenseNet(2017年) 网络参考 DenseNet参考了ResNet和GoogleNet的优点:ResNet的特征passthrough layer(但ResNet的pass layer的特
潘通色卡C结尾的色号都是RGB色系的,而CMYK是印刷系的,两者不能通用的,所以,不能完全对等进行转换Pantone Colors【色卡】PANTONE潘通
近日做项目用到了颜色列表,所以就在这里分享给大家地址: 颜色列表
import numpy as np import cv2 #打开本地摄像头,括号内表示设备编号,第一个设备为0,如果电脑有两个摄像头,第二个摄像头就是1 cap=cv
因为PDF文件的不易修改和安全等特性,现在大部分的合同都采用PDF形式的,如果我们在合同里发现错误的地方,那么要怎么对合同里的文