Android 实现图片水印与隐形数字水印

在使用知乎,微博的时候,我们经常可以看到自己上传的图片被加上了文字水印,在实际的应用开发过程中,很多客户端都需要开发者自己编写 Canvs 绘制图形水印的方法,今天我想在这里介绍一个轻量级的开源 Android 图片水印框架来避免开发过程中编写复杂的绘图代码:AndroidWM,并且剖析它的实现过程和特色用法。

水印

这个框架最大程度上简化了图片水印绘制的问题,暴露了灵活的接口给用户使用。在绘制水印的时候,只需要创建一个水印对象,一个水印创建者对象,然后把定制的水印对象传给水印创建者即可。

实现水印第一步:引用类库 androidWM

如果你使用 Android Studio 进行项目的开发,只需要在应用 app 的 build.gradle 里面添加上这么一行代码:

dependencies {
  ...
  implementation 'com.huangyz0918:androidwm:0.2.3'
  ...
}

然后点击同步 gradle,让其自动下载并且安装好依赖即可。

实现水印第二步:创建一个水印,完工!

androidWM 给我们提供了四种不同的水印:

  • 图形水印
  • 文字水印
  • 隐形图形水印
  • 隐形文字水印

我们可以创建不同的水印对象来实现对水印风格的定制化,比如我想创建一个文字水印,我就实例化一个WatermarkText,并且设置它的属性:

 WatermarkText watermarkText = new WatermarkText(“Hello World”)
                    .setPositionX(0.5) // 横坐标
                    .setPositionY(0.5) // 纵坐标
                    .setTextAlpha(100) // 透明度
                    .setTextColor(Color.WHITE) // 文字水印文字颜色
                    .setTextFont(R.font.champagne) // 字体
                    .setTextShadow(0.1f, 5, 5, Color.BLUE); // 字体的阴影

这样我就拿到了一个文字水印 watermarkText ,接下来,我还需要一个画师帮助我把水印画到我想要的背景底图上面,于是我们需要实例化一个WatermarkBuilder

    WatermarkBuilder.create(this, backgroundBitmap) // 加载背景底图
                    .loadWatermarkText(watermarkText) // 加载水印对象
                    .getWatermark() // 绘制带有水印的图片
                    .setToImageView(backgroundView); // 设置结果到 ImageView 里

这里一气呵成,首先传入一个上下文 context,然后传入一张你想要绘制的背景底图,这个背景图片可以来自于一个 ImageView, 也可以来自于系统的资源(如: R.drawable.image),你要是想直接传入一个 Bitmap 也是可以的。最后,在调用完方法 .getWatermark() 以后,androidWM 可以帮助你直接将带有水印的图片设置到一个ImageView里面,或者使用方法 .getOutputImage() 获得输出的 Bitmap

Android 实现图片水印与隐形数字水印_第1张图片
水印创建好啦!

同理,你也可以通过实例化一个 WatermarkImage 来创建一个图像水印:

  WatermarkImage watermarkImage = new WatermarkImage(watermarkBitmap)
                    .setImageAlpha(80)
                    .setPositionX(0.5)
                    .setPositionY(0.5)
                    .setRotation(15)
                    .setSize(0.3);

            WatermarkBuilder
                    .create(this, backgroundBitmap)
                    .loadWatermarkImage(watermarkImage)
                    .getWatermark()
                    .setToImageView(backgroundView);

Boom ! 画出来的水印长这样:

Android 实现图片水印与隐形数字水印_第2张图片
图片水印!小眼睛!

创建隐形水印

隐形水印是框架 androidWM 的一个特色,它支持两种不同方式的隐形水印:

  • LSB 空域隐形水印
  • 频域水印

创建隐形水印的话,设置水印的位置和大小颜色就没有意义了,水印将被编码到肉眼不可见的空间,只有使用框架的某些方法才能够成功地检测出来。

创建一个隐形的水印也是很简单的,我们同样可以通过 WatermarkBuilder 来绘制一个隐形的文字或者是图形水印:

     WatermarkBuilder
            .create(this, backgroundBitmap)
            .loadWatermarkImage(watermarkBitmap)
            .setInvisibleWMListener(true, new BuildFinishListener() {
                @Override
                public void onSuccess(Bitmap object) {
                    if (object != null) {
                       // do something...
                    }
                }

                @Override
                public void onFailure(String message) {
                      // do something...
                }
            });

在方法 .setInvisibleWMListener() 中第一个参数是用于选择水印模式的参数:

  • true 使用空域 LSB 方法绘制隐形水印
  • false 使用频域隐形水印

同样,对于隐形水印的检测,我们可以初始化一个水印检测器 WatermarkDetector :

     WatermarkDetector
            .create(inputBitmap, true)
            .detect(false, new DetectFinishListener() {
                @Override
                public void onSuccess(DetectionReturnValue returnValue) {
                       Bitmap watermarkImage = returnValue.getWatermarkBitmap();
                       String watermarkString = returnValue.getWatermarkString();
                 }

                @Override
                public void onFailure(String message) {
                       // do something...
                }
            });

在函数创建的时候,也需要选择一种检测模式,如果是 LSB 空域水印的检测,在第二个布尔参数的位置传进 true,反正是频域隐形水印的话就传 false。在检测方法 detect() 的第一个参数也是类似,如果需要检测文字类的水印传入 true,图片类的水印传入 false。

Android 实现图片水印与隐形数字水印_第3张图片
添加图片作为隐形水印

LSB 实现隐形水印的原理

讲完了框架的使用,我们来说一下关于 androidWM 实现图片隐形水印的原理,首先要说 LSB 空域水印。LSB 的全称是 Least Significant Bits,即通过图形中最不重要的一些信息位来储存我们隐藏信息(水印)。LSB 方法是最简单的嵌入水印的方法,事实上,任何一幅图片都具备一定的容噪性,这表现在像素数据的最低有效位 (Least Significant Bit,LSB) 对人眼的视觉影响很小,秘密信息就隐藏在图像每一个像素的最低位或次低位,实现其不可见性。

Android 实现图片水印与隐形数字水印_第4张图片
添加文字作为图片水印

为了实现我们的隐形水印,androidWM 将一幅图片的所有像素提取出来,其中每个像素都可以分解为 ARGB 四位信息(分别是 alpha, red, green, blue),作为标准的 ARGB 编码方式,每一位都由一个从 0 到 255 的整形数字表示,其中作为人眼来说,最后一位色阶的细小变化是根本不会被观察到的,我们将水印的文字(或是图片)信息编码为一串二进制的字符,将每个 ARGB 的值的最后一位清零,用二进制字符替换,达到了利用最后一位色阶来储存隐藏的信息的作用。

Android 实现图片水印与隐形数字水印_第5张图片
LSB 编码方式

在实际的工程中,我们将水印信息编码成二进制以后,加上了非二进制的前后缀用于识别程序检测,同时,为了让整张图片都被水印给覆盖,我们的算法将水印信息循环地铺设在背景图片中,只要图片之中含有一个有效的水印信息,就能够成功地检测到。

对于 LSB 空域数字水印来说,其有如下优点:

  • 支持的水印信息量大
  • 对原图影响小
  • 算法简单

但是其也有不可忽视地巨大缺点:

  • 在空间域上绘制数字水印,水印会随着图片变化而破坏
  • 稳定性差,不能抵抗图像的裁剪、缩放和 jpg 压缩

你可能感兴趣的:(Android 实现图片水印与隐形数字水印)