JavaScriptで美肌フィルタを適用|バイラテラルフィルタで画像を滑らかに
バイラテラルフィルタを適用すると、シミ、そばかす等が薄くなり美肌になります。
適用回数を増やすと効果がありますが、処理がかなり重いので注意してください。
「Pexels」様の画像を使用させていただいています。
https://www.pexels.com/ja-jp/photo/1405872/
使用例
ソースコード
<img src="./imgs/js_bilateral.jpg" id="s" style="width:90%;max-width:400px;height:auto;" width="600" height="400">
<img id="d" style="width:90%;max-width:400px;height:auto;" width="600" height="400">
<button onclick='bilateral_1()'>バイラテラルフィルタを4回適用</button>
<script>
function bilateral_1(){
new TMamBilateral(
document.getElementById("s"), //加工前画像が表示されているimgタグ
document.getElementById("d"), //加工後画像の適用先imgタグ
4 //バイラテラルフィルタを4回適用
)
}
</script>
<button onclick='bilateral_2()'>バイラテラルフィルタを10回適用</button><br>
<script>
function bilateral_2(){
new TMamBilateral(
"./imgs/js_bilateral.jpg", //加工前画像のURL指定
document.getElementById("d"), //加工後画像の適用先imgタグ
10 //バイラテラルフィルタを10回適用
)
}
</script>
<script>
class TMamBilateral{
#gauss;#s;#d;
constructor(src,dest,count=5){
this.#gauss=[
[ 1, 4, 6, 4, 1],
[ 4,16,24,16, 4],
[ 6,24,36,24, 6],
[ 4,16,24,16, 4],
[ 1, 4, 6, 4, 1],
];
this.#s=src;
this.#d=dest;
this.sdata;
this.gray=[];;
this.can;
this.ctx;
this.count=count;
if(
typeof this.#s==="object" &&
this.#s.nodeType===1 &&
typeof this.#s.style==="object" &&
this.#s.tagName==="IMG"
){
//console.log("IMG");
this.can=document.createElement("canvas");
this.w=this.#s.naturalWidth;
this.h=this.#s.naturalHeight;
this.can.width=this.w;
this.can.height=this.h;
this.ctx=this.can.getContext("2d",{willReadFrequently:true});
this.ctx.drawImage(this.#s,0,0);
this.sdata=this.ctx.getImageData(0,0, this.can.width, this.can.height);
this.ddata=new ImageData(this.can.width, this.can.height);
for(let i=0;i<this.count;i++){
this.getGray();
this.bilateral(this.sdata.data, this.ddata.data);
this.sdata.data.set(this.ddata.data);
}
this.ctx.putImageData(this.ddata,0,0);
dest.src=this.can.toDataURL("image/png");
//document.body.appendChild(this.can);
}else if(typeof this.#s==="string"||this.#s instanceof String){
//console.log("string");
let img=new Image();
img.onload=function(){
this.can=document.createElement("canvas");
this.w=img.naturalWidth;
this.h=img.naturalHeight;
this.can.width=this.w;
this.can.height=this.h;
this.ctx=this.can.getContext("2d",{willReadFrequently:true});
this.ctx.drawImage(img,0,0);
this.sdata=this.ctx.getImageData(0,0, this.can.width, this.can.height);
this.ddata=new ImageData(this.can.width, this.can.height);
for(let i=0;i<this.count;i++){
this.getGray();
this.bilateral(this.sdata.data, this.ddata.data);
this.sdata.data.set(this.ddata.data);
}
this.ctx.putImageData(this.ddata,0,0);
dest.src=this.can.toDataURL("image/png");
//document.body.appendChild(this.can);
}.bind(this);
img.src=this.#s;
}else{
return false;
}
}
getGray(){
for(let j=0;j<this.h;j++){
for(let i=0;i<this.w;i++){
let v=Math.round(
0.299*this.sdata.data[(i+j*this.w)*4+0]+
0.587*this.sdata.data[(i+j*this.w)*4+1]+
0.114*this.sdata.data[(i+j*this.w)*4+2]
);
if(v>255){v=255;}
this.gray[i+j*this.w]=v;
}
}
}
bilateral(sdata,ddata){
//console.log(this.gray);
for(let j=0;j<this.h;j++){
for(let i=0;i<this.w;i++){
let sig=400;
let sum=0;
let s=[[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],];
for(let y=0;y<5;y++){
for(let x=0;x<5;x++){
let xx=Math.abs(i+x-2);
let yy=Math.abs(j+y-2);
if(xx>=this.w){xx=i-(x-2);}
if(yy>=this.h){yy=j-(y-2);}
s[x][y]=this.gray[i+j*this.w]-this.gray[xx+yy*this.w];
s[x][y]=Math.pow(2.71828182845905,(-s[x][y]*s[x][y]/sig))*this.#gauss[x][y];
sum+=s[x][y];
}
}
for(let y=0;y<5;y++){
for(let x=0;x<5;x++){
s[x][y]/=sum;
}
}
let r=0, g=0, b=0;
for(let y=0;y<5;y++){
for(let x=0;x<5;x++){
let xx=Math.abs(i+x-2);
let yy=Math.abs(j+y-2);
if(xx>=this.w){xx=i-(x-2);}
if(yy>=this.h){yy=j-(y-2);}
r+=s[x][y]*sdata[(xx+yy*this.w)*4+0];
g+=s[x][y]*sdata[(xx+yy*this.w)*4+1];
b+=s[x][y]*sdata[(xx+yy*this.w)*4+2];
}
}
r=Math.round(Math.abs(r));
g=Math.round(Math.abs(g));
b=Math.round(Math.abs(b));
ddata[(i+j*this.w)*4+0]=r;
ddata[(i+j*this.w)*4+1]=g;
ddata[(i+j*this.w)*4+2]=b;
ddata[(i+j*this.w)*4+3]=sdata[(i+j*this.w)*4+3];
}
}
}
}
</script>
