前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >YUV Alpha Blending

YUV Alpha Blending

作者头像
雪月清
发布2020-06-23 16:21:53
2.9K0
发布2020-06-23 16:21:53
举报
文章被收录于专栏:雪月清的随笔雪月清的随笔

Alpha-Blending,是按照“Alpha”混合向量的值来混合源像素和目标像素的一种图像处理技术

Alpha混合向量表示图片的透明度,取值范围[0,255],0表示全透明,图片无法被看见,255表示原始的图像,无透明效果,取中间值为半透明状态

RGB Alpha Blending

  • 首先将源像素和目标像素的R,G,B分量分别提取出来;
  • 然后将源像素的R分量乘以alpha,目标像素的R分量乘以alpha的反值并相加两者的结果做为新像素的R值;G,B分量做同样的处理;
  • 最后将新的R,G,B分量重新合成为一个新像素

混合算法:

代码语言:javascript
复制
// Alpha做了归一化处理
R3 = R1 * a + R2 * (1 - a)
G3 = G1 * a + G2 * (1 - a)
B3 = B1 * a + B2 * (1 - a)

YUV Alpha Blending

对于YUV数据,我们根据RGB到YUV的转化算法和RGB的Alpha Blending算法做推导

代码语言:javascript
复制
// RGB to YUV
Y = (( 66 * R + 129 * G +  25 * B + 128) >> 8) +  16
U = ((-38 * R -  74 * G + 112 * B + 128) >> 8) + 128
V = ((112 * R -  94 * G -  18 * B + 128) >> 8) + 128

以Y分量做演示

代码语言:javascript
复制
  ((Y1 - 16) << 8) * a + ((Y2 - 16) << 8) * (1 - a)
= (66 * R1 + 129 * G1 +  25 * B1 + 128) * a + 
  (66 * R2 + 129 * G2 +  25 * B2 + 128) * (1 - a)
= 66 * ((R1 * a - R2 * (1 - a)) + 
  129 * ((G1 * a - G2 * (1 - a)) +
  25 * ((B1 * a - B2 * (1 - a)) + 128
= 66 * R3 + 129 * G3 + 25 * B3 + 128
= (Y3 - 16) << 8  

即Y3 = Y1 * a + Y2 * (1 - a)

同理可以验证到U,V具有同样的表现形式f(x, y, a) = x * a + y * (1 - a)

YUV叠加水印

一张图片上添加水印的原理其实就是像素替换,在指定的水印区域内,用水印图片的像素值替换掉原图区域内的像素值。

一般的内容为白色的水印,比如手机相机的时间水印,水印的bitmap,背景为黑色,内容为白色,我们可以直接根据白色byte值为-21来做判断条件进行像素替换

但是在彩色图片作为水印或者水印内容为黑色的时候,上面这种hard code的解决方案就不适用了,此时我们就可以使用YUV Alpha Blending算法了

代码语言:javascript
复制
// Y像素替换,U,V分量同理
int a = alpha[waterMarkYIndex]; // [0, 255]
int dest = waterMarkYuv[waterMarkYIndex];
int source = sourceYuv[sourceYIndex];
sourceYuv[sourceYIndex] = (dest * a + source * (255 - a)) >> 8;

从水印的Bitmap中将YUV数据和Alpha数据提取出来,此处以提取NV21为例,

代码语言:javascript
复制
  /**
     * fetch nv21 data and alpha data from bitmap
     * @param bitmap   bitmap
     * @param nv21     nv21 data
     * @param alpha    alpha data
     */
    public static void fetchNv21(@NonNull Bitmap bitmap, @NonNull byte[] nv21, int[] alpha) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        int size = w * h;
        int[] pixels = new int[size];
        bitmap.getPixels(pixels, 0, w, 0, 0, w, h);

        w &= ~1;
        h &= ~1;

        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                int yIndex = i * w + j;
                int argb = pixels[yIndex];
                int r = (argb >> 16) & 0xff;
                int g = (argb >> 8) & 0xff;
                int b = argb & 0xff;

                int y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
                y = clamp(y, 16, 255);
                nv21[yIndex] = (byte) y;

                if (i % 2 == 0 && j % 2 == 0) {
                    int u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
                    int v = ((112 * r - 94 * g -18 * b + 128) >> 8) + 128;

                    u = clamp(u, 0, 255);
                    v = clamp(v, 0, 255);

                    nv21[size + i / 2 * w + j] = (byte) v;
                    nv21[size + i / 2 * w + j + 1] = (byte) u;
                }

                if (alpha != null) {
                    alpha[yIndex] = (argb >> 24) & 0xff;
                }
            }
        }
    }

从fetchNv21这个方法中可以看到YUV的数据长度为w * h * 3 / 2,而提取的Alpha数据长度只是Y分量的长度,也就是w * h。这样在运用YUV Alpha Blending算法的时候,混合Y分量,每一个Y都对应一个alpha,那么U,V分量的alpha值要怎么取呢?

对于YUV420的格式来说,每四个Y分量共用一个UV分量,而人眼对Y分量,也就是亮度敏感,而对UV分量,即色度不敏感。在进行YUV Alpha Blending,一对UV混合的时候,只需要使用共用这对UV的4个Y分量的第一个Y分量对应的alpha来作为混合因子就可以了

使用这篇文章的封面图作为背景,公众号头像作为水印,采用YUV Alpha Blending算法实现的水印效果如下:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 雪月清的随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档