トップへ(mam-mam.net/)

JavaScriptで美肌フィルタを適用|バイラテラルフィルタで画像を滑らかに

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>