<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图片的频域水印</title>
<!--两张图片格式为左浮动,处在一行-->
<style>
.InputOutput {
float: left;
margin: 10px;
max-width: 300px;
}
canvas,
img {
max-width: 300px;
}
.caption,
input {
color: #000000;
font-size: 16px;
text-align: center;
font-weight: bold;
font-family: "Adobe 宋体 Std L", serif;
}
</style>
</head>
<body>
<div>
<input type="file" id="inputFile" name="file" />
</div>
<div class="InputOutput">
<div class="caption">源图片</div>
<img id="srcImg" alt="No Image" />
</div>
<div class="InputOutput">
<div class="caption">图片的频域图</div>
<canvas id="dstImg">
</canvas>
</div>
<div class="InputOutput">
<div class="caption">加了频域水印的图片</div>
<canvas id="dstToOriginImg">
</canvas>
</div>
<div class="InputOutput">
<div class="caption">加上频域水印的图片的频域图</div>
<canvas id="dstImgWithWaterMask">
</canvas>
</div>
</body>
<!-- 异步的加载使得可以让后面的js先加载好(因为opencv.js很大),所以onload才能执行到下面的函数,如果不是异步,那么会出现openCvReady没有定义的情况-->
<script async="async" src="./opencv.js" onload="openCvReady()" type="text/javascript"></script>
<script type="text/javascript">
// 这个函数确保当opencv已经初始化完成了才执行下面的操作,测试表明,仅仅是加载成功不足以让其他js调用cv,必须使用opencv.js提供的hook:onRuntimeInitialized
function openCvReady() {
cv['onRuntimeInitialized'] = () => {
// 从url中获取当前的图片号
let page = (window.location.search)
.substring(1);
let imgElement = document.getElementById("srcImg");
imgElement.src = "../pics/pic (" + page + ").jpg";
// 也可以选择上传图片
let fileElement = document.getElementById("inputFile");
fileElement.addEventListener("change",
(e) => {
imgElement.src = URL.createObjectURL(e.target.files[0]);
},
false);
<!--添加水印-->
imgElement.onload = function() {
let destImg = getFrequencyDomain(imgElement);
cv.imshow("dstImg", destImg);
let dstToOriginImg = getImgWithWaterMask(imgElement);
cv.imshow("dstToOriginImg", dstToOriginImg);
let dstImgWithWaterMask = getFrequencyDomain(dstToOriginImg);
cv.imshow("dstImgWithWaterMask", dstImgWithWaterMask);
};
};
}
function addWaterMaskText(complexI) {
let waterMask = "blog:wbl-z.github.io"
// 设置颜色
let scalar = new cv.Scalar(0, 0, 0);
// 设置起始位置
let point = new cv.Point(40,40);
// 在图中插入文字
cv.putText(complexI, waterMask, point, cv.FONT_HERSHEY_DUPLEX, 1.0, scalar); // 1.5是插入的字体的大小
cv.flip(complexI, complexI, -1) // 翻转操作
cv.putText(complexI, waterMask, point, cv.FONT_HERSHEY_DUPLEX, 1.0, scalar);
cv.flip(complexI, complexI, -1)
}
function getImgWithWaterMask(img) { //img is not singleChannelImg
let src = cv.imread(img, cv.IMREAD_COLOR);
let imgCols = src.cols;
let imgRows = src.rows;
// 设置存储3种颜色的矩阵vector
let colors = new cv.MatVector();
// 分离图片的4个通道到vector中(后续代码都没有用到透明通道,如果需要在后续merge的时候再加上透明通道即可)
cv.split(src, colors);
let B = colors.get(0)
let G = colors.get(1)
let R = colors.get(2)
src = B;
let complexI = getDFT(src);
addWaterMaskText(complexI);
// 下面是逆傅里叶变换
let ifft = new cv.Mat();
// 逆傅里叶变换,这里的flag需要写成(cv.DFT_REAL_OUTPUT | cv.DFT_INVERSE),opencv.js没有提供idft,事实上在其他语言的opencv即使提供了idft,也是复用了dft,只是传入的flag进行了或操作,这里我直接手动进行或操作
cv.dft(complexI, ifft, (cv.DFT_REAL_OUTPUT | cv.DFT_INVERSE));
cv.normalize(ifft, ifft, 0, 1, cv.NORM_MINMAX);
// 下面把前面分离出来的颜色通道合并回去,否则图片会是灰色的
ifft.convertTo(B, cv.CV_8U, 255.0);
let toMerge = new cv.MatVector();
let res = B.roi(new cv.Rect(0, 0, imgCols, imgRows))
toMerge.push_back(res)
toMerge.push_back(G)
toMerge.push_back(R)
cv.merge(toMerge, res)
// return加了水印后转换回的图片
return res;
}
function getFrequencyDomain(img) { //src is not singleChannelImg
let src;
if (img instanceof cv.Mat) {
src = img;
} else {
src = cv.imread(img, cv.IMREAD_COLOR);
}
let colors = new cv.MatVector();
// 分离图片的4个通道到vector中(后续代码都没有用到透明通道,如果需要在后续merge的时候再加上透明通道即可)
cv.split(src, colors);
src = colors.get(0);
let complexI = getDFT(src);
// compute log(1 + sqrt(Re(DFT(img))**2 + Im(DFT(img))**2))傅立叶变换后的图片进行对数处理,结果会更加清晰
let planes = new cv.MatVector();
cv.split(complexI, planes);
cv.magnitude(planes.get(0), planes.get(1), planes.get(0));
let mag = planes.get(0);
let m1 = new cv.Mat.ones(mag.rows, mag.cols, mag.type());
cv.add(mag, m1, mag);
cv.log(mag, mag);
// crop the spectrum, if it has an odd number of rows or columns
let rect = new cv.Rect(0, 0, mag.cols & -2, mag.rows & -2);
mag = mag.roi(rect);
// 重新按照象限来排列图片,使得亮部集中在中心
let cx = mag.cols / 2;
let cy = mag.rows / 2;
let tmp = new cv.Mat();
let rect0 = new cv.Rect(0, 0, cx, cy);
let rect1 = new cv.Rect(cx, 0, cx, cy);
let rect2 = new cv.Rect(0, cy, cx, cy);
let rect3 = new cv.Rect(cx, cy, cx, cy);
let q0 = mag.roi(rect0);
let q1 = mag.roi(rect1);
let q2 = mag.roi(rect2);
let q3 = mag.roi(rect3);
// exchange 1 and 4 quadrants
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
// exchange 2 and 3 quadrants
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
// The pixel value of cv.CV_32S type image ranges from 0 to 1.归一化
cv.normalize(mag, mag, 0, 1, cv.NORM_MINMAX);
// return傅里叶转换的频域结果图片
return mag;
}
function getDFT(src) { //src is singleChannelImg
// 获取傅里叶变换时最优的尺寸(调整尺寸可以使傅里叶变换的速度更快,快速傅立叶变换要求尺寸为2的n次方)
let optimalRows = cv.getOptimalDFTSize(src.rows);
let optimalCols = cv.getOptimalDFTSize(src.cols);
let s0 = cv.Scalar.all(0);
let padded = new cv.Mat();
// 填充图片,即上面的尺寸比原图大,需要填充大的部分
cv.copyMakeBorder(src, padded, 0, optimalRows - src.rows, 0,
optimalCols - src.cols, cv.BORDER_CONSTANT, s0);
// use cv.MatVector to distribute space for real part and imaginary part
let plane0 = new cv.Mat();
padded.convertTo(plane0, cv.CV_32F);
let planes = new cv.MatVector();
let complexI = new cv.Mat();
let plane1 = new cv.Mat.zeros(padded.rows, padded.cols, cv.CV_32F);
planes.push_back(plane0);
planes.push_back(plane1);
cv.merge(planes, complexI);
// 原地傅里叶变换
cv.dft(complexI, complexI);
return complexI;
}
</script>
</h
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
watermask.zip (2个子文件)
watermask
watermask.html 8KB
opencv.js 8.3MB
共 2 条
- 1
资源评论
文火锅
- 粉丝: 0
- 资源: 4
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功