RGBA alpha 透明度混合算法实现和测试
1、算法叙述
算法参考自:【RGBA alpha 透明度混合算法】 ,下面的叙述和实现中有一些个人修改在里面。
1.1、透明度混合算法1
R1、G1、B1、Alpha1 为前景颜色值,R2、G2、B2、Alpha2 为背景颜色值,则:
Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2)
R = (R1 * Alpha1 + R2 * Alpha2 * (1-Alpha1))/Alpha
G = (G1 * Alpha1 + G2 * Alpha2 * (1-Alpha1))/Alpha
B = (B1 * Alpha1 + B2 * Alpha2 * (1-Alpha1))/Alpha
这里的Alpha取值范围是[0,1],需要使用到浮点计算(实数计算)。对于我们常见的8位图像,我们可以将其值域改为[0,255]进行计算,具体的见下面测试代码。
1.2、AlphaBlend算法介绍
混合算法目前在常用到的算法是AlphaBlend。
计算公式如下:假设一幅图像是A,另一幅透明的图像是B,那么透过B去看A,看上去的图像C就是B和A的混合图像,
设B图像的透明度为alpha
(取值为0-1,1为完全透明,0为完全不透明).
Alpha
混合公式如下:
R(C)=(1-alpha)*R(B) + alpha*R(A)
G(C)=(1-alpha)*G(B) + alpha*G(A)
B(C)=(1-alpha)*B(B) + alpha*B(A)
R(x)、G(x)、B(x)分别指颜色x的RGB分量原色值。从上面的公式可以知道,Alpha其实是一个决定混合透明度的数值。
这里只对B图的Alpha进行了处理,但A图本身如果也有透明通道的,也需要进行一样的处理,即
A(C)=(1-alpha)*A(B) + alpha*A(A)
1.3、简易Alpha混合算法
首先,要能取得上层与下层颜色的 RGB三基色,然后用r,g,b 为最后取得的颜色值;r1、g1、b1是上层的颜色值;r2、g2、b2是下层颜色值,若Alpha=上层透明度,则:
当Alpha=50%时
r = r1/2 + r2/2;
g = g1/2 + g2/2;
b = b1/2 + b2/2;
当Alpha<50%时
r = r1 - r1/Alpha + r2/Alpha;
g = g1 - g1/Alpha + g2/Alpha;
b = b1 - b1/Alpha + b2/Alpha;
当Alpha>50%时
r = r1/Alpha + r2 - r2/Alpha;
g = g1/Alpha + g2 - g2/Alpha;
b = b1/Alpha + b2 - b2/Alpha;
这个算法比较简单,我也没有去做实现。简单来说这里就是看透明度高是否超过50%,来决定是上下层图像谁占主导地位。
2、算法实现代码和测试
实现其实是很简单的,只是注意下面没有实数的计算,都是使用的整数计算,要注意移位与抹零的操作别出错。
下面的rgba1
表示前景图(我的代码里是水印图)的一个像素值,是RGBA8888格式的。rgba2
表示背景图的一个像素值。a1
表示rgba1
中的Alpha
分量值,a2
表示rgba2
中的Alpha
分量值。
2.1、透明度混合算法1实现代码
// 如果alpha的值域是[0,1],这里相当于将其拉伸为[0,255]
// 所以相当于是 Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2)乘以了两次255
// 当a1和a2都接近于0的时候,会导致计算得到的A值不为0,导致叠加不正常
uint32_t A = (0xffff - (0xff - a1)*(0xff - a2));
// 下面左边部分少左移8位,相当于乘以了255
uint32_t R = (((rgba1 << 8 &0xff00U) * a1 + (rgba2 >> 0 &0xffU) * a2 *(0xff - a1))/A)&0xffU;
uint32_t G = (((rgba1 >> 0 &0xff00U) * a1 + (rgba2 >> 8 &0xffU) * a2 *(0xff - a1))/A)&0xffU;
uint32_t B = (((rgba1 >> 8 &0xff00U) * a1 + (rgba2 >> 16&0xffU) * a2 *(0xff - a1))/A)&0xffU;
2.1、AlphaBlend算法实现代码
uint32_t A = a1;
uint32_t R = (((rgba1 >> 0 &0xffU) * A + (rgba2 >> 0 &0xffU) *(0xff - A)) >> 8)&0xffU;
uint32_t G = (((rgba1 >> 8 &0xffU) * A + (rgba2 >> 8 &0xffU) *(0xff - A)) >> 8)&0xffU;
uint32_t B = (((rgba1 >> 16&0xffU) * A + (rgba2 >> 16&0xffU) *(0xff - A)) >> 8)&0xffU;
A = ((a1 * A + a2 *(0xff - A)) >> 8)&0xffU; // 必须对Alpha波段也处理
2.3、测试截图
2.4、完整测试程序代码
#include <QApplication>
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QWebEngineView>
#include <QXmlStreamWriter>
#include <QBuffer>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QImage bkImage,wmImage;
QImage mixImage1,mixImage2;
// 创建窗口
QWidget widget;
// 添加控件
QWebEngineView *wevView = new QWebEngineView(&widget);
QLineEdit* leBkImagePath = new QLineEdit(&widget);
QLineEdit* leWmImagePath = new QLineEdit(&widget);
QPushButton* pbSelectBkFile = new QPushButton(QStringLiteral("选择背景图"),&widget);
QPushButton* pbSelectWmFile = new QPushButton(QStringLiteral("选择水印图"),&widget);
QPushButton* pbRunMix = new QPushButton(QStringLiteral("执行叠加"),&widget);
//pbRunDetect->setEnabled(false);
QHBoxLayout* hbLayout = new QHBoxLayout;
// 设置布局
hbLayout->addWidget(leBkImagePath);
hbLayout->addWidget(pbSelectBkFile);
hbLayout->addWidget(leWmImagePath);
hbLayout->addWidget(pbSelectWmFile);
hbLayout->addWidget(pbRunMix);
QVBoxLayout* vbLayout = new QVBoxLayout(&widget);
vbLayout->addLayout(hbLayout);
vbLayout->addWidget(wevView);
// 添加处理操作
std::function<void()> updateHtmlView =
[wevView,&bkImage,&wmImage,&mixImage1,&mixImage2]()
{
QByteArray html;
{
QXmlStreamWriter writer(&html);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartElement("html");
writer.writeStartElement("body");
writer.writeAttribute("bgcolor","gray");
QStringList imgName =
{
QStringLiteral("背景图"),
QStringLiteral("水印图"),
QStringLiteral("算法1结果图"),
QStringLiteral("算法2结果图")
};
QList<QImage*> imgRef =
{
&bkImage,&wmImage,&mixImage1,&mixImage2
};
for(int i=0;i<imgName.size();++i){
if(imgRef[i]->isNull()){continue;}
writer.writeTextElement("h2",imgName[i]);
writer.writeStartElement("img");
QBuffer buffer;
imgRef[i]->save(&buffer,"PNG");
writer.writeAttribute("src","data:image/png;base64," + buffer.data().toBase64());
writer.writeEndElement();
}
writer.writeEndElement();
writer.writeEndElement();
}
wevView->setHtml(QString::fromUtf8(html));
};
QObject::connect(pbSelectBkFile,&QPushButton::clicked,
[leBkImagePath,&bkImage,&widget,&updateHtmlView]()
{
static QString path(".");
path = QFileDialog::getOpenFileName(&widget,
QStringLiteral("选择背景图"),
path,
QString("Images (*.png *.jpg *.jpeg *.jfif)"));
if(path.isEmpty()){return;}
QImage image;
if(!image.load(path)){return;}
bkImage = image.convertToFormat(QImage::Format_RGBA8888);
leBkImagePath->setText(path);
updateHtmlView();
});
QObject::connect(pbSelectWmFile,&QPushButton::clicked,
[leWmImagePath,&bkImage,&wmImage,&widget,&updateHtmlView]()
{
static QString path(".");
path = QFileDialog::getOpenFileName(&widget,
QStringLiteral("选择水印图"),
path,
QString("Images (*.png)"));
if(path.isEmpty()){return;}
QImage image;
if(!image.load(path)){return;}
// 水印图不能比背景图大
int w = image.width() * 2 > bkImage.width() ? bkImage.width()/2:image.width();
int h = image.height() * w / image.width();
h = h > bkImage.height()?bkImage.height():h;
wmImage = image.scaledToHeight(h).convertToFormat(QImage::Format_RGBA8888);
leWmImagePath->setText(path);
updateHtmlView();
});
QObject::connect(pbRunMix,&QPushButton::clicked,
[&bkImage,&wmImage,&mixImage1,&mixImage2,&updateHtmlView]
{
mixImage1 = mixImage2 = bkImage;
for(int r = 0;r < wmImage.height();++r){
uint32_t* pBgLine = reinterpret_cast<uint32_t*>(bkImage.bits() + bkImage.bytesPerLine()*r);
uint32_t* pWmLine = reinterpret_cast<uint32_t*>(wmImage.bits() + wmImage.bytesPerLine()*r);
uint32_t* pM1Line = reinterpret_cast<uint32_t*>(mixImage1.bits() + mixImage1.bytesPerLine()*r);
uint32_t* pM2Line = reinterpret_cast<uint32_t*>(mixImage2.bits() + mixImage2.bytesPerLine()*r);
for(int c=0;c<wmImage.width();++c){
uint32_t rgba1 = pWmLine[c];
uint32_t rgba2 = pBgLine[c];
uint32_t a1 = rgba1 >> 24;
uint32_t a2 = rgba2 >> 24;
{
// 如果alpha的值域是[0,1],这里相当于将其拉伸为[0,255]
// 所以相当于是 Alpha = 1 - (1 - Alpha1) * ( 1 - Alpha2)乘以了两次255
uint32_t A = (0xffff - (0xff - a1)*(0xff - a2));
// 下面左边部分少左移8位,相当于乘以了255
uint32_t R = (((rgba1 << 8 &0xff00U) * a1 + (rgba2 >> 0 &0xffU) * a2 *(0xff - a1))/A)&0xffU;
uint32_t G = (((rgba1 >> 0 &0xff00U) * a1 + (rgba2 >> 8 &0xffU) * a2 *(0xff - a1))/A)&0xffU;
uint32_t B = (((rgba1 >> 8 &0xff00U) * a1 + (rgba2 >> 16&0xffU) * a2 *(0xff - a1))/A)&0xffU;
pM1Line[c] = R|(G<<8)|(B<<16)|((A&0xff)<<24);
}
{
uint32_t A = a1;
uint32_t R = (((rgba1 >> 0 &0xffU) * A + (rgba2 >> 0 &0xffU) *(0xff - A)) >> 8)&0xffU;
uint32_t G = (((rgba1 >> 8 &0xffU) * A + (rgba2 >> 8 &0xffU) *(0xff - A)) >> 8)&0xffU;
uint32_t B = (((rgba1 >> 16&0xffU) * A + (rgba2 >> 16&0xffU) *(0xff - A)) >> 8)&0xffU;
A = ((a1 * A + a2 *(0xff - A)) >> 8)&0xffU; // 必须对Alpha波段也处理
pM2Line[c] = R|(G<<8)|(B<<16)|(A<<24);
}
}
}
updateHtmlView();
});
widget.resize(1024,768);
widget.show();
return a.exec();
}
RGBA alpha 透明度混合算法实现和测试的更多相关文章
- RGBA alpha 透明度混合算法
RGBA alpha 透明度混合算法 .分类: 图像处理 Ps技术 2011-05-25 09:11 1112人阅读 评论(0) 收藏 举报 Alpha 透明度混合算法,网上收集整理,分成以下三种: ...
- [UnityShader基础]03.透明度混合
如果要渲染半透明物体,那么就需要用到透明度混合. 需要注意的有这几点: 1.设置标签:Tags { "Queue"="Transparent" "Ig ...
- css中filter:alpha透明度使用
css中filter:alpha透明度使用 使用filter可以设置透明度,filter:alpha在IE下是没有问题的,要支持firefox就需要使用-moz-opacity,下面有个不错的示 ...
- OpenCV——PS 图层混合算法(一)
详细的算法原理能够參考 PS图层混合算法之中的一个(不透明度,正片叠底,颜色加深,颜色减淡) // PS_Algorithm.h #ifndef PS_ALGORITHM_H_INCLUDED #de ...
- OpenCV——PS图层混合算法(六)
具体的算法原理可以参考: PS图层混合算法之六(差值,溶解, 排除) // PS_Algorithm.h #ifndef PS_ALGORITHM_H_INCLUDED #define PS_ALGO ...
- Python: PS 图层混合算法汇总
本文用 Python 实现了PS 中的图层混合算法,把很多常见的图层混合算法都汇总到了一起,比起以前写的算法,就是用矩阵运算代替了很耗时的for 循环,运行效率有所提升.具体的代码如下: import ...
- OpenCV——PS 图层混合算法 (三)
具体的算法原理可以参考 PS图层混合算法之三(滤色, 叠加, 柔光, 强光) // PS_Algorithm.h #ifndef PS_ALGORITHM_H_INCLUDED #define PS_ ...
- OpenCV——PS 图层混合算法 (二)
具体的算法原理可以参考 PS图层混合算法之二(线性加深,线性减淡,变亮,变暗) // PS_Algorithm.h #ifndef PS_ALGORITHM_H_INCLUDED #define PS ...
- OpenCV——PS 图层混合算法 (四)
具体的算法原理可以参考 PS图层混合算法之四(亮光, 点光, 线性光, 实色混合) // PS_Algorithm.h #ifndef PS_ALGORITHM_H_INCLUDED #define ...
随机推荐
- day 52 dom 事件
本文转载自cnblog.liwenzhou.com 官网资料: http://www.w3school.com.cn/htmldom/dom_methods.asp 事件的内容很重要的,基本概念有点类 ...
- 12306登录爬虫 session版本
import requests import re import base64 # 定义session headers = { 'User-Agent':'Mozilla/5.0 (Windows N ...
- Java大数相加-hdu1047
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1047 题目描述: 题意有点绕,但是仔细的读了后就发现是处理大数相加的问题.注意:输入数据有多组,每组输 ...
- GUI:GUI的方式创建/训练/仿真/预测神经网络—Jason niu
(1)导入数据:点击最左底部Import 按钮 (2)创建模型network_Jason_niu:点击底部的New按钮 (3)设置参数并训练:点击底部的Open按钮 (4)仿真预测: 大功告成!
- [dotnet core]使用Peach简化Socket网络通讯协议开发
Peach是基于DotNetty的Socket网络通讯帮助类库,可以帮助开发者简化使用DotNetty,关于DotNetty可参考我之前的这篇文章. Peach内置实现了一个基于文本协议的Comman ...
- 001. Ansible简介
一 简介 Ansible是一款极其简单的自动化运维工具, 基于Python开发, 集合了众多运维工具(puppet, cfengine, chef, func, fabric)的优点. 实现了批量系统 ...
- macOS packages安装时的降级处理
一.降级安装 在制作macOS的pkg安装包时,一般新发布的程序版本会比旧版本更高.但是有的时候我们也希望在安装的时候,对程序的某些依赖库进行降级安装.比如高版本依赖库中出现了一些Bug,使用旧版本的 ...
- Intellij IDEA更改项目优先编译顺序
今天启动公司项目突然发现了一个问题,类里面明明有这个方法,但是无论怎样都不编译.(由于公司项目是二次开发,代码都是第三方写的,代码请勿仔细看,怕伤了你的眼睛.) 如图,找不到setJGMC方法. 但是 ...
- 关于Git安装和操作中可能碰到的问题
markdown PDF 大致的安装流程和操作方法可以参照学长给的 Git和GitHub的简单教程 但是在具体实践过程中可能会碰到一些问题 下载 SSH key 先有远程库,要克隆一个本地库 先有本地 ...
- Xamarin Essentials教程打开文件
Xamarin Essentials教程打开文件 FileSystem类的OpenAppPackageFileAsync()方法可以用来打开App包中特定的文件,其语法形式如下: public sta ...