显性水印和不可见数字水印

news/2024/7/19 14:51:58 标签: canvas, html5, css, js

显性水印和不可见数字水印

显性水印(代码见最后)

  1. 获取要绘制的画布所在元素

  2. 获取浏览器的dpr(devicePixelRatio),将画布的宽度和高度乘以dpr, 否则绘制出来的画布会变得模糊

  3. 创建Image元素开始绘制原始图片

  4. 绘制显性水印, 设置水印的样式, 水印位置设置在图片右下角

不可见数字水印

function getBitOffset(color):

获取RGB中某一分量对应的位和偏移量

图片编码
function encodeImg(src):
  1. 获取画布元素的Context

  2. 画布宽高乘以dpr(devicePixelRatio)

  3. 绘制水印, 使用getImageData()方法获得水印的像素信息

  4. 绘制图片, 使用getImageData()方法获得图片的像素信息

  5. 调用mergeData()方法合并像素信息

function mergeData(ctx, newData, color, originalData):

​ 1. 合并原始图片数据和数字水印的rgb数据.

​ 2. 对于规定的某一个RGB分量,

​ 采用将把没有水印信息的像素通过自增方式全改成偶数,

​ 把有水印信息的像素自增全改成奇数这种编码的方式编码图片

  1. 绘制合并后的图片
图片解码
function decodeImg(src,color):
  1. 同encodeImg()方法一样, 使用getImageData()方法获得图片的RGB信息
  2. 调用processData()方法
function processData(ctx, originalData,color):
  1. 解码的方式与编码的方式相反
  2. 将非指定的RGB分量的所有信息置0(像素点置黑)
  3. 对于指定的RGB分量, 将偶数部分(非水印信息)的信息置0, 其他信息置为255
  4. 对于alpha通道的信息不处理

结果如图

显性水印和不可见数字

代码

未解决跨域问题, 不可从外部直接打开html, 需从IDE打开

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>watermark</title>

</head>
<style>
    html{
        margin:0;
        padding: 0;
    }
    .app{
        display: flex;
        justify-content: space-around;
        flex-wrap: wrap;
    }
    .canvas{
        height: 200px;
        width: 540px;
        margin-top: 50px;
    }
    img{
        height: 200px;
        width: 540px;
        margin-top: 50px;
    }

</style>
<body>
<div class="app">
    <img src="../images/1.jpg">
    <canvas class="canvas" id="canvas-mark"></canvas>
    <canvas class="canvas" id="encode-mark"></canvas>
    <canvas class="canvas" id="encode-image"></canvas>

</div>
<script>
        //绘制显性水印
        var canvas_mark=document.getElementById("canvas-mark")
        var dpr = (scale = window.devicePixelRatio || 1);
        var rect = canvas_mark.getBoundingClientRect();
        canvas_mark.width = rect.width * dpr;
        canvas_mark.height = rect.height * dpr;
        canvas_mark.style.width = rect.width + "px";
        canvas_mark.style.height = rect.height + "px";
        let ctx=canvas_mark.getContext('2d');
        let img = new Image();
        img.onload = function () {
            ctx.drawImage(img, 0, 0, canvas_mark.width, canvas_mark.height);
            const txt = '@ ChenYin';
            ctx.fillStyle = '#fff';
            ctx.globalAlpha = 1;
            ctx.font = `12px 微软雅黑 light`;
            ctx.textAlign = 'right';
            ctx.fillText(txt, canvas_mark.width - 10, canvas_mark.height - 10);
        }
        img.src="../images/1.jpg"

        //绘制数字水印和数字水印结果图

        //获取RGB中某一分量对应的位和偏移量
        function getBitOffset(color) {
            let bit, offset;

            switch (color) {
                case 'R':
                    bit = 0;
                    offset = 3;
                    break;
                case 'G':
                    bit = 1;
                    offset = 2;
                    break;
                case 'B':
                    bit = 2;
                    offset = 1;
                    break;
            }
            return [bit,offset];
        }

        //图片编码
        //合并原始图片数据和数字水印的rgb数据, 采用将把没有信息的像素全改成偶数, 把有信息的像素全改成奇数这种编码的方式
        function mergeData(ctx, newData, color, originalData) {
            let oData = originalData.data;

            let [bit,offset]=getBitOffset(color);

            for (var i = 0; i < oData.length; i++) {
                if (i % 4 == bit) {

                    // 只处理目标通道
                    //把没有信息的像素全改成偶数
                    if (newData[i + offset] === 0 && (oData[i] % 2 === 1)) {

                        if (oData[i] === 255) {
                            oData[i]--;
                        } else {
                            oData[i]++;
                        }
                        //把有信息的像素全改成奇数
                    } else if (newData[i + offset] !== 0 && (oData[i] % 2 === 0)) {
                        // // 有信息的像素,该通道最低位置1,可以想想上面的斑点效果是怎么实现的
                        oData[i]++;
                    }
                }
            }
            ctx.putImageData(originalData, 0, 0);


        }

        function encodeImg(src) {
            var textData;
            var can=document.getElementById('encode-mark');
            var dpr = (scale = window.devicePixelRatio || 1);
            var rect = canvas_mark.getBoundingClientRect();
            can.width = rect.width * dpr;
            can.height = rect.height * dpr;
            can.style.width = rect.width + "px";
            can.style.height = rect.height + "px";


            var ctx = can.getContext('2d');
            ctx.font = '30px Microsoft Yahei';
            ctx.fillText('ChenYin', 200, 120);
            textData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;
            var img = new Image();
            // img.crossOrigin = '';
            var originalData;
            img.onload = function () {
                // 获取指定区域的canvas像素信息
                ctx.drawImage(img, 0, 0,can.width,can.height);
                originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
                mergeData(ctx, textData, 'G', originalData)
            };
            img.src = src;
        }

        //图片解码
        function processData(ctx, originalData,color) {
            let data = originalData.data;
            let [bit,offset]=getBitOffset(color);
            for (var i = 0; i < data.length; i++) {
                if (i % 4 == bit) {

                    if (data[i] % 2 == 0) {
                        data[i] = 0;
                    } else {
                        data[i] = 255;
                    }
                } else if (i % 4 == 3) {
                    continue;//alpha通道不处理
                } else {
                    data[i] = 0;
                }
            }
            // 将结果绘制到画布
            ctx.putImageData(originalData, 0, 0);
        }

        function decodeImg(src,color) {
            var encode_image = document.getElementById('encode-image')
            var dpr = (scale = window.devicePixelRatio || 1);
            var rect = canvas_mark.getBoundingClientRect();
            encode_image.width = rect.width * dpr;
            encode_image.height = rect.height * dpr;
            encode_image.style.width = rect.width + "px";
            encode_image.style.height = rect.height + "px";
            var ctx=encode_image.getContext('2d');
            var img = new Image();
            var originalData;
            img.onload = function () {
                // 获取指定区域的canvas像素信息
                ctx.drawImage(img, 0, 0);
                originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
                console.log(originalData)
                processData(ctx, originalData,color)
            };
            img.src = src;
        }
        encodeImg('../images/1.jpg');
        decodeImg('../images/1-encode.png','G');


</script>
</body>

</html>

http://www.niftyadmin.cn/n/780819.html

相关文章

基于springboot vue elementui图书借阅系统源码(毕设)

开发环境及工具&#xff1a; 大于等于jdk1.8&#xff0c;大于mysql5.5&#xff0c;nodejs&#xff0c;idea&#xff08;eclipse&#xff09;&#xff0c;nodejs&#xff0c;vscode&#xff08;webstorm&#xff09; 技术说明&#xff1a; springboot mybatis vue elementui …

番茄时钟微信小程序

番茄时钟微信小程序 番茄时钟微信小程序主要实现了番茄时钟、事务便签、效率统计等功能。 使用的UI组件(css库): colorUI 代码地址: https://github.com/zhengronggui666/tomatoClock 页面截图: 主页面: ##### 便签页面: ##### 统计页面: ##### 我的页面: ##### 二级页面:…

C++各种输入函数

输入函数 1.cin>>s cin函数遇到空格、TAB、换行时读取结束。缓冲区的空格等不会消失&#xff0c;但cin运用时会跳过这些空格&#xff0c;换行等注意&#xff1a;最后的空格或换行不会消失&#xff0c;如果配合其他输入函数需要先getchar() 2.char chcin.get()和cin.ge…

基于java springboot博客网站源码(毕设)

开发环境及工具&#xff1a; 大等于jdk1.8&#xff0c;大于mysql5.5&#xff0c;idea&#xff08;eclipse&#xff09; 技术说明&#xff1a; springboot mybatis html vue.js bootstrap 代码注释齐全&#xff0c;没有多余代码&#xff0c;适合学习(毕设)&#xff0c;二次…

C++宏详解

C宏详解 1. 定义 ​ #define命令是C语言中的一个宏定义命令&#xff0c;它用来将一个标识符定义为一个字符串&#xff0c;该标识符被称为宏名&#xff0c;被定义的字符串称为替换文本。 该命令有两种格式&#xff1a;一种是简单的宏定义&#xff0c;另一种是带参数的宏定义。…

基于java springboot android 安卓校园宿舍报修系统源码(毕设)

开发环境及工具&#xff1a; 大等于jdk1.8&#xff0c;大于mysql5.5&#xff0c;idea&#xff08;eclipse&#xff09;&#xff0c;Android Studio 技术说明&#xff1a; springboot mybatis android 代码注释齐全&#xff0c;没有多余代码&#xff0c;适合学习(毕设)&…

C++小数处理

小数处理 1. 三个取整函数 double a50.25; int res1floor(a);//向下取整函数,res150 int res2ceil(a);//向上取整函数,res251 int res3round(a);//四舍五入函数,res3512. 保留几位小数 重写函数 #include<iostream> #include <iomanip> #include <sstream&g…

基于java springboot android 安卓失物招领源码(毕设)

开发环境及工具&#xff1a; 大等于jdk1.8&#xff0c;大于mysql5.5&#xff0c;idea&#xff08;eclipse&#xff09;&#xff0c;Android Studio 技术说明&#xff1a; springboot mybatis android 代码注释齐全&#xff0c;没有多余代码&#xff0c;适合学习(毕设)&…