RGBA alpha 透明度混合算法实现和测试 您所在的位置:网站首页 算法公式加入exp RGBA alpha 透明度混合算法实现和测试

RGBA alpha 透明度混合算法实现和测试

2024-05-22 10:50| 来源: 网络整理| 查看: 265

目录1、算法叙述1.1、透明度混合算法11.2、AlphaBlend算法介绍1.3、简易Alpha混合算法2、算法实现代码和测试2.1、透明度混合算法1实现代码2.1、AlphaBlend算法实现代码2.3、测试截图2.4、完整测试程序代码

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;

当Alpha50%时

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 > 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 #include #include #include #include #include #include #include #include #include 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 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 imgRef = { &bkImage,&wmImage,&mixImage1,&mixImage2 }; for(int i=0;iisNull()){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(bkImage.bits() + bkImage.bytesPerLine()*r); uint32_t* pWmLine = reinterpret_cast(wmImage.bits() + wmImage.bytesPerLine()*r); uint32_t* pM1Line = reinterpret_cast(mixImage1.bits() + mixImage1.bytesPerLine()*r); uint32_t* pM2Line = reinterpret_cast(mixImage2.bits() + mixImage2.bytesPerLine()*r); for(int c=0;c> 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 > 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)&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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有