js进行数字图像处理:亮度、对比度、马赛克画笔、放大缩小、镜像、贴纸、旋转、颜色值显示
2021-02-01 17:14
标签:href draw files 判断 纹理 odi mac flex 数码 ① 亮度调整:进度条拖动实现亮度改变。 ② 对比度调整:进度条拖动实现对比度改变。 ③ 马赛克笔刷:点击图标,在画布上鼠标-点击-拖动实现局部马赛克处理。 ④ 放大:点击放大图标,实现图片1.2倍放大。可多次点击。 ⑤ 缩小:点击缩小图标,实现图片0.83倍缩小。可多次点击。 ⑥ 旋转:点击旋转图标,每次顺时针旋转角度有上方输入框决定。可多次点击。 ⑦ 垂直镜像:点击图标,实现垂直翻转。 ⑧ 水平镜像:点击图标,实现水平翻转。 ⑨ 颜色值显示:点击颜色显示图标,鼠标进入画布移动,可在color块上观测鼠标所在像素的颜色。 叠图:点击贴纸图标,在画布中点击可进行粘贴图样。 旋转的插值有点问题,但是同样的算法使用matlab效果很好。 采用的程序语言是javascript,图形化界面依靠html、css,依托于html的canvas编写。画布呈现长宽均是实际像素个数。 代码和方法有很多不完美的地方。 注:实现借鉴了很多网上帖 滑动轴 1. 色彩变换原理-亮度调整 1.1算法原理: 图象亮度是指画面的明亮程度。灰度图像中,灰度值越高则图像越亮。 原像素灰度f(i,j),转换后为g(i,j),g(i,j)=f(i,j)+b,系数影响图像的亮度。随着增加b (b>0)和减小b (b>0),图像整体的灰度值上移或者下移, 也就是图像整体变亮或者变暗。设置b为light,值域为【-255,255】 1.2亮度调整 亮度度调整公式如下: RGB’=RGB+ Light 其中RGB’代表了r,g,b各自重新计算后的值。 1.3实施方案 在界面上放置一个滑动轴,范围rgbnum为-50~50,初始值为0。即Light=rgbnum/50*255。除此,叠加对比度调整效果。 拖动条借鉴:https://blog.csdn.net/qq_36818627/article/details/81606778?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase 代码如:注:data[i]、data[i+1]、data[i+2]代表RGB。 后续判断对比度是否改变,改变则叠加。这里没有贴代码。 2. 色彩变换原理-对比度调整 2.1算法原理: 图像对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,即指一幅图像灰度反差的大小。差异范围越大代表对比越大,差异范围越小代表对比越小。 查阅了图像处理软件Photoshop的对比度调整公式。RGB三个通道分别做相同的调整运算。Contrast是调整参数,取值范围为【-255,255】 2.2对比度调整 对比度调整公式如下: RGB’=RGB+(RGB-Average)*Contrast/255 其中nRGB’代表了r,g,b各自重新计算后的值。Average是平均亮度,大部分图片是在100~150之间。取127,与实际相近。 所以当Contrast为0时,RGB值不变,表示当前的一个对比度。 为-255时,RGB都为127,亮度都一样,没有差别,呈灰色。 为255时,在Average之上的像素点会变得更亮,反之变得更暗。 2.3实施方案 在界面上放置一个滑动轴,范围rgbnum为-50~50,初始值为0。即Contrast/255=rgbnum/50。除此,叠加亮度调整效果。 代码如:注:data[i]、data[i+1]、data[i+2]代表RGB。 3. 马赛克笔功能 3.1算法原理: 马赛克像素的rgb=这块马赛克内所有像素rgb的平均值。 3.2马赛克笔设置 为了方便查看效果,设定一块马赛克包含(3*2+1)*(3*2+1)=49个像素点。一块马赛克包含的像素点越多,图像模糊化越重。以鼠标点击为中心,一次涂抹马赛克操作包含(3*2)^2个马赛克。 3.3实施方案 处理马赛克画笔的顺序是,鼠标点击(绘制36个马赛克),移动超出一个马赛克1/2长,便会进行下一区域马赛克涂抹。鼠标抬起,绘制完成。 方法借鉴: https://www.jianshu.com/p/3d2a8bd83191 4. 放大 4.1算法原理: 最简单的方式,如果需要将原图像放大为k倍,则将原图像中的每个像素值,填在新图像中对应的k*k大小的子块中。 4.2放大算法 设原图像大小为M*N,放大为k1M*k2N,(k1>1,k2>1)。 算法步骤如下: 1)设旧图像是F(i,j), i=1,2,…,M, j=1,2,…,N. 新图像是G(i’,j’), i’=1,2,…,k1M, j’=1,2,…,k2N. 2)G(i’,j’)=F(c1*I’,c2*j’) c1=1/k1 c2=1/k2 4.3实施方案 先扩展画布,然后进行等比例填充像素值。 单次放大设置为图像的1.2倍,通过修改代码参数,可修改倍数值,为了界面美观,没有设置可调节倍数。通过点击放大按钮,进行放大图片。 5.缩小 5.1算法原理: 图像的缩小从物理意义上来说,是将描述图像的每个像素的物理尺寸缩小相应的倍数就可以完成;但如果像素的物理尺寸不允许改变,从数码技术的角度来看,图像的缩小实际上就是通过减少像素个数来实现的。 图像缩小实际上就是对原有的多个数据进行挑选或处理,获得期望 缩小尺寸的数据,并且尽量保持原有的特征不丢失。 最简单的方法就是等间隔地选取数据。 5.2缩小算法 设原图像大小为M*N,缩小为k1M*k2N, (k1
算法步骤如下: 1) 设原图为F(i,j), i=1,2,…,M, j=1,2,…,N. 压缩后图像是G(i’,j’), i’=1,2,…,k1M, j’=1,2,…,k2N. 2)G(i’,j’)=F(c1*i’,c2*j’) 其中,c1=1/k1 c2=1/k2 5.3实施方案 先缩小画布,然后进行等间隔取像素值。 单次缩小设置为图像的0.83倍,通过修改代码参数,可修改倍数值,为了界面美观,没有设置可调节倍数。通过点击缩小按钮,进行缩小图片。 6. 旋转 6.1旋转原理: 以图像原点逆时针旋转θ角, 设原图像的坐标点为(x0,y0), 则旋转后图像坐标点的计算公式如下: 6.2旋转方案 图像旋转之前的两个问题: 1)图像旋转计算出的值为小数,而坐标值为正整数。 2)图像旋转计算的结果值所在范围与原来的值所在的范围不同。 因此图像旋转需要做扩大画布,取整处理,平移处理 。 图像旋转之后的两个问题: 1)像素的排列不是完全按照原有的相邻关系。这是因为相邻像素之间只能有8个方向(相邻为45度) 2)会出现许多的空洞点。 因此图像旋转之后需要进行插值处理。 6.3双线性插值 双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。 红色的数据点与待插值得到的绿色点 假如我们想得到未知函数 f 在点 P = (x, y) 的值,假设我们已知函数 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四个点的值。 首先在 x 方向进行线性插值,得到: 然后在 y 方向进行线性插值,得到: 这样就得到所要的结果 f(x, y), 如果选择一个坐标系统使得 f 的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为: f(x,y)=f(0,0)(1-x)(1-y)+f(1,0)x(1-y)+f(0,1)(1-x)y+f(1,1)xy
插值空洞太多,效果不好。。。 7.镜像 7.1垂直镜像原理: 垂直镜像计算公式如下:(图像大小为M*N) 8. 水平镜像 水平镜像计算公式如下:(图像大小为M*N)
因为表示图像的矩阵坐标不能为负,因此需要在进行镜像计算之后再进行坐标的平移。
9. 颜色值显示 原理:鼠标获取鼠标在画布上的位置。通过数据定位获取rgb值显示出来。 截图鼠标截不进去。。。 10.叠图贴纸
原理:在画布上覆盖原有像素。 html布局代码: css js:var rgbnum=parseInt(barleft/(scroll.offsetWidth-bar.offsetWidth) * 100-50)
ptxt.innerHTML = parseInt(rgbnum) + "%";//滚动条获取亮度变化值。
//data0储存改变亮度对比度之前的图像,防止调节亮度过低或过高只有黑白色了。
for(var i = 0; i 255)
data[i]=255;
else if(parseInt(data0[i]+parseInt(rgbnum)/50*255)255)
data[i+1]=255;
else if(parseInt(data0[i+1]+parseInt(rgbnum)/50*255)255)
data[i+2]=255;
else if(parseInt(data0[i+2]+parseInt(rgbnum)/50*255)
var rgbnum=parseInt(barleft0/(scroll0.offsetWidth-bar0.offsetWidth) * 100-50)
ptxt0.innerHTML = parseInt(rgbnum) + "%";
var avg=127;
//RGB = RGB + (RGB - avg) * Contrast / 255
for(var i = 0; i ) {
if(parseInt(data0[i]+(data0[i]-127)*parseInt(rgbnum)/50)>255)
data[i]=255;
else if(parseInt(data0[i]+(data0[i]-127)*parseInt(rgbnum)/50);
else data[i] = data0[i]+(data0[i+1]-127)*parseInt(rgbnum)/50;
if(parseInt(data0[i+1]+(data0[i+1]-127)*parseInt(rgbnum)/50)>255)
data[i+1]=255;
else if(parseInt(data0[i+1]+(data0[i+1]-127)*parseInt(rgbnum)/50);
else data[i+1] = data0[i+1]+ (data0[i+1]-127)*parseInt(rgbnum)/50;
if(parseInt(data0[i+2]+(data0[i+2]-127)*parseInt(rgbnum)/50)>255)
data[i+2]=255;
else if(parseInt(data0[i+2]+(data0[i+2]-127)*parseInt(rgbnum)/50);
else data[i+2] = data0[i+2]+ (data0[i+2]-127)*parseInt(rgbnum)/50;
}
for(var i = 0; i ) {
var dx=i%oCanvas.width;
var dy=(i-dx)/oCanvas.width;
var mx=parseInt(dx/1.2);
var my=parseInt(dy/1.2);
if(parseInt(mx)>parseInt(width))
{
mx=width;
}
if(parseInt(my)>parseInt(height)){
my=height;
}
data[(dy*oCanvas.width+dx)*4+0]=datatemp[(my*width+mx)*4+0];
data[(dy*oCanvas.width+dx)*4+1]=datatemp[(my*width+mx)*4+1];
data[(dy*oCanvas.width+dx)*4+2]=datatemp[(my*width+mx)*4+2];
data[(dy*oCanvas.width+dx)*4+3]=datatemp[(my*width+mx)*4+3];
}
for(var i = 0; i ) {
var dx=i%oCanvas.width;
var dy=(i-dx)/oCanvas.width;
var mx=parseInt(dx*1.2);
var my=parseInt(dy*1.2);
if(parseInt(mx)>parseInt(width))
{
mx=width;
}
if(parseInt(my)>parseInt(height)){
my=height;
}
data[(dy*oCanvas.width+dx)*4+0]=datatemp[(my*width+mx)*4+0];
data[(dy*oCanvas.width+dx)*4+1]=datatemp[(my*width+mx)*4+1];
data[(dy*oCanvas.width+dx)*4+2]=datatemp[(my*width+mx)*4+2];
data[(dy*oCanvas.width+dx)*4+3]=datatemp[(my*width+mx)*4+3];
}
输入逆时针旋转角度,点击旋转,便可实现旋转功能。
var text=document.getElementById("id_xz0").value;
console.log(text);
if(text==NaN||text==undefined ||text==0 ||text==null){
alert("填入角度");
}
var num0=parseInt(text);
console.log(num0);
imageData=ctx.getImageData(0,0,oCanvas.width,oCanvas.height);
data=imageData.data;
var datatemp0=new Array(data.length);
var datatemp1=new Array(data.length);
for(var i=0;i
for(var i=0;i
for(var i=0;i
oCanvas.onmousemove=function(ev){
var datatemp=imageData0.data;
var ev=window.event||ev;
var dx = ev.pageX-oCanvas.offsetLeft;
var dy = ev.pageY-oCanvas.offsetTop;
var pp = parseInt(dy*oCanvas.width+dx);
var temp0=parseInt(pp*4);
var R0 = parseInt(datatemp[temp0+0]);
var G0 = parseInt(datatemp[temp0+1]);
var B0 = parseInt(datatemp[temp0+2]);
display.style.background="rgb("+R0+","+G0+","+B0+")";
}
点击贴纸按钮,蓝色选中后,便可以直接在画布中任意叠加。
var imgObj=new Image();
imgObj.crossOrigin="Anonymous";
imgObj.src="./good.png";
var img_width = 50;
var img_height = 50;
oCanvas.onmousedown=function(ev){
console.log("贴图");
var ev=window.event||ev;
var dx = ev.pageX-oCanvas.offsetLeft;
var dy = ev.pageY-oCanvas.offsetTop;
ctx.drawImage(imgObj, parseInt(dx-(img_width)/2), parseInt(dy-(img_height)/2),50,50);
html>
head>
title>图像处理大作业title>
link rel="stylesheet" href="./style.css">
head>
body>
div class="background">
div class="body-wrapping">
div class="container0">
div>
p>原图:p>
input type="file" id="input-upload" accept=".jpg,.jpeg,.png,.gif,.svg" placeholder="上传本地图片" />
div class="">
img id="input-img" class="">
div>
div>
div>
div class="container0">
div class="container">
p>亮度调节:p>
div class="scroll" id="scroll">
div class="bar" id="bar">
div>
div class="mask" id="mask">div>
div>
p id="light">0p>
div>
div class="container">
p>对比度调节:p>
div class="scroll" id="scroll0">
div class="bar" id="bar0">
div>
div class="mask" id="mask0">div>
div>
p id="compare">0p>
div>
div>
input id="id_xz0" class="text" placeholder="角度°"/>
div class="icons">
li id="msk" onclick="drawmsk(0)">img src="./msk.png" id="id_msk" class="icon-wrapping">li>
li id="fd" onclick="zoomin(1)">img src="./fd.png" id="id_fd" class="icon-wrapping">li>
li id="sx" onclick="zoomout(2)">img src="./sx.png" id="id_sx" class="icon-wrapping">li>
li id="xz" onclick="rotate(3)">img src="./xz.png" id="id_xz" class="icon-wrapping">li>
li id="jx" onclick="mirror(4)">img src="./hjx.png" id="id_jx" class="icon-wrapping">li>
li id="hjx" onclick="hmirror(5)">img src="./jx.png" id="id_hjx" class="icon-wrapping">li>
div>
div class="icons">
li id="right" onclick="addcanvas0(6)">img src="./right.png" id="img_right" class="icon-wrapping">li>
li id="wrong" onclick="addcanvas1(7)">img src="./wrong.png" id="img_wrong" class="icon-wrapping">li>
li id="comeon" onclick="addcanvas2(8)">img src="./comeon.png" id="img_comeon" class="icon-wrapping">li>
li id="good" onclick="addcanvas3(9)">img src="./good.png" id="img_good" class="icon-wrapping">li>
li id="displayxy" onclick="addcanvas4(10)">div class="icon-wrapping icon-text" id="img_displayxy">颜色显示div>li>
li id="display">div class="icon-wrapping icon-text" id="img_display">colordiv>li>
div>
p>处理后图像:p>
div class="container0">
div class="">
div class="img-container" >
canvas id="my-canvas" width="600" height="600">canvas>
div>
div>
div>
a href="void(0);" id="download">点我保存图片a>
div>
div>
script src="./draw.js">script>
body>
html>
body {
font-family: ‘JetBrains Mono Medium‘;
display: flex;
align-items: center;
justify-content: center;
background-color: #0e92b3;
}
li{
list-style-type: none;
}
#input-upload {
width: 200px;
}
.background{
width: 100%;
display: flex;
padding-top: 35px;
padding-bottom: 100px;
align-items: center;
justify-content: center;
}
.body-wrapping{
background-color: rgb(41, 45, 62);
width: 80%;
color: #fff;
align-items: center;
}
/* 图片背景纹理 */
.img-container {
font-size: 0;
background-color: #000;
background-size: 10px 10px;
}
.img{
width: 200px;
height: auto;
}
.img-display{
width: 200px;
height: auto;
}
.err {
border: 1px solid red;
}
.container0{
color: #fff;
display:flex;
justify-content: space-around;
}
.container{
width:200px;
align-items: center;
justify-content: center;
}
.text{
width: 40px;
margin-left: 285px;
}
.icons{
display:flex;
justify-content: space-around;
}
.icon-wrapping{
width: 40px;
height: 40px;
background-color: #fff;
padding: 8%;
margin-bottom: 20px;
margin-top: 10px;
border-radius: 10px;
}
.icon-wrapping:hover{
background-color: rgb(41, 45, 62);
}
.icon-text{
color: red;
text-align: center;
}
.scroll{
margin:30px;
width: 200px;
height: 5px;
background: #ccc;
position: relative;
}
.bar{
width: 10px;
height: 20px;
background: #369;
position: absolute;
top: -7px;
left: 95;
cursor: pointer;
}
.mask{
position: absolute;
left: 0;
top: 0;
background: #369;
width: 0;
height: 5px;
}
1 const oUpload = document.getElementById(‘input-upload‘);
2 const oImg = document.getElementById(‘input-img‘);
3 oUpload.onchange = loadFile;
4 //oInput.onchange = loadUrl;
5
6 // 读取本地文件
7 function loadFile(e) {
8 const file = e.target.files[0];
9 const reader = new FileReader();
10 reader.onload = onFileLoad;
11 reader.readAsDataURL(file);
12 }
13
14 // 加载图片数据
15 function onFileLoad(src) {
16 oImg.onload = function() {
17 onImageLoad(oImg)// 这里使用图像数据
18 };
19 oImg.src = (src.target ? src.target.result : src);
20 }
21
22 const oCanvas = document.getElementById(‘my-canvas‘);
23 var ctx;
24 var width;
25 var height;
26 var imageData;
27 var data;
28 var imageData0;//原图,防止亮度、对比度调节不可逆
29 // 上面读取资源的操作后,将图像画到canvas上
30 function onImageLoad(img) {
31 width =oCanvas.width= img.naturalWidth || img.width;
32 height =oCanvas.height= img.naturalHeight || img.height;
33 ctx = oCanvas.getContext("2d");
34
35 ctx.drawImage(img, 0, 0,width,height);
36 // 获取画布像素信息
37 imageData = ctx.getImageData(0, 0, width, height);
38 data = imageData.data;
39 console.log(data);
40 imageData0=ctx.getImageData(0, 0, width, height);
41 }
42
43 //亮度调节,没有调很暗
44 var scroll = document.getElementById(‘scroll‘);
45 var bar = document.getElementById(‘bar‘);
46 var mask = document.getElementById(‘mask‘);
47 var ptxt = document.getElementsByTagName(‘p‘)[2];
48 var barleft = 0;
49 var data1 = 0; //获取调完亮度后的图片数据
50
51 //对比度调节
52 var scroll0 = document.getElementById(‘scroll0‘);
53 var bar0 = document.getElementById(‘bar0‘);
54 var mask0 = document.getElementById(‘mask0‘);
55 var ptxt0 = document.getElementsByTagName(‘p‘)[4];
56 var barleft0 = 0;
57 var data2;//获取调完对比后的图片数据
58
59 var lastImgArr=[];//设置缓存,方便后续的撤销功能
60
61 //亮度调节
62 bar.onmousedown = function(event){
63 if(data==undefined){
64 alert("请输入图片");
65 }
66 var event = event || window.event;
67 var leftVal = event.clientX - this.offsetLeft;
68 var that = this;
69 var data0=imageData0.data;
70 document.onmousemove = function(event){
71 var event = event || window.event;
72 barleft = event.clientX - leftVal;
上一篇:nodejs_fs模块相关练习1
下一篇:Netty框架
文章标题:js进行数字图像处理:亮度、对比度、马赛克画笔、放大缩小、镜像、贴纸、旋转、颜色值显示
文章链接:http://soscw.com/index.php/essay/49575.html