Repository: zy445566/node-digital-watermarking
Branch: master
Commit: 302d71d773d7
Files: 10
Total size: 10.9 MB
Directory structure:
gitextract_5caq41ia/
├── .github/
│ └── workflows/
│ └── npmpublish.yml
├── .gitignore
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── lib.js
├── opencv.js
├── package.json
└── test/
└── test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/npmpublish.yml
================================================
name: Node.js Package
on:
push:
branches:
- master
jobs:
build-node-8:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 8
- run: npm ci
- run: npm test
build-node-10:
needs: build-node-8
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 10
- run: npm ci
- run: npm test
build-node-12:
needs: build-node-10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm test
publish-npm:
needs: build-node-12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# my
.vscode
build
*.heapsnapshot
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 ZhangYuan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# node-digital-watermarking
[A digital watermark is a kind of marker covertly embedded in a noise-tolerant signal such as an audio, video or image data. It is typically used to identify ownership of the copyright of such signal. "Watermarking" is the process of hiding digital information in a carrier signal; the hidden information should, but does not need to, contain a relation to the carrier signal. Digital watermarks may be used to verify the authenticity or integrity of the carrier signal or to show the identity of its owners. It is prominently used for tracing copyright infringements and for banknote authentication.](https://en.wikipedia.org/wiki/Digital_watermarking)
[数字水印(Digital Watermarking)技术是将一些标识信息(即数字水印)直接嵌入数字载体当中(包括多媒体、文档、软件等)或是间接表示(修改特定区域的结构),且不影响原载体的使用价值,也不容易被探知和再次修改。但可以被生产方识别和辨认。通过这些隐藏在载体中的信息,可以达到确认内容创建者、购买者、传送隐秘信息或者判断载体是否被篡改等目的。数字水印是保护信息安全、实现防伪溯源、版权保护的有效办法,是信息隐藏技术研究领域的重要分支和研究方向。](https://baike.baidu.com/item/%E6%95%B0%E5%AD%97%E6%B0%B4%E5%8D%B0/722667)
# package install
```
npm install digital-watermarking
```
This package is only used for the Node.js, if you are using Web, use [web-digital-watermarking](https://github.com/zy445566/web-digital-watermarking).
# Sample Use
```js
const dw = require('digital-watermarking');
//EnCode Image add digital watermarking
let srcFileName = "srcImg.png";
let watermarkText = "github.com/zy445566";
let fontSize = 1.1;
let enCodeFileName = "enCode.png";
async function run() {
await dw.transformImageWithText(srcFileName,watermarkText,fontSize,enCodeFileName);
//DeCode Image get digital watermarking
let deCodeFileName = "deCode.png";
await dw.getTextFormImage(enCodeFileName,deCodeFileName);
}
run()
```
# Result
## enCode.png

## deCode.png

================================================
FILE: index.d.ts
================================================
import Jimp from "jimp/*";
declare class DigitalWatermarking {
static transformImageWithText(
srcFileName:string,watermarkText:string,
fontSize:number,enCodeFileName:string
):Promise;
static transformImageBufferWithText(srcBuffer:Buffer,watermarkText:string,fontSize:number):Promise;
static getTextFormImage(enCodeFileName:string,deCodeFileName:string):Promise;
static getTextFormImageBuffer(enCodeBuffer:Buffer):Promise;
}
export function transformImageWithText(
srcFileName:string,watermarkText:string,
fontSize:number,enCodeFileName:string
):Promise;
export function transformImageBufferWithText(srcBuffer:Buffer,watermarkText:string,fontSize:number):Promise;
export function getTextFormImage(enCodeFileName:string,deCodeFileName:string):Promise;
export function getTextFormImageBuffer(enCodeBuffer:Buffer):Promise;
================================================
FILE: index.js
================================================
const lib = require('./lib.js');
const path = require('path');
const fs = require('fs');
class DigitalWatermarking{
static getConfig() {
return lib.getConfig()
}
static setConfig({opencvJsPath}) {
return lib.setConfig({opencvJsPath})
}
static getAbsoluteFilePath(filePath)
{
return path.isAbsolute(filePath)?filePath:path.join(process.cwd(),filePath);
}
static existsFilePath(filePath)
{
if(!fs.existsSync(filePath)){throw new Error(`not file in ${filePath}`);}
}
static isBuffer(fileBuf)
{
if((!(fileBuf instanceof Buffer))) {
throw new Error(`input not Buffer`);
}
}
static async transformImageWithText(srcFileName,watermarkText,fontSize,enCodeFileName)
{
const srcFilePath = DigitalWatermarking.getAbsoluteFilePath(srcFileName);
DigitalWatermarking.existsFilePath(srcFilePath);
return await lib.transformImageWithText(
srcFilePath,
watermarkText,fontSize,
DigitalWatermarking.getAbsoluteFilePath(enCodeFileName)
);
}
static async transformImageBufferWithText(srcBuffer,watermarkText,fontSize)
{
DigitalWatermarking.isBuffer(srcBuffer);
return await lib.transformImageWithText(
srcBuffer,
watermarkText,fontSize,
);
}
static async getTextFormImage(enCodeFileName,deCodeFileName)
{
const enCodeFilePath = DigitalWatermarking.getAbsoluteFilePath(enCodeFileName);
DigitalWatermarking.existsFilePath(enCodeFilePath);
return await lib.getTextFormImage(
enCodeFileName,
DigitalWatermarking.getAbsoluteFilePath(deCodeFileName)
);
}
static async getTextFormImageBuffer(enCodeBuffer) {
DigitalWatermarking.isBuffer(enCodeBuffer);
return await lib.getTextFormImage(
enCodeBuffer
);
}
}
module.exports = DigitalWatermarking;
================================================
FILE: lib.js
================================================
const Jimp = require('jimp');
const requireVm = require('require-vm');
let libConfig={
opencvJsPath:''
};
function getConfig() {
return libConfig
}
function setConfig(configData) {
libConfig = configData
}
function isReadyFunc () {
return new Promise((reslove,reject)=>{
const context = {
module:{exports:{}},
Module:{
onRuntimeInitialized() {
context.cv = context.module.exports();
const cv = context.cv;
cv.idft = function(src, dst, flags, nonzero_rows ) {
cv.dft( src, dst, flags | cv.DFT_INVERSE, nonzero_rows );
}
return reslove(context);
},
onAbort() {
return reject(new Error('loading opencv error'))
}
},
print:console.log
}
requireVm(libConfig.opencvJsPath || './opencv.js',context,{},{},true)
})
}
function shiftDFT(cv, mag) {
let rect = new cv.Rect(0, 0, mag.cols & (-2), mag.rows & (-2));
mag.roi(rect);
let cx = mag.cols / 2;
let cy = mag.rows / 2;
let q0 = mag.roi(new cv.Rect(0, 0, cx, cy));
let q1 = mag.roi(new cv.Rect(cx, 0, cx, cy));
let q2 = mag.roi(new cv.Rect(0, cy, cx, cy));
let q3 = mag.roi(new cv.Rect(cx, cy, cx, cy));
let tmp = new cv.Mat();
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
tmp.delete()
q0.delete()
q1.delete()
q2.delete()
q3.delete()
}
function getBlueChannel(cv, image)
{
let nextImg = image;
let channel = new cv.MatVector();
cv.split(nextImg, channel);
return channel.get(0);
}
function getDftMat(cv, padded)
{
let planes = new cv.MatVector();
planes.push_back(padded);
let matZ = new cv.Mat.zeros(padded.size(), cv.CV_32F)
planes.push_back(matZ);
let comImg = new cv.Mat();
cv.merge(planes,comImg);
cv.dft(comImg, comImg);
matZ.delete();
return comImg;
}
function addTextByMat(cv, comImg,watermarkText,point,fontSize)
{
cv.putText(comImg, watermarkText, point, cv.FONT_HERSHEY_DUPLEX, fontSize, cv.Scalar.all(0),2);
cv.flip(comImg, comImg, -1);
cv.putText(comImg, watermarkText, point, cv.FONT_HERSHEY_DUPLEX, fontSize, cv.Scalar.all(0),2);
cv.flip(comImg, comImg, -1);
}
function transFormMatWithText(cv, srcImg, watermarkText,fontSize) {
let padded = getBlueChannel(cv, srcImg);
padded.convertTo(padded, cv.CV_32F);
let comImg = getDftMat(cv, padded);
// add text
let center = new cv.Point(padded.cols/2, padded.rows/2);
addTextByMat(cv, comImg,watermarkText,center,fontSize);
let outer = new cv.Point (45, 45);
addTextByMat(cv, comImg,watermarkText,outer,fontSize);
//back image
let invDFT = new cv.Mat();
cv.idft(comImg, invDFT, cv.DFT_SCALE | cv.DFT_REAL_OUTPUT, 0);
let restoredImage = new cv.Mat();
invDFT.convertTo(restoredImage, cv.CV_8U);
let backPlanes = new cv.MatVector();
cv.split(srcImg, backPlanes);
// backPlanes.erase(backPlanes.get(0));
// backPlanes.insert(backPlanes.get(0), restoredImage);
backPlanes.set(0,restoredImage)
let backImage = new cv.Mat();
cv.merge(backPlanes,backImage);
padded.delete();
comImg.delete();
invDFT.delete();
restoredImage.delete()
return backImage;
}
function getTextFormMat(cv, backImage) {
let padded= getBlueChannel(cv, backImage);
padded.convertTo(padded, cv.CV_32F);
let comImg = getDftMat(cv, padded);
let backPlanes = new cv.MatVector();
// split the comples image in two backPlanes
cv.split(comImg, backPlanes);
let mag = new cv.Mat();
// compute the magnitude
cv.magnitude(backPlanes.get(0), backPlanes.get(1), mag);
// move to a logarithmic scale
let matOne = cv.Mat.ones(mag.size(), cv.CV_32F)
cv.add(matOne, mag, mag);
cv.log(mag, mag);
shiftDFT(cv, mag);
mag.convertTo(mag, cv.CV_8UC1);
cv.normalize(mag, mag, 0, 255, cv.NORM_MINMAX, cv.CV_8UC1);
padded.delete();
comImg.delete();
matOne.delete();
return mag;
}
function matToBuffer(cv, mat){
if(!(mat instanceof cv.Mat)){
throw new Error("Please input the valid new cv.Mat instance.");
}
var img=new cv.Mat();
var depth=mat.type()%8;
var scale=depth<=cv.CV_8S?1:depth<=cv.CV_32S?1/256:255;
var shift=depth===cv.CV_8S||depth===cv.CV_16S?128:0;
mat.convertTo(img,cv.CV_8U,scale,shift);
switch(img.type()){
case cv.CV_8UC1:cv.cvtColor(img,img,cv.COLOR_GRAY2RGBA);break;
case cv.CV_8UC3:cv.cvtColor(img,img,cv.COLOR_RGB2RGBA);break;
case cv.CV_8UC4:break;
default:throw new Error("Bad number of channels (Source image must have 1, 3 or 4 channels)");
}
var imgData=Buffer.from(img.data);
img.delete()
return imgData
}
async function transformImageWithText(srcFileName,watermarkText,fontSize,enCodeFileName='') {
const context = await isReadyFunc ()
const cv = context.cv;
if((typeof srcFileName)!='string' && (!(srcFileName instanceof Buffer))) {
throw new Error('fileName must be string or Buffer')
}
if((typeof watermarkText)!='string') {
throw new Error('waterMarkText must be string')
}
if((typeof fontSize)!='number') {
throw new Error('fontSize must be number')
}
if((typeof enCodeFileName)!='string') {
throw new Error('outFileName must be string')
}
let jimpSrc = await Jimp.read(srcFileName);
let srcImg = new cv.matFromImageData(jimpSrc.bitmap);
if (srcImg.empty()){throw new Error("read image failed");}
let comImg = transFormMatWithText(cv, srcImg, watermarkText, fontSize);
const imgRes = new Jimp({
width: comImg.cols,
height: comImg.rows,
data: matToBuffer(cv, comImg)
});
srcImg.delete();
comImg.delete();
if(enCodeFileName) {
return await imgRes.writeAsync(enCodeFileName);
} else {
return imgRes
}
}
async function getTextFormImage(enCodeFileName,deCodeFileName='') {
const context = await isReadyFunc ()
const cv = context.cv;
if((typeof enCodeFileName)!='string' && (!(enCodeFileName instanceof Buffer))) {
throw new Error('fileName must be string or Buffer')
}
if((typeof deCodeFileName)!='string') {
throw new Error('backFileName must be string')
}
let jimpSrc = await Jimp.read(enCodeFileName);
let comImg = new cv.matFromImageData(jimpSrc.bitmap);
let backImage = getTextFormMat(cv, comImg);
const imgRes = await new Jimp({
width: backImage.cols,
height: backImage.rows,
data: matToBuffer(cv, backImage)
})
comImg.delete();
backImage.delete();
if(deCodeFileName) {
return await imgRes.writeAsync(deCodeFileName);
} else {
return imgRes
}
}
module.exports.transformImageWithText = transformImageWithText;
module.exports.getTextFormImage = getTextFormImage;
module.exports.getConfig = getConfig;
module.exports.setConfig = setConfig;
================================================
FILE: opencv.js
================================================
[File too large to display: 10.9 MB]
================================================
FILE: package.json
================================================
{
"name": "digital-watermarking",
"version": "1.1.15",
"description": "A digital watermark is a kind of marker covertly embedded in a noise-tolerant signal such as an audio, video or image data. It is typically used to identify ownership of the copyright of such signal. \"Watermarking\" is the process of hiding digital information in a carrier signal; the hidden information should, but does not need to, contain a relation to the carrier signal. Digital watermarks may be used to verify the authenticity or integrity of the carrier signal or to show the identity of its owners. It is prominently used for tracing copyright infringements and for banknote authentication.",
"main": "index.js",
"scripts": {
"test": "node ./test/test.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/zy445566/node-digital-watermarking.git"
},
"keywords": [
"digital",
"watermark",
"embedded",
"hidden"
],
"author": "zy445566",
"license": "MIT",
"gypfile": true,
"bugs": {
"url": "https://github.com/zy445566/node-digital-watermarking/issues"
},
"homepage": "https://github.com/zy445566/node-digital-watermarking#readme",
"dependencies": {
"@types/node": "^14.0.12",
"jimp": "^0.12.1",
"require-vm": "^1.0.8"
}
}
================================================
FILE: test/test.js
================================================
const dw = require('../index');
const path = require('path');
const fs = require('fs');
const Jimp = require('jimp');
function getAbsolutePath(fileName) {
return path.join(__dirname,fileName)
}
//EnCode Image add digital watermarking
let srcFileName = getAbsolutePath("srcImg.png");
let watermarkText = "github.com/zy445566";
let fontSize = 1.1;
let enCodeFileName = getAbsolutePath("enCode.png");
async function run() {
dw.setConfig({opencvJsPath:path.join(__dirname,'../opencv.js')})
await dw.transformImageWithText(srcFileName,watermarkText,fontSize,enCodeFileName);
//DeCode Image get digital watermarking
let deCodeFileName = getAbsolutePath("deCode.png");
await dw.getTextFormImage(enCodeFileName,deCodeFileName);
// let startTime = new Date().getTime();
// for(let i=0;i<1000;i++) {
const enCodeFileRes = await dw.transformImageBufferWithText(fs.readFileSync(srcFileName),watermarkText,fontSize);
const deCodeFileRes = await dw.getTextFormImageBuffer(fs.readFileSync(enCodeFileName));
// console.log(enCodeFileRes instanceof Jimp,deCodeFileRes instanceof Jimp, i)
// }
// console.log('runTime',new Date().getTime()-startTime);
}
run()