未验证 提交 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 */
此差异已折叠。
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.
先完成此消息的编辑!
想要评论请 注册