OpenCV教程之“蓝幕抠图”

OpenCV HSV 蓝幕 抠图
xukejing
发布时间: 2018-06-28
阅读: 3639

opencv时候,肯定会接触色彩空间。最常用的是RGB色彩空间,用0~255表示每个通道。

摄影投效里有个技术,叫做蓝幕,也有绿幕。后期把蓝色区域扣掉,然后换成别的背景。

 my2.jpg

在上图中,细心的小伙伴会发现,实际这个蓝色并不一定是纯蓝(0,0,255

  

有脑洞大的小朋友想到了一个方法,只提取蓝色通道,然后把值接近255的都扣掉。

  

但是有个问题,这样会扣到高光区域(RGB三个值都很高的区域)

另外,如果这个蓝色偏亮偏暗或偏绿,或者摄像机有偏色,用(0,0,255)这个参数当特征来匹配是很困难的事。

  

这儿我给大家推荐一个好用的色彩空间,叫HSV色彩空间。

hsv色彩空间.JPG

  

H是个色相参数,用0360度表示了颜色。

S是个色彩鲜艳度的参数,越大颜色越鲜艳,越小颜色越灰。S有个通俗的名字,叫饱和度。

V是个亮度参数,越小越黑


RGB到HSV色彩空间的转换代码如下:

float rgb2h(int B, int G, int R)

{

  float H;

  int max = max(max(R, G), B);

  int min = min(min(R, G), B);

  if (R == max)

    H = (G - B)*60.0 / (max - min);

  if (G == max)

    H = 120+(B - R)*60.0 / (max - min);

  if (B == max)

    H = 240+(R - G)*60.0 / (max - min);

  if (H < 0) H = H + 360;

    return H;

}

 

我们先找一张色彩分布的图来试验一下抠图

a3.jpg

  

蓝色附近的区域就是H240度附近。下面我们写个小程序把240度正负50度的区域抠掉。

void mytest()

{

  Mat frame_t = imread("a3.jpg", CV_LOAD_IMAGE_UNCHANGED);

  int WW, HH;//图像宽度和高度 单位:像素

  HH = frame_t.rows; WW = frame_t.cols; int row2, col2;//循环时候计数第几行和第几列的

  for (row2 = 0; row2 < HH; row2++)

  {

    for (col2 = 0; col2 < WW; col2++)

     {

       if (fabsf(rgb2h(frame_t.at<Vec3b>(row2, col2)[0], frame_t.at<Vec3b>(row2, col2)[1], frame_t.at<Vec3b>(row2, col2)[2]) - cut)<5)

       {

          frame_t.at<Vec3b>(row2, col2)[0] = 0;

          frame_t.at<Vec3b>(row2, col2)[1] = 0;

          frame_t.at<Vec3b>(row2, col2)[2] = 0;

       }

     }

  }

 imshow("hsvcut", frame_t);

}

 

运行效果如图

 hsv抠图.JPG

在上图中,我们发现这个算法可以准确地扣出蓝色区域,而且即使蓝色亮一点暗一点或是灰一点鲜艳一点,也没有影响。

  

接下来,我们找两个图片来抠图和合成图像。

首先是蓝幕原图

 my2.jpg

然后是背景

 my1.jpg

我们来写一下抠除蓝色部分以后与背景叠加的代码

Mat frame_orig;//采集的原始图像

frame_orig = imread("my2.jpg", CV_LOAD_IMAGE_UNCHANGED);

Mat frame_back;//采集的原始图像

frame_back = imread("my1.jpg", CV_LOAD_IMAGE_UNCHANGED);

imshow("orig", frame_orig);

imshow("back", frame_back);

Mat frame_c;//测试图像

frame_c = frame_orig;

int W, H;//图像宽度和高度 单位:像素

H = frame_orig.rows;

W = frame_orig.cols;

int row, col;//循环时候计数第几行和第几列的

for (row = 0; row < H; row++)

 {

   for (col = 0; col < W; col++)

    {

       if (fabsf(rgb2h(frame_orig.at<Vec3b>(row, col)[0], frame_orig.at<Vec3b>(row, col)[1], frame_orig.at<Vec3b>(row, col)[2])-cut)<c_zone)

        {

            frame_c.at<Vec3b>(row, col)[0] = frame_back.at<Vec3b>(row, col)[0];

            frame_c.at<Vec3b>(row, col)[1] = frame_back.at<Vec3b>(row, col)[1];

            frame_c.at<Vec3b>(row, col)[2] = frame_back.at<Vec3b>(row, col)[2];

        }

     }

 }

imshow("MyPic", frame_c);

合成以后的图像见下图右下。

 输出2.JPG

 

接下来,我们再换个图片做一次。为了让图片增加些戏剧效果,我找来了一个热气球的蓝幕原图和一段树枝的背景。

蓝幕原图

a2.jpg

背景

a1.jpg

 

最后合成的图像见下图右上。

输出.JPG

 

这种基于OpenCV的蓝幕抠图算法可以自动地对每一帧图像进行处理和合成,特别适合用于视频的实时抠图,也许可以用在自动换背景等应用上。


原创作品,未经权利人授权禁止转载。详情见转载须知 举报文章

点赞 (0)
xukejing 擅长:其他应用
评论(0)

登录后可评论,请 登录注册

相关文章推荐
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回

我要举报该内容理由

×
请输入您举报的理由(50字以内)