1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
(function(){
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var canvasContext = canvas.getContext('2d');
var audio_context;

function __log(e, data) {
console.log(e + " " + (data || ''));
}

function createRecorder(stream, handleMessage) {
//获取视频数据
if (navigator.mozGetUserMedia) {
video.mozSrcObject = stream;
} else {
var vendorURL = window.URL || window.webkitURL;
video.src = vendorURL.createObjectURL(stream);
}
video.play();

var input = audio_context.createMediaStreamSource(stream);

var recorder = new Recorder(input, {
serverUrl: "wss://rating.llsstaging.com/llcup/stream/upload",
handleMessage: handleMessage
});

__log('Recorder initialised.');

return recorder;
}

var Page = function() {
var self = this;
var inited = false;
var recorder = null;

var handleMessage = function(resp) {
try {
var respObj = JSON.parse(resp);
// if(respObj.decoded){
// textToEmotion(respObj.decoded);
// }
chat(respObj.decoded);
self.overallScore(respObj.decoded);
respObj.details.forEach(function(wordRate) {
self.wordRates.push({
word: wordRate.word,
score: wordRate.confidence
})
});
} catch (e) {
//self.hasError(true);
self.errorResp(resp);
self.errorInfo(e.message);
}
};

this.inited = ko.observable(false);
initAudioSetting(function(stream){
recorder = createRecorder(stream, handleMessage);
self.inited(true);
});

this.hasError = ko.observable(false);
this.errorResp = ko.observable('');
this.errorInfo = ko.observable('');
this.wordRates = ko.observableArray([]);
this.readingRefText = ko.observable(randomPick(Constants.PreparedTexts));
this.recording = ko.observable(false);
this.overallScore = ko.observable();
this.recordButtonText = ko.computed(function() {
return self.recording() ? "▧" : "▶";
});
this.toggleRecording = function() {
self.hasError(false);
self.wordRates.removeAll();
self.recording(!self.recording());
};

//this.switchRefText = function() {
// self.readingRefText(randomPick(Constants.PreparedTexts));
//}

this.recording.subscribe(function(){
if(self.recording()) {
/*
algConfig = {
type: 'readaloud',
quality: -1,
//reftext: self.readingRefText().replace(/[,.]/g, '')
reference: self.readingRefText().toLowerCase().replace(/[^A-Za-z0-9']/g, ' ').trim()
};
*/
algConfig = {
type: 'asr',
quality: -1
};
console.log(algConfig);
recorder.record({
algConfig: algConfig
});
} else {
recorder.stop();
recorder.clear();
}
});
}

var initAudioSetting = function(startUserMediaCallback) {
try {
// webkit shim
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;

audio_context = new AudioContext;
__log('Audio context set up.');
__log('navigator.getUserMedia ' + (navigator.getUserMedia ? 'available.' : 'not present!'));
} catch (e) {
alert('No web audio support in this browser!');
}

navigator.getUserMedia({
audio: true,
video: {
mandatory: {
maxWidth: 320,
maxHeight: 240
}
}
}, startUserMediaCallback, function(e) {
__log('No live audio input: ' + e);
});
}

window.onload = function init() {
window.page = new Page();
ko.applyBindings(window.page);
setTimeout(imgToEmotion, 1000);
};

var tabButton = document.getElementById("tab-change");
tabButton.addEventListener('click',function () {
if(tabButton.checked){
tab_change_state = false;
}else{
tab_change_state = true;
imgToEmotion();
}
});
var tabVideo = document.getElementById("tab-video");
tabVideo.addEventListener('click',function () {
if(tabVideo.checked){
video.style.display = 'none';
}else{
video.style.display = 'block';
}
});

//聊天API
function chat(str){
var data = {data: str};
$.ajax({
url: "https://www.hupeng.wang/PicServer/re_chat.php",
type: "POST",
data: data
}).done(function(data){
document.getElementById("chat-box").innerText = data;
textToEmotion(data);
}).fail(function(){
console.log('Chat Error!');
})
}

//文本分析得到情感json
function textToEmotion(str){
var data = {data:str};
$.ajax({
url: "https://www.hupeng.wang/PicServer/re_text.php",
type: "POST",
data: data
}).done(function(data){
data = JSON.parse(data);
console.log(data);
var res = {name:"def",value:0};
var emotion = data["document_tone"]["tone_categories"][0]["tones"];
for(var i = 0;i<emotion.length;i++){
if(emotion[i]["score"]>res.value){
res.value = emotion[i]["score"];
res.name = emotion[i]["tone_id"];
}
}
console.log(res.name);
changeState(res.name);
}).fail(function(){
console.log('textToEmotion Error');
})
}

//图片上传得到情感json
function imgToEmotion() {
if(!tab_change_state) return;
canvasContext.drawImage(video, 0, 0, 320, 240);
var element = document.createElement("img");
element.src = canvas.toDataURL();

var pic = {data:element.src};

$.ajax({
url: "https://www.hupeng.wang/PicServer/re_pic.php",
type: "POST",
// Request body
data: pic,
}).done(function(data) {
data = JSON.parse(data);
if(data.length!==0){
var res = {name:"def",value:0};
var scores = data['0']["scores"];
for(var score in scores){
//console.log(score,scores[score]);
if(scores[score]>res.value){
res.value = scores[score];
res.name = score;
}
}
changeState(res.name);
}else{
console.log("未识别到人");
}
setTimeout(imgToEmotion,0);
}).fail(function() {
setTimeout(imgToEmotion,0);
});
}
}).call(window);

评论和共享

队友

名字 学校
胡鹏 内蒙古大学
任文頔 中山大学
岳磊 上海理工大学
额…(我不是故意的 ::>_<::) 上海理工大学

时间

2017-05-05~2017-05-07

赛况

参加 HACKxSJTU 比赛,进过和队友的努力奋斗,最终获得 英语流利说黑马奖(企业第二名)。不幸的是英语流利说这个太坑,最后的奖品是9个月的AI英语课程(正好可以学一波英语),但是最气的是没有证书,没有证书!!没有证书!!!连电子版都没有! Orz 重要的话说三遍。

收获

这次比赛最大的收获其实不是技术上的,如果单从技术上说也不比他们差很多,前端和后端本来也没什么好比的。但是这次真的感觉到了环境对人的重要性,还有一个就是英语真的该好好加强一下了,简直太糟糕了。

作品

Emotion-Journal

憋了一肚子负面情绪却没处释放?还在到处寻找管理、控制情绪的工具?快来emotion-journal吧

Emotion-Journal

项目地址

Hackx-Emotion-Journal

创意来源

情绪是一把双刃剑,良好的情绪控制有助于我们健康的生活。很多时候,我们因为外界各种各样的事情产生许许多多的情绪。“病由心生”由此而来。人不可能永远有好心情,生活中既然有挫折、有失败,就会有消极的情绪。所以我们希望推出一款简单,及时,便捷的产品帮助你放松心情以及随时随地监测自己的情绪变化。

详情描述

得益于人工智能的发展,我们使用微软认知服务和IBM Watson文本情绪分析,多方面感知用户当前的情绪状态。并在屏幕上实时反馈。由此我们实现了监控单天和单月的情绪控制情况。用户有2种可以选择的模式,下面将分别介绍

  • 情绪模仿
    用户任意做出表情,我们平台利用摄像头采集数据,接入微软认知服务,实时模仿用户的表情,并反馈在页面中。

  • 人机交互
    用户使用英语语音与本平台交流,我们使用流利说语音解析服务分析用户输入,结合IBM Watson文本情感分析服务得到用户表达的情感,传送到我们搭建的图灵机器人API,获得文字输出,结合相应的情绪,显示在页面中。

图片展示

人脸表情识别
语音聊天

使用的服务与API

  • 微软认知服务(情绪识别api)
  • IBM watson 文本情绪识别
  • 流利说语音识别服务
  • 青云云服务
  • 图灵机器人api
  • 百度翻译api

解决的问题

  • 机器人实时模仿用户的表情。
  • 机器人与用户语言对话,并根据对话内容反馈表情。
  • 憋了一肚子负面情绪却没处释放?还在到处寻找管理、控制情绪的工具?快来emotion-journal吧

项目使用指南

项目界面有3个可见的交互按钮

  • 视频框的打开和关闭
  • 情绪模仿/人机交互的切换
  • 语音采集
    用户打开界面后默认进入情绪模仿功能,页面中的动画表情可以根据用户的表情一起变换。当按下按钮2(情绪模仿/人机交互的切换按钮)进入人机交互的功能,用户按下按钮3开始录音,说一段话(暂时只能输入英语)后按下按钮3结束录音,本平台会根据输入做出相应的回答与表情,在页面中反馈出来。

功能设计说明

项目整体包含2个功能,下面将具体说明:

  • 情绪感知与反馈功能
    整体过程为:情绪图片捕捉、图片情绪分析、情绪模仿展示
  • 人机交互与情绪反馈功能
    整体实现步骤为:语音输入、语音转文字、文字情感分析、文字反馈生成、模拟情绪反馈展示
    在界面上包括辅助的功能:摄像头打开/关闭 、情绪模仿/人机交互切换 、 语音录制开始与结束

核心记说说明

本项目的后台部分核心技术主要由:用户脸部图像的情感分析,用户对话文本情感分析,用户对话交互以及CSS3动画展示4个部分组成。

  • 用户的脸部情感分析:使用了微软的认知服务中的情感部分,由于需要分析的图像的大小较大,为了加快图像识别的速度,我们团队引入了青云的对象存储服务以及CDN加速服务提高了应用服务器到微软的认知服务器之间的访问速度。
  • 用户对话文本情感分析:使用了流利说的语音识别服务获取了用户的语音的文本结果。在文本结果的情感分析分析,使用了IBM Watson的文本的情感分析Tone Analyzer技术。
  • 用户对话交互:使用了图灵机器人的对话API以及百度的翻译API完成。
  • 大量使用 CSS3 动画绘制机器人表情,并使用 HTML5 Media 技术获取用户的音频和视频。

未来发展规划

设计并完善用户注册及登录服务,以便更好得管理、分析用户的情绪状况

将平台移植到移动端,便于用户随时监控情绪,查看统计状态

评论和共享

Atom 常用插件

插件

web开发

插件名 作用 备注
emmet HTML代码补全 emmet代码展开
autoprefixer css自动浏览器前缀适配
minimap 类似sublime右侧代码地图
minimap-linter 代码地图显示linter
minimap-pigments 代码地图显示颜色
highlight-selected 高亮选中单词
open-in-browser 用默认应用打开文件
color-picker 图形界面选取颜色
regex-railroad-diagram 图形化正则表达式
file-icons 美化默认icons
atom-beautify 一键整理代码
atom-ternjs js代码提示很强大,高度定制化。
docblockr js注释doc
autoclose-html 自动闭合HTML标签
pigments 颜色显示插件 必装
linter 错误检查工具
linter-ui-default linter 界面
linter-eslint es语法检错
linter-jslint js语法检错
linter-htmlhint html语法检错
linter-stylelint css语法检错
linter-sass-lint sass语法检错
simplified-chinese-menu 汉化插件
autocomplete-paths 路径自动完成插件
pretty-json json自动整理
language-gitignore gitignore语法高亮
markdown-preview-plus markdown预览
editorconfig 支持.editorconfig
1
apm install emmet autoprefixer minimap highlight-selected open-in-browser color-picker regex-railroad-diagram file-icons atom-beautify docblockr autoclose-html pigments linter linter-ui-default linter-eslint linter-jslint linter-htmlhint linter-csslint linter-sass-lint simplified-chinese-menu autocomplete-paths pretty-json language-gitignore markdown-preview-plus

React

插件名 作用 备注
react-snippets
react-es6-snippets es6写法的react snippet
language-babel 写React必不可少
language-javascript-jsx JavaScript, ES6, ES7, React JSX, Flow支持
atom-react-autocomplete 项目内,组件名及状态的自动补全

外观

插件名 作用 备注
atom-material-syntax

评论和共享

首先本文书写遵守以下约定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function A(){
// 私有属性
var val = 1; // 私有基本属性
var arr = [1]; // 私有引用属性
function fun(){} // 私有函数(引用属性)

// 实例属性
this.val = 1; // 实例基本属性
this.arr = [1]; // 实例引用属性
this.fun = function(){}; // 实例函数(引用属性)
}

// 原型属性
A.prototype.val = 1; // 原型基本属性
A.prototype.arr = [1]; // 原型引用属性
A.prototype.fun = function(){}; // 原型函数(引用属性)

A.f = function(){
//.... // 类方法
}

其中:A 为被继承的父类, B 为继承 A 的子类。

六种继承简单介绍

简单原型继承

简单原型继承

这是最简单的继承方式,就一行代码可以完成

  • 代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function A(){
    this.arr = [1];
    }
    function B(){
    // ...
    }
    B.prototype = new A(); // 核心

    var b1 = new B();
    var b2 = new B();

    sub1.arr.push(2);
    alert(sub1.arr); // 1, 2
    alert(sub2.arr); // 1, 2
  • 优点

    • 非常简单
  • 缺点

    • 来自原型对象的引用属性是所有实例共享的。
    • 创建子类实例时,无法向父类构造函数传参。

借用构造函数

借用构造函数

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function A(val){
    this.arr = [1];

    this.fun = function(){
    // ...
    }
    }
    function B(val){
    Super.call(this, val); // 核心
    // ...
    }

    var b1 = new B(1);
    var b2 = new B(2);
    b1.arr.push(2);

    alert(sub1.arr); // 1, 2
    alert(sub2.arr); // 1

    alert(sub1.fun === sub2.fun); // false
  • 优点

    • 解决了子类实例共享父类引用属性的问题
    • 创建子类实例时,可以向父类构造函数传参
    • (解决了简单原型继承的问题)
  • 缺点
    • 无法实现函数复用,每个子类实例都持有一个新的函数实例,影响性能

组合继承(最常用)

组合继承

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function A(val){
    // 只在此处声明基本属性和引用属性
    this.val = val;
    }
    // 在此处声明函数
    A.prototype.fun1 = function(){};
    A.prototype.fun2 = function(){};

    function B(val){
    A.call(this, val); // 核心
    // ...
    }
    B.prototype = new A(1); // 核心,此处的 1 被覆盖

    var b1 = new B(2);
    var b2 = new B(3);
    alert(b1.fun === b2.fun); // true
    b1.val //2
    b2.val //3
  • 优点

    • 不存在引用属性共享问题
    • 可传参
    • 函数可复用
  • 缺点
    • 父类构造函数被调用了两次,子类原型上的属性被覆盖,形成浪费。(图中 O 处)

寄生组合继承 (最佳方式)

寄生组合继承

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /**
    * 返回一个继承自原型对象p的属性的新对象。
    * 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
    */
    function inherit(p){
    if(p==null) throw TypeError();
    if(Object.create)
    return Object.create(p);
    var t = typeof p;
    if(t !== 'object' && t!== 'function') throw TypeError();
    var f = function(){};
    f.prototype = p;
    return new f();
    }

    function A(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    }
    // 在此处声明函数
    A.prototype.fun1 = function(){};
    A.prototype.fun2 = function(){};

    function B(){
    A.call(this); // 核心
    // ...
    }
    var proto = inherit(Super.prototype); // 核心
    proto.constructor = Sub; // 核心
    Sub.prototype = proto; // 核心,完善原型链

    var b = new B();
    alert(b.val);
  • 优点

    • 解决了上面的所有问题
  • 缺点

    • 写法较麻烦

原型式

原型式

  • 代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * 返回一个继承自原型对象p的属性的新对象。
    * 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
    */
    function inherit(p){
    if(p==null) throw TypeError();
    if(Object.create)
    return Object.create(p);
    var t = typeof p;
    if(t !== 'object' && t!== 'function') throw TypeError();
    var f = function(){};
    f.prototype = p;
    return new f();
    }

    function A(){
    this.val = 1;
    }

    // 拿到父类对象
    var a = new A();
    var b = inherit(a); // 核心
    // 增强
    b.attr1 = 1;
    b.attr2 = 2;


    alert(b.val); // 1
  • 优点

    • 从已有对象衍生新对象,不需要创建自定义类型(复制)
  • 缺点
    • 原型引用属性会被所有实例共享
    • 无法实现代码复用(新对象是现取的,属性是现添的,都没用函数封装,怎么复用)

寄生式

  • 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 返回一个继承自原型对象p的属性的新对象。
* 本函数出自 《JavaScript权威指南 第6版》 P122,例6-1
*/
function inherit(p){
if(p==null) throw TypeError();
if(Object.create)
return Object.create(p);
var t = typeof p;
if(t !== 'object' && t!== 'function') throw TypeError();
var f = function(){};
f.prototype = p;
return new f();
}

function A(){
this.val = 1;
}

function getSubObject(obj){
// 创建新对象
var clone = inherit(obj); // 核心
// 增强
clone.attr1 = 1;
clone.attr2 = 2;

return clone;
}
var b = getSubObject(new A());
alert(b.val); // 1
alert(sub.attr1); // 1

给原型式继承穿了个马甲而已
优缺点同上

六种继承之间的关系

关系

参考文章

重新理解JS的6种继承方式

评论和共享

作者的图片

Archie Shi

Nothing to say


Front-End Development Engineer