当微软牛津计划遇到微信App ——微信实现部分

作者:王豫翔,微软连续多年多个方向的MVP,目前主要关注大数据、云技术和人工智能。在编程道路上遵循自己的“三少”“三多”原则:少讨论概念,少争论特征、少议论模型;多写代码、多做测试、多做应用。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2016年《程序员》

微软牛津计划(Project Oxford)提供了一系列机器学习API,包含计算机视觉、语音识别和语言理解等认知服务,它能为微信开发带来怎样有趣的功能?请看本文分解。

微软牛津计划提供了一组基于Rest架构的API和SDK工具包,帮助开发者轻轻松松使用微软的自然数据理解能力为自己的解决方案增加智能服务。利用微软牛津计划构建你自己的解决方案,支持任意语言及任意开发平台。主要提供了四个自然语言处理方面的核心问题解决方案:人脸识别、语音识别、计算机视觉,以及语言理解智能服务。

图片描述

图1 应用界面

微软提供了这么强大的API,我第一时间就想,是不是可以迁移到微信平台上去做一些好玩的应用,不过在这之前,我没有做过任何微信开发的工作,所以本篇文章将分享整个实现的经验。

ASP.NET WebAPI实现微信接入验证

首先你需要一个微信公众号,很重要的是你需要完成认证,这点非常重要。当你完成公众号的基本设定后,我们需要为开发做第一件事情:让微信验证通过开发者中心页配置的服务器地址。微信服务器将发送GET请求到我们注册的服务器地址URL上,GET请求携带四个参数:signature、timestamp、nonce、echostr。我们编写了一个WebAPI对微信的请求进行反馈。

public HttpResponseMessage Get(string signature, string timestamp, string nonce, string echostr)
{
    string[] ArrTmp = { TOKEN, timestamp, nonce };
    Array.Sort(ArrTmp);
    string tmpStr = string.Join("", ArrTmp);
    var result = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1").ToLower();

    return new HttpResponseMessage (){ 
        Content = new StringContent(result, Encoding.GetEncoding("UTF-8"), "application/x-www-form-urlencoded") 
    };
}

上面这段代码的要点是返回值,很多工程师在使用WebAPI返回给微信验证时一直失败,是因为忽略了返回值的编码要求是application/x-www-form-urlencoded。

ASP.NET WebAPI实现微信JS-SDK接口注入权限验证配置

我们的客户端采用微信的JS-SDK,但是所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用,当使用JS-SDK的时候,微信会将appId、timestamp、nonceStr和signature的参数进行加密和验算是否正确,所以我们需要提供一个正确的签名值。需要获得这个签名必须要完成两步,图2所示的UML描述了这个过程。

图片描述

图2 获取签名过程示意图

第一步:获取Access Token

if (HttpRuntime.Cache["access_token"] == null)
{
    var queryString = HttpUtility.ParseQueryString(string.Empty);
    queryString["grant_type"] = "client_credential";
    queryString["appid"] = APPID;
    queryString["secret"] = APPSECRET;

    var uri = "https://api.weixin.qq.com/cgi-bin/token?" + queryString;

    HttpResponseMessage response;
    response = await client.GetAsync(uri);
    var msg = await response.Content.ReadAsStringAsync();
    var jsonobj = Newtonsoft.Json.Linq.JObject.Parse(msg);

    HttpRuntime.Cache.Add("access_token",
        (string)jsonobj["access_token"],
        null,
        DateTime.Now.AddMinutes((int)jsonobj["expires_in"]),
        new TimeSpan(0, 0, 0),
        System.Web.Caching.CacheItemPriority.AboveNormal,
        null
        );
}

第二步:获取jsapi_ticket。

if (HttpRuntime.Cache["jsapi_ticket"] == null)
{
    var queryString = HttpUtility.ParseQueryString(string.Empty);
    queryString["access_token"] = (string)HttpRuntime.Cache["access_token"];
    queryString["type"] = "jsapi";
    var uri = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" + queryString;
    HttpResponseMessage response;
    response = await client.GetAsync(uri);
    var msg = await response.Content.ReadAsStringAsync();
    var jsonobj = Newtonsoft.Json.Linq.JObject.Parse(msg);
    HttpRuntime.Cache.Add("jsapi_ticket",
    (string)jsonobj["ticket"],
    null,
    DateTime.Now.AddMinutes((int)jsonobj["expires_in"]), 
    new TimeSpan(0, 0, 0), 
    System.Web.Caching.CacheItemPriority.AboveNormal, 
    null
   );
}

我们用于签名的素材都到齐了,我们要实现签名算法了。实现的代码如下。

var pwd = string.Format("jsapi_ticket={0}&noncestr={1}×tamp={2}&url={3}",
    (string)HttpRuntime.Cache["jsapi_ticket"],
    noncestr,
    timestamp,
    url
    );

var tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(pwd, "SHA1");

return Request.CreateResponse(HttpStatusCode.OK, tmpStr);

这时候我们前端的HTML5就可以正确的采用JS-SDK了。

ASP.NET获取微信客户端上传的图片

本来我以为这是个很简单的事情,后来才发现,使用微信JS-SDK的时候,微信的HTML5客户端不会将图片直接POST给我的服务端,而是先提交给微信服务器,然后我的服务端需要通过serverId 来获得图片,大致的流程我绘制了UML,见图3,大家可以理解下。

图片描述

图3 获取微信客户端上传图片的过程

目前我们只关心服务器这段,我们将得到客户端传来的serverID,从微信的服务器上下载图片到本地。我们实现的代码如下。

public async Task<string> Get(string mediaid)
{
    var queryString = HttpUtility.ParseQueryString(string.Empty);
    queryString["access_token"] = await Get();
    queryString["media_id"] = mediaid;

    var uri = "http://file.api.weixin.qq.com/cgi-bin/media/get?" + queryString;

    HttpResponseMessage response;
    response = await client.GetAsync(uri);

    var msg = await response.Content.ReadAsStreamAsync();
    var file = response.Content.Headers.ContentDisposition.FileName.Replace("\"", "");
    var helper = new ProjecToxfordClientHelper();
    var content = await FileHelper.ReadAsync (msg);
    FileHelper.SaveFile(content, file);
    return file;
}</string>

好了,到了现在,我们对微信服务器需要实现的接口都差不多了,接下来就可以设计微信的客户端了。

WeUI设计微信客户端首页样式

WeUI是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信网页开发量身设计,可以令用户的使用感知更加统一。在微信网页开发中使用 WeUI,有如下优势:

  • 同微信客户端一致的视觉效果,令所有微信用户都能更容易地使用你的网站;
  • 便捷获取快速使用,降低开发和设计成本;
  • 微信设计团队精心打造,清晰明确,简洁大方。

该样式库目前包含button、cell、dialog、progress、toast、article、icon等各式元素,我们可以在https://github.com/weui/weui获得源代码和DEMO。

我们先做首页,以了解WeUI样式库的使用方式。

建立Index.html引入样式库和配置head节点。

<meta charset="utf-8">
<title>脸探</title>
<meta name="viewport" content="initial-scale=1.0,user- scalable=no,maximum- scale=1,width=device-width"> 
 <link href="css/weui.css" rel="stylesheet">
 <link href="css/example.css" rel="stylesheet">

Body节点下的直接子元素是< div class=”page”>,其他所有元素都在这个节点下,我们的Index.html页面设计了两个div元素分别是:

<div class="hd">
<div class="bd"></div></div>

hd节点下的代码非常简单,就是title的描述。

<h1 class="page_title">脸探</h1>
<p class="page_desc">测测脸的相似度</p>

bd包含的是一个< div class=”weui_panel weui_panel_access”>,WeUI提供的Panel非常容易设计图文组合列表,WeUI提供了一系列很有用的类:weui_panel、weui_panel_access、weui_panel_hd、weui_panel_bd。

我们的Panel的标题就可以用weui_panel_hd进行修饰。

<div class="weui_panel_hd"></div>

具体的内容可以被weui_panel_bd修饰。

<div class="weui_panel_bd"></div>

weui_panel_bd 的子元素如下:

<a href="" class=" weui_media_box weui_media_appmsg ">
    <div class=" weui_media_hd">
 <imgsrc="img 4432144_111855038929_2.jpg"="" alt="">
    </imgsrc="img></div>
    <div class=" weui_media_bd">
    <h4 class=" weui_media_title ">标题</h4>
    <p class="weui_grid_label">内容 </p>
  </div>  
</a>

了解了如何布局一个列表项,那首页就容易完成了,代码如下。

<div class="page">
   <div class="hd">
      <h1 class="page_title">脸探</h1>
      <p class="page_desc">测测脸的相似度</p>
   </div>
   <div class="bd">
      <div class="weui_panel weui_panel_access">
       <div class="weui_panel_hd">娃像谁     </div>
       <div class="weui_panel_bd">
          <a href="family.html" class="weui_media_box weui_media_appmsg">
             <div class="weui_media_hd">
                 <img class="weui_media_appmsg_thumb" src="fonts/family.jpg" alt="">
             </div>
             <div class="weui_media_bd">
                 <h4 class="weui_media_title">三人照</h4>
                 <p class="weui_media_desc">上传一家三口三人照,立即知道孩子与父母相像指数</p>
             </div>
         </a>
         <a href="family3.html" class="weui_media_box weui_media_appmsg">
           <div class="weui_media_hd">
              <img class="weui_media_appmsg_thumb" src="fonts/one.jpg" alt="">
           </div>
           <div class="weui_media_bd">
              <h4 class="weui_media_title">单人照</h4>
              <p class="weui_media_desc">上传一家三口各自照片,立即知道孩子与父母相像指数</p>
             </div>
           </a>
         </div>
       </div>
    </div>
    <div class="bd">
       <div class="weui_panel weui_panel_access">
       <div class="weui_panel_hd">夫妻相    </div>
    <div class="weui_panel_bd">
       <a href="couple2.html" class="weui_media_box weui_media_appmsg">  
         <div class="weui_media_hd">
            <img class="weui_media_appmsg_thumb" src="fonts/couple.jpg" alt="">
        </div>
       <div class="weui_media_bd">
         <h4 class="weui_media_title">双人照</h4>
         <p class="weui_media_desc">上传你和TA的双人照,你立即知道你们的天生缘分指数</p>
     </div>
   </a>
   <a href="couple.html" class="weui_media_box weui_media_appmsg">
     <div class="weui_media_hd">
       <img class="weui_media_appmsg_thumb" src="fonts/one.jpg" alt="">
     </div>
     <div class="weui_media_bd">
       <h4 class="weui_media_title">单人照</h4>
           <p class="weui_media_desc">上传你们两人各自照片,你立即知道你们的天生缘分指数</p>
         </div>
        </a>
      </div>
    </div>
  </div>
</div>

我们得到的首页效果大致如图4所示。

图片描述

图4 微信客户端首页效果图

设计微信客户端功能页样式

以娃像谁-单人照的页面为例,页面代码如下。

<div class="pic_panel">
    <div class="parent" id="parent1">
        <i class="icon iconfont icon-210 human"></i>
    </div>
    <div class="parent" id="parent2">
        <i class="icon iconfont icon-nv human"></i>
    </div>
    <div class="clear"></div>
    <div class="parent1like like"></div>
    <div class="parent2like like"></div>
    <div class="clear"></div>
    <div class="picture" id="child">
        <i class="icon iconfont icon-child human"></i>
    </div>
    <form>
        <input type="button" class="next" id="uploadImage" value="GO !!!">
    </form>
</div>

id=”parent1” 和 id=”parent2” 为存放父母照片的容器,id=”child”为存放孩子照片的容器,点击容器触发选择照片,选择完成点击按钮作比较。class=”parent1like”和class=”parent2like” 分别显示 id=”child”分别与id=”parent1”和id=”parent2” 对比的结果。

我们得到的页面效果类似图5所示的样子。

图片描述

图5 娃像谁—单人照的页面效果图

实现微信客户端交互

在之前我们写了一个WebAPI接口来实现微信JS-SDK接口注入权限验证配置,现在我们的客户端需要调用这个接口来做验证了。客户端你需要引用jweixin-1.0.0.js。

只要我们的业务需要使用微信JS-SDK,则都需要完成接口注入的权限验证,验证的方式我们来一步步分析见图6。

图片描述

图6 JS-SDK接口注入的权限验证过程

页面将noncestr(这个可以是页面定义一个常数)、timestamp(其实也可以是常数)、url当前页面地址提交给我们最早写的/api/weixin接口,然后将返回的签名提交给wx.config即可。下面的代码可以作为你的模板使用。

$(function () {
   var timestamp = Date.parse(new Date())/1000;
    var localurl = encodeURIComponent(window.location.href.split('#')[0]);
    $.ajax({
      url: 'http://www.********.cn/wxapi/api/weixin',
      dataType: "json",
      data: {
        noncestr: 'FFUmZdbWVT9mVP7a',
        timestamp: timestamp,
        url: window.location.href.split('#')[0]
},
      success: function (data) {
wxFace(data.toLowerCase());
      }
 })
function wxFace(signature) {
   wx.config({
       debug: false,
       appId: 'wxec54ec7f720993da',
       timestamp: timestamp,
       nonceStr: 'FFUmZdbWVT9mVP7a',
       signature: signature,
       jsApiList: [
          'checkJsApi',
          'onMenuShareTimeline',
          'onMenuShareAppMessage',
          'chooseImage',
          'previewImage',
          'uploadImage',
          'downloadImage'
         ]            });
      }
 });

然后我们定义选择图片函数,当选择id=”parent1”、 id=”parent2”、id=”child”时调用。

function chooseUpload(selector) {
    wx.chooseImage({
        success: function (res) {
            $("#loading").show();
         $(function () {
            $.each(res.localIds, function (i, n) {
               wx.uploadImage({
               localId: res.localIds.toString(), // 需要上传的图片的本地ID,由chooseImage接口获得                        
                 isShowProgressTips: 0, // 默认为1,显示进度提示
                 success: function (res1) {
                   $.ajax({
                      url: 'http://www.******.cn/wxapi/face/detect/' + res1.serverId,                                   
                     dataType: "json",
                     success: function (data) {
                       $("#loading").hide(); 
             if (JSON.parse(data).length == 1) { 
               $(selector).html('<img src="' + n + '"> <br>')                                          .data('faceId', JSON.parse(data)[0].faceId);
             } else if (JSON.parse(data).length > 1) { 
               alert('请选择单人照哦')
            } else { 
               alert('啊,我看不到你的脸~')
            }
          }
       })
      },
      fail: function (res) {
        alert(JSON.stringify(res));
       }
     });
    });
   });
  }
 });
}

触发点击事件调用上传图片函数。

document.querySelector('#parent1').onclick = function () {
    chooseUpload('#parent1')
};
定义函数,将拿到的两张照片的id做对比。
  function verify(selector, parent, child) {
      $("#loading").show();
      $.ajax({
          url: 'http://www.******.cn/wxapi/face/verify/' + parent + '/' + child,    
          dataType: "json",
          success: function (data) {
              $("#loading").hide();
              $(selector).html('相似度:' + (JSON.parse(data).confidence * 100).toFixed(2) + '%')
          }
      })      
  }

最后是我们分享朋友圈的功能实现。

var shareData = {
      title: '测测孩子跟谁像',//分享的标题
      desc: '来看看孩子跟爸爸比较像还是跟妈妈比较像',//分享的描述
      link: window.location.href,//分享的快照
      imgUrl: 'http://www. .******.cn/WeFace/fonts/family.jpg'//分享的链接
  };
  wx.onMenuShareAppMessage(shareData);
  wx.onMenuShareTimeline(shareData);

分享结果如图7所示。

图片描述

图7 朋友圈分享功能效果图

总结

本文主要针对如何使用APS.NET WebAPI实现微信注入进行了深入讲解。下期会承接本文,重点分享服务的实现过程,内容主要有:调用封装微软牛津计划API、使用MongoDB存储数据和客户端如何使用。全文阅读完毕后,你将可以自己去编写更有价值的应用了。


订阅2016年程序员(含iOS、Android及印刷版)请访问 http://dingyue.programmer.com.cn
图片描述

订阅咨询:

• 在线咨询(QQ):2251809102
• 电话咨询:010-64351436
• 更多消息,欢迎关注“程序员编辑部

智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值