zoukankan      html  css  js  c++  java
  • 使用opencv为没有透明通道的图像加入透明通道

      在图像处理中,我们经常需要处理带透明通道的图片,比如为图片或视频添加水印,为图片或视频添加字幕、贴图等。然而,我们的素材图片未必总是带有透明通道。比如,素材的背景本该透明的地方,却是黑色和白色。有时,我们甚至需要让素材本身有图像的部分半透明。接下来,我将介绍两个方法,一种是使用opencv内置方法,另一种是自己写代码,来为图像添加透明通道。文末有第二种方法的效果展示

      1.首先,是opencv中的cvtColor方法。 

    C++: void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0 );

    参数解释:

    . InputArray src: 输入图像即要进行颜色空间变换的原图像,可以是Mat类
    . OutputArray dst: 输出图像即进行颜色空间变换后存储图像,也可以Mat类
    . int code: 转换的代码或标识,即在此确定将什么制式的图片转换成什么制式的图片,后面会详细将
    . int dstCn = 0: 目标图像通道数,如果取值为0,则由src和code决定

      

      我们可以令code参数为COLOR_BGRABGRA,将图像转化为带透明通道的图片。这里要注意,加上的透明通道,默认值为255,也就是说,默认将图像转换为不透明图。如果需要对图像的透明度进行调整,则还需要另写代码。下面是部分代码,来验证默认值确实为255。

      

     1 std::string path = "E:/140.jpg";
     2 Mat image = cv::imread(image_path);
     3 std::cout << "原图像通道数: " << image.channels() << std::endl;
     4 cvtColor(image, image, COLOR_BGR2BGRA);
     5 std::cout << "转换后图像通道数: " << image.channels() << std::endl;
     6 for (int i = 0; i < image.rows; i++) {
     7     for (int j = 0; j < image.cols; j++) {
     8         std::cout<<(int)image.at<Vec4b>(i, j)[3]<<std::endl;
     9     }
    10 }    

      得到的输出为:

      

      代码中,image.at可以获取图像像素值,而中括号内,0代表B,1代表G,2代表R,3代表A,所以括号中为3。而强制类型转换为int,则是因为在opencv中,单像素的类型为uchar,如果直接标准输出,则会输出一大堆字符,而不是我们想要的像素值。如果图像在读取的时候没有要求读取透明通道,或者图像本身没有透明通道,那么图像的通道数默认为3,可以简单地说,这个内置方法,就是为图像的通道数组多加了一列作为透明通道,这个数组类型为Mat类型。

      

      2.接下来,是手写代码的方法 

      由上述说明可知,默认方法所做的就是给图像的通道数组再加上一列,而这一列所表示的,就是图像每个像素的透明度。而这个透明度数组,也是一个Mat类型数组。

      所以,我们可以新建一个Mat类型数组,数组大小与图像的分辨率一致。这里,我们还可以以图像的灰度图作为参考,将图像的每个像素以灰度值来设置透明度,这样一来,就实现了图像按像素值自动的调整每一个像素点的透明度。创建透明通道的方法如下:

      

     1 //创建透明通道
     2 cv::Mat createAlpha(cv::Mat& src)
     3 {
     4     cv::Mat alpha = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
     5     cv::Mat gray = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
     6 
     7     //根据灰度创建透明度通道
     8     cv::cvtColor(src, gray, cv::COLOR_RGB2GRAY);
     9 
    10     for (int i = 0; i < src.rows; i++)
    11     {
    12         for (int j = 0; j < src.cols; j++)
    13         {
    14             //透明度为灰度的两倍,可自行调整
    15             
    16             alpha.at<uchar>(i, j) = gray.at<uchar>(i, j) * 2;
    17         }
    18     }
    19 
    20     return alpha;
    21 }

      这个alpha,就是图像的透明通道。然而,这里的透明通道仅仅是被创建了出来,并没有被加入图像中。我们可以使用opencv中的split和merge函数来添加透明通道。其中,split函数作用是分割图像的通道,merge函数则是合并图像的各通道。我们可以先把原图像的各个通道分开,然后再连带着透明通道合并,就得到了带透明通道的图像。代码如下:

     1 int addAlpha(cv::Mat& src, cv::Mat& dst, cv::Mat& alpha)
     2 {
     3     if (src.channels() == 4)
     4     {
     5         return -1;
     6     }
     7     else if (src.channels() == 1)
     8     {
     9         cv::cvtColor(src, src, cv::COLOR_GRAY2RGB);
    10     }
    11 
    12     dst = cv::Mat(src.rows, src.cols, CV_8UC4);
    13 
    14     std::vector<cv::Mat> srcChannels;
    15     std::vector<cv::Mat> dstChannels;
    16     //分离通道
    17     cv::split(src, srcChannels);
    18 
    19     dstChannels.push_back(srcChannels[0]);
    20     dstChannels.push_back(srcChannels[1]);
    21     dstChannels.push_back(srcChannels[2]);
    22     //添加透明度通道
    23     dstChannels.push_back(alpha);
    24     //合并通道
    25     cv::merge(dstChannels, dst);
    26 
    27     return 0;
    28 }

      再处理的过程中,先调用createAlpha函数创建透明通道,再调用addAlpha函数加入透明通道即可。下面放一个测试结果。

      原图:

      

      加入透明通道:

      

      可以看到,一些像素变成了全透明,而一些像素是半透明。如果把这个图贴在其他图上的话,看的更明显一点:

      

      

  • 相关阅读:
    CentOS 6.4 x64 zabbix 2.2.2 编译安装
    Monitorix 监控 安装配置
    CentOS 6.4 x64 Percona-Server-5.6.15 源码安装
    CentOS 6.4 x64 安装 配置 Redmine 2.4.1
    ActiviMQ的基本使用
    Java内存 模型理解
    线程池的两种创建方式及区别
    线程创建的三种方式及区别
    Spring cloud 之Ribbon(二)负载均衡原理
    Spring cloud 之Ribbon(一)基本使用
  • 原文地址:https://www.cnblogs.com/zhnblog/p/13403257.html
Copyright © 2011-2022 走看看