在图像处理中,我们经常需要处理带透明通道的图片,比如为图片或视频添加水印,为图片或视频添加字幕、贴图等。然而,我们的素材图片未必总是带有透明通道。比如,素材的背景本该透明的地方,却是黑色和白色。有时,我们甚至需要让素材本身有图像的部分半透明。接下来,我将介绍两个方法,一种是使用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。

  

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

  得到的输出为:

  

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

  

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

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

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

  

 //创建透明通道
cv::Mat createAlpha(cv::Mat& src)
{
cv::Mat alpha = cv::Mat::zeros(src.rows, src.cols, CV_8UC1);
cv::Mat gray = cv::Mat::zeros(src.rows, src.cols, CV_8UC1); //根据灰度创建透明度通道
cv::cvtColor(src, gray, cv::COLOR_RGB2GRAY); for (int i = ; i < src.rows; i++)
{
for (int j = ; j < src.cols; j++)
{
//透明度为灰度的两倍,可自行调整 alpha.at<uchar>(i, j) = gray.at<uchar>(i, j) * ;
}
} return alpha;
}

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

 int addAlpha(cv::Mat& src, cv::Mat& dst, cv::Mat& alpha)
{
if (src.channels() == )
{
return -;
}
else if (src.channels() == )
{
cv::cvtColor(src, src, cv::COLOR_GRAY2RGB);
} dst = cv::Mat(src.rows, src.cols, CV_8UC4); std::vector<cv::Mat> srcChannels;
std::vector<cv::Mat> dstChannels;
//分离通道
cv::split(src, srcChannels); dstChannels.push_back(srcChannels[]);
dstChannels.push_back(srcChannels[]);
dstChannels.push_back(srcChannels[]);
//添加透明度通道
dstChannels.push_back(alpha);
//合并通道
cv::merge(dstChannels, dst); return ;
}

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

  原图:

  

  加入透明通道:

  

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

  

  

使用opencv为没有透明通道的图像加入透明通道的更多相关文章

  1. 多通道(Multichannel)单通道(singlechannel)图像概念梳理

    在做机器视觉时,常常要将一个多通道图像分离成几个单通道图像或者将几个单通道图像合成一个多通道图像,以方便图像处理,但是.写这篇博客,是为加深对这两个概念的理解,下面会给出部分OpenCV对单通道与多通 ...

  2. OpenCV计算机视觉学习(2)——图像算术运算 & 掩膜mask操作(数值计算,图像融合,边界填充)

    在OpenCV中我们经常会遇到一个名字:Mask(掩膜).很多函数都使用到它,那么这个Mask到底是什么呢,下面我们从图像基本运算开始,一步一步学习掩膜. 1,图像算术运算 图像的算术运算有很多种,比 ...

  3. OpenCV中IplImage图像格式与BYTE图像数据的转换

    最近在将Karlsruhe Institute of Technology的Andreas Geiger发表在ACCV2010上的Efficent Large-Scale Stereo Matchin ...

  4. OpenCV学习笔记:如何扫描图像、利用查找表和计时

    目的 我们将探索以下问题的答案: 如何遍历图像中的每一个像素? OpenCV的矩阵值是如何存储的? 如何测试我们所实现算法的性能? 查找表是什么?为什么要用它? 测试用例 这里我们测试的,是一种简单的 ...

  5. OpenCV 编程简单介绍(矩阵/图像/视频的基本读写操作)

    PS. 因为csdn博客文章长度有限制,本文有部分内容被截掉了.在OpenCV中文站点的wiki上有可读性更好.而且是完整的版本号,欢迎浏览. OpenCV Wiki :<OpenCV 编程简单 ...

  6. opencv::将两幅图像合并后,在同一个窗口显示;并将合并的图像流保存成视频文件

    /** * @file main-opencv.cpp * @date July 2014 * @brief An exemplative main file for the use of ViBe ...

  7. 使用GDI+保存带Alpha通道的图像

    带Alpha通道的图像(ARBG)在通过GDIPlus::Bitmap::FromHBITMAP等转为GDI+位图,再存储时,透明区域会变成纯黑(也有可能是纯白?).   网上找了两段保持透明的实现代 ...

  8. 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整

    今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...

  9. 【opencv系列02】OpenCV4.X图像读取与显示

    一.读取图片 opencv中采用imread() 函数读取图像 imread(filename, flags=None)     filename 图片的路径     flags 图像读取方式 ● c ...

随机推荐

  1. kubernetes-pod驱逐机制

    1.驱逐策略 kubelet持续监控主机的资源使用情况,并尽量防止计算资源被耗尽.一旦出现资源紧缺的迹象,kubelet就会主动终止部分pod的运行,以回收资源. 2.驱逐信号 以下是一些kubele ...

  2. html实现时间轴_纯css实现响应式竖着/垂直时间抽布局效果

    1.概述 html实现用时间点来展示事件发生点来代替用table展示一条条数据,能够给人清晰.一目了然能够看清事情发生的过程,UI页面也显示的那么清晰.如何用css+html做出时间轴展示事件点的?先 ...

  3. 14.刚体组件Rigidbody

    刚体组件是物理类组件,添加有刚体组件的物体,会像现实生活中的物体一样有重力.会下落.能碰撞. 给物体添加刚体: 选中游戏物体->菜单Component->Physics->Rigid ...

  4. 发布.net core Web到CentOS7

    1.发布一个.net core(只安装了.Net Core运行时,而没有安装ASP.NET Core运行时,需要添加以下节点再发布).  <PublishWithAspNetCoreTarget ...

  5. Beautiful Soup 4 方法便捷查询

    目录 BS4便捷查询 属性 子节点 父节点 兄弟节点 格式化输出 搜索文档树 修改文档树 BS4便捷查询 准备 : import requests, re from bs4 import Beauti ...

  6. HTTP协议——详细版

    一 HTTP协议简介 作为学习前端开发的开始,我们必须搞明白以下几件事 1.什么是互联网      互联网=物理连接介质+互联网协议 2.互联网建立的目的?         数据传输打破地域限制,否则 ...

  7. electron设置window系统托盘

    electron设置托盘 // 设置系统托盘 const setAppTray = () => { // 托盘对象 var appTray = null // 系统托盘右键菜单 var tray ...

  8. shell专题(一):Shell概述

    大数据程序员为什么要学习Shell呢? 1)需要看懂运维人员编写的Shell程序. 2)偶尔会编写一些简单Shell程序来管理集群.提高开发效

  9. 数据可视化之powerBI技巧(三)这个Power BI技巧很可爱:利用DAX制作时钟

    周末放松一下,给大家分享一个小技巧,仅利用DAX制作一个简易的时钟. 时钟效果如下: 这个时钟的制作只需一个度量值,你信吗? 事实上确实如此,制作步骤介绍如下: 1,新建参数,生成一个数字序列作为小时 ...

  10. [Cordova]Cordova6.x自定义插件之Andorid

    1.继承了CordovaPlugin的Java Class 需要重写execute方法,如下: 2.在res/xml/config.xml中关联上述java class 3.在assets/www/p ...