这个本应该在紧接着基于深度卷积生成对抗网络的自动化头像生成器之后写完,没想到到现在已经两个月了,这拖延症没救了

先在这里放一下最终的成果www.dogcraft.top/dcgan/,这个网页是一个纯静态网页,完全可以安排到Github Page上面,上面的所有计算都是在浏览器端本地完成的,模型则是存放在github,利用jsdelivr的github镜像CDN进行加速。模型总共70多MB放在阿里云上要猴年马月才能加载出来,国内的cdn免费额度有限,放上面用不了多少次,jsdelivr真良心,在测试的时候最大下载速度可以达到10MB(联通宽带),只需要四五秒就可以加载出来。

具体的操作过程可以参考TensorFlow.js的官方文档,上面已经比较全面了,照着做基本上都可以给安排上。

转换模型

首先要有一个模型,在基于python的TensorFlow之中,利用Keras也可将整个模型保存到一个h5文件里面。

转换方式很简单,如果没有tensorflowjs_converter 可以通过pip安装tensorflowjs来解决

# bash

tensorflowjs_converter --input_format keras  path/to/my_model.h5  path/to/tfjs_target_dir

然后会在目录下面生成model.json和若干个4MB大小的bin二进制文件,这一套文件组合非常方便在互联网环境下的使用与cdn网络的缓存。

最初的调试阶段可以在浏览器里面进行,这时候需要把模型文件放在一个http静态文件服务器上面,考虑到文件大小与加载速度,我是安排在内网的树莓派部署的nginx上面。

模型的简单使用

由于模型是已经训练完成了的,只需要进行"预测",不需要进行训练与修改了。

var model;
      async function init () {
        const model2 = await tf.loadLayersModel('https://cdn.jsdelivr.net/gh/ybw2016v/dcgan-tf2/model.json');
        cpp.lftu=false;
        model = model2;
        return model;
      }
     //上面这些应该是为了应对JavaScript的异步加载,在模型加载完毕之后利用vue的条件渲染显示出相关按钮
      var mod= init();
      function shengcheng(sjs,pic) {
        
        var uui=model.predict(sjs);

        var ttui=uui.reshape([96,96,3]);

        var ttu2=ttui.abs();
        ddd=tf.browser.toPixels(ttu2,pic); //将tf张量转化为图片。
      }
      var addr;
      var a;
      function scq() {
            ccy2=bpp.ccy.map(Number)
            var dd=tf.tensor(ccy2,[1,100]);
            shengcheng(dd,drawing)
            
            console.log("生成完毕")
        

      }

在python 将一个二维数组转化为图片比较麻烦,需要跨越好几道程序,但在JavaScript里面,借助canvas可以很方便的转化成图像。

<p class="text-center">

        <canvas id="drawing" width="96" height="96">A Drawing of something</canvas>
    </p>

只需要将一个canvas元素的标签和一个图像数组传递给tf.browser.toPixels就可以顺利在网页上面显示的,使用方法与说明在https://js.tensorflow.org/api/latest/?hl=zh-cn#browser.toPixels

核心的内容就这么多,剩下的都是一些随机数的生成与vue列表的双向绑定还有生成图片的下载问题。JavaScript写的比较乱,过了两个月我自己都快看不懂了。不写注释的屑程序员 |´・ω・)ノ

相关代码会放到最后。

发布与总结

html静态文件与模型文件安排好之后就可以发布到web上面了,模型文件比较大,可以安排在cdn上解决。经过测试,在主流的手机电脑浏览器上都可以完美运行,就是有的手机浏览器比较傻,不知道缓存文件,打开一次下载一次……

这个tensorflow.js好像是通过WebGL调用不同架构平台上的GPU计算设备,在没有GPU或老旧浏览器上直接调用CPU计算,完全不需要服务端的计算资源,如果客户端。而且通用性强,在python版本上面只能用安装cuda的NVIDIAGPU,而在tensorflow.js上面甚至连Android上面的arm架构的GPU都可以用。


 <div id="loadm">

        <p v-if="lftu" class="text-center">DCGAN加载中……</p>
        <p v-else class="text-center"> DCGAN加载完毕 <br> 
            <button onclick="scq()">生成图片</button>
            <button onclick="rerandom();scq();">重置随机数并生成图片</button>
            <!-- <img src=""  id="downloadog"/> -->
            <button onclick="dow();">生成下载连接</button>
            <br>
            <a href="" download="" id="hkdog"></a>
             </p>
    </div>
    <br>
    <p class="text-center">

        <canvas id="drawing" width="96" height="96">A Drawing of something</canvas>
    </p>
    
</div>

<script>
    var cpp=new Vue({
        el: '#loadm',
        data: {
            lftu:true
        }
    })
</script>

    
  
  <br>
  <br>
  <div class="text-center">
    <button onclick="rerandom()">重置随机数</button>
  </div>
  
  <br>
  <div id="dogss" class="container text-center">
    <div v-if="seen">
        <button onclick="seedog()">隐藏随机数详情</button>
      </div>
      <div v-else>
        <button onclick="seedog()">显示随机数详情</button>
      </div>
  </div>
  <div class="container">
    <ul id="v-for-object" class="list-group" v-show="seee" >
        <li v-for="(item, index) in ccy" :key="index" class="list-group-item text-center">
            {{index}}->
          <input v-model="ccy[index]" type="number" step="0.01" number  @change="print">
        </li>

      </ul>
  </div>


  <script>
      var app3 = new Vue({
  el: '#dogss',
  data: {
    seen: false
  }
});
        function seedog() 
        {
            app3.seen=!app3.seen;
            bpp.seee=!bpp.seee;
        }

  </script>

  <script>
        doh=tf.randomNormal([1,100]);
        var cc=doh.dataSync();
        var ccy=new Array();
        for (let idog = 0; idog < cc.length; idog++) {
            ccy[idog] = cc[idog];
        }
      var bpp = new Vue({
  el: '#v-for-object',
  data: {
    ccy,
    seee:false
  },
    methods: {
        print() {
            // ccy=ccy.map(Number)
            // console.log(this.ccy);
        }
    }
  
})
  </script>
  <script>
      function rerandom(params) {
        doh=tf.randomNormal([1,100]);
        var cc=doh.dataSync();
        for (let idog = 0; idog < cc.length; idog++) {
            Vue.set(ccy,idog,cc[idog])
        }
      }
  </script>

  <script>
      var model;
      async function init () {
        const model2 = await tf.loadLayersModel('https://cdn.jsdelivr.net/gh/ybw2016v/dcgan-tf2/model.json');
        cpp.lftu=false;
        model = model2;
        return model;
      }
      var mod= init();
      function shengcheng(sjs,pic) {
        
        var uui=model.predict(sjs);

        var ttui=uui.reshape([96,96,3]);

        var ttu2=ttui.abs();
        ddd=tf.browser.toPixels(ttu2,pic);
      }
      var addr;
      var a;
      function scq() {
            ccy2=bpp.ccy.map(Number)
            var dd=tf.tensor(ccy2,[1,100]);
            shengcheng(dd,drawing)
            
            console.log("生成完毕")
        

      }
      function dow() {
            var addr = drawing.toDataURL("image/png");
            var a = document.getElementById("hkdog");
            a.href=addr;
            var ttt=Date.now()
            a.download=ttt+".png";
            a.innerHTML=ttt+"下载地址"
        }

  </script>