<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
rel="stylesheet"
type="text/css"
href="https://unpkg.com/view-design/dist/styles/iview.css"
/>
<link rel="stylesheet" href="./index.css" />
<script
type="text/javascript"
src="https://vuejs.org/js/vue.min.js"
></script>
<script
type="text/javascript"
src="https://unpkg.com/view-design/dist/iview.min.js"
></script>
<title>图像算法</title>
</head>
<body>
<a
class="github-fork-ribbon right-top"
href="https://github.com/zxpsuper/Demo"
title="Fork me on GitHub"
>Fork me on GitHub</a
>
<div id="app">
<div class="text-center">
<h1>小皮咖图像滤镜</h1>
<img
src="https://avatars.githubusercontent.com/u/28730619?s=460&v=4"
alt=""
class="avatar"
id="avatar"
/>
<div>
<i-select
v-model="pictureMode"
style="width:200px"
placeholder="请选择图像模式"
@on-change="selectMode"
>
<i-option
v-for="item in selectList"
:value="item.value"
:key="item.value"
>{{ item.label }}</i-option
>
</i-select>
</div>
<div style="margin: 24px 0">
<i-button
icon="ios-cloud-upload-outline"
type="primary"
@click="$refs.input.click()"
>上传图片</i-button
>
<i-button
icon="ios-cloud-download-outline"
type="primary"
@click="downloadImage"
>下载图片</i-button
>
<input
type="file"
ref="input"
@change="uploadImage"
style="display: none;"
/>
</div>
<div>
<!-- 用户原始图片 -->
<canvas
id="origin"
:width="width"
:height="height"
v-show="image"
></canvas>
<!-- 目标图片 -->
<canvas
id="new"
:width="width"
:height="height"
v-show="image"
></canvas>
</div>
</div>
</div>
<script>
function handleEdge(i, x, w) {
var m = x + i;
if (m < 0) {
m = -m;
} else if (m >= w) {
m = w + i - x;
}
return m;
}
new Vue({
el: '#app',
data: {
visible: false,
selectList: [
{
label: '灰度滤镜',
value: '0'
},
{
label: '黑白滤镜',
value: '1'
},
{
label: '反向滤镜',
value: '2'
},
{
label: '去色滤镜',
value: '3'
},
{
label: '单色滤镜',
value: '4'
},
{
label: '高斯模糊滤镜',
value: '5'
},
{
label: '怀旧滤镜',
value: '6'
},
{
label: '熔铸滤镜',
value: '7'
},
{
label: '冰冻滤镜',
value: '8'
},
{
label: '连环画滤镜',
value: '9'
},
{
label: '褐色滤镜',
value: '10'
}
],
pictureMode: '', // 图片滤镜模式
width: '', // canvas宽
height: '', // canvas高
image: null //
},
methods: {
// 选择不同模式
selectMode() {
if (!this.image) {
this.$Modal.error({
title: '错误',
content: '请上传图片先啦!!'
});
return;
}
// 为何延迟呢?若不延迟执行,下拉框会等待数据处理完成再收起,有明显的卡顿现象,尤其是高斯模糊滤镜
setTimeout(() => {
this.drawImage();
}, 17);
},
// 下载图片
downloadImage(image, name) {
if (!this.image) {
this.$Modal.error({
title: '错误',
content: '请上传图片先啦!!'
});
return;
}
var image = new Image();
var canvas = document.getElementById('new');
image.src = canvas.toDataURL();
console.log(image);
this.downLoad(image, 'suporka-image-filter.jpg');
},
// 下载
downLoad(image, name) {
const dataURL = image.src;
const link = document.createElement('a');
link.download = name;
link.href = dataURL;
link.dispatchEvent(new MouseEvent('click'));
},
// 上传图片
uploadImage(e) {
var that = this;
var file = e.target.files[0];
if (typeof FileReader === 'undefined') {
alert('您的浏览器不支持图片上传,请升级您的浏览器');
return false;
}
var image = new Image();
image.crossOrigin = 'Anonymous'; // 解决一些跨域问题
image.onload = function () {
that.width = image.width; // 设置canvas的宽
that.height = image.height; // 设置canvas的高
that.image = image;
// 等待canvas的宽高属性渲染完毕绘制canvas
that.$nextTick(() => {
that.drawOriginImage(image);
});
};
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = e => {
image.src = e.target.result;
};
},
// 画出原始图像
drawOriginImage(image) {
var canvasOrigin = document.getElementById('origin');
var ctxOrigin = canvasOrigin.getContext('2d');
var canvasNew = document.getElementById('new');
var ctxNew = canvasNew.getContext('2d');
ctxOrigin.drawImage(image, 0, 0, image.width, image.height);
ctxNew.drawImage(image, 0, 0, image.width, image.height);
},
// 画出目标图像
drawImage() {
var canvasOrigin = document.getElementById('origin');
var ctxOrigin = canvasOrigin.getContext('2d');
var canvasNew = document.getElementById('new');
var ctxNew = canvasNew.getContext('2d');
var imageData = ctxOrigin.getImageData(
0,
0,
this.width,
this.height
);
var data = imageData.data; // 获取原始图像每一个像素
this.chooseFilter(data, canvasNew, imageData);
ctxNew.putImageData(imageData, 0, 0);
},
// 根据不同的滤镜处理data
chooseFilter(data, canvas, imgData) {
if (this.pictureMode === '0') {
// 灰度
for (var i = 0; i < data.length; i += 4) {
var grey = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = grey;
}
} else if (this.pictureMode === '1') {
// 黑白滤镜
for (var i = 0; i < data.length; i += 4) {
var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg >= 100 ? 255 : 0;
}
} else if (this.pictureMode === '2') {
// 反向滤镜
for (var i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i];
data[i + 1] = 255 - data[i + 1];
data[i + 2] = 255 - data[i + 2];
}
} else if (this.pictureMode === '3') {
// 去色
for (var i = 0; i < data.length; i++) {
var avg = Math.floor(
(Math.min(data[i], data[i + 1], data[i + 2]) +
Math.max(data[i], data[i + 1], data[i + 2])) /
2
);
data[i] = data[i + 1] = data[i + 2] = avg;
}
} else if (this.pictureMode === '4') {
// 单色滤镜
for (var i = 0; i < canvas.height * canvas.width; i++) {
data[i * 4 + 2] = 0;
data[i * 4 + 1] = 0;
}
} else if (this.pictureMode === '5') {
// 高斯模糊
try {
var pixes = imgData.data,
height = imgData.height,
width = imgData.width,
radius = 5,
sigma = radius / 3;
var gaussEdge = radius * 2 + 1;
var gaussMatrix = [],
gaussSum = 0,
a = 1 / (2 * sigma * sigma * Math.PI),
b = -a * Math.PI;
for (var i = -radius; i <= radius; i++) {
for (var j = -radius; j <= radius; j++) {
var gxy = a * Math.exp((i * i + j * j) * b);
gaussMatrix.push(gxy);
gaussSum += gxy;
}
}
var gaussNum = (radius + 1) * (radius + 1);
for (var i = 0; i < gaussNum; i++) {
gaussMatrix[i] /= gaussSum;
}
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var r = (g = b = 0);
for (var i = -radius; i <= radius; i++) {
var m = handleEdge(i, x, width);
for (var j = -radius; j <= radius; j++) {
var mm = handleEdge(j, y, height);
var currentPixId = (mm * width + m) * 4;
var jj = j + radius;
var ii = i + radius;
r +=
pixes[currentPixId] *
gaussMatrix[jj * gaussEdge + ii];
g +=
pixes[currentPixId + 1] *
gaussMatrix[jj * gaussEdge + ii];
b +=
pixes[currentPixId + 2] *
gaussMatrix[jj * gaussEdge + ii];
}
}
var pixId = (y * width + x) * 4;
pixes[pixId] = ~~r;
pixes[pixId + 1] = ~~g;
pixes[pixId + 2] = ~~b;
}
}
imgData.data = pixes;
} catch (err) {
console.log(err);
}
} else if (this.pictureMode === '6') {
// 怀旧滤镜
for (var i = 0; i < imgData.height * imgData.width; i++) {
var r = imgData.data[i * 4],
g = imgData.data[i * 4 + 1],
b = imgData.data[i * 4 + 2];
var newR = 0.393 * r + 0.769 * g + 0.189 * b;
var newG = 0.349 * r + 0.686 * g + 0.168 * b;
var newB = 0.272 * r + 0.534 * g + 0.131 * b;
var rgbArr = [newR, newG, newB].map(e => {
return e < 0 ? 0 : e > 255 ? 255 : e;
});
[
imgData.data[i * 4],
imgData.data[i * 4 + 1],
imgData.data[i * 4 + 2]
] = rgbArr;
}
} else if (this.pictureMode === '7') {
// 熔铸滤镜
for (var i = 0; i < imgData.height * imgData.width; i++) {
var r = imgData.data[i * 4],
g = imgData.data[i * 4 + 1],
b = imgData.data[i * 4 + 2];
var newR = (r * 128) / (g + b + 1);
var newG = (g * 128) / (r + b + 1);
var newB = (b * 128) / (g + r + 1);
var rgbArr = [newR, newG, newB].map(e => {
return e < 0 ? 0 : e > 255 ? 255 : e;
});
[
imgData.data[i * 4],
imgData.data[i * 4 + 1],
imgData.data[i * 4 + 2]
] = rgbArr;
}
} else if (this.pictureMode === '8') {
// 冰冻滤镜
for (var i = 0; i < imgData.height * imgData.width; i++) {
var r = imgData.data[i * 4],
g = imgData.data[i * 4 + 1],
b = imgData.data[i * 4 + 2];
var newR = ((r - g - b) * 3) / 2;
var newG = ((g - r - b) * 3) / 2;
var newB = ((b - g - r) * 3) / 2;
var rgbArr = [newR, newG, newB].map(e => {
return e < 0 ? 0 : e > 255 ? 255 : e;
});
[
imgData.data[i * 4],
imgData.data[i * 4 + 1],
imgData.data[i * 4 + 2]
] = rgbArr;
}
} else if (this.pictureMode === '9') {
// 连环画滤镜
for (var i = 0; i < imgData.height * imgData.width; i++) {
var r = imgData.data[i * 4],
g = imgData.data[i * 4 + 1],
b = imgData.data[i * 4 + 2];
var newR = (Math.abs(g - b + g + r) * r) / 256;
var newG = (Math.abs(b - g + b + r) * r) / 256;
var newB = (Math.abs(b - g + b + r) * g) / 256;
var rgbArr = [newR, newG, newB];
[
imgData.data[i * 4],
imgData.data[i * 4 + 1],
imgData.data[i * 4 + 2]
] = rgbArr;
}
} else if (this.pictureMode === '10') {
// 褐色滤镜
for (var i = 0; i < imgData.height * imgData.width; i++) {
var r = imgData.data[i * 4],
g = imgData.data[i * 4 + 1],
b = imgData.data[i * 4 + 2];
var newR = r * 0.393 + g * 0.769 + b * 0.189;
var newG = r * 0.349 + g * 0.686 + b * 0.168;
var newB = r * 0.272 + g * 0.534 + b * 0.131;
var rgbArr = [newR, newG, newB];
[
imgData.data[i * 4],
imgData.data[i * 4 + 1],
imgData.data[i * 4 + 2]
] = rgbArr;
}
}
}
}
});
</script>
</body>
</html>
.github-fork-ribbon {
width: 12.1em;
height: 12.1em;
position: absolute;
overflow: hidden;
top: 0;
right: 0;
z-index: 9999;
pointer-events: none;
font-size: 13px;
text-decoration: none;
text-indent: -999999px;
}
.github-fork-ribbon.fixed {
position: fixed;
}
.github-fork-ribbon:before,
.github-fork-ribbon:after {
position: absolute;
display: block;
width: 15.38em;
height: 1.54em;
top: 3.23em;
right: -3.23em;
-webkit-box-sizing: content-box;
box-sizing: content-box;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
.github-fork-ribbon:before {
content: "";
padding: 0.38em 0;
background-color: #f06;
background-image: -webkit-gradient(
linear,
left top,
left bottom,
from(rgba(0, 0, 0, 0)),
to(rgba(0, 0, 0, 0.15))
);
background-image: -webkit-linear-gradient(
top,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.15)
);
background-image: linear-gradient(
to bottom,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.15)
);
/* Add a drop shadow */
-webkit-box-shadow: 0 0.15em 0.23em 0 rgba(0, 0, 0, 0.5);
box-shadow: 0 0.15em 0.23em 0 rgba(0, 0, 0, 0.5);
pointer-events: auto;
}
.github-fork-ribbon:after {
/* Set the text from the title attribute */
content: attr(title);
/* Set the text properties */
color: #fff;
font: 700 1em "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.54em;
text-decoration: none;
text-shadow: 0 -0.08em rgba(0, 0, 0, 0.5);
text-align: center;
text-indent: 0;
/* Set the layout properties */
padding: 0.15em 0;
margin: 0.15em 0;
/* Add "stitching" effect */
border-width: 0.08em 0;
border-style: dotted;
border-color: #fff;
border-color: rgba(255, 255, 255, 0.7);
}
.github-fork-ribbon.left-top:before,
.github-fork-ribbon.left-top:after {
right: auto;
left: -3.23em;
}
.github-fork-ribbon.left-top:before,
.github-fork-ribbon.left-top:after {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
#app {
max-width: 800px;
margin: 0 auto;
padding-top: 60px;
}
.text-center {
text-align: center;
}
.avatar {
width: 160px;
height: 160px;
margin-bottom: 24px;
margin-top: 24px;
border-radius: 50%;
border: 2px solid #ccc;
}
#origin,
#new {
width: 380px;
border: 2px solid #eee;
}
@media screen and (max-width: 800px) {
#origin,
#new {
display: block;
width: 90%;
margin: 10px auto;
border: 2px solid #eee;
}
}