未验证 提交 74c85279 编写于 作者: Y Yanzhan Yang 提交者: GitHub

setup paddle web (#1715)

上级 5724eab2
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# Referenced from https://github.com/github/gitignore/blob/master/Node.gitignore
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# next.js build output
.next
# other stuff
.DS_Store
Thumbs.db
# IDE configurations
.idea
.vscode
# build assets
/output
/dist
/dll
.cache
package-lock.json
registry=http://registry.npm.baidu-int.com
# paddle-web-demo 前端机器学习框架
[icode地址](http://icode.baidu.com/repos/baidu/mms/paddle-web-demo/tree/master)
## get start
## 编译编译
本地环境已安装node
```bash
# 安装编译依赖
npm i
# 本地编译部署
npm run server
```
Global:
tool: build_submitter
Default:
profile: [buildProduction]
Profiles:
- profile:
name: buildProduction
env: cmc_standard
command: export NODE_ENV=production && sh scripts/build.sh
release: true
- profile:
name: buildDevelopment
env: cmc_standard
command: export NODE_ENV=development && sh scripts/build.sh
release: true
{
"name": "paddle-web-demo",
"version": "1.0.0",
"description": "paddle",
"main": "index.js",
"scripts": {
"server": "parcel ./src/index.html",
"testDemo": "parcel ./demo/index.html",
"unit": "parcel ./test/unitTest.html",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "ssh://yangmingming@icode.baidu.com:8235/baidu/mms/paddle-web-demo"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"parcel-bundler": "^1.10.3",
"axios": "^0.17.1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"js-file-download": "^0.4.5"
}
}
export PATH=$NODEJS_BIN_LATEST:$PATH
echo "node: $(node -v)"
echo "npm: v$(npm -v)"
npm install
npm run build
/* eslint-disable */
/**
* @file GraphExecutor,封装可执行单元
* @author wangqun@baidu.com
*/
// const fileDownload = require('js-file-download');
let start;
export default class GraphExecutor {
constructor(model) {
this.inputs = model.inputs;
this.outputs = model.outputs;
this.attrs = model.attrs;
this.type = model.type;
this.finish = false;
this.next = null;
this.opData = null;
this.id = +new Date() + model.type + Math.floor(Math.random() * 10 + 1) + model.idx;
}
get inputsName() {
if (this.type === 'feed') {
return this.inputs.X;
}
else if (this.type === 'batchnorm' || this.type === 'batch_norm') {
return this.inputs.X;
}
else if (this.type === 'conv2d') {
return this.inputs.Input;
}
else if (this.type === 'depthwise_conv2d') {
return this.inputs.Input;
}
else if (this.type === 'elementwise_add') {
return this.inputs.X;
}
else if (this.type === 'relu' || this.type === 'leaky_relu') {
return this.inputs.X;
}
else if (this.type === 'pool2d') {
return this.inputs.X;
}
else if (this.type === 'mul') {
return this.inputs.X;
}
else if (this.type === 'softmax') {
return this.inputs.X;
}
else if (this.type === 'scale') {
return this.inputs.X;
}
else if (this.type === 'fetch') {
return this.inputs.X;
}
return null;
}
get outputsName() {
if (this.type === 'conv2d') {
return this.outputs.Output;
}
else if (this.type === 'depthwise_conv2d') {
return this.outputs.Output;
}
else if (this.type === 'batchnorm' || this.type === 'batch_norm') {
this.outputs.out = this.outputs.Y;
return this.outputs.Y;
}
else {
return this.outputs.Out;
}
}
/**
* 将输入数据和具体op进行关联,触发执行具体每一个op
* @param inputs
* @param runtime
*/
execute(runtime) {
// console.log(inputs, outputs);
if (this.type !== 'feed') {
let time = +Date.now();
runtime.run(this.type, this.opData);
// if (runtime.gpu.frameBufferIsComplete().isComplete) {
// var result = runtime.read();
// let res = Array.prototype.slice.call(result);
// fileDownload(res, "result.csv");
// }
let length = statistic.length;
statistic[length - 1].type = this.type;
statistic[length - 1].runTime = +Date.now() - time;
// if (this.type === 'scale') {
// console.log('时间是:' + (+Date.now() - start));
// }
} else {
start = +Date.now();
}
}
}
/* eslint-enable */
/* eslint-disable */
import GraphExecutor from './executor';
import IO from '../feed/imageFeed';
import Runtime from '../../src/runtime/runtime';
import OpData from '../utils/opData';
import Factory from '../factory/fshader/factory';
import Utils from '../utils/utils';
/**
* @file GraphModel,绘制生成model网络
* @author wangqun@baidu.com
*/
// 生成factory实例
const factory = new Factory({});
// 获取op的输入配置
const opConfs = factory.getOpConfs();
export default class GraphModel {
constructor(modelGonfig, loadOptions) {
this.version = '0.0.1';
this.handler = 'io.IOHandler';
this.modelGonfig = modelGonfig;
this.loadOptions = loadOptions;
this.multipart = false;
// feed数据
this.feed = null;
this.index = 0;
this.feedOp = null;
this.feedItem = null;
this.isExecuted = false;
// fetch xhr jsonp
this.params = {type: 'fetch'};
// 设置分片加载model
if (this.loadOptions) {
this.multipart = this.loadOptions.multipart;
this.feed = {input: this.loadOptions.feed};
if (loadOptions.dataType === 'binary') {
this.binaryOption = loadOptions.binaryOption;
}
}
// op runner
this.inst = Runtime.init({
'width_raw_canvas': 512,
'height_raw_canvas': 512
});
if (this.loadOptions === null) {
this.loadOptions = {};
}
}
fetchOneChunk(path) {
console.time(path)
return fetch(path).then(request => {
console.timeEnd(path);
return request.arrayBuffer();
})
}
fetchAllData() {
// todo 兼容一下json的模式
let counts = this.binaryOption.fileCount;
let chunkArray = [];
for (let i = 1; i <= counts; i++) {
chunkArray.push(
this.fetchOneChunk(this.modelGonfig.dir + this.binaryOption.getFileName(i))
);
}
// 1个文件
// let chunkArray = [this.fetchOneChunk('/faceModel/mergedData.dat')];
console.time('加载时间');
return Promise.all(chunkArray).then(chunks => {
console.timeEnd('加载时间');
let chunksLength = 0;
let f32Array = [];
let float32Chunk;
chunks.forEach(i => {
float32Chunk = new Float32Array(i);
f32Array.push(float32Chunk);
chunksLength += float32Chunk.length;
});
this.allData = new Float32Array(chunksLength);
let offset = 0;
f32Array.forEach(i => {
i.forEach(num => {
this.allData[offset] = num;
offset += 1;
})
});
});
}
traverse (arr) {
const TMP_SCHEME_REGEX = /\.tmp/;
const TMP_REGEX = /\-/;
let marker = 0; // 读到哪个位置了
let len; // 当前op长度
arr.filter(item => {
return item.name
&& item.name.match(TMP_SCHEME_REGEX) === null
&& item.name.match(TMP_REGEX) === null;
})
// .sort((a, b) => {
// if (a.name > b.name) {
// return 1;
// }
// if (a.name < b.name) {
// return -1;
// }
// return 0;
// }) // 按字母顺序排列 在model.json里
.forEach(item => {
len = item.shape.reduce((a, b) => a * b); // 长度为shape的乘积
item.data = this.allData.slice(marker, marker + len);
marker += len;
});
}
fetchModel(params) {
params = params || this.params;
const path = this.modelGonfig.dir + this.modelGonfig.main;
let URL_SCHEME_REGEX = /^https?:\/\//;
let load = null;
let method = params.method || 'get';
let mode = params.mode || 'cors';
// jsonp请求方式
if (params && params.type === 'jsonp') {
let json;
let s = document.createElement('script');
s.src = path + '&jsonpCallback=fn';
window.fn = function(data) {
json = data;
// console.log(json);
};
//当script被插入文档中时,src中的资源就会开始加载
document.body.appendChild(s);
load = new Promise((resolve, reject) => {
s.onload = function(e) {
resolve(json);
}
s.onerror = function() {
reject(json);
}
});
this.handler = load;
}
// 原生fetch
else if (params.type === 'fetch') {
let myHeaders = new Headers();
load = new Promise((resolve, reject) => {
fetch(path, {
method: method,
mode: mode,
credentials: "include",
headers: myHeaders
})
.then(response => response.json())
.then(responseData => resolve(responseData))
.then(err => reject(err))
});
this.handler = load;
}
// ajax
else if (params.type === 'xhr') {
this.handler = load;
}
return load;
}
async load() {
let that = this;
console.time('生成op数据之前')
console.time('fetchModel');
const artifacts = this.handler = await this.fetchModel();
console.timeEnd('fetchModel');
if (this.multipart === true) {
console.time('6个文件准备好op数据');
await this.fetchAllData()
.then(() => this.traverse(artifacts.vars));
console.timeEnd('6个文件准备好op数据');
}
console.time('createOpsMap');
const opsMap = this.createOpsMap(artifacts.ops, artifacts.vars);
console.timeEnd('createOpsMap');
console.time('constructOpsMap');
this.weightMap = this.constructOpsMap(opsMap);
console.timeEnd('constructOpsMap');
console.timeEnd('生成op数据之前')
// 生成op数据
this.weightMap.forEach(op => {
const type = op.type;
if (type !== 'feed' && type !== 'fetch') {
that.buildOpData(op);
}
});
return true;
}
buildOpData(op) {
const tensor = this.constructTensor(op);
const opData = new OpData(op.type, tensor.inputs, tensor.outputs, tensor.attrs);
const name = opData.name;
const fsCode = factory.buildShader(name, opData.data);
opData.fshader = this.inst.createFragmentShader(fsCode);
opData.renderData = opConfs[name].map(elem => {
let item = Object.assign({}, elem);
const tensorData = opData.tensor[item.tensor];
if (item.type === 'texture') {
item.data = tensorData.data;
if (this.feedOp.id === op.id) {
item.shape = tensorData.shape;
this.feedItem = item;
}
item['width_texture'] = tensorData['width_texture'];
item['height_texture'] = tensorData['height_texture'];
} else if (item.type === 'uniform') {
item.data = tensorData[item.variable];
}
return item;
});
op.opData = opData;
// delete op.inputs;
// delete op.outputs;
// delete op.attrs;
}
execute_(executor) {
if (executor.type === 'fetch') {
return;
}
executor.execute(this.inst);
if (executor.next) {
const id = executor.next;
const next = this.getTensor(id);
this.execute_(next[0])
}
}
/**
* Executes inference for the model for given input tensors.
* @param inputs
* @param outputs
* @returns {*}
*/
execute(inputs) {
this.feed = inputs;
const executor = this.getNetsStart(this.weightMap);
if (!this.inst) {
this.inst = Runtime.init({
'width_raw_canvas': 512,
'height_raw_canvas': 512
});
}
if (this.isExecuted) {
this.updateFeed();
}
let start = +Date.now();
this.execute_(executor[0]);
console.log('总的执行时间是' + (+Date.now() - start));
this.isExecuted = true;
return this.inst;
}
updateFeed() {
this.feedItem.data = this.feed.input[0].data;
Utils.img2texture(this.feedItem);
}
/**
* predict enter
* @param inputs
* @param config
*/
predict(inputs, config) {
return this.execute_(inputs, true, this.outputNodes);
}
getTensorAttr(name) {
return this.handler.vars.filter((item, i) => {
if (name === item.name)
return item;
});
}
constructTensor(executor) {
const that = this;
const inputName = executor.inputsName[0];
const input = executor.inputs;
const output = executor.outputs;
Object.keys(output).forEach(function(key){
output[key] = that.getTensorAttr(output[key][0]);
});
Object.keys(input).forEach(function(key){
if ((key === 'Input') && (inputName === 'pixel')) {
const pixel = that.getTensorAttr(inputName);
const io = new IO();
input[key] = io.fromPixels(data, pixel);
}
else if ((key === 'Input') && (inputName === 'image' || inputName === 'x')) {
input[key] = that.feed.input;
that.feedOp = executor;
}
else {
input[key] = that.getTensorAttr(input[key][0]);
}
});
const tensor = {
inputs: input,
outputs: output,
attrs: executor.attrs,
type: executor.type,
next: executor.next
};
return tensor;
}
/**
* Construct Ops Relationship
* @param ops
* @returns {*}
*/
constructOpsMap(ops) {
return ops.map((item, idx) => {
const outputsName = item.outputsName[0];
const next = this.getNextExecutor(ops, outputsName);
if (next.length > 0) {
item.next = next[0].id;
}
return item;
});
}
/**
* Get Ops Nets Start Node
* @param ops
* @returns {*}
*/
getNetsStart(ops) {
return ops.filter((item) => {
if (item.type === 'feed') {
return true;
}
});
}
/**
* Get Ops Nets Last Node
* @param ops
* @returns {*}
*/
getNetsEnd(ops) {
return ops.filter((item) => {
if (item.type === 'fetch') {
return true;
}
});
}
/**
* get tensor by id
* @param id
* @returns {*}
*/
getTensor(id) {
return this.weightMap.filter((item, i) => {
if (id === item.id)
return item;
});
}
/**
* Create Ops Executor Object Map
* @param ops
* @returns {*}
*/
createOpsMap(ops) {
return ops.map((item, idx) => {
item.idx = idx;
const graphExecutor = new GraphExecutor(item);
return graphExecutor;
});
}
/**
* Get The Next Executor need Exec
* @param ops
* @param id
* @returns {*}
*/
getNextExecutor(ops, id) {
return ops.filter((item, key) => {
if (id === item.inputsName[0]) {
return true;
}
});
}
/**
* Load a graph model given a URL to the model definition.
* @param modelGonfig
* @param options
* @returns {Promise<void>}
*/
async loadGraphModel(modelGonfig, options) {
if (modelGonfig === null) {
// todo saniac 报错提示修改
throw new Error(
'modelGonfig in loadGraphModel() cannot be null. Please provide a url ' +
'or an IOHandler that loads the model');
}
if (options === null) {
options = {};
}
const model = new GraphModel(modelGonfig, options);
await model.load();
return model;
}
/**
* dispose
*/
dispose() {
this.executor.dispose();
}
}
/* eslint-enable */
import ops from './ops';
/**
* @file 工厂类,生成fragment shader
* @author yangmingming
*/
export default class Factory {
constructor(opts) {
this.defaultOpts = Object.assign({}, opts);
}
buildShader(opName, data) {
let result = '';
result = this.buildPrefix(opName);
result += this.buildCommon(opName);
result += this.buildOp(opName);
result = this.populateData(result, data);
return result;
}
buildPrefix(opName) {
return ops.common.prefix;
}
buildCommon(opName) {
return ops.common.params + ops.common.func;
}
buildOp(opName) {
let code = ops.ops[opName].params;
// 依赖的方法
let atoms = ops.atoms;
let confs = ops.ops[opName].confs;
let dep = confs.dep || [];
dep.map(item => {
let func = item.func;
let data = item.conf;
let snippet = atoms[func];
code += this.populateData(snippet, data);
});
// suffix
code += this.buildSuffix(opName);
// main方法
code += ops.ops[opName].func;
return code;
}
buildSuffix(opName) {
return ops.common.suffix;
}
populateData(result, data) {
let code = result;
for (let key in data) {
code = code.replace(new RegExp(key.toUpperCase(), 'g'),
((typeof data[key]) === 'undefined') ? 1 : data[key]);
}
return code;
}
getOpConfs() {
const opsConfs = {};
for (let key in ops.ops) {
if (ops.ops.hasOwnProperty(key)) {
opsConfs[key] = ops.ops[key].confs.input;
}
}
return opsConfs;
}
}
/* eslint-disable */
import common_params from '../../shader/atom/common_params';
import common_func from '../../shader/atom/common_func';
import prefix from '../../shader/atom/prefix';
import suffix from '../../shader/atom/suffix';
import ivec56 from '../../shader/atom/type_ivec56';
import conv2d_params from '../../shader/conv2d/params';
import conv2d_func from '../../shader/conv2d/main';
import conv2d_conf from '../../shader/conv2d/conf';
import dynamic_params from '../../shader/dynamic/params';
import dynamic_func from '../../shader/dynamic/main';
import dynamic_conf from '../../shader/dynamic/conf';
import pool2d_params from '../../shader/pool2d/params';
import pool2d_func from '../../shader/pool2d/main';
import pool2d_conf from '../../shader/pool2d/conf';
import elementwise_add_params from '../../shader/elementwise_add/params';
import elementwise_add_func from '../../shader/elementwise_add/main';
import elementwise_add_conf from '../../shader/elementwise_add/conf';
import mul_params from '../../shader/mul/params';
import mul_func from '../../shader/mul/main';
import mul_conf from '../../shader/mul/conf';
import softmax_params from '../../shader/softmax/params';
import softmax_func from '../../shader/softmax/main';
import softmax_conf from '../../shader/softmax/conf';
import batchnorm_params from '../../shader/batchnorm/params';
import batchnorm_func from '../../shader/batchnorm/main';
import batchnorm_conf from '../../shader/batchnorm/conf';
import getArrayIndexFromTensorPos from '../../shader/atom/getArrayIndexFromTensorPos';
import getArrayIndexFromTexturePos from '../../shader/atom/getArrayIndexFromTexturePos';
import getTensorPosFromArrayIndex from '../../shader/atom/getTensorPosFromArrayIndex';
import getTexturePosFromArrayIndex from '../../shader/atom/getTexturePosFromArrayIndex';
import getValueFromTexturePos from '../../shader/atom/getValueFromTexturePos';
import getValueFromTensorPos from '../../shader/atom/getValueFromTensorPos';
import moveTexture2PosToReal from '../../shader/atom/moveTexture2PosToReal';
import getPixelsFromTexturePos from '../../shader/atom/getPixelsFromTexturePos';
import getRangePowSumFromArrayIndex from '../../shader/atom/getRangePowSumFromArrayIndex';
import getRangeSumFromArrayIndex from '../../shader/atom/getRangeSumFromArrayIndex';
import sigmoid from '../../shader/atom/sigmoid';
import prelu from '../../shader/atom/prelu';
import scale from '../../shader/atom/scale';
import softmax from '../../shader/atom/softmax';
/**
* @file op文件
* @author yangmingming
*/
export default {
common: {
params: common_params,
func: common_func,
prefix,
suffix,
ivec56
},
ops: {
conv2d: {
params: conv2d_params,
func: conv2d_func,
confs: conv2d_conf
},
dynamic: {
params: dynamic_params,
func: dynamic_func,
confs: dynamic_conf
},
pool2d: {
params: pool2d_params,
func: pool2d_func,
confs: pool2d_conf
},
elementwise_add: {
params: elementwise_add_params,
func: elementwise_add_func,
confs: elementwise_add_conf
},
mul: {
params: mul_params,
func: mul_func,
confs: mul_conf
},
relu: {
params: dynamic_params,
func: dynamic_func,
confs: dynamic_conf
},
scale: {
params: dynamic_params,
func: dynamic_func,
confs: dynamic_conf
},
softmax: {
params: softmax_params,
func: softmax_func,
confs: softmax_conf
},
batchnorm: {
params: batchnorm_params,
func: batchnorm_func,
confs: batchnorm_conf
}
},
atoms: {
getArrayIndexFromTensorPos,
getArrayIndexFromTexturePos,
getTensorPosFromArrayIndex,
getTexturePosFromArrayIndex,
getValueFromTexturePos,
getValueFromTensorPos,
moveTexture2PosToReal,
getPixelsFromTexturePos,
getRangeSumFromArrayIndex,
getRangePowSumFromArrayIndex,
sigmoid,
prelu,
scale,
softmax
}
};
/* eslint-disable */
/**
* @file image,feed 获取图像相关输入
* @author wangqun@baidu.com
*/
export default class imageFeed {
constructor() {
this.fromPixels2DContext = document.createElement('canvas').getContext('2d');
this.defaultWidth = 224;
this.defaultHeight = 224;
this.minPixels = 225;
this.pixels = '';
this.defaultParams = {
gapFillWith: '#000',
std: [1, 1, 1]
};
};
/**
* 处理图像方法
* @param inputs
*/
process(inputs) {
const input = inputs.input;
const mode = inputs.mode;
const channel = inputs.channel;
const rotate = inputs.rotate;
const params = {
...this.defaultParams,
...inputs.params
};
let output = [];
output = this.fromPixels(input, params);
return output;
};
/**
* crop图像&重新设定图片tensor形状
* @param shape
*/
reshape(imageData, opt, scaleSize) {
const {sw, sh} = scaleSize;
const {width, height} = opt;
const hPadding = Math.ceil((sw - width) / 2);
const vPadding = Math.ceil((sh - height) / 2);
let data = imageData.data;
let red = [];
let green = [];
let blue = [];
let mean = opt.mean;
let std = opt.std;
for (let i = 0; i < data.length; i += 4) {
// img_mean 0.485, 0.456, 0.406
//img_std 0.229, 0.224, 0.225
let index = i / 4;
let vIndex = Math.floor(index / sw);
let hIndex = index - (vIndex * sw) - 1;
if (hIndex >= hPadding && hIndex < (hPadding + width) &&
vIndex >= vPadding && vIndex < (vPadding + height)) {
red.push(((data[i] / 255) - mean[0]) / std[0]); // red
green.push(((data[i + 1] / 255) - mean[1]) / std[1]); // green
blue.push(((data[i + 2] / 255) - mean[2]) / std[2]); // blue
}
}
let tmp = green.concat(blue);
return red.concat(tmp);
};
/**
* 全部转rgb * H * W
* @param shape
*/
allReshapeToRGB(imageData, opt, scaleSize) {
const {sw, sh} = scaleSize;
const {width, height} = opt;
let data = imageData.data;
let mean = opt.mean;
let dataLength = data.length;
let result = new Float32Array(dataLength * 3 / 4);
let offsetR = 0;
let offsetG = dataLength / 4;
let offsetB = dataLength / 2;
for (let i = 0; i < data.length; i += 4) {
result[offsetR++] = (data[i] - mean[0]) / 256;
result[offsetG++] = (data[i + 1] - mean[1]) / 256;
result[offsetB++] = (data[i + 2] - mean[2]) / 256;
// result.push((data[i] - mean[0]) / 256); // red
// result.push((data[i + 1] - mean[1]) / 256); // green
// result.push((data[i + 2] - mean[2]) / 256); // blue
}
return result;
};
/**
* 根据scale缩放图像
* @param image
* @param params
* @return {Object} 缩放后的尺寸
*/
reSize(image, params) {
// 原始图片宽高
const width = this.pixelWidth;
const height = this.pixelHeight;
// 缩放后的宽高
let sw = width;
let sh = height;
// 最小边缩放到scale
if (width < height) {
sw = params.scale;
sh = Math.round(sw * height / width);
} else {
sh = params.scale;
sw = Math.round(sh * width / height);
}
this.fromPixels2DContext.canvas.width = sw;
this.fromPixels2DContext.canvas.height = sh;
this.fromPixels2DContext.drawImage(
image, 0, 0, sw, sh);
return {sw, sh};
};
/**
* 缩放成目标尺寸并居中
*/
fitToTargetSize(image, params, center) {
// 目标尺寸
const targetWidth = params.targetSize.width;
const targetHeight = params.targetSize.height;
this.fromPixels2DContext.canvas.width = targetWidth;
this.fromPixels2DContext.canvas.height = targetHeight;
this.fromPixels2DContext.fillStyle = params.gapFillWith;
this.fromPixels2DContext.fillRect(0, 0, targetHeight, targetWidth);
// 缩放后的宽高
let sw = targetWidth;
let sh = targetHeight;
let x = 0;
let y = 0;
// target的长宽比大些 就把原图的高变成target那么高
if (targetWidth / targetHeight * this.pixelHeight / this.pixelWidth >= 1) {
sw = Math.round(sh * this.pixelWidth / this.pixelHeight);
x = Math.floor((targetWidth - sw) / 2);
}
// target的长宽比小些 就把原图的宽变成target那么宽
else {
sh = Math.round(sw * this.pixelHeight / this.pixelWidth);
y = Math.floor((targetHeight - sh) / 2);
}
// console.log(x, y, sw, sh);
if (center) {
this.fromPixels2DContext.drawImage(
image, x, y, sw, sh);
}
else {
this.fromPixels2DContext.drawImage(
image, 0, 0, sw, sh);
}
document.getElementById('p-c').appendChild(this.fromPixels2DContext.canvas);// test only, demele me
return {sw: targetWidth, sh: targetHeight};
}
/**
* 获取图像内容
* @param pixels
* @returns {Uint8ClampedArray}
*/
getImageData(pixels, scaleSize) {
const {sw, sh} = scaleSize;
let vals = this.fromPixels2DContext
.getImageData(0, 0, sw, sh);
// crop图像
const width = pixels.width;
const height = pixels.height;
return vals;
};
/**
* 计算灰度图
* @param imageData
* @returns {*}
*/
grayscale (imageData) {
let data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
let avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
return data;
};
fromPixels(pixels, opt) {
let data;
let scaleSize;
if (pixels instanceof HTMLImageElement || pixels instanceof HTMLVideoElement) {
this.pixelWidth = pixels.naturalWidth || pixels.width;
this.pixelHeight = pixels.naturalHeight || pixels.height;
if (opt.scale) { // 兼容以前的,如果有scale就是短边缩放到scale模式
scaleSize = this.reSize(pixels, opt);
data = this.getImageData(opt, scaleSize);
}
else if (opt.targetSize) { // 如果有targetSize,就是装在目标宽高里的模式
scaleSize = this.fitToTargetSize(pixels, opt);
data = this.getImageData(opt, scaleSize);
}
}
if (opt.gray) {
data = grayscale(data);
}
if (opt.shape) {
data = this.reshape(data, opt, scaleSize);
}
if (opt.targetShape) {
data = this.allReshapeToRGB(data, opt, scaleSize);
}
return [{data: data, shape: opt.shape || opt.targetShape, name: 'image'}];
}
}
/* eslint-enable */
/* eslint-disable */
/**
* @file io,loader相关输入输出
* @author wangqun@baidu.com
*/
export default class io {
constructor() {
this.fromPixels2DContext = document.createElement('canvas').getContext('2d');
};
fromPixels(pixels, opt) {
pixels = pixels.input;
const shape = opt[0].shape;
const numChannels = opt[0].shape[0];
if (pixels == null) {
throw new Error(
'pixels passed to tf.browser.fromPixels() can not be null');
}
let vals;
// tslint:disable-next-line:no-any
// tslint:disable-next-line:no-any
if (pixels.getContext != null) {
// tslint:disable-next-line:no-any
vals = pixels
.getContext('2d')
.getImageData(0, 0, pixels.width, pixels.height)
.data;
} else if (pixels instanceof ImageData) {
vals = pixels.data;
} else if (
pixels instanceof HTMLImageElement ||
pixels instanceof HTMLVideoElement) {
if (this.fromPixels2DContext == null) {
throw new Error(
'Can\'t read pixels from HTMLImageElement outside ' +
'the browser.');
}
this.fromPixels2DContext.canvas.width = pixels.width;
this.fromPixels2DContext.canvas.height = pixels.height;
this.fromPixels2DContext.drawImage(
pixels, 0, 0, pixels.width, pixels.height);
vals = this.fromPixels2DContext
.getImageData(0, 0, pixels.width, pixels.height)
.data;
} else {
}
let values;
if (numChannels === 4) {
values = new Array(vals);
} else {
const numPixels = (shape[1] || pixels.width) * (shape[2] ||pixels.height);
// console.log(numPixels, numPixels * numChannels);
values = new Array(numPixels * numChannels);
for (let i = 0; i < numPixels; i++) {
for (let channel = 0; channel < numChannels; ++channel) {
values[i * numChannels + channel] = vals[i * 4 + channel];
}
}
}
// console.log(pixels.height, pixels.width, numChannels, values);
// const outShape: [number, number, number] =
// [pixels.height, pixels.width, numChannels];
values = [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
7.0,
0.0,
0.0,
0.0,
0.0,
0.0,
6.0,
7.0,
0.0,
0.0,
0.0,
0.0,
3.0,
1.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,
0.0,
3.0,
0.0,
0.0,
14.0,
16.0,
8.0,
1.0,
0.0,
0.0,
0.0,
14.0,
1.0,
0.0,
0.0,
14.0,
4.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,
0.0,
5.0,
13.0,
0.0,
0.0,
0.0,
9.0,
0.0,
27.0,
0.0,
0.0,
0.0,
5.0,
0.0,
0.0,
3.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,
4.0,
0.0,
0.0,
5.0,
11.0,
5.0,
4.0,
8.0,
0.0,
0.0,
15.0,
7.0,
0.0,
2.0,
7.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,
0.0,
0.0,
11.0,
2.0,
0.0,
0.0,
0.0,
0.0,
4.0,
11.0,
3.0,
0.0,
2.0,
0.0,
5.0,
3.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,
0.0,
0.0,
2.0,
0.0,
0.0,
10.0,
6.0,
0.0,
0.0,
0.0,
0.0,
4.0,
9.0,
0.0,
0.0,
2.0,
3.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,
8.0,
0.0,
8.0,
11.0,
0.0,
4.0,
113.0,
202.0,
249.0,
255.0,
255.0,
135.0,
44.0,
0.0,
7.0,
3.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,
2.0,
0.0,
2.0,
0.0,
33.0,
188.0,
230.0,
101.0,
52.0,
6.0,
106.0,
162.0,
183.0,
11.0,
0.0,
4.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,
9.0,
0.0,
4.0,
58.0,
230.0,
189.0,
31.0,
0.0,
3.0,
0.0,
14.0,
0.0,
204.0,
17.0,
7.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,
0.0,
0.0,
20.0,
24.0,
231.0,
181.0,
0.0,
0.0,
5.0,
4.0,
2.0,
0.0,
119.0,
228.0,
0.0,
1.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,
0.0,
0.0,
0.0,
173.0,
232.0,
32.0,
4.0,
10.0,
0.0,
0.0,
7.0,
79.0,
230.0,
108.0,
18.0,
0.0,
10.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,
2.0,
100.0,
246.0,
47.0,
0.0,
5.0,
0.0,
1.0,
8.0,
63.0,
216.0,
109.0,
0.0,
0.0,
6.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,
0.0,
8.0,
122.0,
210.0,
0.0,
31.0,
0.0,
8.0,
28.0,
109.0,
235.0,
182.0,
0.0,
13.0,
0.0,
22.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,
0.0,
0.0,
128.0,
233.0,
0.0,
6.0,
66.0,
126.0,
180.0,
191.0,
220.0,
27.0,
0.0,
0.0,
11.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,
0.0,
0.0,
0.0,
78.0,
246.0,
233.0,
220.0,
255.0,
199.0,
59.0,
235.0,
68.0,
12.0,
0.0,
1.0,
2.0,
1.0,
10.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,
2.0,
0.0,
80.0,
120.0,
139.0,
62.0,
0.0,
155.0,
211.0,
5.0,
10.0,
0.0,
0.0,
0.0,
3.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,
0.0,
1.0,
0.0,
5.0,
2.0,
0.0,
0.0,
90.0,
255.0,
70.0,
0.0,
0.0,
0.0,
9.0,
0.0,
0.0,
9.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,
0.0,
17.0,
5.0,
0.0,
11.0,
47.0,
227.0,
159.0,
0.0,
0.0,
8.0,
0.0,
0.0,
2.0,
6.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,
0.0,
5.0,
0.0,
0.0,
0.0,
4.0,
213.0,
207.0,
19.0,
0.0,
0.0,
3.0,
12.0,
0.0,
2.0,
4.0,
2.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,
1.0,
0.0,
16.0,
7.0,
91.0,
253.0,
50.0,
0.0,
0.0,
4.0,
0.0,
2.0,
0.0,
1.0,
2.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,
0.0,
2.0,
5.0,
0.0,
45.0,
252.0,
131.0,
0.0,
8.0,
0.0,
7.0,
0.0,
15.0,
5.0,
0.0,
0.0,
2.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,
1.0,
8.0,
11.0,
207.0,
205.0,
30.0,
2.0,
0.0,
0.0,
22.0,
0.0,
0.0,
4.0,
9.0,
11.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,
0.0,
0.0,
14.0,
155.0,
255.0,
28.0,
0.0,
0.0,
6.0,
4.0,
0.0,
5.0,
150.0,
210.0,
91.0,
17.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,
0.0,
14.0,
40.0,
250.0,
91.0,
0.0,
0.0,
7.0,
0.0,
0.0,
24.0,
0.0,
10.0,
130.0,
183.0,
147.0,
11.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,
0.0,
207.0,
146.0,
4.0,
0.0,
4.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,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
25.0,
237.0,
29.0,
0.0,
12.0,
0.0,
0.0,
14.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,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
13.0,
0.0,
15.0,
7.0,
0.0,
9.0,
2.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,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
4.0,
0.0,
4.0,
3.0,
4.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,
0.0,
0.0,
0.0
];
return [{data: values, shape: shape, name: 'pixel'}];
}
}
/* eslint-enable */
/* eslint-disable */
import VSHADER from '../shader/v_shader';
/**
* @file gpu运算
* @author yangmingming
*/
export default class gpu {
constructor(opts = {}) {
this.opts = opts;
opts.width_raw_canvas = Number(opts.width_raw_canvas) || 512;
opts.height_raw_canvas = Number(opts.height_raw_canvas) || 512;
let canvas = opts.el ? opts.el : document.createElement('canvas');
canvas.width = opts.width_raw_canvas;
canvas.height = opts.height_raw_canvas;
this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
this.gl.viewport(0, 0, canvas.width, canvas.height);
// Attempt to activate the extension, returns null if unavailable
this.textureFloat = this.gl.getExtension('OES_texture_float');
// this.setOutProps();
this.initCache();
console.log('float extension is started or not? ' + !!this.textureFloat);
console.log('WebGl版本是 ' + this.gl.getParameter(this.gl.SHADING_LANGUAGE_VERSION));
}
initCache() {
// 运行次数
this.times = 0;
const gl = this.gl;
// 缓存每个op的texture
// this.textures = [];
// 顶点数据
let vertices = new Float32Array([
-1.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 0.0]);
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// shader
this.vertexShader = null;
// 生成vertextShader
this.initShader(VSHADER);
this.fragmentShader = null;
// 上一个texture
this.prevTexture = null;
// 当前op输出texture
this.currentTexture = null;
// 帧缓存
this.frameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
// 计算texture cache, 最多3个
this.cacheTextures = [gl.createTexture(), gl.createTexture(), gl.createTexture()];
// texture buffer
this.textureBuffer = [gl.createTexture(), gl.createTexture()];
// program
this.programs = [gl.createProgram(), gl.createProgram()];
this.program = this.programs[0];
this.textureBufferIndex = 0;
for (let i = 0; i < 2; i++) {
gl.bindTexture(gl.TEXTURE_2D, this.textureBuffer[i]);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D, null);
}
}
runVertexShader() {
const gl = this.gl;
let aPosition = gl.getAttribLocation(this.program, 'position');
// Turn on the position attribute
gl.enableVertexAttribArray(aPosition);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 16, 0);
}
setOutProps(opts) {
this.width_shape_out = opts.width_shape || 1;
this.height_shape_out = opts.height_shape || 1;
this.width_texture_out = opts.width_texture || 1;
this.height_texture_out = opts.height_texture || 1;
this.total_shape = opts.total_shape || 0;
}
isFloatingTexture() {
return (this.textureFloat !== null);
}
attachShader(fshader) {
const gl = this.gl;
let index = this.textureBufferIndex % 2;
const program = this.programs[index];
this.program = program;
if (this.times < 2) {
gl.attachShader(program, this.vertexShader);
}
this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1;
this.gl.attachShader(program, fshader);
gl.linkProgram(program);
gl.useProgram(program);
if (this.times++ < 2) {
this.runVertexShader();
}
if (!!this.fragmentShader) {
const cache = this.programs[(index + 1) % 2];
gl.detachShader(cache, this.fragmentShader);
// gl.deleteShader(this.fragmentShader);
}
this.fragmentShader = fshader;
}
create(vshaderCode, fshaderCode) {
let gl = this.gl;
if (this.program) {
this.dispose();
}
// 创建 & 绑定程序对象
let program = this.program = gl.createProgram();
// 创建&绑定vertex&frament shader
this.initShader(vshaderCode);
this.fragmentShader = this.initShader(fshaderCode, 'fragment');
this.gl.attachShader(program, this.vertexShader);
this.gl.attachShader(program, this.fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
let aPosition = gl.getAttribLocation(program, 'position');
// Turn on the position attribute
gl.enableVertexAttribArray(aPosition);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 16, 0);
}
/**
* 初始化shader
* @param code shader代码
* @param type shader类型
* @return {object} 初始化成功返回shader
*/
initShader(code, type = 'vertex') {
const shaderType = type === 'vertex' ? this.gl.VERTEX_SHADER : this.gl.FRAGMENT_SHADER;
let shader;
if (type === 'vertex' && this.vertexShader) {
shader = this.vertexShader;
} else {
shader = this.gl.createShader(shaderType);
if (type === 'vertex') {
this.vertexShader = shader;
}
this.gl.shaderSource(shader, code);
this.gl.compileShader(shader);
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
throw new Error("compile: " + this.gl.getShaderInfoLog(shader));
}
}
return shader;
}
/**
* 更新fragment shader
* @param code shader代码
* @return {boolean} 更新成功过返回true
*/
updateShader(code) {
this.gl.useProgram(this.program);
// 删除fragment shader
if (this.fragmentShader) {
this.gl.detachShader(this.program, this.fragmentShader);
this.gl.deleteShader(this.fragmentShader);
// 删除texture
this.gl.deleteTexture(this.texture);
}
// 更新
this.fragmentShader = this.initShader(code, 'fragment');
return true;
}
/**
* 创建并绑定framebuffer, 之后attach a texture
* @param {WebGLTexture} texture 材质
* @returns {WebGLFramebuffer} The framebuffer
*/
attachFrameBuffer(opts = {}) {
this.prevTexture = this.currentTexture;
this.currentTexture = this.textureBuffer[this.textureBufferIndex % 2];
// this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1;
const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, // The target is always a FRAMEBUFFER.
gl.COLOR_ATTACHMENT0, // We are providing the color buffer.
gl.TEXTURE_2D, // This is a 2D image texture.
this.currentTexture, // The texture.
0 // 0, we aren't using MIPMAPs
);
gl.viewport(
0,
0,
opts.width_texture_out || this.width_texture_out,
opts.height_texture_out || this.height_texture_out
);
return this.frameBuffer;
}
// 帧缓存检测
frameBufferIsComplete() {
let gl = this.gl;
let message;
let status;
let value;
status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
switch (status)
{
case gl.FRAMEBUFFER_COMPLETE:
message = "Framebuffer is complete.";
value = true;
break;
case gl.FRAMEBUFFER_UNSUPPORTED:
message = "Framebuffer is unsupported";
value = false;
break;
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
message = "Framebuffer incomplete attachment";
value = false;
break;
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
message = "Framebuffer incomplete (missmatched) dimensions";
value = false;
break;
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
message = "Framebuffer incomplete missing attachment";
value = false;
break;
default:
message = "Unexpected framebuffer status: " + status;
value = false;
}
return {isComplete: value, message: message};
}
/**
* 更新材质
* @param {WebGLTexture} texture 材质对象
* @param {number} type 材质类型. FLOAT, UNSIGNED_BYTE, etc.
* @param {Float32Array[]} data 材质数据
*/
refreshTexture(texture, type, data) {
const gl = this.gl;
// Bind the texture so the following methods effect it.
gl.bindTexture(gl.TEXTURE_2D, texture);
// Replace the texture data
gl.texSubImage2D(gl.TEXTURE_2D, // Target, matches bind above.
0, // Level of detail.
0, // xOffset
0, // yOffset
this.opts.width_raw_canvas, // Width - normalized to s.
this.opts.height_raw_canvas, // Height - normalized to t.
gl.RGBA, // Format for each pixel.
type, // Data type for each chanel.
data); // Image data in the described format.
// Unbind the texture.
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
/**
* 初始化材质
* @param {int} index 材质索引
* @param {string} tSampler 材质名称
* @param {Object} bufferData 数据
*/
initTexture(index, item) {
const gl = this.gl;
let texture;
if (!item.data) {
texture = this.prevTexture;
} else {
// texture = gl.createTexture();
texture = this.cacheTextures[index];
// this.textures.push(texture);
}
gl.activeTexture(gl[`TEXTURE${index}`]);
gl.bindTexture(gl.TEXTURE_2D, texture);
if (item.data) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, item.width_texture || this.opts.width_raw_canvas,
item.height_texture || this.opts.height_raw_canvas, 0,
gl.RGBA, gl.FLOAT, item.data, 0);
}
}
getUniformLoc(name) {
let loc = this.gl.getUniformLocation(this.program, name);
if (loc === null) throw `getUniformLoc ${name} err`;
return loc;
}
// 生成帧缓存的texture
makeTexure(type, data, opts = {}) {
const gl = this.gl;
let index = this.textureBufferIndex % 2;
let texture = this.textureBuffer[index];
gl.bindTexture(gl.TEXTURE_2D, texture);
// Pixel format and data for the texture
gl.texImage2D(gl.TEXTURE_2D, // Target, matches bind above.
0, // Level of detail.
gl.RGBA, // Internal format.
opts.width_texture_out || this.width_texture_out,
opts.height_texture_out || this.height_texture_out,
0, // Always 0 in OpenGL ES.
gl.RGBA, // Format for each pixel.
type, // Data type for each chanel.
data); // Image data in the described format, or null.
// Unbind the texture.
// gl.bindTexture(gl.TEXTURE_2D, null);
this.attachFrameBuffer();
return texture;
}
render(data = []) {
const gl = this.gl;
let textureIndex = 0;
// 输入数据
data.forEach(item => {
if (item.type === 'texture') {
this.initTexture(textureIndex, item);
gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor), textureIndex++);
} else if (item.type === 'uniform') {
gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor), item.data);
}
});
// gl.clearColor(.0, .0, .0, 1);
// gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
compute() {
let gl = this.gl;
let pixels = new Float32Array(this.width_texture_out * this.height_texture_out * 4);
// gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl.RGBA, gl.FLOAT, pixels, 0);
let result = [];
for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) {
result.push(pixels[4 * i]);
}
return result;
}
dispose() {
const gl = this.gl;
this.cacheTextures.forEach(texture => {
gl.deleteTexture(texture);
});
this.cacheTextures = [];
this.programs.forEach(program => {
gl.detachShader(program, this.vertexShader);
gl.deleteShader(this.vertexShader);
gl.deleteProgram(program);
});
this.programs = [];
}
}
import 'babel-polyfill';
import Graph from './executor/loader';
import IO from './executor/io';
/**
* @file model demo 入口文件
* @author yangmingming@baidu.com
*
*/
// 'http://mms-xr.cdn.bcebos.com/paddle/mnist/model.json'
const MODEL_URL = '../demo/model/model.json';
const graphModel = new Graph();
const model = graphModel.loadGraphModel(MODEL_URL);
const cat = document.getElementById('pic');
const io = new IO();
let inst = model.execute({input: cat});
let res = inst.read();
console.dir(['result', res]);
var fileDownload = require('js-file-download');
fileDownload(res, "result.csv");
<!DOCYTPE html>
<html>
<head>
<meta charset="utf-8">
<title>paddle web demo</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>
<div><img id="pic" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+vTPDHwP8TeJ9DtdXiuLCzt7kbo0uWcOU7NgKRgjkc81i+O/hvrPgW8xco1zp7ELHfIm1HYqCRjJIPUc9cHFcbSgEnABJ9BXaafH8Rrrw3NpdjBrkmjohLQLE/l7c5OOPUHgV6Fcw3um/sxXNt4hZo7qW5X7FDdLtlRfOU7QG5zgSH/dPpXhFel/Bzxj4a8H6vfzeILZy86ILe6WLzPI27i3HUZ+XkA9PQ16Pc/Hfw7pM91LaXusa20wDRxSQRQww9eAdob35DfWuNg+Ny67Dfab430SDUNLuQxjW2UK8BwcAZPPOPmyCOvPSvH6KKKK//9k=" ></div>
</body>
<script src="index.es6"></script>
</html>
module.exports.create = function (args, scope, gl) {
const output = scope[args.outputs.Out[0]]
return {
inferShape() {
output.dim = output.dim.forEach(d => d === -1 ? 1 : d)
},
compute() {
console.log(output)
}
}
}
\ No newline at end of file
module.exports.create = function (args, scope, gl) {
const output = scope[args.outputs.Out[0]]
const input = scope[args.inputs.X[0]]
return {
inferShape() {
output.dim = output.dim.map(d => d === -1 ? 1 : d)
},
compute() {
console.log(input)
console.log(output)
}
}
}
module.exports.create = function (args, scope, gl) {
// const output = scope[args.outputs.Out[0]]
return {
inferShape() {
// output.dim = output.dim.forEach(d => d === -1 ? 1 : d)
},
compute() {
console.log(`${args.type} is going to be implemented`)
}
}
}
\ No newline at end of file
{
"name": "paddel-web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
/* eslint-disable */
import Gpu from '../gpu/gpu';
/**
* @file gpu运行时
* @author yangmingming
*
*/
export default {
/**
* 初始化, 生成gpu实例
* @param {Object} opts 运行时参数,包含el:canvas,dim: 256
* @return {Object} this 实例对象
*/
init(opts = {}) {
const gpu = this.gpu = new Gpu(opts);
if (gpu.isFloatingTexture()) {
return this;
} else {
return null;
}
},
run(opName, opData) {
let time = +Date.now();
let start = time;
let timeObj = {};
if (!opData.isPass) {
console.log('跳过当前op:' + opName);
return this;
}
// 设置gpu参数
const gpu = this.gpu;
gpu.setOutProps(opData.tensor['out']);
// 生成帧缓存材质
gpu.makeTexure(WebGLRenderingContext.FLOAT, null);
let end = +Date.now();
let bufferStatus = gpu.frameBufferIsComplete();
if (bufferStatus.isComplete) {
start = +Date.now();
timeObj['buferstatus-time'] = start - end;
gpu.attachShader(opData.fshader);
end = +Date.now();
timeObj['createshader-time'] = end - start;
timeObj['jsTime'] = end - time;
statistic.push(timeObj);
// 开始计算
this.gpu.render(opData.renderData);
return this;
} else {
return bufferStatus.message;
}
},
/**
* 读取op计算结果, 并返回数据
*/
read() {
return this.gpu.compute();
},
createFragmentShader(fsCode) {
return this.gpu.initShader(fsCode, 'fragment');
},
// 释放资源
dispose() {
this.gpu.dispose();
}
};
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
export default `
// 激活函数
float prelu(float x, float p, float b) {
float result = x;
if (x < 0.0) {
result = x * p;
}
return result;
}
float leakyRelu(float x, float p, float b) {
float result = max(x, x * p);
return result;
}
float scale(float x, float p, float b) {
float result = p * x + b;
return result;
}
float sigmoid(float x, float y, float z) {
float result = 1.0 / (1.0 + exp(-x));
return result;
}
float softmax(float x, float p, float b) {
float result = exp(x) / (10.0 * exp(x));
return result;
}
`;
/* eslint-disable */
/**
* @file 公共参数
* @author yangmingming
*/
export default `
// varying变量
// 顶点shader透传的材质坐标
varying vec2 vCoord;
// 扩展shader的ivec类型
// struct ivec5 {
// int x;
// int y;
// int z;
// int w;
// int u;
// };
// struct ivec6 {
// int x;
// int y;
// int z;
// int w;
// int u;
// int v;
// };
// dynamic的input数据
const float multi_value = float(MULTI_VALUE);
const float bias_value = float(BIAS_VALUE);
// 输出数据
const int width_shape_out = WIDTH_SHAPE_OUT;
const int height_shape_out = HEIGHT_SHAPE_OUT;
const int width_texture_out = WIDTH_TEXTURE_OUT;
const int height_texture_out = HEIGHT_TEXTURE_OUT;
const int channel_out = CHANNEL_OUT;
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
export default `
// TENSOR_TYPE, tensor坐标的类型,ivec4
// TENSOR_NAME, tensor name
// 获取tensor坐标对应数组中的索引
// uniform int numbers_shape_TENSOR_NAME[LENGTH_SHAPE_TENSOR_NAME];
int getArrayIndexFromTensorPos_TENSOR_NAME(TENSOR_TYPE tensorPos) {
int index = 0;
for (int i = 0; i < length_shape_TENSOR_NAME; i++) {
index += tensorPos[i] * numbers_shape_TENSOR_NAME[i];
}
return index;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, texture name
// WIDTH_TEXTURE_NAME_VALUE, texture的宽度
// 获取材质元素在数组中的索引
// const int width_TEXTURE_NAME = WIDTH_TEXTURE_NAME_VALUE;
export default `
int getArrayIndexFromTexturePos_TEXTURE_NAME(vec3 pos) {
int x = int(floor(pos.x));
int y = int(floor(pos.y));
int d = int(floor(pos.z));
return (width_TEXTURE_NAME * y + x) * 4 + d;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
export default `
ivec4 getOutputTensorPos() {
// 获取原始长度
vec2 outCoord = moveTexture2PosToReal_texture_out(vCoord.xy);
int x = int(outCoord.x / float(channel_out));
int c = int(mod(outCoord.x, float(channel_out)));
int y = int(mod(outCoord.y, float(height_shape_out)));
int b = int(outCoord.y / float(height_shape_out));
return ivec4(b, c, y, x);
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, tensor name
// 获取材质中的像素
// uniform sampler2D TEXTURE_NAME;
export default `
#define getPixelsFromTexturePos_TEXTURE_NAME(pos) texture2D(TEXTURE_NAME, pos)
`;
/* eslint-disable */
/**
* @file 公共方法, 获取[H, W]的power的总和
* @author yangmingming
*/
export default `
float getRangePowSumFromArrayIndex_TEXTURE_NAME(int start, float p, float mean) {
float result = 0.0;
for (int i = 0; i < (width_shape_TENSOR_NAME * height_shape_TENSOR_NAME); i++) {
vec3 pos = getTexturePosFromArrayIndex_TEXTURE_NAME(i + start);
result += pow(getValueFromTexturePos_TEXTURE_NAME(pos) - mean, p);
}
return result;
}
`;
/* eslint-disable */
/**
* @file 公共方法, 获取[H, W]的总和
* @author yangmingming
*/
export default `
float getRangeSumFromArrayIndex_TEXTURE_NAME(int start) {
float result = 0.0;
for (int i = 0; i < (width_shape_TENSOR_NAME * height_shape_TENSOR_NAME); i++) {
vec3 pos = getTexturePosFromArrayIndex_TEXTURE_NAME(i + start);
result += getValueFromTexturePos_TEXTURE_NAME(pos);
}
return result;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TENSOR_NAME, tensor name
// 获取数组元素索引为N的元素,在tensor上的坐标ivec4(batch, channel, height, width)
export default `
iTENSOR_TYPE getTensorPosFromArrayIndex_TENSOR_NAME(int n) {
iTENSOR_TYPE pos;
pos[0] = n / numbers_shape_TENSOR_NAME[0];
for (int i = 1; i < length_shape_TENSOR_NAME; i++) {
n = int(mod(float(n), float(numbers_shape_TENSOR_NAME[i - 1])));
pos[i] = n / numbers_shape_TENSOR_NAME[i];
}
return pos;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, 材质名称
// WIDTH_TEXTURE_NAME_VALUE, 材质宽度
// HEIGHT_TEXTURE_NAME_VALUE, 材质高度
// 获取数组元素索引为N的元素,在texture上的坐标
// const int width_TEXTURE_NAME = WIDTH_TEXTURE_NAME_VALUE;
// const int height_TEXTURE_NAME = HEIGHT_TEXTURE_NAME_VALUE;
export default `
vec3 getTexturePosFromArrayIndex_TEXTURE_NAME(int n) {
vec3 pos;
pos.z = mod(float(n), 4.0);
n /= 4;
int y = n / width_TEXTURE_NAME;
float width = float(width_TEXTURE_NAME);
float x = mod(float(n), width);
pos.x = x / width;
pos.y = float(y) / float(height_TEXTURE_NAME);
return pos;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
export default `
float getValueFromTensorPos_TENSOR_NAME(int r, int g, int b, int a) {
vec4 pixels = texture2D(texture_TENSOR_NAME, vec2((float(a * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME), (float(r * height_shape_TENSOR_NAME + b) + 0.5) / float(height_texture_TENSOR_NAME)));
return pixels.r;
}
float getValueFromTensorPos_TENSOR_NAME(ivec4 pos) {
float offset = 0.5;
float width = float(pos.a * channel_TENSOR_NAME + pos.g) + offset;
float height = float(pos.r * height_shape_TENSOR_NAME + pos.b) + offset;
vec4 pixels = texture2D(texture_TENSOR_NAME, vec2(width / float(width_texture_TENSOR_NAME), height / float(height_texture_TENSOR_NAME)));
return pixels.r;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, tensor name
// 获取材质中的数据
// uniform sampler2D TEXTURE_NAME;
export default `
float getValueFromTexturePos_TEXTURE_NAME(vec3 pos) {
vec4 pixels = texture2D(TEXTURE_NAME, pos.xy);
int d = int(pos.z);
if (d == 0) {
return pixels.r;
} else if (d == 1) {
return pixels.g;
} else if (d == 2) {
return pixels.b;
}
return pixels.a;
}
`;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, 材质name
// 材质坐标转化成真实尺寸坐标
export default `
vec2 _2d_shape_TEXTURE_NAME = vec2(float(width_TEXTURE_NAME), float(height_TEXTURE_NAME));
vec2 moveTexture2PosToReal_TEXTURE_NAME(vec2 v) {
return v * _2d_shape_TEXTURE_NAME;
// vec2 v2;
// v2.x = v.x * float(width_TEXTURE_NAME);
// v2.y = v.y * float(height_TEXTURE_NAME);
// return v2;
}
`;
/* eslint-disable */
/**
* @file 预设条件
* @author yangmingming
*/
export default `
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
`;
/* eslint-disable */
/**
* @file 激活函数
* @author yangmingming
*/
// 激活函数
export default `
float prelu(float x, float p, float b) {
float result = x;
if (x < 0.0) {
result = x * p;
}
return result;
}
`;
/* eslint-disable */
/**
* @file 激活函数
* @author yangmingming
*/
export default `
float scale(float x, float p, float b) {
float result = p * x + b;
return result;
}
`;
/* eslint-disable */
/**
* @file 激活函数
* @author yangmingming
*/
// 激活函数
export default `
float sigmoid(float x, float y, float z) {
float result = 1.0 / (1.0 + exp(-x));
return result;
}
`;
/* eslint-disable */
/**
* @file softmax激活函数
* @author yangmingming
*/
export default `
float softmax(float x, float p, float b) {
float result = x;
if (x < 0.0) {
result = x * p;
}
return result;
}
`;
/* eslint-disable */
/**
* @file 公共方法-尾部, 方法1: 获取输出坐标
* @author yangmingming
*/
export default `
vec2 _2d_shape_texture_out = vec2(float(width_texture_out), float(height_texture_out));
ivec4 getOutputTensorPos() {
// 获取原始长度
vec2 outCoord = vCoord.xy * _2d_shape_texture_out;
int x = int(outCoord.x / float(channel_out));
int c = int(mod(outCoord.x, float(channel_out)));
int y = int(mod(outCoord.y, float(height_shape_out)));
int b = int(outCoord.y / float(height_shape_out));
return ivec4(b, c, y, x);
}
`;
/* eslint-disable */
/**
* @file 新增类型
* @author yangmingming
*/
// 扩展shader的ivec类型
export default `
struct ivec5 {
int x;
int y;
int z;
int w;
int u;
};
struct ivec6 {
int x;
int y;
int z;
int w;
int u;
int v;
};
`;
/* eslint-disable */
/**
* @file batchnorm的配置文件
* @author yangmingming
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
},
{
func: 'getPixelsFromTexturePos',
conf: {
TEXTURE_NAME: 'texture_scale'
}
}
],
conf: [
'WIDTH_SHAPE_ORIGIN',
'HEIGHT_SHAPE_ORIGIN',
'LENGTH_SHAPE_ORIGIN',
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'CHANNEL_ORIGIN',
'TOTAL_SHAPE_ORIGIN',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'EPSILON',
'WIDTH_TEXTURE_SCALE',
'HEIGHT_TEXTURE_SCALE',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'scale',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file softmax主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
// 输出数据
ivec4 oPos = getOutputTensorPos();
float o = getValueFromTensorPos_origin(oPos);
// 归一化数据
vec4 scale = getPixelsFromTexturePos_texture_scale(vec2((float(int(oPos.g)) + 0.5) / float(width_texture_scale), 0.0));
float x = (o - scale[3]) / sqrt(scale[2] + epsilon);
float res = scale[0] * x + scale[1];
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file batchnorm参数文件
* @author yangmingming
*/
export default `
// 输入数据
const int width_shape_origin = WIDTH_SHAPE_ORIGIN;
const int height_shape_origin = HEIGHT_SHAPE_ORIGIN;
const int length_shape_origin = LENGTH_SHAPE_ORIGIN;
const int width_texture_origin = WIDTH_TEXTURE_ORIGIN;
const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN;
const int channel_origin = CHANNEL_ORIGIN;
const int total_shape_origin = TOTAL_SHAPE_ORIGIN;
// 计算数据
const float epsilon = float(EPSILON);
const int width_texture_scale = WIDTH_TEXTURE_SCALE;
const int height_texture_scale = HEIGHT_TEXTURE_SCALE;
// 输入数据
uniform sampler2D texture_origin;
uniform sampler2D texture_scale;
`;
/* eslint-disable */
/**
* @file conv2d的配置文件
* @author yangmingming
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
},
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'filter'
}
}
],
conf: [
'LENGTH_SHAPE_FILTER',
'WIDTH_SHAPE_FILTER',
'HEIGHT_SHAPE_FILTER',
'WIDTH_TEXTURE_FILTER',
'HEIGHT_TEXTURE_FILTER',
'CHANNEL_FILTER',
'WIDTH_SHAPE_ORIGIN',
'HEIGHT_SHAPE_ORIGIN',
'LENGTH_SHAPE_ORIGIN',
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'CHANNEL_ORIGIN',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'STRIDE_HORIZONTAL',
'STRIDE_VERTICAL',
'PAD_LEFT',
'PAD_TOP',
'DILATION_HORIZONTAL',
'DILATION_VERTICAL',
'GROUPS',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
// {
// tensor: 'filter',
// variable: 'numbers_shape',
// setter: 'uniform1iv',
// type: 'uniform'
// },
{
tensor: 'filter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
// {
// tensor: 'origin',
// variable: 'numbers_shape',
// setter: 'uniform1iv',
// type: 'uniform'
// },
// {
// tensor: 'out',
// variable: 'numbers_shape',
// setter: 'uniform1iv',
// type: 'uniform'
// }
]
};
/* eslint-disable */
/**
* @file 主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
ivec4 oPos = getOutputTensorPos();
int x = oPos.a;
int c = oPos.g;
int y = oPos.b;
int b = oPos.r;
float res = 0.0;
// 获取output的坐标
int oTensorChannel = (c / (channel_out / groups)) * channel_filter;
int oy = y * stride_v - padTop;
for (int fy = 0; fy < height_shape_filter; fy++) {
if (oy >= height_shape_origin) {
break;
}
if (oy < 0) {
oy += dilation_v;
continue;
}
int ox = x * stride_h - padLeft;
for (int fx = 0; fx < width_shape_filter; fx++) {
if (ox >= width_shape_origin) {
break;
}
if (ox < 0) {
ox += dilation_h;
continue;
}
// channel计算
for (int j = 0; j < channel_filter; j++) {
float f = getValueFromTensorPos_filter(c, j, fy, fx);
float o = getValueFromTensorPos_origin(b, oTensorChannel + j, oy, ox);
res += f * o;
}
ox += dilation_h;
}
oy += dilation_v;
}
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file 参数文件
* @author yangmingming
*/
export default `
// conv2d的input数据
// 常量
// 卷积核
const int length_shape_filter = LENGTH_SHAPE_FILTER;
const int width_shape_filter = WIDTH_SHAPE_FILTER;
const int height_shape_filter = HEIGHT_SHAPE_FILTER;
const int width_texture_filter = WIDTH_TEXTURE_FILTER;
const int height_texture_filter = HEIGHT_TEXTURE_FILTER;
const int channel_filter = CHANNEL_FILTER;
// 输入数据
const int width_shape_origin = WIDTH_SHAPE_ORIGIN;
const int height_shape_origin = HEIGHT_SHAPE_ORIGIN;
const int length_shape_origin = LENGTH_SHAPE_ORIGIN;
const int width_texture_origin = WIDTH_TEXTURE_ORIGIN;
const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN;
const int channel_origin = CHANNEL_ORIGIN;
// 计算相关
// 拆分步长
const int stride_h = STRIDES_X;
const int stride_v = STRIDES_Y;
// padding的数目
const int padLeft = PADDINGS_X;
const int padTop = PADDINGS_Y;
// dilation膨胀系数
const int dilation_h = DILATIONS_X;
const int dilation_v = DILATIONS_Y;
// groups
const int groups = GROUPS;
// uniform变量
// 卷积核
uniform sampler2D texture_filter;
// 输入数据
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file dynamic的配置文件
* @author yangmingming
*/
export default {
dep: [
{
func: 'getPixelsFromTexturePos',
conf: {
TEXTURE_NAME: 'texture_origin'
}
}
],
conf: [
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file 主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
// 输出数据
float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float res = ACTIVE_FUNCTION(o, multi_value, bias_value);
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file 参数文件
* @author yangmingming
*/
export default `
// 输入数据
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file 加法的配置文件
* @author yangmingming
*/
export default {
dep: [
{
func: 'getPixelsFromTexturePos',
conf: {
TEXTURE_NAME: 'texture_origin'
}
},
{
func: 'getPixelsFromTexturePos',
conf: {
TEXTURE_NAME: 'texture_counter'
}
}
],
conf: [
'WIDTH_SHAPE_ORIGIN',
'HEIGHT_SHAPE_ORIGIN',
'LENGTH_SHAPE_ORIGIN',
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'CHANNEL_ORIGIN',
'TOTAL_SHAPE_COUNTER',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'AXIS',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'counter',
variable: 'data',
setter: 'uniform1fv',
type: 'uniform'
}
]
};
/* eslint-disable */
/**
* @file 加法主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
// 输出数据
ivec4 oPos = getOutputTensorPos();
int index = oPos[axis];
float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float c = getValueFromCounter(index);
float res = ACTIVE_FUNCTION(o + c, multi_value, bias_value);
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file 加法参数
* @author yangmingming
*/
export default `
// 输入数据
const int axis = AXIS;
// const int total_shape_counter = TOTAL_SHAPE_COUNTER;
uniform float data_counter[TOTAL_SHAPE_COUNTER];
uniform sampler2D texture_origin;
float getValueFromCounter(int index) {
for (int i = 0; i < TOTAL_SHAPE_COUNTER; i++) {
if (i == index) {
return data_counter[i];
}
}
return 0.0;
}
`;
/* eslint-disable */
/**
* @file mul的配置文件
* @author yangmingming zhangmiao06
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'counter'
}
},
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
}
],
conf: [
'LENGTH_SHAPE_COUNTER',
'WIDTH_SHAPE_COUNTER',
'HEIGHT_SHAPE_COUNTER',
'WIDTH_TEXTURE_COUNTER',
'HEIGHT_TEXTURE_COUNTER',
'CHANNEL_COUNTER',
'WIDTH_SHAPE_ORIGIN',
'HEIGHT_SHAPE_ORIGIN',
'LENGTH_SHAPE_ORIGIN',
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'CHANNEL_ORIGIN',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT'
],
input: [
{
tensor: 'counter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file mul主函数
*/
export default `
// start函数
void main(void) {
float res = 0.0;
// 获取output的坐标
ivec4 out_pos = getOutputTensorPos();
for (int j = 0; j < width_shape_origin; j++) {
float c = getValueFromTensorPos_counter(out_pos[0], out_pos[1], j, out_pos[3]);
float o = getValueFromTensorPos_origin(out_pos[0], out_pos[1], out_pos[2], j);
res += c * o;
}
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file mul参数文件
*/
export default `
// mul的input数据
// 常量
// 输入数据
const int length_shape_counter = LENGTH_SHAPE_COUNTER;
const int width_shape_counter = WIDTH_SHAPE_COUNTER;
const int height_shape_counter = HEIGHT_SHAPE_COUNTER;
const int width_texture_counter = WIDTH_TEXTURE_COUNTER;
const int height_texture_counter = HEIGHT_TEXTURE_COUNTER;
const int channel_counter = CHANNEL_COUNTER;
const int width_shape_origin = WIDTH_SHAPE_ORIGIN;
const int height_shape_origin = HEIGHT_SHAPE_ORIGIN;
const int length_shape_origin = LENGTH_SHAPE_ORIGIN;
const int width_texture_origin = WIDTH_TEXTURE_ORIGIN;
const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN;
const int channel_origin = CHANNEL_ORIGIN;
// uniform变量
// 输入数据
uniform sampler2D texture_counter;
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file pool2d的配置文件
* @author yangmingming zhangmiao06
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
}
],
conf: [
'KSIZE_X',
'KSIZE_Y',
'TYPE_POOL',
'WIDTH_SHAPE_ORIGIN',
'HEIGHT_SHAPE_ORIGIN',
'LENGTH_SHAPE_ORIGIN',
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'CHANNEL_ORIGIN',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'STRIDES_X',
'STRIDES_Y',
'PADDING_X',
'PADDING_Y'
],
input: [
// texture类型,若添加from: 'prev', 表示读取上一个op的产出
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file pool2d主函数
*/
export default `
// start函数
void main(void) {
float res = (-1.0 / exp(-20.0));
// 获取output的坐标
ivec4 out_pos = getOutputTensorPos();
// X、Y方向的移动步长
int count_pool = 0;
int oy_base = out_pos[2] * stride_v - padTop;
int ox_base = out_pos[3] * stride_h - padLeft;
for (int fy = 0; fy < height_shape_pool; fy++) {
int oy = oy_base + fy;
if (oy >= height_shape_origin) {
break;
}
if (oy < 0) {
continue;
}
for (int fx = 0; fx < width_shape_pool; fx++) {
int ox = ox_base + fx;
if (ox >= width_shape_origin) {
break;
}
if (ox < 0) {
continue;
}
// origin数据
float curr = getValueFromTensorPos_origin(out_pos[0], out_pos[1], oy, ox);
if (type_pool == 1) {
if (curr > res) {
res = curr;
}
} else {
res += curr;
// 在平均池化模式忽略填充值(exclusive默认为true)
count_pool++;
}
}
}
if (type_pool != 1) {
res = res / float(count_pool);
}
gl_FragColor.r = res;
}
`;
/* eslint-disable */
/**
* @file pool2d参数文件
*/
export default `
// 常量
// 池化大小
const int width_shape_pool = KSIZE_X;
const int height_shape_pool = KSIZE_Y;
const int type_pool = TYPE_POOL;
// 输入数据
const int width_shape_origin = WIDTH_SHAPE_ORIGIN;
const int height_shape_origin = HEIGHT_SHAPE_ORIGIN;
const int length_shape_origin = LENGTH_SHAPE_ORIGIN;
const int width_texture_origin = WIDTH_TEXTURE_ORIGIN;
const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN;
const int channel_origin = CHANNEL_ORIGIN;
// 计算相关
// 拆分步长
const int stride_h = STRIDES_X;
const int stride_v = STRIDES_Y;
// padding的数目
const int padLeft = PADDINGS_X;
const int padTop = PADDINGS_Y;
// uniform变量
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file softmax的配置文件
* @author yangmingming
*/
export default {
dep: [
{
func: 'getPixelsFromTexturePos',
conf: {
TEXTURE_NAME: 'texture_origin'
}
}
],
conf: [
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'TOTAL_SHAPE_ORIGIN'
],
input: [
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file softmax主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
vec4 v4 = getPixelsFromTexturePos_texture_origin(vCoord);
vec2 onePixel = vec2(1.0 / float(width_texture_origin), 1.0 / float(height_texture_origin));
float total = 0.0;
float maxValue = getPixelsFromTexturePos_texture_origin(onePixel).r;
int number = 0;
vec4 pixels;
// 求最大
for (int i = 0; i < height_texture_origin; i++) {
for (int j = 0; j < width_texture_origin; j++) {
pixels = getPixelsFromTexturePos_texture_origin(onePixel * vec2(float(j), float(i)));
number = i * width_texture_origin + j;
if ((number * 4 + 1) < total_shape_origin) {
maxValue = max(pixels.r, maxValue);
}
if ((number * 4 + 2) < total_shape_origin) {
maxValue = max(pixels.g, maxValue);
}
if ((number * 4 + 3) < total_shape_origin) {
maxValue = max(pixels.b, maxValue);
}
if ((number * 4 + 4) < total_shape_origin) {
maxValue = max(pixels.a, maxValue);
}
}
}
// 求和
for (int i = 0; i < height_texture_origin; i++) {
for (int j = 0; j < width_texture_origin; j++) {
pixels = getPixelsFromTexturePos_texture_origin(onePixel * vec2(float(j), float(i)));
number = i * width_texture_origin + j;
if ((number * 4 + 1) < total_shape_origin) {
total += exp(pixels.r - maxValue);
}
if ((number * 4 + 2) < total_shape_origin) {
total += exp(pixels.g - maxValue);
}
if ((number * 4 + 3) < total_shape_origin) {
total += exp(pixels.b - maxValue);
}
if ((number * 4 + 4) < total_shape_origin) {
total += exp(pixels.a - maxValue);
}
}
}
gl_FragColor = exp(v4 - vec4(maxValue, maxValue, maxValue, maxValue)) / vec4(total, total, total, total) ;
}
`;
/* eslint-disable */
/**
* @file softmax参数文件
* @author yangmingming
*/
export default `
// 输入数据
const int width_texture_origin = WIDTH_TEXTURE_ORIGIN;
const int height_texture_origin = HEIGHT_TEXTURE_ORIGIN;
const int total_shape_origin = TOTAL_SHAPE_ORIGIN;
// uniform变量
// 输入数据
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file 顶点文件
* @author yangmingming
*/
export default `
attribute vec4 position;
varying vec2 vCoord;
void main() {
vCoord.x = (position.x + 1.0) / 2.0;
vCoord.y = (position.y + 1.0) / 2.0;
gl_Position = position;
}
`;
/* eslint-disable */
import Utils from './utils';
import Tensor from './tensor';
/**
* @file op的数据对象
* @author yangmingming
*
*/
const keys = [
'paddings',
'strides',
'dilations',
'ksize'
];
// 从tensor对象中获取的数据
const tensorAttrs = [
'length_shape',
'width_shape',
'height_shape',
'width_texture',
'height_texture',
'channel',
'total_shape'
];
// shader中需要的常量
const shaderAttrs = {
scale: {
'bias': 'bias_value',
'scale': 'multi_value'
},
pool2d: {
'pooling_type': 'type_pool'
}
};
// model的名字和paddle web的tensor名字mapping
const tensorName = {
'input': 'origin',
'x': 'origin',
'filter': 'filter',
'y': 'counter',
'output': 'out',
'out': 'out',
'scale': 'scale',
'bias': 'bias',
'mean': 'mean',
'variance': 'variance'
};
// unique behavior
const opBehavior = {
conv2d: [
'needBatch'
],
batchnorm: [
'needBatch',
'mergeTensor'
],
elementwise_add: [
'broadcast',
'needBatch'
],
pool2d: [
'isMax',
'needBatch',
'isGlobalPooling'
],
relu: [
'transToPrelu',
'needBatch'
],
leaky_relu: [
'transToLeakyrelu',
'needBatch'
],
mul: [
'reshape',
'needBatch'
]
};
export default class OpData {
constructor(name, input = {}, output = {}, attrs = {}) {
this.name = name;
this.attrs = attrs;
// 是否忽略当前当前op, 使用dropout
this.isPass = this.checkIsPass();
if (this.isPass) {
this.input = input;
this.output = output;
// op数据, 当前不扩展
this.data = {
'active_function': 'scale',
'multi_value': '1.0',
'bias_value': '0.0'
};
// tensor数据
this.tensor = {};
this.buildTensor();
this.buildAttrs();
}
}
buildTensor() {
// todo: 是否需要形状对齐
// todo: 是否需要广播tensor
const tensorData = [];
for (let key in this.input) {
if (this.input.hasOwnProperty(key)) {
const data = this.input[key] || [{}];
// 默认取第一个数据
if (tensorName[key.toLowerCase()]) {
data[0].tensorName = tensorName[key.toLowerCase()];
tensorData.push(data[0]);
}
}
}
// debugger
// todo: 临时删除output里的Y
delete this.output.Y;
// 输出tensor
for (let key in this.output) {
if (this.output.hasOwnProperty(key)) {
// 默认取第一个数据
const data = this.output[key] || [{}];
if (tensorName[key.toLowerCase()]) {
data[0].tensorName = tensorName[key.toLowerCase()];
tensorData.push(data[0]);
}
}
}
// unique behavior
const behavior = opBehavior[this.name] || [];
behavior.forEach(behavior => {
this[behavior](tensorData);
});
// 生成tensor对象
tensorData.forEach(data => {
if (data) {
if (data.notTensor) {
this.tensor[data.tensorName] = {
name: data.tensorName,
data: new Float32Array(data.data),
total_shape: data.data.length
};
} else {
this.tensor[data.tensorName] = new Tensor({
name: data.tensorName,
shape: data.shape,
data: data.data,
needBatch: data.needBatch || false,
notCompressed: data.notCompressed || false
});
}
}
});
// console.dir(['tensors', this.tensor]);
}
buildAttrs() {
// 计算属性
for (let key in this.attrs) {
if (this.attrs.hasOwnProperty(key)) {
const item = this.attrs[key];
if (Object.prototype.toString.call(item) === '[object Array]') {
if (keys.indexOf(key) > -1) {
this.data[key + '_x'] = item[0];
this.data[key + '_y'] = item[1];
}
} else {
this.data[key] = item;
// 获取shader所需的数据
let shaderAttr = shaderAttrs[this.name];
if (shaderAttr && shaderAttr.hasOwnProperty(key)) {
this.data[shaderAttr[key]] = item;
}
}
}
}
// 获取tensor的数据
for (let key in this.tensor) {
const tensor = this.tensor[key];
tensorAttrs.forEach(attr => {
this.data[attr+ '_' + tensor.name] = tensor[attr];
});
}
}
needBatch(tensorData = []) {
tensorData.forEach(data => (data.needBatch = true));
}
isGlobalPooling(tensorData = []) {
let counter = tensorData.filter(tensor => (tensor.tensorName === 'origin'))[0] || {};
let length = counter.shape && counter.shape.length || 0;
if (length > 2 && this.attrs['global_pooling']) {
this.attrs.ksize = [counter.shape[length - 2], counter.shape[length - 1]];
}
}
broadcast(tensorData = []) {
const x = tensorData[0];
const y = tensorData[1];
let small = y;
if (x.shape.length - y.shape.length < 0) {
small = x;
}
// face model
small.notTensor = true;
return;
// mobilenet model
// todo: 默认y的shape length是1, 以后需要实现通用版本
let shape = Utils.getBroadcastShapeInPaddle(x.shape, y.shape, this.attrs['axis']);
// 填充shape数据
if (small.shape.length === 1) {
const result = [];
small.shape = shape;
let total = shape.reduce((all, num) => all * num);
for (let i = 0; i < small.shape[0]; i++) {
let item = small.data[i];
for (let j = 0; j < total / shape[0]; j++) {
result.push(item);
}
}
small.data = result;
}
}
isMax(tensorData = []) {
const type = this.attrs['pooling_type'] === 'max' ? 1 : 0;
this.attrs['pooling_type'] = type;
}
transToPrelu(tensorData = []) {
this.data['multi_value'] = '0.0';
this.data['active_function'] = 'prelu';
}
transToLeakyrelu(tensorData = []) {
this.data['multi_value'] = this.attrs.alpha;
this.data['active_function'] = 'leakyRelu';
this.name = 'relu';
}
setActiveFunc(tensorData = []) {
this.data['multi_value'] = '0.0';
this.data['active_function'] = 'softmax';
}
reshape(tensorData = []) {
let input = tensorData[0];
let counter = tensorData[1];
if (counter.shape.length > input.shape.length) {
input = tensorData[1];
counter = tensorData[0];
}
if (input.shape.length > 2 && counter.shape.length === 2) {
let shape = Utils.getReshapeInPaddle(input.shape, counter.shape, tensorData[2].shape);
input.shape = shape;
}
}
mergeTensor(tensorData = []) {
// 融合scale、bias、variance、mean
let constants = ['scale', 'bias', 'variance', 'mean'];
let result = {};
let data = [];
tensorData.forEach((tensor, index) => {
result[tensor.tensorName] = tensor;
result[tensor.tensorName + 'Index'] = index;
});
for (let i = 0; i < result[constants[0]].shape[0]; i++) {
data.push(result[constants[0]].data[i]);
data.push(result[constants[1]].data[i]);
data.push(result[constants[2]].data[i]);
data.push(result[constants[3]].data[i]);
}
tensorData[result[constants[0] + 'Index']].data = data;
// 充分利用shader空间
tensorData[result[constants[0] + 'Index']].notCompressed = true;
tensorData[result[constants[0] + 'Index']].shape[0] *= 4;
tensorData.splice(result[constants[1] + 'Index'], 1, 0);
tensorData.splice(result[constants[2] + 'Index'], 1, 0);
tensorData.splice(result[constants[3] + 'Index'], 1, 0);
}
checkIsPass() {
if (this.name === 'dropout') {
if (this.attrs['dropout_implementation'] === 'downgrade_in_infer') {
this.name = 'scale';
this.attrs['scale'] = this.attrs['dropout_prob'];
this.attrs['bias'] = 0.0;
return true;
}
return false;
}
if (this.name === 'depthwise_conv2d') {
this.name = 'conv2d';
}
return true;
}
dispose() {
this.input = null;
this.output = null;
this.attrs = null;
for (let key in this.tensor) {
this.tensor[key].dispose();
}
this.tensor = {};
}
}
/* eslint-disable */
import Utils from './utils';
/**
* @file Tensor类
* @author yangmingming
*/
export default class Tensor {
constructor(opts = {}) {
this.opts = opts;
// 设置tensor名字
this.name = opts.name;
// tensor的形状
let shape = this.shape = opts.shape;
// 原始数据个数
this.total = shape.reduce((all, num) => all * num);
// 图像tensor是否带有batch
if (opts.needBatch && shape.length < 4) {
let batch = [];
for (let i = 0; i < (4 - shape.length); i++) {
batch.push(1);
}
shape = batch.concat(shape);
this.shape = shape;
}
// 获取转换到texture后的信息
let {zeroNumber, shape: shape_texture} = Utils.getTextureInfoFromTensorShape(shape);
this.shape_texture = shape_texture;
// tensor数据
let data = [];
if (opts.data && opts.data.length) {
if (!opts.notCompressed) {
let b = shape[0];
let c = shape[1];
let h = shape[2];
let w = shape[3];
for (let i = 0; i < opts.data.length; i++) {
let j = Math.floor(i / (c * w));
let k = Math.floor(i % (c * w));
let b1 = Math.floor(j / h);
let h1 = Math.floor(j % h);
let c1 = Math.floor(k % c);
let w1 = Math.floor(k / c);
let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1;
data.push(opts.data[l]);
data.push(0);
data.push(0);
data.push(0);
}
} else {
// batchnorm的scale
this.shape_texture = [4, 1, this.total / 4];
data = [].concat(opts.data);
}
this.data = new Float32Array(data);
// 清理缓存
opts.data = null;
}
}
/**
* 获取数组下标, shape例子[M, W, H, D]
* @param pos {Array} tensor坐标索引
* @return {Number} tensor数据
*/
getValue(pos = []) {
let p = [].concat(pos);
let len = p.length;
let sLen = this.shape.length;
// 补齐
for (let i = 0; i < (sLen - len); i++) {
p.unshift(0);
}
let index = 0;
for (let i = 0; i < sLen; i++) {
index += p[i] * this.shapeNumbers[i];
}
return this.data[index];
}
get width_texture() {
let length = this.shape_texture.length;
return this.shape_texture[length - 1];
}
get height_texture() {
let length = this.shape_texture.length;
return this.shape_texture[length - 2];
}
get width_shape() {
let length = this.shape.length;
return this.shape[length - 1];
}
get height_shape() {
let length = this.shape.length;
return this.shape[length - 2];
}
get channel() {
let length = this.shape.length;
if (length >= 3) {
return this.shape[length - 3];
}
return 0;
}
get length_shape() {
return this.shape.length || 0;
}
/**
* 获取shape对应的个数
* @return {Array} 和shape长度相等的对应个数
*/
get numbers_shape() {
let numbers = [];
let sLen = this.shape.length;
for (let i = 0; i < (sLen - 1); i++) {
let number = this.shape.slice(i + 1).reduce((total, num) => total * num);
numbers.push(number);
}
// 和shape长度保持一致
numbers.push(1);
return numbers;
}
get total_shape() {
return this.total;
}
dispose() {
if (this.data) {
this.data = null;
}
}
}
/* eslint-enable */
/**
* @file 工具类
* @author yangmingming
*/
/* eslint-disable */
export default {
// todo: 适用2维矩阵乘法,以后实现通用版本
getReshapeInPaddle(inputShape = [], counterShape = [], outShape = []) {
let total = inputShape.reduce((all, num) => all * num);
if (outShape.length === 1) {
return [1, total];
} else {
return [outShape[0], total / outShape[0]];
}
},
getBroadcastShapeInPaddle(shapeA= [], shapeB = [], axis = 1) {
// todo: 简易版本,以后需要实现一个通用版本
let bigger = shapeA;
let result = shapeB;
if (shapeA.length - shapeB.length < 0) {
bigger = shapeB;
result = shapeA;
}
return result.concat(bigger.slice(axis));
},
getBroadcastDims(inShape = [], outShape = []) {
const inRank = inShape.length;
const dims = [];
for (let i = 0; i < inRank; i++) {
const dim = inRank - 1 - i;
const a = inShape[dim] || 1;
const b = outShape[outShape.length - 1 - i] || 1;
if (b > 1 && a === 1) {
dims.unshift(dim);
}
}
return dims;
},
getBroadcastShape(shapeA = [], shapeB = []) {
const result = [];
const max = Math.max(shapeA.length, shapeB.length);
for (let i = 0; i < max; i++) {
let a = shapeA[shapeA.length - i - 1];
if (a === null) {
a = 1;
}
let b = shapeB[shapeB.length - i - 1];
if (b === null) {
b = 1;
}
if (a === 1) {
result.unshift(b);
} else if (b === 1) {
result.unshift(a);
} else if (a !== b) {
return null;
} else {
result.unshift(a);
}
}
return result;
},
/**
* 获取texture形状和补0个数
* @param shape {Array} tensor的形状
* @return {{shape: *[], zeroNumber: number}} {Object} texture信息
*/
getTextureInfoFromTensorShape(shape = []) {
let b = shape[0];
let c = shape[1];
let h = shape[2];
let w = shape[3];
return {
shape: [4, b * h, c * w],
zeroNumber: 0
};
},
// 获取数组中的最大值和索引
getMaxItem(datas = []) {
let max = Math.max.apply(null, datas);
let index = datas.indexOf(max);
return {value: max, index};
},
// 压缩
async loadShader(name) {
let shader = await fetch(this.getShaderFile(name));
return shader.text();
},
getShaderFile(url) {
// todo: 根据脚手架获取shader文件
const aa = url.split('/');
let length = aa.length;
return '/' + aa[length - 1];
},
img2texture(renderData = {}) {
const {height_texture, width_texture, shape} = renderData;
const total = height_texture * width_texture * 4;
const b = shape[0];
const c = shape[1];
const h = shape[2];
const w = shape[3];
const data = [];
for (let i = 0; i < total; i++) {
let j = Math.floor(i / (c * w));
let k = Math.floor(i % (c * w));
let b1 = Math.floor(j / h);
let h1 = Math.floor(j % h);
let c1 = Math.floor(k % c);
let w1 = Math.floor(k / c);
let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1;
data.push(renderData.data[l]);
data.push(0);
data.push(0);
data.push(0);
}
renderData.data = new Float32Array(data);
}
};
/* eslint-enable */
/*!
diff v2.0.1
Software License Agreement (BSD License)
Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
All rights reserved.
Redistribution and use of this software in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of Kevin Decker nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@license
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define(factory);
else if(typeof exports === 'object')
exports["JsDiff"] = factory();
else
root["JsDiff"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
/* See LICENSE file for terms of use */
/*
* Text diff implementation.
*
* This library supports the following APIS:
* JsDiff.diffChars: Character by character diff
* JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace
* JsDiff.diffLines: Line based diff
*
* JsDiff.diffCss: Diff targeted at CSS content
*
* These methods are based on the implementation proposed in
* "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
*/
'use strict';
exports.__esModule = true;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _diffBase = __webpack_require__(1);
var _diffBase2 = _interopRequireDefault(_diffBase);
var _diffCharacter = __webpack_require__(3);
var _diffWord = __webpack_require__(4);
var _diffLine = __webpack_require__(5);
var _diffSentence = __webpack_require__(6);
var _diffCss = __webpack_require__(7);
var _diffJson = __webpack_require__(8);
var _patchApply = __webpack_require__(9);
var _patchCreate = __webpack_require__(10);
var _convertDmp = __webpack_require__(12);
var _convertXml = __webpack_require__(13);
exports.Diff = _diffBase2['default'];
exports.diffChars = _diffCharacter.diffChars;
exports.diffWords = _diffWord.diffWords;
exports.diffWordsWithSpace = _diffWord.diffWordsWithSpace;
exports.diffLines = _diffLine.diffLines;
exports.diffTrimmedLines = _diffLine.diffTrimmedLines;
exports.diffSentences = _diffSentence.diffSentences;
exports.diffCss = _diffCss.diffCss;
exports.diffJson = _diffJson.diffJson;
exports.structuredPatch = _patchCreate.structuredPatch;
exports.createTwoFilesPatch = _patchCreate.createTwoFilesPatch;
exports.createPatch = _patchCreate.createPatch;
exports.applyPatch = _patchApply.applyPatch;
exports.convertChangesToDMP = _convertDmp.convertChangesToDMP;
exports.convertChangesToXML = _convertXml.convertChangesToXML;
exports.canonicalize = _diffJson.canonicalize;
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports['default'] = Diff;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _utilMap = __webpack_require__(2);
var _utilMap2 = _interopRequireDefault(_utilMap);
function Diff(ignoreWhitespace) {
this.ignoreWhitespace = ignoreWhitespace;
}
Diff.prototype = {
diff: function diff(oldString, newString, callback) {
var self = this;
function done(value) {
if (callback) {
setTimeout(function () {
callback(undefined, value);
}, 0);
return true;
} else {
return value;
}
}
// Allow subclasses to massage the input prior to running
oldString = this.castInput(oldString);
newString = this.castInput(newString);
// Handle the identity case (this is due to unrolling editLength == 0
if (newString === oldString) {
return done([{ value: newString }]);
}
if (!newString) {
return done([{ value: oldString, removed: true }]);
}
if (!oldString) {
return done([{ value: newString, added: true }]);
}
newString = this.removeEmpty(this.tokenize(newString));
oldString = this.removeEmpty(this.tokenize(oldString));
var newLen = newString.length,
oldLen = oldString.length;
var editLength = 1;
var maxEditLength = newLen + oldLen;
var bestPath = [{ newPos: -1, components: [] }];
// Seed editLength = 0, i.e. the content starts with the same values
var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
// Identity per the equality and tokenizer
return done([{ value: newString.join('') }]);
}
// Main worker method. checks all permutations of a given edit length for acceptance.
function execEditLength() {
for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
var basePath = undefined;
var addPath = bestPath[diagonalPath - 1],
removePath = bestPath[diagonalPath + 1],
_oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
if (addPath) {
// No one else is going to attempt to use this value, clear it
bestPath[diagonalPath - 1] = undefined;
}
var canAdd = addPath && addPath.newPos + 1 < newLen,
canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen;
if (!canAdd && !canRemove) {
// If this path is a terminal then prune
bestPath[diagonalPath] = undefined;
continue;
}
// Select the diagonal that we want to branch from. We select the prior
// path whose position in the new string is the farthest from the origin
// and does not pass the bounds of the diff graph
if (!canAdd || canRemove && addPath.newPos < removePath.newPos) {
basePath = clonePath(removePath);
self.pushComponent(basePath.components, undefined, true);
} else {
basePath = addPath; // No need to clone, we've pulled it from the list
basePath.newPos++;
self.pushComponent(basePath.components, true, undefined);
}
_oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath);
// If we have hit the end of both strings, then we are done
if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) {
return done(buildValues(basePath.components, newString, oldString, self.useLongestToken));
} else {
// Otherwise track this path as a potential candidate and continue.
bestPath[diagonalPath] = basePath;
}
}
editLength++;
}
// Performs the length of edit iteration. Is a bit fugly as this has to support the
// sync and async mode which is never fun. Loops over execEditLength until a value
// is produced.
if (callback) {
(function exec() {
setTimeout(function () {
// This should not happen, but we want to be safe.
/* istanbul ignore next */
if (editLength > maxEditLength) {
return callback();
}
if (!execEditLength()) {
exec();
}
}, 0);
})();
} else {
while (editLength <= maxEditLength) {
var ret = execEditLength();
if (ret) {
return ret;
}
}
}
},
pushComponent: function pushComponent(components, added, removed) {
var last = components[components.length - 1];
if (last && last.added === added && last.removed === removed) {
// We need to clone here as the component clone operation is just
// as shallow array clone
components[components.length - 1] = { count: last.count + 1, added: added, removed: removed };
} else {
components.push({ count: 1, added: added, removed: removed });
}
},
extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) {
var newLen = newString.length,
oldLen = oldString.length,
newPos = basePath.newPos,
oldPos = newPos - diagonalPath,
commonCount = 0;
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {
newPos++;
oldPos++;
commonCount++;
}
if (commonCount) {
basePath.components.push({ count: commonCount });
}
basePath.newPos = newPos;
return oldPos;
},
equals: function equals(left, right) {
var reWhitespace = /\S/;
return left === right || this.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
},
removeEmpty: function removeEmpty(array) {
var ret = [];
for (var i = 0; i < array.length; i++) {
if (array[i]) {
ret.push(array[i]);
}
}
return ret;
},
castInput: function castInput(value) {
return value;
},
tokenize: function tokenize(value) {
return value.split('');
}
};
function buildValues(components, newString, oldString, useLongestToken) {
var componentPos = 0,
componentLen = components.length,
newPos = 0,
oldPos = 0;
for (; componentPos < componentLen; componentPos++) {
var component = components[componentPos];
if (!component.removed) {
if (!component.added && useLongestToken) {
var value = newString.slice(newPos, newPos + component.count);
value = _utilMap2['default'](value, function (value, i) {
var oldValue = oldString[oldPos + i];
return oldValue.length > value.length ? oldValue : value;
});
component.value = value.join('');
} else {
component.value = newString.slice(newPos, newPos + component.count).join('');
}
newPos += component.count;
// Common case
if (!component.added) {
oldPos += component.count;
}
} else {
component.value = oldString.slice(oldPos, oldPos + component.count).join('');
oldPos += component.count;
// Reverse add and remove so removes are output first to match common convention
// The diffing algorithm is tied to add then remove output and this is the simplest
// route to get the desired output with minimal overhead.
if (componentPos && components[componentPos - 1].added) {
var tmp = components[componentPos - 1];
components[componentPos - 1] = components[componentPos];
components[componentPos] = tmp;
}
}
}
return components;
}
function clonePath(path) {
return { newPos: path.newPos, components: path.components.slice(0) };
}
module.exports = exports['default'];
/***/ },
/* 2 */
/***/ function(module, exports) {
// Following this pattern to make sure the ignore next is in the correct place after babel builds
"use strict";
exports.__esModule = true;
exports["default"] = map;
/* istanbul ignore next */
function map(arr, mapper, that) {
if (Array.prototype.map) {
return Array.prototype.map.call(arr, mapper, that);
}
var other = new Array(arr.length);
for (var i = 0, n = arr.length; i < n; i++) {
other[i] = mapper.call(that, arr[i], i, arr);
}
return other;
}
module.exports = exports["default"];
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffChars = diffChars;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var characterDiff = new _base2['default']();
exports.characterDiff = characterDiff;
function diffChars(oldStr, newStr, callback) {
return characterDiff.diff(oldStr, newStr, callback);
}
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffWords = diffWords;
exports.diffWordsWithSpace = diffWordsWithSpace;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode
//
// Ranges and exceptions:
// Latin-1 Supplement, 0080–00FF
// - U+00D7 × Multiplication sign
// - U+00F7 ÷ Division sign
// Latin Extended-A, 0100–017F
// Latin Extended-B, 0180–024F
// IPA Extensions, 0250–02AF
// Spacing Modifier Letters, 02B0–02FF
// - U+02C7 ˇ &#711; Caron
// - U+02D8 ˘ &#728; Breve
// - U+02D9 ˙ &#729; Dot Above
// - U+02DA ˚ &#730; Ring Above
// - U+02DB ˛ &#731; Ogonek
// - U+02DC ˜ &#732; Small Tilde
// - U+02DD ˝ &#733; Double Acute Accent
// Latin Extended Additional, 1E00–1EFF
var _base2 = _interopRequireDefault(_base);
var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/;
var wordDiff = new _base2['default'](true);
exports.wordDiff = wordDiff;
var wordWithSpaceDiff = new _base2['default']();
exports.wordWithSpaceDiff = wordWithSpaceDiff;
wordDiff.tokenize = wordWithSpaceDiff.tokenize = function (value) {
var tokens = value.split(/(\s+|\b)/);
// Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set.
for (var i = 0; i < tokens.length - 1; i++) {
// If we have an empty string in the next field and we have only word chars before and after, merge
if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) {
tokens[i] += tokens[i + 2];
tokens.splice(i + 1, 2);
i--;
}
}
return tokens;
};
function diffWords(oldStr, newStr, callback) {
return wordDiff.diff(oldStr, newStr, callback);
}
function diffWordsWithSpace(oldStr, newStr, callback) {
return wordWithSpaceDiff.diff(oldStr, newStr, callback);
}
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffLines = diffLines;
exports.diffTrimmedLines = diffTrimmedLines;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var lineDiff = new _base2['default']();
exports.lineDiff = lineDiff;
var trimmedLineDiff = new _base2['default']();
exports.trimmedLineDiff = trimmedLineDiff;
trimmedLineDiff.ignoreTrim = true;
lineDiff.tokenize = trimmedLineDiff.tokenize = function (value) {
var retLines = [],
lines = value.split(/^/m);
for (var i = 0; i < lines.length; i++) {
var line = lines[i],
lastLine = lines[i - 1],
lastLineLastChar = lastLine && lastLine[lastLine.length - 1];
// Merge lines that may contain windows new lines
if (line === '\n' && lastLineLastChar === '\r') {
retLines[retLines.length - 1] = retLines[retLines.length - 1].slice(0, -1) + '\r\n';
} else {
if (this.ignoreTrim) {
line = line.trim();
// add a newline unless this is the last line.
if (i < lines.length - 1) {
line += '\n';
}
}
retLines.push(line);
}
}
return retLines;
};
function diffLines(oldStr, newStr, callback) {
return lineDiff.diff(oldStr, newStr, callback);
}
function diffTrimmedLines(oldStr, newStr, callback) {
return trimmedLineDiff.diff(oldStr, newStr, callback);
}
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffSentences = diffSentences;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var sentenceDiff = new _base2['default']();
exports.sentenceDiff = sentenceDiff;
sentenceDiff.tokenize = function (value) {
return value.split(/(\S.+?[.!?])(?=\s+|$)/);
};
function diffSentences(oldStr, newStr, callback) {
return sentenceDiff.diff(oldStr, newStr, callback);
}
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffCss = diffCss;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var cssDiff = new _base2['default']();
exports.cssDiff = cssDiff;
cssDiff.tokenize = function (value) {
return value.split(/([{}:;,]|\s+)/);
};
function diffCss(oldStr, newStr, callback) {
return cssDiff.diff(oldStr, newStr, callback);
}
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.diffJson = diffJson;
exports.canonicalize = canonicalize;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var _line = __webpack_require__(5);
var objectPrototypeToString = Object.prototype.toString;
var jsonDiff = new _base2['default']();
// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
// dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
exports.jsonDiff = jsonDiff;
jsonDiff.useLongestToken = true;
jsonDiff.tokenize = _line.lineDiff.tokenize;
jsonDiff.castInput = function (value) {
return typeof value === 'string' ? value : JSON.stringify(canonicalize(value), undefined, ' ');
};
jsonDiff.equals = function (left, right) {
return _base2['default'].prototype.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
};
function diffJson(oldObj, newObj, callback) {
return jsonDiff.diff(oldObj, newObj, callback);
}
// This function handles the presence of circular references by bailing out when encountering an
// object that is already on the "stack" of items being processed.
function canonicalize(obj, stack, replacementStack) {
stack = stack || [];
replacementStack = replacementStack || [];
var i = undefined;
for (i = 0; i < stack.length; i += 1) {
if (stack[i] === obj) {
return replacementStack[i];
}
}
var canonicalizedObj = undefined;
if ('[object Array]' === objectPrototypeToString.call(obj)) {
stack.push(obj);
canonicalizedObj = new Array(obj.length);
replacementStack.push(canonicalizedObj);
for (i = 0; i < obj.length; i += 1) {
canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack);
}
stack.pop();
replacementStack.pop();
} else if (typeof obj === 'object' && obj !== null) {
stack.push(obj);
canonicalizedObj = {};
replacementStack.push(canonicalizedObj);
var sortedKeys = [],
key = undefined;
for (key in obj) {
/* istanbul ignore else */
if (obj.hasOwnProperty(key)) {
sortedKeys.push(key);
}
}
sortedKeys.sort();
for (i = 0; i < sortedKeys.length; i += 1) {
key = sortedKeys[i];
canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack);
}
stack.pop();
replacementStack.pop();
} else {
canonicalizedObj = obj;
}
return canonicalizedObj;
}
/***/ },
/* 9 */
/***/ function(module, exports) {
'use strict';
exports.__esModule = true;
exports.applyPatch = applyPatch;
function applyPatch(oldStr, uniDiff) {
var diffstr = uniDiff.split('\n'),
hunks = [],
i = 0,
remEOFNL = false,
addEOFNL = false;
// Skip to the first change hunk
while (i < diffstr.length && !/^@@/.test(diffstr[i])) {
i++;
}
// Parse the unified diff
for (; i < diffstr.length; i++) {
if (diffstr[i][0] === '@') {
var chnukHeader = diffstr[i].split(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/);
hunks.unshift({
start: chnukHeader[3],
oldlength: +chnukHeader[2],
removed: [],
newlength: chnukHeader[4],
added: []
});
} else if (diffstr[i][0] === '+') {
hunks[0].added.push(diffstr[i].substr(1));
} else if (diffstr[i][0] === '-') {
hunks[0].removed.push(diffstr[i].substr(1));
} else if (diffstr[i][0] === ' ') {
hunks[0].added.push(diffstr[i].substr(1));
hunks[0].removed.push(diffstr[i].substr(1));
} else if (diffstr[i][0] === '\\') {
if (diffstr[i - 1][0] === '+') {
remEOFNL = true;
} else if (diffstr[i - 1][0] === '-') {
addEOFNL = true;
}
}
}
// Apply the diff to the input
var lines = oldStr.split('\n');
for (i = hunks.length - 1; i >= 0; i--) {
var hunk = hunks[i];
// Sanity check the input string. Bail if we don't match.
for (var j = 0; j < hunk.oldlength; j++) {
if (lines[hunk.start - 1 + j] !== hunk.removed[j]) {
return false;
}
}
Array.prototype.splice.apply(lines, [hunk.start - 1, hunk.oldlength].concat(hunk.added));
}
// Handle EOFNL insertion/removal
if (remEOFNL) {
while (!lines[lines.length - 1]) {
lines.pop();
}
} else if (addEOFNL) {
lines.push('');
}
return lines.join('\n');
}
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
exports.structuredPatch = structuredPatch;
exports.createTwoFilesPatch = createTwoFilesPatch;
exports.createPatch = createPatch;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _diffPatch = __webpack_require__(11);
var _utilMap = __webpack_require__(2);
var _utilMap2 = _interopRequireDefault(_utilMap);
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
if (!options) {
options = { context: 4 };
}
var diff = _diffPatch.patchDiff.diff(oldStr, newStr);
diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier
function contextLines(lines) {
return _utilMap2['default'](lines, function (entry) {
return ' ' + entry;
});
}
var hunks = [];
var oldRangeStart = 0,
newRangeStart = 0,
curRange = [],
oldLine = 1,
newLine = 1;
var _loop = function (i) {
var current = diff[i],
lines = current.lines || current.value.replace(/\n$/, '').split('\n');
current.lines = lines;
if (current.added || current.removed) {
// If we have previous context, start with that
if (!oldRangeStart) {
var prev = diff[i - 1];
oldRangeStart = oldLine;
newRangeStart = newLine;
if (prev) {
curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
oldRangeStart -= curRange.length;
newRangeStart -= curRange.length;
}
}
// Output our changes
curRange.push.apply(curRange, _utilMap2['default'](lines, function (entry) {
return (current.added ? '+' : '-') + entry;
}));
// Track the updated file position
if (current.added) {
newLine += lines.length;
} else {
oldLine += lines.length;
}
} else {
// Identical context lines. Track line changes
if (oldRangeStart) {
// Close out any changes that have been output (or join overlapping)
if (lines.length <= options.context * 2 && i < diff.length - 2) {
// Overlapping
curRange.push.apply(curRange, contextLines(lines));
} else {
// end the range and output
var contextSize = Math.min(lines.length, options.context);
curRange.push.apply(curRange, contextLines(lines.slice(0, contextSize)));
var hunk = {
oldStart: oldRangeStart,
oldLines: oldLine - oldRangeStart + contextSize,
newStart: newRangeStart,
newLines: newLine - newRangeStart + contextSize,
lines: curRange
};
if (i >= diff.length - 2 && lines.length <= options.context) {
// EOF is inside this hunk
var oldEOFNewline = /\n$/.test(oldStr);
var newEOFNewline = /\n$/.test(newStr);
if (lines.length == 0 && !oldEOFNewline) {
// special case: old has no eol and no trailing context; no-nl can end up before adds
curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file');
} else if (!oldEOFNewline || !newEOFNewline) {
curRange.push('\\ No newline at end of file');
}
}
hunks.push(hunk);
oldRangeStart = 0;
newRangeStart = 0;
curRange = [];
}
}
oldLine += lines.length;
newLine += lines.length;
}
};
for (var i = 0; i < diff.length; i++) {
_loop(i);
}
return {
oldFileName: oldFileName, newFileName: newFileName,
oldHeader: oldHeader, newHeader: newHeader,
hunks: hunks
};
}
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
var ret = [];
if (oldFileName == newFileName) {
ret.push('Index: ' + oldFileName);
}
ret.push('===================================================================');
ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader));
ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader));
for (var i = 0; i < diff.hunks.length; i++) {
var hunk = diff.hunks[i];
ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@');
ret.push.apply(ret, hunk.lines);
}
return ret.join('\n') + '\n';
}
function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
}
/***/ },
/* 11 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
exports.__esModule = true;
// istanbul ignore next
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _base = __webpack_require__(1);
var _base2 = _interopRequireDefault(_base);
var patchDiff = new _base2['default']();
exports.patchDiff = patchDiff;
patchDiff.tokenize = function (value) {
var ret = [],
linesAndNewlines = value.split(/(\n|\r\n)/);
// Ignore the final empty token that occurs if the string ends with a new line
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
linesAndNewlines.pop();
}
// Merge the content and line separators into single tokens
for (var i = 0; i < linesAndNewlines.length; i++) {
var line = linesAndNewlines[i];
if (i % 2) {
ret[ret.length - 1] += line;
} else {
ret.push(line);
}
}
return ret;
};
/***/ },
/* 12 */
/***/ function(module, exports) {
// See: http://code.google.com/p/google-diff-match-patch/wiki/API
"use strict";
exports.__esModule = true;
exports.convertChangesToDMP = convertChangesToDMP;
function convertChangesToDMP(changes) {
var ret = [],
change = undefined,
operation = undefined;
for (var i = 0; i < changes.length; i++) {
change = changes[i];
if (change.added) {
operation = 1;
} else if (change.removed) {
operation = -1;
} else {
operation = 0;
}
ret.push([operation, change.value]);
}
return ret;
}
/***/ },
/* 13 */
/***/ function(module, exports) {
'use strict';
exports.__esModule = true;
exports.convertChangesToXML = convertChangesToXML;
function convertChangesToXML(changes) {
var ret = [];
for (var i = 0; i < changes.length; i++) {
var change = changes[i];
if (change.added) {
ret.push('<ins>');
} else if (change.removed) {
ret.push('<del>');
}
ret.push(escapeHTML(change.value));
if (change.added) {
ret.push('</ins>');
} else if (change.removed) {
ret.push('</del>');
}
}
return ret.join('');
}
function escapeHTML(s) {
var n = s;
n = n.replace(/&/g, '&amp;');
n = n.replace(/</g, '&lt;');
n = n.replace(/>/g, '&gt;');
n = n.replace(/"/g, '&quot;');
return n;
}
/***/ }
/******/ ])
});
;
\ No newline at end of file
import 'babel-polyfill';
// import model from '../data/model.test2';
// import model from '../data/model.test.conv2d';
import GraphExecutor from '../../src/executor/executor';
import Loader from '../../src/executor/loader';
import Runtime from '../../src/runtime/runtime';
// 获取map表
import Map from '../data/map';
console.dir(['map', Map]);
let Diff = require('./diff');
let datas;
let otherResult;
let output
async function run() {
const MODEL_URL = '/test/unitData/model.test.batchnorm.json';
const graphModel= new Loader();
const model = await graphModel.loadGraphModel(MODEL_URL);
datas = model.handler;
output = deepCopy(model.handler);
// 测试单元
let item = getTensor('batchnorm');
func(item);
// let inst = model.execute({input: cat});
// console.dir(['result', inst.read()]);
}
run();
function deepCopy (data) {
return JSON.parse(JSON.stringify(data));
}
// let output = deepCopy(datas);
let getTensor = function(id, times = 1) {
let find = 0;
let data = datas.ops.filter((item, idx) => {
if (id === item.type) {
++find;
if (find === times) {
return true;
}
}
});
return getInputs(data[0]);
};
let getInputs = function(data) {
Object.keys(data.inputs).forEach(function(key){
data.inputs[key] = getValue(data.inputs[key][0], datas);
});
Object.keys(data.outputs).forEach(function(key){
let out = getValue(data.outputs[key][0], datas)
data.outputs[key] = out;
otherResult = out[0].data;
});
return data;
};
let getResult = function(id) {
let data = output.ops.filter((item, idx) => {
if (id === item.type) {
return true;
}
});
return getoutputs(data[0]);
};
let getoutputs = function(data) {
let otherResult;
Object.keys(data.outputs).forEach(function(key){
let out = getValue(data.outputs[key][0], output);
otherResult = out[0].data;
});
return otherResult;
};
let getValue = function(name, datas) {
return datas.vars.filter((item, idx) => {
if (name === item.name) {
return item;
}
});
};
// // 测试单元
// let item = getTensor('conv2d');
let func = function (item) {
let inst = Runtime.init({
'width_raw_canvas': 512,
'height_raw_canvas': 512
});
const executor = new GraphExecutor(item);
executor.execute(executor, {}, inst);
console.dir(['result', inst.read()]);
// var one = inst.read();
// var other = getResult('softmax');
// var color ='';
// var span = null;
// var diff = Diff.diffChars(one.toString(), other.toString()),
// display = document.getElementById('display'),
// fragment = document.createDocumentFragment();
//
// diff.forEach(function(part){
// // green for additions, red for deletions
// // grey for common parts
// color = part.added ? 'green' :
// part.removed ? 'red' : 'grey';
// span = document.createElement('span');
// span.style.color = color;
// span.appendChild(document
// .createTextNode(part.value));
// fragment.appendChild(span);
// });
//
// display.appendChild(fragment);
};
import 'babel-polyfill';
import units from './units/units';
let qs = require('qs');
/**
* @file 入口文件
* @author wangqun@baidu.com
*
*/
// 引入 op
const FSHADER_CON2D = require('../src/shader/f_elementwise_conv2d3_shader.c');
const shapeA = [1, 3, 256, 256];
const shapeB = [3];
const imgUrl = require('./data/banana.jpeg');
let shapeAData;
let shapeBData;
let inst;
const matrix = units.mockOrigin();
const filter = units.mockFilter();
// 原始张量,上下左右1个单位的padding,步长是1
let conf = {
'filter_size_width': 3,
'filter_size_height': 3,
'origin_size_width': matrix.sx,
'origin_size_height': matrix.sx,
'out_size_width': 3,
'out_size_height': 3,
'stride_horizontal': 1,
'stride_vertical': 1,
'pad_left': 1,
'pad_top': 1,
'dilation_horizontal': 2,
'dilation_vertical': 2
}
units.init(conf, FSHADER_CON2D).then(instance => {
if (!instance || typeof instance === 'string') {
throw new Error(instance || '不支持float texture');
}
inst = instance;
}).then(() => {
console.dir(['卷积核', filter]);
console.dir(['origin data', matrix.data]);
// 执行conv2d
inst.compute(filter, matrix.data, 'conv2d');
}).then(() => {
// 读取结果
const result = inst.read();
console.dir(['conv2d的执行结果', result]);
let input = {
filter: filter,
origin: matrix.data,
};
Object.assign(input, conf);
console.dir(['完整input', input]);
// console.dir(['完整输入和输出', params]);
inst.getResult('pool2d', input, result);
}).catch(err => {
console.log('-----------error---------' + err);
});
<!DOCYTPE html>
<html>
<head>
<meta charset="utf-8">
<title>paddle web unitTest</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<style>
body {
margin: 0;
padding: 0;
}
.paddle-web-wrapper {
position: relative;
width: 100%;
}
.paddle-web-title {
width: 100%;
background-color: blueviolet;
height: 44px;
text-align: center;
color: #fff;
line-height: 44px;
font-size: 18px;
}
.paddle-web-body {
}
#paddle-web-unit-list {
}
#paddle-web-unit-list li {
}
.unit-li-name {
font-size: 16px;
margin: 5px;
font-weight: 700;
letter-spacing: 0;
line-height: 14px;
}
.unit-li-assert {
font-size: 16px;
margin: 5px;
font-weight: 700;
letter-spacing: 0;
line-height: 14px;
}
.pass {
color: #34B458;
}
.no-pass {
color: #b4231b;
}
.unit-li-diff {
margin: 5px;
border: 1px dotted #f71111;
}
p {
word-wrap: break-word;
word-break: normal;
}
span {
word-wrap: break-word;
word-break: normal;
}
#display {
width: 100%;
}
</style>
<!--<script src="unitTest.es6"></script>-->
<!--<script src="testUtils/diff.js"></script>-->
<script src="testUtils/testUtils.es6"></script>
</head>
<body>
<div class="paddle-web-wrapper">
<div class="paddle-web-title">
paddle Web Unit Test
</div>
<div class="paddle-web-body">
<ul id="paddle-web-unit-list">
<li class="unit-li">
<div class="unit-li-name">pool</div>
<div class="unit-li-assert pass">pass</div>
</li>
<li class="unit-li">
<div class="unit-li-name">relu</div>
<div class="unit-li-assert pass">pass</div>
</li>
<li class="unit-li">
<div class="unit-li-name">prelu</div>
<div class="unit-li-assert pass">pass</div>
</li>
<li class="unit-li">
<div class="unit-li-name">softmax</div>
<div class="unit-li-assert pass">pass</div>
</li>
<li class="unit-li">
<div class="unit-li-name">dropout</div>
<div class="unit-li-assert pass">pass</div>
</li>
<li class="unit-li">
<div class="unit-li-name">conv2d</div>
<div class="unit-li-assert pass">pass</div>
</li>
</ul>
<div id="display"></div>
</div>
</div>
</body>
</html>
\ No newline at end of file
import Utils from '../../src/utils/utils';
import Gpu from '../../src/gpu/gpu';
import Matrix from '../../src/utils/dims';
import axios from 'axios';
let qs = require('qs');
/**
* @file gpu运行时
* @author wangqun
*
*/
// v_shader.c表示计算容器
const VSHADER = require('../../src/shader/v_shader.c');
export default {
/**
* 初始化op
* @param {Object} opts 运行时参数,包含el:canvas,dim: 256
* @return {Object} this 实例对象
*/
async init(opts = {}, opShader) {
const gpu = this.gpu = new Gpu(opts);
if (gpu.isFloatingTexture()) {
let texture = gpu.makeTexure(WebGLRenderingContext.FLOAT, null);
let framebuffer = gpu.attachFrameBuffer(texture);
let bufferStatus = gpu.frameBufferIsComplete();
if (bufferStatus.isComplete) {
console.log(bufferStatus.isComplete);
// 获取shader
const vshaderCode = await Utils.loadShader(VSHADER);
let fshaderCode = await Utils.loadShader(opShader);
fshaderCode = Utils.populateData('conv2d', fshaderCode, opts);
gpu.create(vshaderCode, fshaderCode);
return this;
} else {
return bufferStatus.message;
}
} else {
return null;
}
},
/**
* 计算op
* @param bufferA
* @param bufferB
*/
compute(bufferA, bufferB, type) {
this.gpu.render(bufferA, bufferB, type);
},
/**
* 读取op计算结果, 并返回数据
*/
read() {
return this.gpu.compute();
},
// 生成feed数据
feed(pixelData, size) {
return Utils.shapeData(pixelData, size);
},
// mock生成shapeB的数据
mockShapeB(shapeA, shapeB) {
return Utils.mock(shapeA, shapeB);
},
// mock origin 1 * 5 * 5
mockOrigin() {
return new Matrix({
sx: 5,
sy: 5,
depth: 4
});
},
// mock filter 1 * 3 * 3
mockFilter() {
return new Float32Array([1.0, 1.0, 0.0, 0.0, -2.0, 0.0, 1.0, -3.0, 1.0]);
},
// 更新op
updateOp(name) {
// this.gpu.updateShader();
},
// get paddle mobile result
getResult(name, input, output) {
if (name) {
let that = this;
axios.defaults.withCredentials = false;
axios.defaults.headers = {
'Content-type': 'application/x-www-form-urlencoded'
}
axios.post('http://yq01-paddle-mobile.epc.baidu.com:8088/uniTest', qs.stringify({
name: name,
input: JSON.stringify(input, function (key, value) {
if (value.constructor === Float32Array) {
return that.formatData(value);
}else {
return that.formatData(value);
}
}),
output: JSON.stringify(output, function (key, value) {
return that.formatData(value);
})
},{ indices: false }))
.then(function (response) {
if (response.status === 200) {
that.displayResult(response.data);
}
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
},
displayResult(res) {
if (res.name) {
let assert = (res.correct == 1? 'Pass' : 'Not pass');
let passCls = (res.correct == 1? 'pass' : 'no-pass');
if (res.correct === 1) {
let unitHtml = '<li class="unit-li"><div class="unit-li-name">' + res.name + '</div>' +
'<div class="unit-li-assert">' + assert + '</div>'
'</li>';
let oli = document.createElement('li');
oli.innerHTML = unitHtml;
document.getElementById('paddle-web-unit-list').appendChild(oli);
}
else if (res.correct === 0) {
let serverData = res.server_data;
let unitHtml = '<li class="unit-li"><div class="unit-li-name">' + res.name + '</div>' +
'<div class="unit-li-assert ' + passCls + '">' + assert + '</div>' +
'<div class="unit-li-diff"><p>' + serverData + '</p></div>'
'</li>';
let oli = document.createElement('li');
oli.innerHTML = unitHtml;
document.getElementById('paddle-web-unit-list').appendChild(oli);
}
}
},
formatData(list) {
if (list.constructor === Float32Array) {
return '[' + list.toString() + ']';
}
else {
return list;
}
},
// 释放资源
dispose() {
this.gpu.dispose();
}
};
#coding:utf-8
#! /bin/python
# 把文本文件转为二进制文件的工具
import os
import struct
import random
import math
class BinaryFileConverter:
def __init__(self, delimiter, ignorChar, ignorLine, types, originDir, resultDir, ext, formatter, dotPrintRatio, merge):
# 每行中数字间的分隔符
self.delimiter = delimiter
# 需要忽略的符号
self.ignorChar = ignorChar
# 需要忽略的行
self.ignorLine = ignorLine
# 需要转的文件
self.types = types
# 转之前的
self.originDir = originDir
# 转之后的文件夹
self.resultDir = resultDir
# 转换后的后缀
self.ext = ext
# 格式 可选内容参考:https://docs.python.org/3/library/struct.html?#format-characters
self.formatter = formatter
# 打点率
self.dotPrintRatio = dotPrintRatio
# 合成几个文件 0代表不合并
self.merge = merge
# 存合并的数据的文件
self.mergedResultFileName = resultDir + '/mergedData.dat'
# 计数器
self.i = 0
def dfs(self, rootDir):
for item in sorted(os.listdir(rootDir)):
path = os.path.join(rootDir, item)
if os.path.isdir(path):
self.dfs(path)
else:
self.process(path)
# print(path)
def process(self, path):
(curFile, curType) = os.path.splitext(path)
if curType in self.types:
originFile = open(path, 'r')
if not self.merge:
newFilePath = self.resultDir + curFile[len(self.originDir):] + self.ext
newFileDir = os.path.dirname(newFilePath)
if not os.path.exists(newFileDir):
os.makedirs(newFileDir)
self.resultFile = open(newFilePath ,'wb')
print('开始写啦' + path)
self.writeToFile(originFile, self.resultFile)
if not self.merge:
self.resultFile.close()
print('\n')
print('写完啦' + path)
def writeToFile(self, originFile, resultFile):
lines = originFile.readlines()
for line in lines:
if (line in self.ignorLine) or (line.strip() in self.ignorLine):
continue
curLine = line.strip().split(self.delimiter)
for i in curLine:
if (not len(i.strip())) or (i in self.ignorChar):
continue
if random.random() < self.dotPrintRatio:
print('.', end = '')
self.i += 1
parsedata = struct.pack(self.formatter, float(i))
resultFile.write(parsedata)
originFile.close()
def convert(self):
if self.merge:
if not os.path.exists(self.resultDir):
os.makedirs(self.resultDir)
self.resultFile = open(self.mergedResultFileName ,'wb')
self.dfs(self.originDir)
print('共写入了%s条数据' % self.i)
self.resultFile.close()
if self.merge > 1:
f = open(self.mergedResultFileName, 'rb')
data = f.read() # read the entire content of the file
f.close()
bytes = len(data)
size = (int(bytes / self.merge // 16) + 1) * 16
count = 1
for i in range(0, bytes + 1, size):
fni = self.resultDir + '/chunk_%s' % count
f = open(fni, 'wb')
f.write(data[i : i + size])
f.close()
count += 1
BinaryFileConverter(
delimiter = ',',
ignorChar = ['[', ']'],
ignorLine = ['[', ']'],
types = ['.txt', '.json'],
originDir = './mobileNet',
resultDir = './binf',
ext = '.dat',
formatter = 'f',
dotPrintRatio = 0,
merge = 6).convert()
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册