提交 7449fa23 编写于 作者: Y yangmingming 提交者: Yanzhan Yang

paddle web (#1811)

speed up WebGL implementation
上级 07f044b1
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
import 'babel-polyfill';
import VConsole from 'vconsole';
import Graph from '../src/executor/loader';
import IO from '../src/feed/imageFeed';
import Logger from '../tools/logger';
window.log = new Logger();
// var vConsole = new VConsole();
// 统计参数
window.badCases = [];
// import Utils from '../src/utils/utils';
// 获取map表
// import Map from '../test/data/map';
// import demoPic from './bbt1.jpg';
// import demoPic2 from './bbt2.jpg';
// import demoPic3 from './bbt3.jpg';
// import demoPic4 from './bbt4.jpg';
// import demoPic5 from './bbt5.jpg';
// import testOutput from './data.json';
// 后处理测试用例
// let tempPic = [demoPic, demoPic2, demoPic3, demoPic4, demoPic5];
/**
* @file model demo 入口文件
* @author wangqun@baidu.com
*
*/
// 'http://mms-xr.cdn.bcebos.com/paddle/mnist/model.json'
// 模型输出shape
const outputShapes = {
'608': {
from: [19, 19, 25, 1],
to: [19, 19, 5, 5]
},
'320': {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
},
'320fused': {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
},
'separate': {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
}
};
// 模型feed数据
const feedShape = {
'608': {
fw: 608,
fh: 608
},
'320': {
fw: 320,
fh: 320
},
'320fused': {
fw: 320,
fh: 320
},
'separate': {
fw: 320,
fh: 320
}
};
// 模型路径
const modelPath = {
'608': 'faceModel',
'320': 'facemodel320',
'320fused': 'facemodelfused',
'separate': 'separablemodel'
};
const modelType = 'separate';
const path = modelPath[modelType];
// 统计参数
let loaded = false;
let model = {};
window.statistic = [];
const {fw, fh} = feedShape[modelType];
// 第一遍执行比较慢 所以预热一下
async function preheat() {
const io = new IO();
let feed = io.process({
input: video,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
targetSize: {
height: fw,
width: fh
},
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
// shape: [3, 608, 608], // 预设tensor形状
mean: [117.001, 114.697, 97.404], // 预设期望
// std: [0.229, 0.224, 0.225] // 预设方差
}
});
const MODEL_URL = `/${path}/model.json`;
const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const graphModel = new Graph();
log.start('加载模型');
model = await graphModel.loadGraphModel(MODEL_CONFIG, {
multipart: true,
dataType: 'binary',
binaryOption: {
fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_0.dat';
}
},
feed
});
log.end('加载模型');
let inst = model.execute({
input: feed
});
}
async function run(input) {
// const input = document.getElementById('mobilenet');
log.start('总耗时');
const io = new IO();
log.start('预处理');
let feed = io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
targetSize: {
height: fw,
width: fh
},
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
// shape: [3, 608, 608], // 预设tensor形状
mean: [117.001, 114.697, 97.404], // 预设期望
// std: [0.229, 0.224, 0.225] // 预设方差
}
});
log.end('预处理');
if (!loaded) {
const MODEL_URL = `/${path}/model.json`;
const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const graphModel = new Graph();
log.start('加载模型');
model = await graphModel.loadGraphModel(MODEL_CONFIG, {
multipart: true,
dataType: 'binary',
binaryOption: {
fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_0.dat';
}
},
feed
});
log.end('加载模型');
}
log.start('运行耗时');
let inst = model.execute({
input: feed
});
// 其实这里应该有个fetch的执行调用或者fetch的输出
let result = await inst.read();
log.end('后处理-读取数据');
// console.dir(['result', result]);
log.start('后处理-形状调整');
const newData = [];
let newIndex = -1;
const [w, h, c, b] = outputShapes[modelType].from;
// c channel
for (let i = 0; i < c; i++) {
// height channel
for (let j = 0; j < h; j++) {
// width channel
for (let k = 0; k < w; k++) {
// position: (0, 0, 0, 0)
const index = j * (c * h) + k * c + i;
// const index = j * (i * k) + k * i + i;
newData[++newIndex] = result[index];
}
}
}
log.end('后处理-形状调整');
log.start('后处理-画框');
testRun(newData, input);
log.end('后处理-画框');
log.end('后处理');
log.end('总耗时');
}
var image = '';
function selectImage(file) {
if (!file.files || !file.files[0]) {
return;
}
let reader = new FileReader();
reader.onload = function (evt) {
let img = document.getElementById('image');
img.src = evt.target.result;
img.onload = function() {
log.during('每次执行的时间间隔');
run(img);
};
image = evt.target.result;
}
reader.readAsDataURL(file.files[0]);
}
// selectImage
document.getElementById("uploadImg").onchange = function () {
selectImage(this);
};
/* 后处理图片 by zhangmiao06 */
let preTestRun = (index) => {
let img = document.getElementById('image');
img.src = tempPic[index];
img.onload = function() {
testRun(testOutput.data[index], img);
};
};
let testRun = (data, img) => {
// console.log('ori', data);
const {from, to} = outputShapes[modelType];
// let shape = [1, 25, 19, 19];
let shape = [].concat(from).reverse();
// 1.从一维数组到1*25*19*19
let formatData = reshapeMany({
data: data,
reshapeShape: shape
});
// console.log('一维到多维', formatData);
// 2.从1*25*19*19 到 19*19*25*1
let formatData2 = transpose({
data: formatData,
shape: shape,
transposeShape: [2, 3, 1, 0]
});
// console.log('transpose', formatData2);
// 3.从19*19*25*1到19*19*5*5
let formatData3 = reshape({
data: formatData2,
shape: from,
reshapeShape: to
});
// console.log('reshape', formatData3);
// 4.运算
let finalData = handleFinal(formatData3, shape, img);
// console.log('final', finalData);
// 5.处理画布
// handleCanvas(finalData, img);
handleDiv(finalData, img);
};
// sigmoid
let sigmoid = (x) => {
if (x < -100) {
return 0.0;
}
return 1 / (1 + Math.exp(-x));
}
// transpose
let transpose = (data) => {
let shape = data.shape;
let transposeShape = data.transposeShape;
let formatData = data.data;
let formatData2 = [];
for(let n = 0; n < shape[transposeShape[0]]; n++) {
let nData = [];
for(let c = 0; c < shape[transposeShape[1]]; c++) {
let cData = [];
for(let row = 0; row < shape[transposeShape[2]]; row++) {
let rowData = [];
for(let col = 0; col < shape[transposeShape[3]]; col++) {
let tempArr = [n, c, row, col];
let newN = n;
let newC = c;
let newW = row;
let newH = col;
transposeShape.forEach((item, index)=> {
switch(item) {
case 0:
newN = tempArr[index];
break;
case 1:
newC = tempArr[index];
break;
case 2:
newW = tempArr[index];
break;
case 3:
newH = tempArr[index];
}
});
rowData.push(formatData[newN][newC][newW][newH]);
}
cData.push(rowData);
}
nData.push(cData);
}
formatData2.push(nData);
}
return formatData2;
};
// reshape
let reshape = (data) =>{
let formatData2 = data.data;
let shape = data.shape;
let reshapeShape = data.reshapeShape;
// 1.变成一维
let tempData = reshapeOne({
data: formatData2,
shape: shape
});
// 2.变成多维
let formatData3 = reshapeMany({
data: tempData,
reshapeShape: reshapeShape
});
return formatData3;
};
// 变成一维
let reshapeOne = (data) => {
let formatData2 = data.data;
let shape = data.shape;
let tempData = [];
for(let n = 0; n < shape[0]; n++) {
for(let c = 0; c < shape[1]; c++) {
for(let row = 0; row < shape[2]; row++) {
for(let col = 0; col < shape[3]; col++) {
tempData.push(formatData2[n][c][row][col]);
}
}
}
}
return tempData;
};
// 变成多维
let reshapeMany = (data) => {
let tempData = data.data;
let reshapeShape = data.reshapeShape;
let formatData3 = [];
for(let n = 0; n < reshapeShape[0]; n++) {
let nData = [];
for(let c = 0; c < reshapeShape[1]; c++) {
let cData = [];
for(let row = 0; row < reshapeShape[2]; row++) {
let rowData = [];
for(let col = 0; col < reshapeShape[3]; col++) {
let tempN = n * reshapeShape[1] * reshapeShape[2] * reshapeShape[3];
let tempC = c * reshapeShape[2] * reshapeShape[3];
let tempRow = row * reshapeShape[3];
rowData.push(tempData[tempN + tempC + tempRow + col]);
}
cData.push(rowData);
}
nData.push(cData);
}
formatData3.push(nData);
}
return formatData3;
};
let calSize = (img) => {
let w1 = img.width;
let h1 = img.height;
let wh1 = Math.max(w1, h1);
// let factor = 608.0 / wh1;
let factor = fw / wh1;
let width = Math.round(w1 * factor);
let height = Math.round(h1 * factor);
return [w1, h1, width, height];
};
// 处理运算
let handleFinal = (formatData3, shape, img) => {
let finalData = [];
let c = shape[2];
let [w1, h1, width, height] = calSize(img);
let factorX = Math.max(width, height) / width;
let factorY = Math.max(width, height) / height;
let maxProb = 0.0;
let anchors = [[1.603231, 2.094468], [6.041143, 7.080126], [2.882459, 3.518061], [4.266906, 5.178857], [9.041765, 10.66308]];
for(let i = 0; i < shape[2]; i++) {
for(let j = 0; j < shape[3]; j++) {
for(let k = 0; k < anchors.length; k++) {
let [a1, a2, a3, a4, prob] = formatData3[i][j][k];
prob = sigmoid(prob);
if (prob > maxProb && prob >= 0.5) {
let ctx = (j + sigmoid(a1)) / c * factorX;
let cty = (i + sigmoid(a2)) / c * factorY;
let col = Math.exp(a3) * anchors[k][0] / c * factorX;
let row = Math.exp(a4) * anchors[k][1] / c * factorY;
let x = (ctx - (col / 2));
let y = (cty - (row / 2));
finalData.push([x * w1, y * h1, col * w1, row * h1, prob]);
}
}
}
}
return finalData;
};
// 处理画布
let handleCanvas = (finalData, img) => {
let myCanvas = document.getElementById('myCanvas');
let [w1, h1, width, height] = calSize(img);
myCanvas.width = w1;
myCanvas.height = h1;
let ctx = myCanvas.getContext("2d");
ctx.drawImage(img, 0, 0, w1, h1);
finalData.forEach((demoArr,index) => {
let [demoLeft, demoTop, demoWidth, demoHeight, prob] = demoArr;
ctx.beginPath();
ctx.strokeStyle="red";
ctx.moveTo(demoLeft, demoTop);
ctx.lineTo(demoLeft + demoWidth, demoTop);
ctx.lineTo(demoLeft + demoWidth, demoTop + demoHeight);
ctx.lineTo(demoLeft, demoTop + demoHeight);
ctx.closePath();
ctx.stroke();
});
};
let handleDiv = (finalData, img) => {
if (finalData.length < 1) {
return false;
}
let myCanvas = document.getElementById('myDiv');
let maxIndex = 0;
if (finalData.length > 1) {
for(let i = 1; i < finalData.length; i++) {
if (finalData[i].prob > finalData[maxIndex].prob) {
maxIndex = i;
}
}
}
let [demoLeft, demoTop, demoWidth, demoHeight, prob] = finalData[maxIndex];
myCanvas.style.width = demoWidth;
myCanvas.style.height = demoHeight;
myCanvas.style.left = demoLeft;
myCanvas.style.top = demoTop;
};
// preTestRun(0);
// run(document.getElementById('pic'));
<!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">
<style>
.image-wrap {
position: relative;
}
#myDiv {
position: absolute;
border: 1px solid red;
box-sizing: border-box;
}
</style>
</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>-->
<div><img id="mobilenet" src=""></div>
<!-- <p>处理后图片</p> -->
<!-- <div id="p-c"></div> -->
<!-- <p>预处理测试图片</p> -->
<!-- <img id="pic" src="./tbbt.jpg"/> -->
<p>原图片</p>
<div class="image-wrap">
<img id="image" src=""/>
<div id="myDiv"></div>
</div>
<p>画布</p>
<canvas id="myCanvas"></canvas>
<br/>
<input type="file" id="uploadImg"/>
<div id="txt"></div>
</body>
<script src="index.es6"></script>
</html>
import 'babel-polyfill';
import Runner from '../src/executor/runner';
import Camera from '../src/executor/camera';
// 调试工具
// import vConsole from 'vconsole';
// const theConsole = new vConsole();
let startBtn = document.getElementById('start');
let stopBtn = document.getElementById('stop')
const runner = new Runner({
// 用哪个模型
modelName: 'separate' // '608' | '320' | '320fused' | 'separate'
});
startBtn.disabled = true;
runner.preheat()
.then(() =>{
startBtn.disabled = false
});
const domElement = document.getElementById('video');
const myCanvas = document.getElementById('myDiv');
const videoSelect = document.getElementById('videoSelect');
let camera = new Camera({
// 用来显示摄像头图像的dom
videoDom: domElement
});
camera.getDevices().then(devices => {
if (devices.length) {
camera.run(devices[0].deviceId);
devices.forEach((element, index) => {
let option = document.createElement('option');
option.value = element.deviceId;
option.text = (index + 1);
videoSelect.appendChild(option);
});
videoSelect.onchange = () => {
camera.run(videoSelect.value);
};
}
else {
camera.run();
}
});
const handleDiv = function (data) {
myCanvas.style.width = (data ? data[0] : 0) + 'px';
myCanvas.style.height = (data ? data[0] : 0) + 'px';
myCanvas.style.left = (data ? data[2] : 0) + 'px';
myCanvas.style.top = (data ? data[3] : 0) + 'px';
}
startBtn.addEventListener('click', function () {
startBtn.disabled = true;
runner.startStream(() => camera.curVideo, handleDiv);
});
stopBtn.addEventListener('click', function () {
startBtn.disabled = false;
runner.stopStream();
});
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>识别摄像头里的脸</title>
<style>
body {
margin: 0;
padding: 0;
}
#myDiv {
position: fixed;
border: 1px solid red;
box-sizing: border-box;
}
#video {
background: red;
}
</style>
</head>
<body>
<video id="video">
</video>
<p>
<button id="start">开始识别</button>
<button id="stop">结束</button>
</p>
<select id="videoSelect"></select>
<p id="tips">tips</p>
<div id="myDiv"></div>
<script src="./videoDemo.es6"></script>
</body>
</html>
{ {
"name": "paddle-web", "name": "paddle-web-demo",
"version": "1.0.0", "version": "1.0.0",
"description": "paddle", "description": "paddle",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"server": "parcel ./src/index.html", "server": "parcel ./src/index.html",
"testDemo": "parcel ./demo/index.html", "testDemo": "parcel ./demo/index.html",
"testSDemo": "parcel ./demo/index.html --port 8125 --https",
"testVideoDemo": "parcel ./demo/videoDemo.html --port 8123 --https",
"unit": "parcel ./test/unitTest.html", "unit": "parcel ./test/unitTest.html",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": {
"type": "git",
"url": "https://github.com/PaddlePaddle/paddle-mobile"
},
"devDependencies": { "devDependencies": {
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
...@@ -24,12 +22,13 @@ ...@@ -24,12 +22,13 @@
"babel-preset-stage-0": "^6.24.1", "babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"parcel-bundler": "^1.10.3", "parcel-bundler": "^1.10.3",
"axios": ">=0.18.1" "axios": "^0.17.1"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"js-file-download": "^0.4.5" "js-file-download": "^0.4.5",
"vconsole": "^3.3.2"
} }
} }
/**
* @file 视频流类
* @author zhangmiao06
*/
import $ from 'webpack-zepto';
export default class Camera {
constructor(option) {
this.option = option;
this.video = option.videoDom;
// 标志是否可以切换摄像头
this.haveDevice = false;
// 设置视频流宽度
if (option.width) {
this.video.width = option.width;
}
else if (option.height) {
this.video.height = option.height;
}
else {
this.video.width = window.innerWidth;
}
this.deviceInfos = [];
if(navigator.mediaDevices) {
this.haveDevice = true;
}
}
// 访问用户媒体设备的兼容方法
run(deviceId) {
if (window.stream) {
window.stream.getTracks().forEach(function (track) {
track.stop();
});
}
let constraints = {
video: {}
};
const success = this.success.bind(this);
const error = this.error.bind(this);
if (this.deviceInfos.length) {
constraints.video.deviceId= {exact: deviceId || this.deviceInfos[0]};
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// 最新的标准API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
}
else if (navigator.webkitGetUserMedia) {
// webkit核心浏览器
navigator.webkitGetUserMedia(constraints, success, error);
}
else if (navigator.mozGetUserMedia) {
// firfox浏览器
navigator.mozGetUserMedia(constraints, success, error);
}
else if (navigator.getUserMedia) {
// 旧版API
navigator.getUserMedia(constraints, success, error);
}
else {
console.log('您的浏览器不支持获取视频流~');
}
}
success(stream) {
const domElement = this.video;
// make stream available to console
window.stream = stream;
// 旧的浏览器可能没有srcObject
const URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
if ('srcObject' in domElement) {
try {
domElement.srcObject = stream;
} catch (error) {
domElement.src = URL.createObjectURL(stream) || stream;
}
} else {
// 防止再新的浏览器里使用它,应为它已经不再支持了
domElement.src = URL.createObjectURL(stream) || stream;
}
domElement.addEventListener('loadeddata', () => {
// 设置视频流高度
if (this.option.height) {
domElement.width = $(domElement).width();
}
else {
domElement.height = $(domElement).height();
}
domElement.play();
}, false);
}
error(error) {
alert(`访问用户媒体设备失败${error.name}, ${error.message}`);
}
// 处理摄像头列表
gotDevices(deviceInfos) {
const ua = navigator.userAgent;
const isIos = /iphone|ipod|ipad/ig.test(ua);
let delt = -1;
const range = deviceInfos.length;
let start = range - 1;
let end = - 1;
// ios机型camare顺序相反
if (isIos) {
delt = 1;
start = 0;
end = range;
}
for (let i = start; i !== end; i += delt) {
const deviceInfo = deviceInfos[i];
if (deviceInfo.kind === 'videoinput') {
this.deviceInfos.push(deviceInfos[i]);
}
}
}
get curVideo() {
return this.video;
}
getDevices() {
return new Promise((resolve, reject)=> {
if (this.haveDevice) {
if (this.deviceInfos.length) {
resolve(this.deviceInfos);
}
else {
navigator.mediaDevices.enumerateDevices()
.then(this.gotDevices.bind(this))
.then(()=> {
resolve(this.deviceInfos);
});
}
}
else {
resolve([]);
}
});
}
}
...@@ -10,7 +10,7 @@ export default class GraphExecutor { ...@@ -10,7 +10,7 @@ export default class GraphExecutor {
constructor(model) { constructor(model) {
this.inputs = model.inputs; this.inputs = model.inputs;
this.outputs = model.outputs; this.outputs = model.outputs;
this.attrs = model.attrs; this.attrs = model.attrs || model['sub-attrs'];
this.type = model.type; this.type = model.type;
this.finish = false; this.finish = false;
this.next = null; this.next = null;
...@@ -53,7 +53,7 @@ export default class GraphExecutor { ...@@ -53,7 +53,7 @@ export default class GraphExecutor {
else if (this.type === 'fetch') { else if (this.type === 'fetch') {
return this.inputs.X; return this.inputs.X;
} }
return null; return this.inputs.Input || this.inputs.X;
} }
get outputsName() { get outputsName() {
...@@ -68,29 +68,31 @@ export default class GraphExecutor { ...@@ -68,29 +68,31 @@ export default class GraphExecutor {
return this.outputs.Y; return this.outputs.Y;
} }
else { else {
return this.outputs.Out; return this.outputs.Out || this.outputs.Output;
} }
} }
/** /**
* 将输入数据和具体op进行关联,触发执行具体每一个op * 将输入数据和具体op进行关联,触发执行具体每一个op
* @param inputs
* @param runtime * @param runtime
* @param isRendered
*/ */
execute(runtime) { execute(runtime, isRendered) {
// console.log(inputs, outputs); // console.log(inputs, outputs);
if (this.type !== 'feed') { if (this.type !== 'feed') {
let time = +Date.now(); // let time = +Date.now();
runtime.run(this.type, this.opData); log.start(this.opData.iLayer + '-' + this.type);
runtime.run(this.type, this.opData, isRendered);
log.end(this.opData.iLayer + '-' + this.type);
// if (runtime.gpu.frameBufferIsComplete().isComplete) { // if (runtime.gpu.frameBufferIsComplete().isComplete) {
// var result = runtime.read(); // var result = runtime.read();
// let res = Array.prototype.slice.call(result); // let res = Array.prototype.slice.call(result);
// fileDownload(res, "result.csv"); // fileDownload(res, "result.csv");
// } // }
let length = statistic.length; // let length = statistic.length;
statistic[length - 1].type = this.type; // statistic[length - 1].type = this.type;
statistic[length - 1].runTime = +Date.now() - time; // statistic[length - 1].runTime = +Date.now() - time;
// if (this.type === 'scale') { // if (this.type === 'scale') {
// console.log('时间是:' + (+Date.now() - start)); // console.log('时间是:' + (+Date.now() - start));
// } // }
......
...@@ -26,6 +26,8 @@ export default class GraphModel { ...@@ -26,6 +26,8 @@ export default class GraphModel {
this.feedOp = null; this.feedOp = null;
this.feedItem = null; this.feedItem = null;
this.isExecuted = false; this.isExecuted = false;
// 网络层数
this.iLayer = 0;
// fetch xhr jsonp // fetch xhr jsonp
this.params = {type: 'fetch'}; this.params = {type: 'fetch'};
// 设置分片加载model // 设置分片加载model
...@@ -36,22 +38,41 @@ export default class GraphModel { ...@@ -36,22 +38,41 @@ export default class GraphModel {
this.binaryOption = loadOptions.binaryOption; this.binaryOption = loadOptions.binaryOption;
} }
} }
// op runner
this.inst = Runtime.init({ if (!this.loadOptions) {
'width_raw_canvas': 512,
'height_raw_canvas': 512
});
if (this.loadOptions === null) {
this.loadOptions = {}; this.loadOptions = {};
} else {
// op runner
this.inst = Runtime.init();
factory.setWebglVersion(this.inst.getWebglVersion());
// this.fetchJson(this.modelGonfig.dir + 'x.json').then(data => {
// const [b, c, h, w] = [1, 3, 320, 320];
// const size = data.length;
// const total = 3 * 320 * 320;
// this.testData = new Float32Array(total);
// for (let i = 0; i < size; i++) {
// let j = i / (c * w) | 0;
// let k = i % (c * w);
// let b1 = j / h | 0;
// let h1 = j % h;
// let c1 = k % c;
// let w1 = k / c | 0;
// let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1;
// this.testData[i] = data[l];
// }
// });
} }
} }
fetchOneChunk(path) { fetchOneChunk(path) {
console.time(path) return this.fetch(path).then(request => {
return fetch(path).then(request => {
console.timeEnd(path);
return request.arrayBuffer(); return request.arrayBuffer();
}) })
} }
fetchJson(path) {
return this.fetch(path).then(request => {
return request.json();
})
}
fetchAllData() { fetchAllData() {
// todo 兼容一下json的模式 // todo 兼容一下json的模式
let counts = this.binaryOption.fileCount; let counts = this.binaryOption.fileCount;
...@@ -61,8 +82,6 @@ export default class GraphModel { ...@@ -61,8 +82,6 @@ export default class GraphModel {
this.fetchOneChunk(this.modelGonfig.dir + this.binaryOption.getFileName(i)) this.fetchOneChunk(this.modelGonfig.dir + this.binaryOption.getFileName(i))
); );
} }
// 1个文件
// let chunkArray = [this.fetchOneChunk('/faceModel/mergedData.dat')];
console.time('加载时间'); console.time('加载时间');
return Promise.all(chunkArray).then(chunks => { return Promise.all(chunkArray).then(chunks => {
console.timeEnd('加载时间'); console.timeEnd('加载时间');
...@@ -109,13 +128,24 @@ export default class GraphModel { ...@@ -109,13 +128,24 @@ export default class GraphModel {
marker += len; marker += len;
}); });
} }
fetch(path, params) {
params = params || this.params;
let method = params.method || 'get';
let mode = params.mode || 'cors';
let myHeaders = new Headers();
return fetch(path, {
method: method,
mode: mode,
credentials: 'include',
headers: myHeaders
});
}
fetchModel(params) { fetchModel(params) {
params = params || this.params; params = params || this.params;
const path = this.modelGonfig.dir + this.modelGonfig.main; const path = this.modelGonfig.dir + this.modelGonfig.main;
let URL_SCHEME_REGEX = /^https?:\/\//;
let load = null; let load = null;
let method = params.method || 'get';
let mode = params.mode || 'cors';
// jsonp请求方式 // jsonp请求方式
if (params && params.type === 'jsonp') { if (params && params.type === 'jsonp') {
let json; let json;
...@@ -139,14 +169,8 @@ export default class GraphModel { ...@@ -139,14 +169,8 @@ export default class GraphModel {
} }
// 原生fetch // 原生fetch
else if (params.type === 'fetch') { else if (params.type === 'fetch') {
let myHeaders = new Headers();
load = new Promise((resolve, reject) => { load = new Promise((resolve, reject) => {
fetch(path, { this.fetch(path, params)
method: method,
mode: mode,
credentials: "include",
headers: myHeaders
})
.then(response => response.json()) .then(response => response.json())
.then(responseData => resolve(responseData)) .then(responseData => resolve(responseData))
.then(err => reject(err)) .then(err => reject(err))
...@@ -161,23 +185,13 @@ export default class GraphModel { ...@@ -161,23 +185,13 @@ export default class GraphModel {
} }
async load() { async load() {
let that = this; let that = this;
console.time('生成op数据之前')
console.time('fetchModel');
const artifacts = this.handler = await this.fetchModel(); const artifacts = this.handler = await this.fetchModel();
console.timeEnd('fetchModel');
if (this.multipart === true) { if (this.multipart === true) {
console.time('6个文件准备好op数据');
await this.fetchAllData() await this.fetchAllData()
.then(() => this.traverse(artifacts.vars)); .then(() => this.traverse(artifacts.vars));
console.timeEnd('6个文件准备好op数据');
} }
console.time('createOpsMap');
const opsMap = this.createOpsMap(artifacts.ops, artifacts.vars); const opsMap = this.createOpsMap(artifacts.ops, artifacts.vars);
console.timeEnd('createOpsMap');
console.time('constructOpsMap');
this.weightMap = this.constructOpsMap(opsMap); this.weightMap = this.constructOpsMap(opsMap);
console.timeEnd('constructOpsMap');
console.timeEnd('生成op数据之前')
// 生成op数据 // 生成op数据
this.weightMap.forEach(op => { this.weightMap.forEach(op => {
const type = op.type; const type = op.type;
...@@ -192,23 +206,27 @@ export default class GraphModel { ...@@ -192,23 +206,27 @@ export default class GraphModel {
const opData = new OpData(op.type, tensor.inputs, tensor.outputs, tensor.attrs); const opData = new OpData(op.type, tensor.inputs, tensor.outputs, tensor.attrs);
const name = opData.name; const name = opData.name;
const fsCode = factory.buildShader(name, opData.data); const fsCode = factory.buildShader(name, opData.data);
opData.fshader = this.inst.createFragmentShader(fsCode); opData.fsCode = fsCode;
opData.program = this.inst.createProgram(fsCode, opData.tensor['out']);
opData.renderData = opConfs[name].map(elem => { opData.renderData = opConfs[name].map(elem => {
let item = Object.assign({}, elem); let item = Object.assign({}, elem);
const tensorData = opData.tensor[item.tensor]; const tensorData = opData.tensor[item.tensor];
if (item.type === 'texture') { if (item.type === 'texture') {
item.data = tensorData.data; item.data = tensorData.data;
if (this.feedOp.id === op.id) { if (this.feedOp.id === op.id && item.tensor === 'origin') {
item.shape = tensorData.shape; item.shape = tensorData.shape;
this.feedItem = item; this.feedItem = item;
} }
item['width_texture'] = tensorData['width_texture']; item['width_texture'] = tensorData['width_texture'];
item['height_texture'] = tensorData['height_texture']; item['height_texture'] = tensorData['height_texture'];
item['channel'] = tensorData['channel'];
} else if (item.type === 'uniform') { } else if (item.type === 'uniform') {
item.data = tensorData[item.variable]; item.data = tensorData[item.variable];
} }
return item; return item;
}); });
// console.timeEnd('opData.renderData');
opData.iLayer = this.iLayer++;
op.opData = opData; op.opData = opData;
// delete op.inputs; // delete op.inputs;
// delete op.outputs; // delete op.outputs;
...@@ -218,7 +236,7 @@ export default class GraphModel { ...@@ -218,7 +236,7 @@ export default class GraphModel {
if (executor.type === 'fetch') { if (executor.type === 'fetch') {
return; return;
} }
executor.execute(this.inst); executor.execute(this.inst, this.isExecuted);
if (executor.next) { if (executor.next) {
const id = executor.next; const id = executor.next;
const next = this.getTensor(id); const next = this.getTensor(id);
...@@ -245,13 +263,12 @@ export default class GraphModel { ...@@ -245,13 +263,12 @@ export default class GraphModel {
} }
let start = +Date.now(); let start = +Date.now();
this.execute_(executor[0]); this.execute_(executor[0]);
console.log('总的执行时间是' + (+Date.now() - start));
this.isExecuted = true; this.isExecuted = true;
return this.inst; return this.inst;
} }
updateFeed() { updateFeed() {
this.feedItem.data = this.feed.input[0].data; this.feedItem.data = this.feed.input[0].data;
Utils.img2texture(this.feedItem); // Utils.img2texture(this.feedItem);
} }
/** /**
* predict enter * predict enter
...@@ -282,6 +299,7 @@ export default class GraphModel { ...@@ -282,6 +299,7 @@ export default class GraphModel {
input[key] = io.fromPixels(data, pixel); input[key] = io.fromPixels(data, pixel);
} }
else if ((key === 'Input') && (inputName === 'image' || inputName === 'x')) { else if ((key === 'Input') && (inputName === 'image' || inputName === 'x')) {
// that.feed.input[0].data = that.testData;
input[key] = that.feed.input; input[key] = that.feed.input;
that.feedOp = executor; that.feedOp = executor;
} }
...@@ -289,6 +307,7 @@ export default class GraphModel { ...@@ -289,6 +307,7 @@ export default class GraphModel {
input[key] = that.getTensorAttr(input[key][0]); input[key] = that.getTensorAttr(input[key][0]);
} }
}); });
// console.log(input);
const tensor = { const tensor = {
inputs: input, inputs: input,
outputs: output, outputs: output,
......
/* eslint-disable */
/* 后处理图片 by zhangmiao06 */
// let preTestRun = index => {
// let img = document.getElementById('image');
// img.src = tempPic[index];
// img.onload = function () {
// testRun(testOutput.data[index], img);
// };
// };
import models from '../utils/models';
const isSimilar = (r1, r2, threshold = 5) => {
return Math.max(Math.abs(r1[0] - r2[0]), Math.abs(r1[1] - r2[1])) < threshold;
// return Math.abs((r1[0] + r1[1] + r1[2] + r1[3]) - (r2[0] + r2[1] + r2[2] + r2[3])) < threshold;
}
// sigmoid
let sigmoid = (x) => {
if (x < -100) {
return 0.0;
}
return 1 / (1 + Math.exp(-x));
};
// transpose
let transpose = (data) => {
let shape = data.shape;
let transposeShape = data.transposeShape;
let formatData = data.data;
let formatData2 = [];
for (let n = 0; n < shape[transposeShape[0]]; n++) {
let nData = [];
for (let c = 0; c < shape[transposeShape[1]]; c++) {
let cData = [];
for (let row = 0; row < shape[transposeShape[2]]; row++) {
let rowData = [];
for (let col = 0; col < shape[transposeShape[3]]; col++) {
let tempArr = [n, c, row, col];
let newN = n;
let newC = c;
let newW = row;
let newH = col;
transposeShape.forEach((item, index) => {
switch (item) {
case 0:
newN = tempArr[index];
break;
case 1:
newC = tempArr[index];
break;
case 2:
newW = tempArr[index];
break;
case 3:
newH = tempArr[index];
}
});
rowData.push(formatData[newN][newC][newW][newH]);
}
cData.push(rowData);
}
nData.push(cData);
}
formatData2.push(nData);
}
return formatData2;
};
// reshape
const reshape = (data) => {
let formatData2 = data.data;
let shape = data.shape;
let reshapeShape = data.reshapeShape;
// 1.变成一维
let tempData = reshapeOne({
data: formatData2,
shape: shape
});
// 2.变成多维
let formatData3 = reshapeMany({
data: tempData,
reshapeShape: reshapeShape
});
return formatData3;
};
// 变成一维
const reshapeOne = (data) => {
let formatData2 = data.data;
let shape = data.shape;
let tempData = [];
for (let n = 0; n < shape[0]; n++) {
for (let c = 0; c < shape[1]; c++) {
for (let row = 0; row < shape[2]; row++) {
for (let col = 0; col < shape[3]; col++) {
tempData.push(formatData2[n][c][row][col]);
}
}
}
}
return tempData;
};
// 变成多维
const reshapeMany = data => {
let tempData = data.data;
let reshapeShape = data.reshapeShape;
let formatData3 = [];
for (let n = 0; n < reshapeShape[0]; n++) {
let nData = [];
for (let c = 0; c < reshapeShape[1]; c++) {
let cData = [];
for (let row = 0; row < reshapeShape[2]; row++) {
let rowData = [];
for (let col = 0; col < reshapeShape[3]; col++) {
let tempN = n * reshapeShape[1] * reshapeShape[2] * reshapeShape[3];
let tempC = c * reshapeShape[2] * reshapeShape[3];
let tempRow = row * reshapeShape[3];
rowData.push(tempData[tempN + tempC + tempRow + col]);
}
cData.push(rowData);
}
nData.push(cData);
}
formatData3.push(nData);
}
return formatData3;
};
export default class PostProcess {
constructor(options) {
this.modelConfig = models[options.modelName];
this.count = 0;
this.lastRect = [0, 0, 0, 0]
}
run(data, img, callback) {
let {from, to} = this.modelConfig.outputShapes;
let shape = [].concat(from).reverse();
// 1.从一维数组到1*25*19*19
let formatData = reshapeMany({
data: data,
reshapeShape: shape
});
// console.log('一维到多维', formatData);
// 2.从1*25*19*19 到 19*19*25*1
let formatData2 = transpose({
data: formatData,
shape: shape,
transposeShape: [2, 3, 1, 0]
});
// console.log('transpose', formatData2);
// 3.从19*19*25*1到19*19*5*5
let formatData3 = reshape({
data: formatData2,
// shape: [19, 19, 25, 1],
// reshapeShape: [19, 19, 5, 5]
shape: from,
reshapeShape: to
});
// console.log('reshape', formatData3);
// 4.运算
let finalData = this.handleFinal(formatData3, shape, img);
// console.log('final', finalData);
// 5.处理画布
// finalData.length && handleCanvas(finalData, img);
this.handleDiv(finalData, img, callback);
}
calSize(img) {
let w1 = img.width;
let h1 = img.height;
let wh1 = Math.max(w1, h1);
let factor = this.modelConfig.feedShape.fw / wh1;
// let factor = 608.0 / wh1;
let width = Math.round(w1 * factor);
let height = Math.round(h1 * factor);
return [w1, h1, width, height];
}
// 处理运算
handleFinal(formatData3, shape, img) {
let finalData = [];
let c = shape[2];
let [w1, h1, width, height] = this.calSize(img);
let factorX = Math.max(width, height) / width;
let factorY = Math.max(width, height) / height;
let maxProb = 0.0;
let anchors = [[1.603231, 2.094468], [6.041143, 7.080126], [2.882459, 3.518061], [4.266906, 5.178857], [9.041765, 10.66308]];
for (let i = 0; i < shape[2]; i++) {
for (let j = 0; j < shape[3]; j++) {
for (let k = 0; k < anchors.length; k++) {
let [a1, a2, a3, a4, prob] = formatData3[i][j][k];
prob = sigmoid(prob);
if (prob > maxProb && prob >= 0.5) {
let ctx = (j + sigmoid(a1)) / c * factorX;
let cty = (i + sigmoid(a2)) / c * factorY;
let col = Math.exp(a3) * anchors[k][0] / c * factorX;
let row = Math.exp(a4) * anchors[k][1] / c * factorY;
let x = (ctx - (col / 2));
let y = (cty - (row / 2));
finalData.push([x * w1, y * h1, col * w1, row * h1, prob]);
}
}
}
}
return finalData;
}
handleDiv(finalData, img, callback) {
if (finalData.length < 1) {
callback();
return false;
}
let maxIndex = 0;
if (finalData.length > 1) {
for (let i = 1; i < finalData.length; i++) {
if (finalData[i].prob > finalData[maxIndex].prob) {
maxIndex = i;
}
}
}
let [demoLeft, demoTop, demoWidth, demoHeight] = finalData[maxIndex];
if (!isSimilar(this.lastRect, [demoLeft, demoTop, demoWidth, demoHeight])) {
callback([demoWidth, demoHeight,demoLeft, demoTop]);
};
this.lastRect = [demoLeft, demoTop, demoWidth, demoHeight];
}
// 处理画布
handleCanvas(finalData, img) {
let myCanvas = document.getElementById('myCanvas');
let [w1, h1, width, height] = calSize(img);
myCanvas.width = w1;
myCanvas.height = h1;
let ctx = myCanvas.getContext('2d');
// ctx.drawImage(img, 0, 0, w1, h1);
// finalData.forEach((demoArr, index) => {
// let [demoLeft, demoTop, demoWidth, demoHeight, prob] = demoArr;
let [demoLeft, demoTop, demoWidth, demoHeight, prob] = finalData[0];
ctx.beginPath();
ctx.lineWidth = 4;
ctx.strokeStyle = 'red';
ctx.moveTo(demoLeft, demoTop);
ctx.lineTo(demoLeft + demoWidth, demoTop);
ctx.lineTo(demoLeft + demoWidth, demoTop + demoHeight);
ctx.lineTo(demoLeft, demoTop + demoHeight);
ctx.closePath();
ctx.stroke();
// });
}
}
/**
* @file Runner 整个流程封装一下
* @author hantian(hantianjiao@baidu.com)
* 使用方法:
* const runner = new Runner({
* modelName: 'separate' // '608' | '320' | '320fused' | 'separate'
* });
* runner.preheat().then(r => {
* r.run(document.getElementById('test'));
* });
*/
import IO from '../feed/ImageFeed';
import DataFeed from '../feed/dataFeed';
import Graph from './loader';
import PostProcess from './postProcess';
import models from '../utils/models';
import Logger from '../../tools/logger';
window.log = new Logger();
export default class Runner {
// 加载模型&预热
constructor(options) {
this.modelConfig = models[options.modelName];
this.flags = {
isRunning: false,
isPreheating: false,
runVideoPaused: false
};
this.buffer = new Float32Array();
this.io = new IO();
this.postProcess = new PostProcess(options);
}
// 预热 用用空数据跑一遍
async preheat() {
this.flags.isPreheating = true;
let {fh, fw} = this.modelConfig.feedShape;
let path = this.modelConfig.modelPath;
let feed = [{
data: new Float32Array(3 * fh * fw),
name: 'image',
shape: [1, 3, fh, fw]
}];
const MODEL_URL = `/${path}/model.json`;
const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹
// dir: `https://graph.baidu.com/mms/graph/static/asset/dll/${path}/`, // rd测试地址
// dir: `/src/view/common/lib/paddle/dist/${path}/`, // 本地测试地址
main: 'model.json' // 主文件
};
const graphModel = new Graph();
this.model = await graphModel.loadGraphModel(MODEL_CONFIG, {
multipart: true,
dataType: 'binary',
binaryOption: {
fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_0.dat';
}
},
feed
});
this.model.execute({
input: feed
});
this.flags.isPreheating = false;
return this;
}
// 跑一遍
async run(input, callback) {
this.flags.isRunning = true;
let {fh, fw} = this.modelConfig.feedShape;
let path = this.modelConfig.modelPath;
if (!this.model) {
console.warn('It\'s better to preheat the model before running.');
await this.preheat();
}
log.start('总耗时'); // eslint-disable-line
log.start('预处理'); // eslint-disable-line
let feed;
if (typeof input === 'string') {
const dfIO = new DataFeed();
feed = await dfIO.process({
input: `/${path}/${input}`,
shape: [1, 3, fh, fw]
});
}
else {
feed = this.io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
targetSize: {
height: fw,
width: fh
},
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
// shape: [3, 608, 608], // 预设tensor形状
mean: [117.001, 114.697, 97.404] // 预设期望
// std: [0.229, 0.224, 0.225] // 预设方差
}
});
}
log.end('预处理'); // eslint-disable-line
log.start('运行耗时'); // eslint-disable-line
let inst = this.model.execute({
input: feed
});
let result = await inst.read();
log.end('后处理-读取数据'); // eslint-disable-line
const newData = [];
let newIndex = -1;
const [w, h, c, b] = this.modelConfig.outputShapes.from;
// c channel
for (let i = 0; i < c; i++) {
// height channel
for (let j = 0; j < h; j++) {
// width channel
for (let k = 0; k < w; k++) {
// position: (0, 0, 0, 0)
const index = j * (c * h) + k * c + i;
// const index = j * (i * k) + k * i + i;
newData[++newIndex] = result[index];
}
}
}
this.postProcess.run(newData, input, callback);
log.end('后处理'); // eslint-disable-line
this.flags.isRunning = false;
log.end('总耗时'); // eslint-disable-line
}
// 传入获取图片的function
async runStream(getMedia, callback) {
await this.run(getMedia(), callback);
if (!this.flags.runVideoPaused) {
setTimeout(async () => {
await this.runStream(getMedia, callback);
}, 0);
}
}
stopStream() {
this.flags.runVideoPaused = true;
}
startStream(getMedia, callback) {
this.flags.runVideoPaused = false;
this.runStream(getMedia, callback);
}
}
...@@ -6,6 +6,15 @@ import ops from './ops'; ...@@ -6,6 +6,15 @@ import ops from './ops';
export default class Factory { export default class Factory {
constructor(opts) { constructor(opts) {
this.defaultOpts = Object.assign({}, opts); this.defaultOpts = Object.assign({}, opts);
this.webglVersion = 2;
this.texture2d = 'texture';
}
setWebglVersion(vs = 0) {
this.webglVersion = vs;
if (vs === 1) {
this.texture2d = 'texture2D';
}
} }
buildShader(opName, data) { buildShader(opName, data) {
...@@ -13,12 +22,16 @@ export default class Factory { ...@@ -13,12 +22,16 @@ export default class Factory {
result = this.buildPrefix(opName); result = this.buildPrefix(opName);
result += this.buildCommon(opName); result += this.buildCommon(opName);
result += this.buildOp(opName); result += this.buildOp(opName);
data.texture2d = this.texture2d;
result = this.populateData(result, data); result = this.populateData(result, data);
return result; return result;
} }
buildPrefix(opName) { buildPrefix(opName) {
return ops.common.prefix; if (this.webglVersion === 1) {
return ops.common.prefix;
}
return ops.common.prefix2;
} }
buildCommon(opName) { buildCommon(opName) {
......
...@@ -2,18 +2,28 @@ ...@@ -2,18 +2,28 @@
import common_params from '../../shader/atom/common_params'; import common_params from '../../shader/atom/common_params';
import common_func from '../../shader/atom/common_func'; import common_func from '../../shader/atom/common_func';
import prefix from '../../shader/atom/prefix'; import prefix from '../../shader/atom/prefix';
import prefix2 from '../../shader/atom/prefix2';
import suffix from '../../shader/atom/suffix'; import suffix from '../../shader/atom/suffix';
import ivec56 from '../../shader/atom/type_ivec56'; import ivec56 from '../../shader/atom/type_ivec56';
import conv2d_params from '../../shader/conv2d/params'; import conv2d_params from '../../shader/conv2d/params';
import conv2d_func from '../../shader/conv2d/main'; import conv2d_func from '../../shader/conv2d/main';
import conv2d_conf from '../../shader/conv2d/conf'; import conv2d_conf from '../../shader/conv2d/conf';
import conv2d_depthwise_params from '../../shader/conv2d_depthwise/params';
import conv2d_depthwise_func from '../../shader/conv2d_depthwise/main';
import conv2d_depthwise_conf from '../../shader/conv2d_depthwise/conf';
import dynamic_params from '../../shader/dynamic/params'; import dynamic_params from '../../shader/dynamic/params';
import dynamic_func from '../../shader/dynamic/main'; import dynamic_func from '../../shader/dynamic/main';
import dynamic_conf from '../../shader/dynamic/conf'; import dynamic_conf from '../../shader/dynamic/conf';
import pool2d_params from '../../shader/pool2d/params'; import pool2d_params from '../../shader/pool2d/params';
import pool2d_func from '../../shader/pool2d/main'; import pool2d_func from '../../shader/pool2d/main';
import pool2d_conf from '../../shader/pool2d/conf'; import pool2d_conf from '../../shader/pool2d/conf';
import pool2d_max_params from '../../shader/pool2d_max/params';
import pool2d_max_func from '../../shader/pool2d_max/main';
import pool2d_max_conf from '../../shader/pool2d_max/conf';
import pool2d_winograd_params from '../../shader/pool2d_winograd/params';
import pool2d_winograd_func from '../../shader/pool2d_winograd/main';
import pool2d_winograd_conf from '../../shader/pool2d_winograd/conf';
import elementwise_add_params from '../../shader/elementwise_add/params'; import elementwise_add_params from '../../shader/elementwise_add/params';
import elementwise_add_func from '../../shader/elementwise_add/main'; import elementwise_add_func from '../../shader/elementwise_add/main';
import elementwise_add_conf from '../../shader/elementwise_add/conf'; import elementwise_add_conf from '../../shader/elementwise_add/conf';
...@@ -27,12 +37,21 @@ import batchnorm_params from '../../shader/batchnorm/params'; ...@@ -27,12 +37,21 @@ import batchnorm_params from '../../shader/batchnorm/params';
import batchnorm_func from '../../shader/batchnorm/main'; import batchnorm_func from '../../shader/batchnorm/main';
import batchnorm_conf from '../../shader/batchnorm/conf'; import batchnorm_conf from '../../shader/batchnorm/conf';
import conv2d_elementwise_add_params from '../../shader/conv2d_elementwise_add/params';
import conv2d_elementwise_add_func from '../../shader/conv2d_elementwise_add/main';
import conv2d_elementwise_add_conf from '../../shader/conv2d_elementwise_add/conf';
import conv2d_elementwise_add_winograd_params from '../../shader/conv2d_elementwise_add_winograd/params';
import conv2d_elementwise_add_winograd_func from '../../shader/conv2d_elementwise_add_winograd/main';
import conv2d_elementwise_add_winograd_conf from '../../shader/conv2d_elementwise_add_winograd/conf';
import getArrayIndexFromTensorPos from '../../shader/atom/getArrayIndexFromTensorPos'; import getArrayIndexFromTensorPos from '../../shader/atom/getArrayIndexFromTensorPos';
import getArrayIndexFromTexturePos from '../../shader/atom/getArrayIndexFromTexturePos'; import getArrayIndexFromTexturePos from '../../shader/atom/getArrayIndexFromTexturePos';
import getTensorPosFromArrayIndex from '../../shader/atom/getTensorPosFromArrayIndex'; import getTensorPosFromArrayIndex from '../../shader/atom/getTensorPosFromArrayIndex';
import getTexturePosFromArrayIndex from '../../shader/atom/getTexturePosFromArrayIndex'; import getTexturePosFromArrayIndex from '../../shader/atom/getTexturePosFromArrayIndex';
import getValueFromTexturePos from '../../shader/atom/getValueFromTexturePos'; import getValueFromTexturePos from '../../shader/atom/getValueFromTexturePos';
import getValueFromTensorPos from '../../shader/atom/getValueFromTensorPos'; import getValueFromTensorPos from '../../shader/atom/getValueFromTensorPos';
import getValueFromTensorPosPacked from '../../shader/atom/getValueFromTensorPosPacked';
import moveTexture2PosToReal from '../../shader/atom/moveTexture2PosToReal'; import moveTexture2PosToReal from '../../shader/atom/moveTexture2PosToReal';
import getPixelsFromTexturePos from '../../shader/atom/getPixelsFromTexturePos'; import getPixelsFromTexturePos from '../../shader/atom/getPixelsFromTexturePos';
import getRangePowSumFromArrayIndex from '../../shader/atom/getRangePowSumFromArrayIndex'; import getRangePowSumFromArrayIndex from '../../shader/atom/getRangePowSumFromArrayIndex';
...@@ -51,6 +70,7 @@ export default { ...@@ -51,6 +70,7 @@ export default {
params: common_params, params: common_params,
func: common_func, func: common_func,
prefix, prefix,
prefix2,
suffix, suffix,
ivec56 ivec56
}, },
...@@ -60,6 +80,21 @@ export default { ...@@ -60,6 +80,21 @@ export default {
func: conv2d_func, func: conv2d_func,
confs: conv2d_conf confs: conv2d_conf
}, },
conv2d_depthwise: {
params: conv2d_depthwise_params,
func: conv2d_depthwise_func,
confs: conv2d_depthwise_conf
},
conv2d_elementwise_add: {
params: conv2d_elementwise_add_params,
func: conv2d_elementwise_add_func,
confs: conv2d_elementwise_add_conf
},
conv2d_elementwise_add_winograd: {
params: conv2d_elementwise_add_winograd_params,
func: conv2d_elementwise_add_winograd_func,
confs: conv2d_elementwise_add_winograd_conf
},
dynamic: { dynamic: {
params: dynamic_params, params: dynamic_params,
func: dynamic_func, func: dynamic_func,
...@@ -70,6 +105,16 @@ export default { ...@@ -70,6 +105,16 @@ export default {
func: pool2d_func, func: pool2d_func,
confs: pool2d_conf confs: pool2d_conf
}, },
pool2d_max: {
params: pool2d_max_params,
func: pool2d_max_func,
confs: pool2d_max_conf
},
pool2d_winograd: {
params: pool2d_winograd_params,
func: pool2d_winograd_func,
confs: pool2d_winograd_conf
},
elementwise_add: { elementwise_add: {
params: elementwise_add_params, params: elementwise_add_params,
func: elementwise_add_func, func: elementwise_add_func,
...@@ -108,6 +153,7 @@ export default { ...@@ -108,6 +153,7 @@ export default {
getTexturePosFromArrayIndex, getTexturePosFromArrayIndex,
getValueFromTexturePos, getValueFromTexturePos,
getValueFromTensorPos, getValueFromTensorPos,
getValueFromTensorPosPacked,
moveTexture2PosToReal, moveTexture2PosToReal,
getPixelsFromTexturePos, getPixelsFromTexturePos,
getRangeSumFromArrayIndex, getRangeSumFromArrayIndex,
......
...@@ -30,7 +30,10 @@ export default class imageFeed { ...@@ -30,7 +30,10 @@ export default class imageFeed {
...inputs.params ...inputs.params
}; };
let output = []; let output = [];
if (!this.result) {
const [b, c, h, w] = params.targetShape;
this.result = new Float32Array(h * w * 3);
}
output = this.fromPixels(input, params); output = this.fromPixels(input, params);
return output; return output;
}; };
...@@ -74,21 +77,27 @@ export default class imageFeed { ...@@ -74,21 +77,27 @@ export default class imageFeed {
*/ */
allReshapeToRGB(imageData, opt, scaleSize) { allReshapeToRGB(imageData, opt, scaleSize) {
const {sw, sh} = scaleSize; const {sw, sh} = scaleSize;
const {width, height} = opt; const [b, c, h, w] = opt.targetShape;
let data = imageData.data; let data = imageData.data;
let mean = opt.mean; let mean = opt.mean;
let dataLength = data.length; let dataLength = data.length;
let result = new Float32Array(dataLength * 3 / 4); // let result = new Float32Array(dataLength * 3);
let offsetR = 0; let result = this.result;
let offsetG = dataLength / 4; // let offsetR = 0;
let offsetB = dataLength / 2; // let offsetG = dataLength / 4;
for (let i = 0; i < data.length; i += 4) { // let offsetB = dataLength / 2;
result[offsetR++] = (data[i] - mean[0]) / 256; let offset = 0;
result[offsetG++] = (data[i + 1] - mean[1]) / 256; let size = h * w;
result[offsetB++] = (data[i + 2] - mean[2]) / 256; // h w c
// result.push((data[i] - mean[0]) / 256); // red for (let i = 0; i < h; ++i) {
// result.push((data[i + 1] - mean[1]) / 256); // green let iw = i * w;
// result.push((data[i + 2] - mean[2]) / 256); // blue for (let j = 0; j < w; ++j) {
let iwj = iw + j;
for (let k = 0; k < c; ++k) {
let a = iwj * 4 + k;
result[offset++] = (data[a] - mean[k]) / 256;
}
}
} }
return result; return result;
}; };
...@@ -156,8 +165,10 @@ export default class imageFeed { ...@@ -156,8 +165,10 @@ export default class imageFeed {
else { else {
this.fromPixels2DContext.drawImage( this.fromPixels2DContext.drawImage(
image, 0, 0, sw, sh); image, 0, 0, sw, sh);
// currentPic = this.fromPixels2DContext.canvas.toDataURL();
} }
document.getElementById('p-c').appendChild(this.fromPixels2DContext.canvas);// test only, demele me // window.currentPic = this.fromPixels2DContext.canvas;// test only, demele me
// document.getElementById('p-c').appendChild(this.fromPixels2DContext.canvas);// test only, demele me
return {sw: targetWidth, sh: targetHeight}; return {sw: targetWidth, sh: targetHeight};
} }
......
/**
* @file 直接数据输入
* @author hantianjiao@baidu.com
*/
export default class dataFeed {
toFloat32Array(data) {
for (let i = 0; i < data.length; i++) {
this.f32Arr[i] = data[i];
}
}
getLengthFromShape(shape) {
return shape.reduce((a, b) => a * b);
}
loadData() {
return fetch(this.dataPath).then(res => res.json());
}
getOutput() {
return this.loadData().then(data => {
this.toFloat32Array(data);
return [{
data: this.f32Arr,
shape: this.shape,
name: 'x'
}];
});
}
async process(input) {
this.len = this.getLengthFromShape(input.shape);
if (!this.f32Arr || this.len > this.f32Arr.length) {
this.f32Arr = new Float32Array(this.len);
}
this.shape = input.shape;
this.dataPath = input.input;
let output = await this.getOutput();
return output;
}
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* @file io,loader相关输入输出 * @file io,loader相关输入输出
* @author wangqun@baidu.com * @author wangqun@baidu.com
*/ */
export default class io { export default class io {
constructor() { constructor() {
this.fromPixels2DContext = document.createElement('canvas').getContext('2d'); this.fromPixels2DContext = document.createElement('canvas').getContext('2d');
......
/* eslint-disable */ /* eslint-disable */
import VSHADER from '../shader/v_shader'; import VSHADER from '../shader/v_shader';
import VSHADER2 from '../shader/v_shader2';
/** /**
* @file gpu运算 * @file gpu运算
* @author yangmingming * @author yangmingming
*/ */
const CONF = {
alpha: false,
antialias: false,
premultipliedAlpha: false,
preserveDrawingBuffer: false,
depth: false,
stencil: false,
failIfMajorPerformanceCaveat: true
};
const MAX_WAIT = 100;
export default class gpu { export default class gpu {
constructor(opts = {}) { constructor(opts = {}) {
// 版本, 默认webgl version 2.0
this.version = 2;
this.opts = opts; this.opts = opts;
opts.width_raw_canvas = Number(opts.width_raw_canvas) || 512; opts.width_raw_canvas = Number(opts.width_raw_canvas) || 512;
opts.height_raw_canvas = Number(opts.height_raw_canvas) || 512; opts.height_raw_canvas = Number(opts.height_raw_canvas) || 512;
let canvas = opts.el ? opts.el : document.createElement('canvas'); const canvas = opts.el ? opts.el : document.createElement('canvas');
canvas.width = opts.width_raw_canvas; canvas.addEventListener('webglcontextlost', evt => {
canvas.height = opts.height_raw_canvas; evt.preventDefault();
this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); console.log('webgl context is lost~');
this.gl.viewport(0, 0, canvas.width, canvas.height); }, false);
// Attempt to activate the extension, returns null if unavailable let gl = canvas.getContext('webgl2', CONF);
this.textureFloat = this.gl.getExtension('OES_texture_float'); if (!!gl) {
// this.setOutProps(); // 开启float32
this.version = 2;
this.textureFloat = gl.getExtension('EXT_color_buffer_float');
this.internalFormat = gl.R32F;
this.textureFormat = gl.RED;
this.downloadInternalFormat = gl.RGBA32F;
} else {
gl = canvas.getContext('webgl', CONF) || canvas.getContext('experimental-webgl', CONF);
this.version = 1;
this.internalFormat = gl.RGBA;
this.textureFormat = gl.RGBA;
this.downloadInternalFormat = gl.RGBA;
if (!gl) {
this.version = 0;
alert('当前环境创建webgl context失败');
} else {
// 开启扩展
this.textureFloat = gl.getExtension('OES_texture_float');
console.log('float extension is started or not? ' + !!this.textureFloat);
}
}
// 关闭相关功能
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.STENCIL_TEST);
gl.disable(gl.BLEND);
gl.disable(gl.DITHER);
gl.disable(gl.POLYGON_OFFSET_FILL);
gl.disable(gl.SAMPLE_COVERAGE);
gl.enable(gl.SCISSOR_TEST);
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.BACK);
this.gl = gl;
this.initCache(); this.initCache();
console.log('float extension is started or not? ' + !!this.textureFloat); // 同步查看次数
console.log('WebGl版本是 ' + this.gl.getParameter(this.gl.SHADING_LANGUAGE_VERSION)); this.waits = 0;
console.log('WebGl版本是 ' + this.version);
console.log('MAX_TEXTURE_SIZE is ' + gl.getParameter(gl.MAX_TEXTURE_SIZE));
console.log('MAX_TEXTURE_IMAGE_UNITS is ' + gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
}
getWebglVersion() {
return this.version;
} }
initCache() { initCache() {
// 运行次数 // 运行次数
this.times = 0; this.times = 0;
const gl = this.gl; const gl = this.gl;
// 缓存每个op的texture
// this.textures = [];
// 顶点数据 // 顶点数据
let vertices = new Float32Array([ let vertices = new Float32Array([
-1.0, 1.0, 0.0, 1.0, -1.0, 1.0, 0.0, 1.0,
...@@ -40,7 +90,7 @@ export default class gpu { ...@@ -40,7 +90,7 @@ export default class gpu {
// shader // shader
this.vertexShader = null; this.vertexShader = null;
// 生成vertextShader // 生成vertextShader
this.initShader(VSHADER); this.initShader(this.version === 2 ? VSHADER2 : VSHADER);
this.fragmentShader = null; this.fragmentShader = null;
// 上一个texture // 上一个texture
this.prevTexture = null; this.prevTexture = null;
...@@ -49,27 +99,18 @@ export default class gpu { ...@@ -49,27 +99,18 @@ export default class gpu {
// 帧缓存 // 帧缓存
this.frameBuffer = gl.createFramebuffer(); this.frameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
// 计算texture cache, 最多3个 // 计算texture cache
this.cacheTextures = [gl.createTexture(), gl.createTexture(), gl.createTexture()]; this.cacheTextures = {};
this.uniformLocations = {};
// texture buffer // texture buffer
this.textureBuffer = [gl.createTexture(), gl.createTexture()]; this.outTextures = [];
// program // pbo
this.programs = [gl.createProgram(), gl.createProgram()]; this.pbo = gl.createBuffer();
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() { runVertexShader(program) {
const gl = this.gl; const gl = this.gl;
let aPosition = gl.getAttribLocation(this.program, 'position'); let aPosition = gl.getAttribLocation(program, 'position');
// Turn on the position attribute // Turn on the position attribute
gl.enableVertexAttribArray(aPosition); gl.enableVertexAttribArray(aPosition);
// Bind the position buffer. // Bind the position buffer.
...@@ -82,6 +123,7 @@ export default class gpu { ...@@ -82,6 +123,7 @@ export default class gpu {
this.height_shape_out = opts.height_shape || 1; this.height_shape_out = opts.height_shape || 1;
this.width_texture_out = opts.width_texture || 1; this.width_texture_out = opts.width_texture || 1;
this.height_texture_out = opts.height_texture || 1; this.height_texture_out = opts.height_texture || 1;
this.channel = opts.channel || 0;
this.total_shape = opts.total_shape || 0; this.total_shape = opts.total_shape || 0;
} }
...@@ -89,27 +131,63 @@ export default class gpu { ...@@ -89,27 +131,63 @@ export default class gpu {
return (this.textureFloat !== null); return (this.textureFloat !== null);
} }
attachShader(fshader) { createProgram(fshader, out) {
const gl = this.gl; const gl = this.gl;
let index = this.textureBufferIndex % 2; const program = gl.createProgram();
const program = this.programs[index]; gl.attachShader(program, this.vertexShader);
gl.attachShader(program, fshader);
gl.linkProgram(program);
// 生成output的texture缓存
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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, // Target, matches bind above.
0, // Level of detail.
this.downloadInternalFormat, // Internal format.
out.width_texture,
out.height_texture,
0, // Always 0 in OpenGL ES.
gl.RGBA, // Format for each pixel.
gl.FLOAT, // Data type for each chanel.
null);
gl.bindTexture(gl.TEXTURE_2D, null);
this.outTextures.push(texture);
return program;
}
setProgram(program, isRendered) {
const gl = this.gl;
gl.useProgram(program);
this.program = program; this.program = program;
if (this.times < 2) { if (!isRendered) {
gl.attachShader(program, this.vertexShader); this.runVertexShader(program);
} }
}
attachShader(fshader) {
const gl = this.gl;
// let index = this.textureBufferIndex % 2;
// const program = this.programs[index];
// this.program = program;
const program = this.program;
// if (this.times < 2) {
// gl.attachShader(program, this.vertexShader);
// }
this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1; this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1;
if (!!this.fragmentShader) {
gl.detachShader(program, this.fragmentShader);
}
this.gl.attachShader(program, fshader); this.gl.attachShader(program, fshader);
this.fragmentShader = fshader;
gl.linkProgram(program); gl.linkProgram(program);
gl.useProgram(program); if (this.times++ === 0) {
if (this.times++ < 2) { gl.useProgram(program);
this.runVertexShader(); 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) { create(vshaderCode, fshaderCode) {
...@@ -185,10 +263,11 @@ export default class gpu { ...@@ -185,10 +263,11 @@ export default class gpu {
* @param {WebGLTexture} texture 材质 * @param {WebGLTexture} texture 材质
* @returns {WebGLFramebuffer} The framebuffer * @returns {WebGLFramebuffer} The framebuffer
*/ */
attachFrameBuffer(opts = {}) { attachFrameBuffer(iLayer) {
this.prevTexture = this.currentTexture; this.prevTexture = this.currentTexture;
this.currentTexture = this.textureBuffer[this.textureBufferIndex % 2]; // this.currentTexture = this.textureBuffer[this.textureBufferIndex % 2];
// this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1; // this.textureBufferIndex = (this.textureBufferIndex + 1) >= 2 ? 0 : 1;
this.currentTexture = this.outTextures[iLayer];
const gl = this.gl; const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, // The target is always a FRAMEBUFFER. gl.framebufferTexture2D(gl.FRAMEBUFFER, // The target is always a FRAMEBUFFER.
gl.COLOR_ATTACHMENT0, // We are providing the color buffer. gl.COLOR_ATTACHMENT0, // We are providing the color buffer.
...@@ -199,8 +278,14 @@ export default class gpu { ...@@ -199,8 +278,14 @@ export default class gpu {
gl.viewport( gl.viewport(
0, 0,
0, 0,
opts.width_texture_out || this.width_texture_out, this.width_texture_out,
opts.height_texture_out || this.height_texture_out this.height_texture_out
);
gl.scissor(
0,
0,
this.width_texture_out,
this.height_texture_out
); );
return this.frameBuffer; return this.frameBuffer;
} }
...@@ -243,66 +328,53 @@ export default class gpu { ...@@ -243,66 +328,53 @@ export default class gpu {
return {isComplete: value, message: message}; 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 {int} index 材质索引
* @param {string} tSampler 材质名称 * @param {string} tSampler 材质名称
* @param {Object} bufferData 数据 * @param {Object} bufferData 数据
* @param {boolean} isRendered 是否已运行过
*/ */
initTexture(index, item) { initTexture(index, item, iLayer, isRendered) {
const gl = this.gl; const gl = this.gl;
let texture; let texture;
if (!item.data) { if (!item.data) {
texture = this.prevTexture; texture = this.prevTexture;
} else { } else {
// texture = gl.createTexture(); // texture = gl.createTexture();
texture = this.cacheTextures[index]; if (isRendered && (iLayer > 0 || (iLayer === 0 && item.tensor !== 'origin'))) {
// this.textures.push(texture); const tData = this.cacheTextures['' + iLayer];
texture = tData[item.variable + '_' + item.tensor];
} else {
texture = gl.createTexture();
if (index === 0) {
this.cacheTextures['' + iLayer] = this.cacheTextures['' + iLayer] || {};
}
this.cacheTextures['' + iLayer][item.variable + '_' + item.tensor] = texture;
}
} }
gl.activeTexture(gl[`TEXTURE${index}`]); gl.activeTexture(gl[`TEXTURE${index}`]);
gl.bindTexture(gl.TEXTURE_2D, texture); gl.bindTexture(gl.TEXTURE_2D, texture);
if (item.data) { if (item.data && (!isRendered || (isRendered && iLayer === 0 && item.tensor === 'origin'))) {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 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_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_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, 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, gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, item.width_texture,
item.height_texture || this.opts.height_raw_canvas, 0, item.height_texture, 0,
gl.RGBA, gl.FLOAT, item.data, 0); this.textureFormat, gl.FLOAT, item.data, 0);
} }
} }
getUniformLoc(name) { getUniformLoc(name, ilayer, isRendered) {
if (isRendered) {
return this.uniformLocations['' + ilayer][name];
}
let loc = this.gl.getUniformLocation(this.program, name); let loc = this.gl.getUniformLocation(this.program, name);
if (loc === null) throw `getUniformLoc ${name} err`; if (loc === null) throw `getUniformLoc ${name} err`;
// 缓存
this.uniformLocations['' + ilayer] = this.uniformLocations['' + ilayer] || {};
this.uniformLocations['' + ilayer][name] = loc;
return loc; return loc;
} }
...@@ -330,16 +402,16 @@ export default class gpu { ...@@ -330,16 +402,16 @@ export default class gpu {
return texture; return texture;
} }
render(data = []) { render(data = [], iLayer = 0, isRendered = false) {
const gl = this.gl; const gl = this.gl;
let textureIndex = 0; let textureIndex = 0;
// 输入数据
data.forEach(item => { data.forEach(item => {
if (item.type === 'texture') { if (item.type === 'texture') {
this.initTexture(textureIndex, item); this.initTexture(textureIndex, item, iLayer, isRendered);
gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor), textureIndex++); gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor, iLayer, isRendered), textureIndex++);
} else if (item.type === 'uniform') { }
gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor), item.data); else if (item.type === 'uniform') {
gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor, iLayer, isRendered), item.data);
} }
}); });
// gl.clearColor(.0, .0, .0, 1); // gl.clearColor(.0, .0, .0, 1);
...@@ -347,25 +419,127 @@ export default class gpu { ...@@ -347,25 +419,127 @@ export default class gpu {
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
} }
createPBO() {
const gl2 = this.gl;
const buffer = this.pbo;
gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer);
const sizeBytes = 4 * 4 * this.width_texture_out * this.height_texture_out;
gl2.bufferData(gl2.PIXEL_PACK_BUFFER, sizeBytes, gl2.STREAM_READ);
gl2.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl2.RGBA, gl2.FLOAT, 0);
gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null);
return buffer;
}
downloadFoat32TensorFromBuffer(buffer) {
const gl2 = this.gl;
const size = 4 * this.width_texture_out * this.height_texture_out;
const pixels = new Float32Array(size);
gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, buffer);
gl2.getBufferSubData(gl2.PIXEL_PACK_BUFFER, 0, pixels);
gl2.bindBuffer(gl2.PIXEL_PACK_BUFFER, null);
log.start('后处理-readloop');
// let result = [];
// let offset = 0;
// for (let h = 0; h < this.height_texture_out; h++) {
// // 纪录第1和2行数据
// let temp1 = [];
// let temp2 = [];
// for (let w = 0; w < this.width_texture_out; w++) {
// temp1.push(pixels[offset]);
// temp1.push(pixels[offset + 1]);
// temp2.push(pixels[offset + 2]);
// temp2.push(pixels[offset + 3]);
// offset += 4;
// }
// result = result.concat(temp1);
// result = result.concat(temp2);
// }
let result = [];
for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) {
result.push(pixels[4 * i]);
}
// const result = Array.prototype.slice.call(pixels);
// console.dir(['result', result]);
log.end('后处理-readloop');
return result;
}
getWebglError(status) {
const gl2 = this.gl;
switch (status) {
case gl2.NO_ERROR:
return 'NO_ERROR';
case gl2.INVALID_ENUM:
return 'INVALID_ENUM';
case gl2.INVALID_VALUE:
return 'INVALID_VALUE';
case gl2.INVALID_OPERATION:
return 'INVALID_OPERATION';
case gl2.INVALID_FRAMEBUFFER_OPERATION:
return 'INVALID_FRAMEBUFFER_OPERATION';
case gl2.OUT_OF_MEMORY:
return 'OUT_OF_MEMORY';
case gl2.CONTEXT_LOST_WEBGL:
return 'CONTEXT_LOST_WEBGL';
default:
return `Unknown error code ${status}`;
}
}
createAndWaitForFence() {
const gl2 = this.gl;
const isFenceEnabled = (gl2.fenceSync !== null);
let isFencePassed = () => true;
if (isFenceEnabled) {
const sync = gl2.fenceSync(gl2.SYNC_GPU_COMMANDS_COMPLETE, 0);
gl2.flush();
isFencePassed = () => {
const status = gl2.clientWaitSync(sync, 0, 0);
return status === gl2.ALREADY_SIGNALED ||
status === gl2.CONDITION_SATISFIED;
};
}
return new Promise(resolve => {
this.pollItem(isFencePassed, resolve);
});
}
pollItem(isDone, resolveFn) {
const fn = () => {
if (isDone()) {
resolveFn();
return;
}
setTimeout(fn, 1);
};
fn();
}
compute() { compute() {
let gl = this.gl; let gl = this.gl;
log.start('后处理-readinside');
const tt = +Date.now();
let pixels = new Float32Array(this.width_texture_out * this.height_texture_out * 4); let pixels = new Float32Array(this.width_texture_out * this.height_texture_out * 4);
// gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); // gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
const tt2 = +Date.now();
gl.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl.RGBA, gl.FLOAT, pixels, 0); gl.readPixels(0, 0, this.width_texture_out, this.height_texture_out, gl.RGBA, gl.FLOAT, pixels, 0);
// console.log('本次读取数据时间是' + (+Date.now() - tt2)+ ',' + (tt2 - tt));
log.end('后处理-readinside');
log.start('后处理-readloop');
let result = []; let result = [];
for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) { for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) {
result.push(pixels[4 * i]); result.push(pixels[4 * i]);
} }
log.end('后处理-readloop');
return result; return result;
} }
dispose() { dispose() {
const gl = this.gl; const gl = this.gl;
this.cacheTextures.forEach(texture => { // this.cacheTextures.forEach(texture => {
gl.deleteTexture(texture); // gl.deleteTexture(texture);
}); // });
this.cacheTextures = []; this.cacheTextures = {};
this.programs.forEach(program => { this.programs.forEach(program => {
gl.detachShader(program, this.vertexShader); gl.detachShader(program, this.vertexShader);
gl.deleteShader(this.vertexShader); gl.deleteShader(this.vertexShader);
......
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)
}
}
}
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`)
}
}
}
{
"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 */ /* eslint-disable */
import Gpu from '../gpu/gpu'; import Gpu from '../gpu/gpu';
import getMaxUniforms from '../test/getMaxUniforms';
/** /**
* @file gpu运行时 * @file gpu运行时
* @author yangmingming * @author yangmingming
...@@ -20,10 +21,15 @@ export default { ...@@ -20,10 +21,15 @@ export default {
} }
}, },
run(opName, opData) { getWebglVersion() {
let time = +Date.now(); return this.gpu.getWebglVersion();
let start = time; },
let timeObj = {};
run(opName, opData, isRendered) {
// console.dir(['fscode', opData.fsCode]);
// let time = +Date.now();
// let start = time;
// let timeObj = {};
if (!opData.isPass) { if (!opData.isPass) {
console.log('跳过当前op:' + opName); console.log('跳过当前op:' + opName);
return this; return this;
...@@ -32,19 +38,20 @@ export default { ...@@ -32,19 +38,20 @@ export default {
const gpu = this.gpu; const gpu = this.gpu;
gpu.setOutProps(opData.tensor['out']); gpu.setOutProps(opData.tensor['out']);
// 生成帧缓存材质 // 生成帧缓存材质
gpu.makeTexure(WebGLRenderingContext.FLOAT, null); gpu.attachFrameBuffer(opData.iLayer);
let end = +Date.now(); // let end = +Date.now();
let bufferStatus = gpu.frameBufferIsComplete(); let bufferStatus = gpu.frameBufferIsComplete();
if (bufferStatus.isComplete) { if (bufferStatus.isComplete) {
start = +Date.now(); // start = +Date.now();
timeObj['buferstatus-time'] = start - end; // timeObj['buferstatus-time'] = start - end;
gpu.attachShader(opData.fshader); // gpu.attachShader(opData.fshader);
end = +Date.now(); gpu.setProgram(opData.program, isRendered);
timeObj['createshader-time'] = end - start; // end = +Date.now();
timeObj['jsTime'] = end - time; // timeObj['createshader-time'] = end - start;
statistic.push(timeObj); // timeObj['jsTime'] = end - time;
// statistic.push(timeObj);
// 开始计算 // 开始计算
this.gpu.render(opData.renderData); this.gpu.render(opData.renderData, opData.iLayer, isRendered);
return this; return this;
} else { } else {
return bufferStatus.message; return bufferStatus.message;
...@@ -54,12 +61,34 @@ export default { ...@@ -54,12 +61,34 @@ export default {
/** /**
* 读取op计算结果, 并返回数据 * 读取op计算结果, 并返回数据
*/ */
read() { read2() {
return this.gpu.compute(); let bufferStatus = this.gpu.frameBufferIsComplete();
if (bufferStatus.isComplete) {
return this.gpu.compute();
}
return null;
},
async read() {
const pbo = this.gpu.createPBO();
await this.gpu.createAndWaitForFence();
log.end('运行耗时');
log.start('后处理');
// 其实这里应该有个fetch的执行调用或者fetch的输出
log.start('后处理-读取数据');
// 开始读数据
return this.gpu.downloadFoat32TensorFromBuffer(pbo);
}, },
createFragmentShader(fsCode) { createProgram(fsCode, outTensor) {
return this.gpu.initShader(fsCode, 'fragment'); const fshader = this.gpu.initShader(fsCode, 'fragment');
const program = this.gpu.createProgram(fshader, outTensor);
// test uniforms的个数
// const maxUniforms = getMaxUniforms(this.gpu.gl, program);
// alert(maxUniforms.maxFragmentShader);
// console.table(maxUniforms.uniforms);
return program;
}, },
// 释放资源 // 释放资源
......
...@@ -4,25 +4,6 @@ ...@@ -4,25 +4,6 @@
* @author yangmingming * @author yangmingming
*/ */
export default ` 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数据 // dynamic的input数据
const float multi_value = float(MULTI_VALUE); const float multi_value = float(MULTI_VALUE);
const float bias_value = float(BIAS_VALUE); const float bias_value = float(BIAS_VALUE);
...@@ -33,4 +14,5 @@ export default ` ...@@ -33,4 +14,5 @@ export default `
const int width_texture_out = WIDTH_TEXTURE_OUT; const int width_texture_out = WIDTH_TEXTURE_OUT;
const int height_texture_out = HEIGHT_TEXTURE_OUT; const int height_texture_out = HEIGHT_TEXTURE_OUT;
const int channel_out = CHANNEL_OUT; const int channel_out = CHANNEL_OUT;
const int offset_y_out = OFFSET_Y_OUT;
`; `;
...@@ -4,11 +4,6 @@ ...@@ -4,11 +4,6 @@
* @author yangmingming * @author yangmingming
*/ */
export default ` 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 getArrayIndexFromTensorPos_TENSOR_NAME(TENSOR_TYPE tensorPos) {
int index = 0; int index = 0;
......
...@@ -3,11 +3,7 @@ ...@@ -3,11 +3,7 @@
* @file 公共方法 * @file 公共方法
* @author yangmingming * @author yangmingming
*/ */
// TEXTURE_NAME, texture name
// WIDTH_TEXTURE_NAME_VALUE, texture的宽度
// 获取材质元素在数组中的索引
// const int width_TEXTURE_NAME = WIDTH_TEXTURE_NAME_VALUE;
export default ` export default `
int getArrayIndexFromTexturePos_TEXTURE_NAME(vec3 pos) { int getArrayIndexFromTexturePos_TEXTURE_NAME(vec3 pos) {
int x = int(floor(pos.x)); int x = int(floor(pos.x));
......
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
* @file 公共方法 * @file 公共方法
* @author yangmingming * @author yangmingming
*/ */
// TEXTURE_NAME, tensor name
// 获取材质中的像素 // 获取材质中的像素
// uniform sampler2D TEXTURE_NAME;
export default ` export default `
#define getPixelsFromTexturePos_TEXTURE_NAME(pos) texture2D(TEXTURE_NAME, pos) #define getPixelsFromTexturePos_TEXTURE_NAME(pos) TEXTURE2D(TEXTURE_NAME, pos)
`; `;
...@@ -5,15 +5,28 @@ ...@@ -5,15 +5,28 @@
*/ */
export default ` export default `
float getValueFromTensorPos_TENSOR_NAME(int r, int g, int b, int a) { 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))); 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; return pixels.r;
} }
float getValueFromTensorPos_TENSOR_NAME(ivec4 pos) { float getValueFromTensorPosLimit_TENSOR_NAME(int r, int g, int b, int a) {
float offset = 0.5; float halfW = ceil(float(width_shape_TENSOR_NAME) / 2.0);
float width = float(pos.a * channel_TENSOR_NAME + pos.g) + offset; int x = int(mod(float(a), halfW));
float height = float(pos.r * height_shape_TENSOR_NAME + pos.b) + offset; int offsetY = 0;
vec4 pixels = texture2D(texture_TENSOR_NAME, vec2(width / float(width_texture_TENSOR_NAME), height / float(height_texture_TENSOR_NAME))); if (a > x) {
offsetY = height_shape_TENSOR_NAME;
}
vec4 pixels = TEXTURE2D(texture_TENSOR_NAME,
vec2(
(float(x * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME),
(float(r * 2 * height_shape_TENSOR_NAME + b + offsetY) + 0.5) / float(height_texture_TENSOR_NAME)
)
);
return pixels.r; return pixels.r;
} }
`; `;
/* eslint-disable */
/**
* @file 公共方法
* @author yangmingming
*/
export default `
float getValueFromTensorPosPacked_TENSOR_NAME(int r, int g, int b, int a) {
int y = b / 2;
int yOffset = int(mod(float(b), 2.0));
int x = a / 2;
int xOffset = int(mod(float(a), 2.0));
int height = height_shape_TENSOR_NAME + offset_y_TENSOR_NAME;
vec4 pixels = TEXTURE2D(texture_TENSOR_NAME, vec2((float(x) + 0.5) / float(width_texture_TENSOR_NAME), (float(g * height / 2 + y) + 0.5) / float(height_texture_TENSOR_NAME)));
int index = 0;
if (xOffset == 0 && yOffset == 0) {
return pixels[0];
}
else if (xOffset == 1 && yOffset == 0) {
return pixels[1];
}
else if (xOffset == 0 && yOffset == 1) {
return pixels[2];
}
return pixels[3];
}
`;
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
// uniform sampler2D TEXTURE_NAME; // uniform sampler2D TEXTURE_NAME;
export default ` export default `
float getValueFromTexturePos_TEXTURE_NAME(vec3 pos) { float getValueFromTexturePos_TEXTURE_NAME(vec3 pos) {
vec4 pixels = texture2D(TEXTURE_NAME, pos.xy); vec4 pixels = TEXTURE2D(TEXTURE_NAME, pos.xy);
int d = int(pos.z); int d = int(pos.z);
if (d == 0) { if (d == 0) {
return pixels.r; return pixels.r;
......
...@@ -11,4 +11,8 @@ export default ` ...@@ -11,4 +11,8 @@ export default `
precision mediump float; precision mediump float;
precision mediump int; precision mediump int;
#endif #endif
void setOutput(float result) {
gl_FragColor.r = result;
}
`; `;
/* eslint-disable */
/**
* @file 预设条件, webgl 2.0版本
* @author yangmingming
*/
export default `#version 300 es
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
precision highp int;
#else
precision mediump float;
precision mediump int;
#endif
// 顶点shader透传的材质坐标
in vec2 vCoord;
out vec4 outColor;
void setOutput(float result) {
outColor.r = result;
}
`;
...@@ -14,4 +14,29 @@ ivec4 getOutputTensorPos() { ...@@ -14,4 +14,29 @@ ivec4 getOutputTensorPos() {
int b = int(outCoord.y / float(height_shape_out)); int b = int(outCoord.y / float(height_shape_out));
return ivec4(b, c, y, x); return ivec4(b, c, y, x);
} }
ivec4 getOutputTensorPosLimit() {
// 获取原始长度
vec2 outCoord = vCoord.xy * _2d_shape_texture_out;
float offsetY = floor(outCoord.y / float(height_shape_out));
int x = int(outCoord.x / float(channel_out));
if (mod(offsetY, 2.0) > 0.0) {
x += int(ceil(float(width_shape_out) / 2.0));
}
int y = int(mod(outCoord.y, float(height_shape_out)));
int c = int(mod(outCoord.x, float(channel_out)));
int b = int(outCoord.y / float(2 * height_shape_out));
return ivec4(b, c, y, x);
}
ivec4 getOutputPackedTensorPos() {
// 获取原始长度
vec2 outCoord = vCoord.xy * _2d_shape_texture_out;
int height = height_shape_out + offset_y_out;
int x = int(outCoord.x);
int c = int(outCoord.y / float(height / 2));
int y = int(mod(outCoord.y, float(height / 2)));
int b = 0;
return ivec4(b, c, y, x);
}
`; `;
...@@ -32,6 +32,7 @@ export default { ...@@ -32,6 +32,7 @@ export default {
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT', 'CHANNEL_OUT',
'OFFSET_Y_OUT',
'EPSILON', 'EPSILON',
'WIDTH_TEXTURE_SCALE', 'WIDTH_TEXTURE_SCALE',
......
...@@ -13,6 +13,6 @@ void main(void) { ...@@ -13,6 +13,6 @@ void main(void) {
vec4 scale = getPixelsFromTexturePos_texture_scale(vec2((float(int(oPos.g)) + 0.5) / float(width_texture_scale), 0.0)); 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 x = (o - scale[3]) / sqrt(scale[2] + epsilon);
float res = scale[0] * x + scale[1]; float res = scale[0] * x + scale[1];
gl_FragColor.r = res; setOutput(res);
} }
`; `;
...@@ -38,6 +38,7 @@ export default { ...@@ -38,6 +38,7 @@ export default {
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT', 'CHANNEL_OUT',
'OFFSET_Y_OUT',
'STRIDE_HORIZONTAL', 'STRIDE_HORIZONTAL',
'STRIDE_VERTICAL', 'STRIDE_VERTICAL',
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
export default ` export default `
// start函数 // start函数
void main(void) { void main(void) {
ivec4 oPos = getOutputTensorPos(); ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int x = oPos.a; int x = oPos.a;
int c = oPos.g; int c = oPos.g;
int y = oPos.b; int y = oPos.b;
...@@ -35,14 +35,14 @@ export default ` ...@@ -35,14 +35,14 @@ export default `
} }
// channel计算 // channel计算
for (int j = 0; j < channel_filter; j++) { for (int j = 0; j < channel_filter; j++) {
float f = getValueFromTensorPos_filter(c, j, fy, fx); float f = getValueFromTensorPosLIMIT_FILTER_filter(c, j, fy, fx);
float o = getValueFromTensorPos_origin(b, oTensorChannel + j, oy, ox); float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, oTensorChannel + j, oy, ox);
res += f * o; res += f * o;
} }
ox += dilation_h; ox += dilation_h;
} }
oy += dilation_v; oy += dilation_v;
} }
gl_FragColor.r = res; setOutput(res);
} }
`; `;
/* 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',
'OFFSET_Y_OUT',
'STRIDE_HORIZONTAL',
'STRIDE_VERTICAL',
'PAD_LEFT',
'PAD_TOP',
'DILATION_HORIZONTAL',
'DILATION_VERTICAL',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'filter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file 主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int x = oPos.a;
int c = oPos.g;
int y = oPos.b;
int b = oPos.r;
float res = 0.0;
int top = y * stride_v - padTop;
int left = x * stride_h - padLeft;
for (int fy = 0; fy < height_shape_filter; fy++) {
int oy = top + fy * dilation_v;
if (oy >= height_shape_origin) {
break;
}
if (oy < 0) {
continue;
}
for (int fx = 0; fx < width_shape_filter; fx++) {
int ox = left + fx * dilation_h;
if (ox >= width_shape_origin) {
break;
}
if (ox < 0) {
continue;
}
// b默认是0
float f = getValueFromTensorPosLIMIT_FILTER_filter(c, 0, fy, fx);
float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, c, oy, ox);
res += f * o;
}
}
setOutput(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;
// uniform变量
// 卷积核
uniform sampler2D texture_filter;
// 输入数据
uniform sampler2D texture_origin;
`;
/* eslint-disable */
/**
* @file conv2d-elementwise_add的配置文件
* @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',
'OFFSET_Y_OUT',
'WIDTH_SHAPE_COUNTER',
'STRIDE_HORIZONTAL',
'STRIDE_VERTICAL',
'PAD_LEFT',
'PAD_TOP',
'DILATION_HORIZONTAL',
'DILATION_VERTICAL',
'GROUPS',
'AXIS',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'filter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'counter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
tensor: 'origin',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
}
]
};
/* eslint-disable */
/**
* @file 主函数
* @author yangmingming
*/
export default `
// start函数
void main(void) {
ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int x = oPos.a;
int c = oPos.g;
int y = oPos.b;
int b = oPos.r;
int addAxis = oPos[axis];
float res = getValueFromCounter(addAxis);
// 获取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 = getValueFromTensorPosLIMIT_FILTER_filter(c, j, fy, fx);
float o = getValueFromTensorPosLIMIT_ORIGIN_origin(b, oTensorChannel + j, oy, ox);
res += f * o;
}
ox += dilation_h;
}
oy += dilation_v;
}
setOutput(ACTIVE_FUNCTION(res, multi_value, bias_value));
// outColor.r = float(b);
// outColor.g = float(c);
// outColor.b = float(y);
// outColor.a = float(x);
}
`;
/* eslint-disable */
/**
* @file 参数文件
* @author yangmingming
*/
export default `
// 卷积核
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;
// 加法
const int axis = AXIS;
// uniform变量
// 卷积核
uniform sampler2D texture_filter;
// 输入数据
uniform sampler2D texture_origin;
// 加法
uniform sampler2D texture_counter;
// 加法用到的函数
float getValueFromCounter(int index) {
float xPos = float(index) / float(WIDTH_SHAPE_COUNTER);
vec4 pixels = TEXTURE2D(texture_counter, vec2(xPos, 0.5));
return pixels.r;
}
`;
/* eslint-disable */
/**
* @file conv2d-elementwise_add的配置文件
* @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',
'OFFSET_Y_OUT',
'TOTAL_SHAPE_COUNTER',
'PAD_LEFT',
'PAD_TOP',
'AXIS',
'MULTI_VALUE',
'BIAS_VALUE',
'ACTIVE_FUNCTION'
],
input: [
{
tensor: 'filter',
variable: 'texture',
setter: 'initTexture',
type: 'texture'
},
{
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 = getOutputPackedTensorPos();
int x = oPos.a;
int c = oPos.g;
int y = oPos.b;
int b = oPos.r;
// b = 0;
// c = 1;
// y = 0;
// x = 0;
int addAxis = oPos[axis];
float res = getValueFromCounter(addAxis);
// 输出结果
vec4 v4 = vec4(res);
float I[16];
float B[16];
float T[16];
float f[16];
for (int cl = 0; cl < channel_filter; cl++) {
// 获取output的坐标
int oy = 2*y - padTop;
// 计算输入 4 * 4矩阵 和filter
for (int fy = 0; fy < 4; fy++) {
int ox = 2*x - padLeft;
int index = fy * 4;
for (int fx = 0; fx < 4; fx++) {
if (oy < 0 || oy >= height_shape_origin || ox >= width_shape_origin || ox < 0) {
I[index + fx] = 0.0;
} else {
I[index + fx] = getValueFromTensorPos_origin(b, cl, oy, ox);
}
f[index + fx] = getValueFromTensorPos_filter(c, cl, fy, fx);
ox += 1;
}
oy += 1;
}
// input转化
float tmp1 = I[2] - I[10];
float tmp2 = I[9] - I[1];
B[0] = I[0] - I[8] - tmp1;
B[1] = tmp1 - tmp2;
B[2] = tmp1 + tmp2;
B[3] = I[3] - I[11] + tmp2;
tmp1 = I[6] + I[10];
tmp2 = I[5] + I[9];
B[4] = I[4] + I[8] - tmp1;
B[5] = tmp1 + tmp2;
B[6] = tmp1 - tmp2;
B[7] = I[7] + I[11] - tmp2;
tmp1 = I[10] - I[6];
tmp2 = I[5] - I[9];
B[8] = I[8] - I[4] - tmp1;
B[9] = tmp1 - tmp2;
B[10] = tmp1 + tmp2;
B[11] = tmp2 - I[7] + I[11];
tmp1 = I[14] - I[6];
tmp2 = I[5] - I[13];
B[12] = I[12] - I[4] - tmp1;
B[13] = tmp1 - tmp2;
B[14] = tmp1 + tmp2;
B[15] = tmp2 - I[7] + I[15];
// 点乘
for (int i = 0; i < 16; i++) {
T[i] = B[i] * f[i];
}
// final output
tmp1 = T[1] + T[5] + T[9];
tmp2 = T[2] + T[6] + T[10];
v4[0] += T[0] + T[4] + T[8] + tmp1 + tmp2;
v4[1] += T[3] + T[7] + T[11] + tmp1 - tmp2;
tmp1 = T[5] - T[9] + T[13];
tmp2 = T[6] - T[10] + T[14];
v4[2] += T[4] - T[8] + T[12] + tmp1 + tmp2;
v4[3] += T[7] - T[11] + T[15] + tmp1 - tmp2;
}
outColor.r = ACTIVE_FUNCTION(v4[0], multi_value, bias_value);
outColor.g = ACTIVE_FUNCTION(v4[1], multi_value, bias_value);
outColor.b = ACTIVE_FUNCTION(v4[2], multi_value, bias_value);
outColor.a = ACTIVE_FUNCTION(v4[3], multi_value, bias_value);
// outColor = v4;
// outColor.r = I[0];
// outColor.g = I[1];
// outColor.b = I[2];
// outColor.a = I[3];
// outColor.r = float(b);
// outColor.g = float(c);
// outColor.b = float(y);
// outColor.a = float(x);
}
`;
/* eslint-disable */
/**
* @file 参数文件
* @author yangmingming
*/
export default `
// 卷积核
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;
// 计算相关
// padding的数目
const int padLeft = PADDINGS_X;
const int padTop = PADDINGS_Y;
// 加法
const int axis = AXIS;
uniform float data_counter[TOTAL_SHAPE_COUNTER];
// uniform变量
// 卷积核
uniform sampler2D texture_filter;
// 输入数据
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;
}
`;
...@@ -18,6 +18,7 @@ export default { ...@@ -18,6 +18,7 @@ export default {
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT', 'CHANNEL_OUT',
'OFFSET_Y_OUT',
'MULTI_VALUE', 'MULTI_VALUE',
'BIAS_VALUE', 'BIAS_VALUE',
......
...@@ -9,6 +9,6 @@ void main(void) { ...@@ -9,6 +9,6 @@ void main(void) {
// 输出数据 // 输出数据
float o = getPixelsFromTexturePos_texture_origin(vCoord).r; float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float res = ACTIVE_FUNCTION(o, multi_value, bias_value); float res = ACTIVE_FUNCTION(o, multi_value, bias_value);
gl_FragColor.r = res; setOutput(res);
} }
`; `;
...@@ -33,6 +33,7 @@ export default { ...@@ -33,6 +33,7 @@ export default {
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT', 'CHANNEL_OUT',
'OFFSET_Y_OUT',
'AXIS', 'AXIS',
'MULTI_VALUE', 'MULTI_VALUE',
......
...@@ -7,11 +7,11 @@ export default ` ...@@ -7,11 +7,11 @@ export default `
// start函数 // start函数
void main(void) { void main(void) {
// 输出数据 // 输出数据
ivec4 oPos = getOutputTensorPos(); ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int index = oPos[axis]; int index = oPos[axis];
float o = getPixelsFromTexturePos_texture_origin(vCoord).r; float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float c = getValueFromCounter(index); float c = getValueFromCounter(index);
float res = ACTIVE_FUNCTION(o + c, multi_value, bias_value); float res = ACTIVE_FUNCTION(o + c, multi_value, bias_value);
gl_FragColor.r = res; setOutput(res);
} }
`; `;
...@@ -37,7 +37,8 @@ export default { ...@@ -37,7 +37,8 @@ export default {
'HEIGHT_SHAPE_OUT', 'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT' 'CHANNEL_OUT',
'OFFSET_Y_OUT'
], ],
input: [ input: [
{ {
......
...@@ -13,6 +13,6 @@ void main(void) { ...@@ -13,6 +13,6 @@ void main(void) {
float o = getValueFromTensorPos_origin(out_pos[0], out_pos[1], out_pos[2], j); float o = getValueFromTensorPos_origin(out_pos[0], out_pos[1], out_pos[2], j);
res += c * o; res += c * o;
} }
gl_FragColor.r = res; setOutput(res);
} }
`; `;
...@@ -29,6 +29,7 @@ export default { ...@@ -29,6 +29,7 @@ export default {
'WIDTH_TEXTURE_OUT', 'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT', 'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT', 'CHANNEL_OUT',
'OFFSET_Y_OUT',
'STRIDES_X', 'STRIDES_X',
'STRIDES_Y', 'STRIDES_Y',
......
...@@ -7,7 +7,7 @@ export default ` ...@@ -7,7 +7,7 @@ export default `
void main(void) { void main(void) {
float res = (-1.0 / exp(-20.0)); float res = (-1.0 / exp(-20.0));
// 获取output的坐标 // 获取output的坐标
ivec4 out_pos = getOutputTensorPos(); ivec4 out_pos = getOutputTensorPosLIMIT_OUT();
// X、Y方向的移动步长 // X、Y方向的移动步长
int count_pool = 0; int count_pool = 0;
int oy_base = out_pos[2] * stride_v - padTop; int oy_base = out_pos[2] * stride_v - padTop;
...@@ -29,7 +29,7 @@ void main(void) { ...@@ -29,7 +29,7 @@ void main(void) {
continue; continue;
} }
// origin数据 // origin数据
float curr = getValueFromTensorPos_origin(out_pos[0], out_pos[1], oy, ox); float curr = getValueFromTensorPosLIMIT_ORIGIN_origin(out_pos[0], out_pos[1], oy, ox);
if (type_pool == 1) { if (type_pool == 1) {
if (curr > res) { if (curr > res) {
res = curr; res = curr;
...@@ -44,6 +44,6 @@ void main(void) { ...@@ -44,6 +44,6 @@ void main(void) {
if (type_pool != 1) { if (type_pool != 1) {
res = res / float(count_pool); res = res / float(count_pool);
} }
gl_FragColor.r = res; setOutput(res);
} }
`; `;
/* eslint-disable */
/**
* @file pool2d_avg的配置文件
* @author yangmingming zhangmiao06
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
}
],
conf: [
'KSIZE_X',
'KSIZE_Y',
'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',
'OFFSET_Y_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_avg主函数
* @author yangmingming zhangmiao06
*/
export default `
// start函数
void main(void) {
float res = 0.0;
// 获取output的坐标
ivec4 out_pos = getOutputTensorPos();
// X、Y方向的移动步长
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);
res += curr;
// 在平均池化模式忽略填充值(exclusive默认为true)
}
}
res = res / float(height_shape_pool * width_shape_pool);
setOutput(res);
}
`;
/* eslint-disable */
/**
* @file pool2d_avg参数文件
* @author yangmingming zhangmiao06
*/
export default `
// 常量
// 池化大小
const int width_shape_pool = KSIZE_X;
const int height_shape_pool = KSIZE_Y;
// 输入数据
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 pool2d的配置文件
* @author yangmingming zhangmiao06
*/
export default {
dep: [
{
func: 'getValueFromTensorPos',
conf: {
TENSOR_NAME: 'origin'
}
}
],
conf: [
'KSIZE_X',
'KSIZE_Y',
'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',
'OFFSET_Y_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 = getOutputTensorPosLIMIT_OUT();
int b = out_pos[0];
int c = out_pos[1];
int y = out_pos[2];
int x = out_pos[3];
// X、Y方向的移动步长
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 = getValueFromTensorPosLIMIT_ORIGIN_origin(out_pos[0], out_pos[1], oy, ox);
res = max(res, curr);
}
}
setOutput(res);
// outColor.r = float(b);
// outColor.g = float(c);
// outColor.b = float(y);
// outColor.a = float(x);
}
`;
/* eslint-disable */
/**
* @file pool2d参数文件
*/
export default `
// 常量
// 池化大小
const int width_shape_pool = KSIZE_X;
const int height_shape_pool = KSIZE_Y;
// 输入数据
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 pool2d的配置文件
* @author yangmingming zhangmiao06
*/
export default {
dep: [
{
func: 'getValueFromTensorPosPacked',
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',
'OFFSET_X_ORIGIN',
'OFFSET_Y_ORIGIN',
'WIDTH_SHAPE_OUT',
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_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();
// int b = out_pos[0];
// int c = out_pos[1];
// int y = out_pos[2];
// int x = out_pos[3];
// X、Y方向的移动步长
int count_pool = 0;
int oy_base = out_pos[2] * stride_v - padTop;
int ox_base = out_pos[3] * stride_h - padLeft;
// int offset = 0;
// vec4 v4 = texture(texture_origin, vec2((float(0) + 0.5) / float(width_texture_origin), (float(1 * height_shape_origin / 2 + 0) + 0.5) / float(height_texture_origin)));
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 = getValueFromTensorPosPacked_origin(out_pos[0], out_pos[1], oy, ox);
// y = oy;
// x = ox;
// v4[offset++] = curr;
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);
}
setOutput(res);
// outColor = v4;
// outColor.r = float(b);
// outColor.g = float(c);
// outColor.b = float(y);
// outColor.a = float(x);
}
`;
/* 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 offset_x_origin = OFFSET_X_ORIGIN;
const int offset_y_origin = OFFSET_Y_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;
`;
...@@ -15,7 +15,8 @@ export default { ...@@ -15,7 +15,8 @@ export default {
conf: [ conf: [
'WIDTH_TEXTURE_ORIGIN', 'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN', 'HEIGHT_TEXTURE_ORIGIN',
'TOTAL_SHAPE_ORIGIN' 'TOTAL_SHAPE_ORIGIN',
'OFFSET_Y_OUT'
], ],
input: [ input: [
{ {
......
/* eslint-disable */
/**
* @file 顶点文件,webgl 2.0
* @author yangmingming
*/
export default `#version 300 es
in vec4 position;
out vec2 vCoord;
void main() {
vCoord.x = (position.x + 1.0) / 2.0;
vCoord.y = (position.y + 1.0) / 2.0;
gl_Position = position;
}
`;
/**
* @file 获取当前环境的max uniform变量
* @author yangmingming
*/
// uniform变量类型
const enums = {
0x8B50: 'FLOAT_VEC2',
0x8B51: 'FLOAT_VEC3',
0x8B52: 'FLOAT_VEC4',
0x8B53: 'INT_VEC2',
0x8B54: 'INT_VEC3',
0x8B55: 'INT_VEC4',
0x8B56: 'BOOL',
0x8B57: 'BOOL_VEC2',
0x8B58: 'BOOL_VEC3',
0x8B59: 'BOOL_VEC4',
0x8B5A: 'FLOAT_MAT2',
0x8B5B: 'FLOAT_MAT3',
0x8B5C: 'FLOAT_MAT4',
0x8B5E: 'SAMPLER_2D',
0x8B60: 'SAMPLER_CUBE',
0x1400: 'BYTE',
0x1401: 'UNSIGNED_BYTE',
0x1402: 'SHORT',
0x1403: 'UNSIGNED_SHORT',
0x1404: 'INT',
0x1405: 'UNSIGNED_INT',
0x1406: 'FLOAT'
};
export default function(gl, program) {
// max fragment shader, 安卓是256, 桌面chrome浏览器是1024
const result = {
attributes: [],
uniforms: [],
attributeCount: 0,
uniformCount: 0,
maxVertexShader: gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS),
maxFragmentShader: gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS)
};
const activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
const activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
// Loop through active uniforms
for (let i = 0; i < activeUniforms; i++) {
const uniform = gl.getActiveUniform(program, i);
uniform.typeName = enums[uniform.type];
result.uniforms.push(uniform);
result.uniformCount += uniform.size;
}
// Loop through active attributes
for (let i = 0; i < activeAttributes; i++) {
const attribute = gl.getActiveAttrib(program, i);
attribute.typeName = enums[attribute.type];
result.attributes.push(attribute);
result.attributeCount += attribute.size;
}
return result;
};
export default {
'608': {
modelPath: 'faceModel',
feedShape: {
fw: 608,
fh: 608
},
outputShapes: {
from: [19, 19, 25, 1],
to: [19, 19, 5, 5]
}
},
'320': {
modelPath: 'facemodel320',
feedShape: {
fw: 320,
fh: 320
},
outputShapes: {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
}
},
'320fused': {
modelPath: 'facemodelfused',
feedShape: {
fw: 320,
fh: 320
},
outputShapes: {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
}
},
'separate': {
modelPath: 'separablemodel',
feedShape: {
fw: 320,
fh: 320
},
outputShapes: {
from: [10, 10, 25, 1],
to: [10, 10, 5, 5]
}
}
};
...@@ -19,6 +19,9 @@ const tensorAttrs = [ ...@@ -19,6 +19,9 @@ const tensorAttrs = [
'height_shape', 'height_shape',
'width_texture', 'width_texture',
'height_texture', 'height_texture',
'offset_x',
'offset_y',
'limit',
'channel', 'channel',
'total_shape' 'total_shape'
]; ];
...@@ -30,6 +33,9 @@ const shaderAttrs = { ...@@ -30,6 +33,9 @@ const shaderAttrs = {
}, },
pool2d: { pool2d: {
'pooling_type': 'type_pool' 'pooling_type': 'type_pool'
},
pool2d_winograd: {
'pooling_type': 'type_pool'
} }
}; };
// model的名字和paddle web的tensor名字mapping // model的名字和paddle web的tensor名字mapping
...@@ -48,7 +54,8 @@ const tensorName = { ...@@ -48,7 +54,8 @@ const tensorName = {
// unique behavior // unique behavior
const opBehavior = { const opBehavior = {
conv2d: [ conv2d: [
'needBatch' 'needBatch',
'isApplySeparableConv'
], ],
batchnorm: [ batchnorm: [
'needBatch', 'needBatch',
...@@ -58,9 +65,15 @@ const opBehavior = { ...@@ -58,9 +65,15 @@ const opBehavior = {
'broadcast', 'broadcast',
'needBatch' 'needBatch'
], ],
conv2d_elementwise_add: [
'mergeAttrs',
'setActiveFunc',
'needBatch'
],
pool2d: [ pool2d: [
'isMax', 'isMax',
'needBatch', 'needBatch',
'setPacked',
'isGlobalPooling' 'isGlobalPooling'
], ],
relu: [ relu: [
...@@ -76,10 +89,14 @@ const opBehavior = { ...@@ -76,10 +89,14 @@ const opBehavior = {
'needBatch' 'needBatch'
] ]
}; };
const mergeType = 'conv2d-elementwise_add';
export default class OpData { export default class OpData {
constructor(name, input = {}, output = {}, attrs = {}) { constructor(name, input = {}, output = {}, attrs = {}) {
this.realName = name;
this.name = name; this.name = name;
this.attrs = attrs; this.attrs = attrs;
// 检查是否是融合op
this.checkIsMerge();
// 是否忽略当前当前op, 使用dropout // 是否忽略当前当前op, 使用dropout
this.isPass = this.checkIsPass(); this.isPass = this.checkIsPass();
if (this.isPass) { if (this.isPass) {
...@@ -133,6 +150,7 @@ export default class OpData { ...@@ -133,6 +150,7 @@ export default class OpData {
}); });
// 生成tensor对象 // 生成tensor对象
tensorData.forEach(data => { tensorData.forEach(data => {
// console.log(data);
if (data) { if (data) {
if (data.notTensor) { if (data.notTensor) {
this.tensor[data.tensorName] = { this.tensor[data.tensorName] = {
...@@ -142,11 +160,13 @@ export default class OpData { ...@@ -142,11 +160,13 @@ export default class OpData {
}; };
} else { } else {
this.tensor[data.tensorName] = new Tensor({ this.tensor[data.tensorName] = new Tensor({
type: data.name,
name: data.tensorName, name: data.tensorName,
shape: data.shape, shape: data.shape,
data: data.data, data: data.data,
needBatch: data.needBatch || false, needBatch: data.needBatch || false,
notCompressed: data.notCompressed || false notCompressed: data.notCompressed || false,
isPacked: data.isPacked || false
}); });
} }
} }
...@@ -195,15 +215,74 @@ export default class OpData { ...@@ -195,15 +215,74 @@ export default class OpData {
} }
} }
broadcast(tensorData = []) { mergeAttrs() {
const x = tensorData[0]; this.attrs = this.attrs.reduce((attrs, item) => {
const y = tensorData[1]; return Object.assign(attrs, item);
let small = y; }, {});
if (x.shape.length - y.shape.length < 0) { }
small = x;
isApplyWinoGrad(tensorData = []) {
const filter = tensorData.filter(item => {
const [b, c, h, w] = item.shape;
return (h === 3) && (w === 3) && (item.tensorName === 'filter');
});
// 使用winograd算法
if (filter && filter.length) {
this.setPacked(tensorData);
this.applyWinograd(tensorData);
this.setOutputPacked(tensorData);
this.name += '_winograd';
} }
// face model }
small.notTensor = true;
isApplySeparableConv(tensorData = []) {
const groups = this.attrs.groups;
const filter = tensorData.filter(item => {
const [b, c, h, w] = item.shape;
return (b === groups) && (c === 1) && (item.tensorName === 'filter');
});
if (filter && filter.length) {
// 可以执行separable conv
this.name += '_depthwise';
}
}
setPacked(tensorData = []) {
const isPacked = this.attrs.ispacked;
tensorData.forEach(item => {
if (item.tensorName === 'origin' && isPacked) {
item.isPacked = true;
if (this.name.indexOf('pool') > -1) {
this.name += '_winograd';
}
}
});
}
applyWinograd(tensorData = []) {
tensorData.forEach(item => {
if (item.tensorName === 'filter') {
const [b, c, h, w] = item.shape;
item.shape = [b, c, 4, 4];
item.data = Utils.applyFilterWinograd(item.data, item.shape);
}
});
}
setOutputPacked(tensorData = []) {
tensorData.forEach(item => {
if (item.tensorName === 'out') {
item.isPacked = true;
}
});
}
broadcast(tensorData = []) {
tensorData.forEach(item => {
if (item.tensorName === 'counter') {
item.notTensor = true;
}
});
return; return;
// mobilenet model // mobilenet model
...@@ -227,6 +306,9 @@ export default class OpData { ...@@ -227,6 +306,9 @@ export default class OpData {
isMax(tensorData = []) { isMax(tensorData = []) {
const type = this.attrs['pooling_type'] === 'max' ? 1 : 0; const type = this.attrs['pooling_type'] === 'max' ? 1 : 0;
this.attrs['pooling_type'] = type; this.attrs['pooling_type'] = type;
if (type === 1) {
this.name += '_max';
}
} }
transToPrelu(tensorData = []) { transToPrelu(tensorData = []) {
...@@ -240,10 +322,13 @@ export default class OpData { ...@@ -240,10 +322,13 @@ export default class OpData {
this.name = 'relu'; this.name = 'relu';
} }
setActiveFunc(tensorData = []) { setActiveFunc() {
this.data['multi_value'] = '0.0'; // 用于融合op
this.data['active_function'] = 'softmax'; const suffix = this.realName.replace(mergeType + '-', '');
if (suffix === 'leaky_relu') {
this.data['multi_value'] = this.attrs.alpha;
this.data['active_function'] = 'leakyRelu';
}
} }
reshape(tensorData = []) { reshape(tensorData = []) {
...@@ -284,6 +369,16 @@ export default class OpData { ...@@ -284,6 +369,16 @@ export default class OpData {
tensorData.splice(result[constants[3] + 'Index'], 1, 0); tensorData.splice(result[constants[3] + 'Index'], 1, 0);
} }
checkIsMerge() {
if (this.name.indexOf(mergeType) > -1
&& Object.prototype.toString.apply(this.attrs) === '[object Array]') {
// 第一个融合op
this.name = 'conv2d_elementwise_add';
return true;
}
return false;
}
checkIsPass() { checkIsPass() {
if (this.name === 'dropout') { if (this.name === 'dropout') {
if (this.attrs['dropout_implementation'] === 'downgrade_in_infer') { if (this.attrs['dropout_implementation'] === 'downgrade_in_infer') {
......
...@@ -7,6 +7,8 @@ import Utils from './utils'; ...@@ -7,6 +7,8 @@ import Utils from './utils';
export default class Tensor { export default class Tensor {
constructor(opts = {}) { constructor(opts = {}) {
this.opts = opts; this.opts = opts;
// 数据存储方式
this.isPacked = this.isPacked || false;
// 设置tensor名字 // 设置tensor名字
this.name = opts.name; this.name = opts.name;
// tensor的形状 // tensor的形状
...@@ -23,36 +25,44 @@ export default class Tensor { ...@@ -23,36 +25,44 @@ export default class Tensor {
this.shape = shape; this.shape = shape;
} }
// 获取转换到texture后的信息 // 获取转换到texture后的信息
let {zeroNumber, shape: shape_texture} = Utils.getTextureInfoFromTensorShape(shape); let {offsetX, offsetY, exceedMax, zeroNumber, shape: shape_texture} = Utils.getTextureInfoFromTensorShape(shape, opts.isPacked);
this.shape_texture = shape_texture; this.shape_texture = shape_texture;
this.exceedMax = exceedMax;
this.offsetX = offsetX;
this.offsetY = offsetY;
// tensor数据 // tensor数据
let data = []; let data;
if (opts.data && opts.data.length) { if (opts.type === 'image' || opts.type === 'x') {
this.data = opts.data;
}
else if (opts.data && opts.data.length) {
data = new Float32Array(opts.data.length);
if (!opts.notCompressed) { if (!opts.notCompressed) {
let b = shape[0]; let b = shape[0];
let c = shape[1]; let c = shape[1];
let h = shape[2]; let h = shape[2];
let w = shape[3]; let w = shape[3];
for (let i = 0; i < opts.data.length; i++) { for (let i = 0; i < opts.data.length; i++) {
let j = Math.floor(i / (c * w)); let j = i / (c * w) | 0;
let k = Math.floor(i % (c * w)); let k = i % (c * w);
let b1 = Math.floor(j / h); let b1 = j / h | 0;
let h1 = Math.floor(j % h); let h1 = j % h;
let c1 = Math.floor(k % c); let c1 = k % c;
let w1 = Math.floor(k / c); let w1 = k / c | 0;
let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1; let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1;
data.push(opts.data[l]); data[i] = opts.data[l];
data.push(0);
data.push(0);
data.push(0);
} }
this.data = data;
} else { } else {
// batchnorm的scale // batchnorm的scale
this.shape_texture = [4, 1, this.total / 4]; this.shape_texture = [4, 1, this.total / 4];
data = [].concat(opts.data); // data = [].concat(opts.data);
this.data = new Float32Array(opts.data);
} }
this.data = new Float32Array(data); // this.data = new Float32Array(data);
// console.log('this.data.length', this.data.length);
// 清理缓存 // 清理缓存
opts.data = null; opts.data = null;
} }
...@@ -106,6 +116,18 @@ export default class Tensor { ...@@ -106,6 +116,18 @@ export default class Tensor {
return 0; return 0;
} }
get offset_x() {
return this.offsetX;
}
get offset_y() {
return this.offsetY;
}
get limit() {
return this.exceedMax ? 'Limit' : '';
}
get length_shape() { get length_shape() {
return this.shape.length || 0; return this.shape.length || 0;
} }
......
...@@ -64,18 +64,78 @@ export default { ...@@ -64,18 +64,78 @@ export default {
return result; return result;
}, },
applyFilterWinograd(data, shape) {
const [b, c, h, w] = shape;
let offset = 0;
let index = 0;
const result = new Float32Array(b * c * 16);
// h和w是3、3
const size2D = 9;
for (let i = 0; i < b; i++) {
// let index = i * c * size2D;
for (let j = 0; j < c; j++) {
// index += j * size2D;
const filter = data.subarray(index, index + size2D);
const [f11, f12, f13, f21, f22, f23, f31, f32, f33] = filter;
const square = [
f11,
0.5 * f11 + 0.5 * f12 + 0.5 * f13,
0.5 * f11 - 0.5 * f12 + 0.5 * f13,
f13,
0.5 * f11 + 0.5 * f21 + 0.5 * f31,
0.25 * f11 + 0.25 * f12 + 0.25 * f13 + 0.25 * f21 + 0.25 * f22 + 0.25 * f23 + 0.25 * f31 + 0.25 * f32 + 0.25 * f33,
0.25 * f11 - 0.25 * f12 + 0.25 * f13 + 0.25 * f21 - 0.25 * f22 + 0.25 * f23 + 0.25 * f31 - 0.25 * f32 + 0.25 * f33,
0.5 * f13 + 0.5 * f23 + 0.5 * f33,
0.5 * f11 - 0.5 * f21 + 0.5 * f31,
0.25 * f11 + 0.25 * f12 + 0.25 * f13 - 0.25 * f21 - 0.25 * f22 - 0.25 * f23 + 0.25 * f31 + 0.25 * f32 + 0.25 * f33,
0.25 * f11 - 0.25 * f12 + 0.25 * f13 - 0.25 * f21 + 0.25 * f22 - 0.25 * f23 + 0.25 * f31 - 0.25 * f32 + 0.25 * f33,
0.5 * f13 - 0.5 * f23 + 0.5 * f33,
f31,
0.5 * f31 + 0.5 * f32 + 0.5 * f33,
0.5 * f31 - 0.5 * f32 + 0.5 * f33,
f33
];
result.set(square, offset);
offset += 16;
index += size2D;
}
}
return result;
},
/** /**
* 获取texture形状和补0个数 * 获取texture形状和补0个数
* @param shape {Array} tensor的形状 * @param shape {Array} tensor的形状
* @return {{shape: *[], zeroNumber: number}} {Object} texture信息 * @return {{shape: *[], zeroNumber: number}} {Object} texture信息
*/ */
getTextureInfoFromTensorShape(shape = []) { getTextureInfoFromTensorShape(shape = [], isPacked = false) {
let b = shape[0]; let b = shape[0];
let c = shape[1]; let c = shape[1];
let h = shape[2]; let h = shape[2];
let w = shape[3]; let w = shape[3];
let height = b * h;
let width = c * w;
let offsetX = 0;
let offsetY = 0;
// 安卓和ios的max texture size是4096, 改造存储空间(2bh, cw / 2)
let exceedMax = false;
if (height > 4096 || width > 4096) {
height *= 2;
width = c * (Math.ceil(w / 2));
exceedMax = true;
}
if (isPacked) {
// 紧凑布局
height = b * c * Math.ceil(h / 2);
width = Math.ceil(w / 2);
offsetX = w % 2;
offsetY = h % 2;
}
return { return {
shape: [4, b * h, c * w], offsetX,
offsetY,
exceedMax,
shape: [4, height, width],
zeroNumber: 0 zeroNumber: 0
}; };
}, },
...@@ -107,21 +167,24 @@ export default { ...@@ -107,21 +167,24 @@ export default {
const c = shape[1]; const c = shape[1];
const h = shape[2]; const h = shape[2];
const w = shape[3]; const w = shape[3];
const data = []; let data = new Float32Array(b * c * h * w * 4);
let offset = 0;
for (let i = 0; i < total; i++) { for (let i = 0; i < total; i++) {
let j = Math.floor(i / (c * w)); let j = (i / (c * w)) | 0;
let k = Math.floor(i % (c * w)); let k = i % (c * w);
let b1 = Math.floor(j / h); let b1 = j / h | 0;
let h1 = Math.floor(j % h); let h1 = j % h;
let c1 = Math.floor(k % c); let c1 = k % c;
let w1 = Math.floor(k / c); let w1 = k / c | 0;
let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1; let l = b1 * (c * h * w) + c1 * (h * w) + h1 * (w) + w1;
data.push(renderData.data[l]); data[offset] = renderData.data[l];
data.push(0); offset += 4;
data.push(0); // data.push(renderData.data[l]);
data.push(0); // data.push(0);
// data.push(0);
// data.push(0);
} }
renderData.data = new Float32Array(data); renderData.data = data;
} }
}; };
/* eslint-enable */ /* 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;
}
/***/ }
/******/ ])
});
;
/**
* file tools/logger logger工具
* author saniac(snailsword@gmail.com)
*/
export default class Logger {
constructor() {
this.timeTable = {};
this.countTable = {};
this.duringTable = {};
this.lastStopTable = {};
}
start(key) {
let arr = this.timeTable[key];
if (!arr) {
arr = [{}];
}
else {
if (!arr[arr.length - 1].endTime) {
console.error('[logger] key:' + key + ' duplicate start logger');
return;
}
arr.push({});
}
arr[arr.length - 1].startTime = this.time;
this.timeTable[key] = arr;
return this;
}
end(key) {
// console.log(this.timeTable[key]);
if (!this.timeTable[key]) {
console.log(this.timeTable[key]);
console.error('[logger] key:' + key + ' no matching start logger');
return;
}
let currentObj = this.timeTable[key][this.timeTable[key].length - 1];
if (currentObj.endTime) {
console.error('[logger] key:' + key + ' duplicate end logger');
return;
}
currentObj.endTime = this.time;
currentObj.during = currentObj.endTime - currentObj.startTime;
return this;
}
// 数次数
count(key) {
if (this.countTable[key]) {
this.countTable[key]++;
}
else {
this.countTable[key] = 1;
}
return this;
}
// 看每次执行的时间间隔
during(key) {
if (this.lastStopTable[key]) {
this.duringTable[key].push(this.time - this.lastStopTable[key]);
this.lastStopTable[key] = this.time;
}
else {
this.lastStopTable[key] = this.time;
this.duringTable[key] = [];
}
return this;
}
get time() {
return +new Date().getTime();
}
get statistics() {
// time
let timeResult = [];
let item;
for (let key in this.timeTable) {
item = this.timeTable[key];
let len = item.length;
let max = 0;
let min = Number.MAX_VALUE;
let sum = 0;
for (let i = 0; i < len; i++) {
max = Math.max(max, item[i].during);
min = Math.min(min, item[i].during);
sum += item[i].during;
}
timeResult.push({
name: key,
length: len,
avg: sum / len,
max,
min
});
}
console.table(timeResult);
return {timeResult};
}
}
...@@ -99,13 +99,13 @@ class BinaryFileConverter: ...@@ -99,13 +99,13 @@ class BinaryFileConverter:
BinaryFileConverter( BinaryFileConverter(
delimiter = ',', delimiter = ',',
ignorChar = ['[', ']'], ignorChar = ['[', ']'],
ignorLine = ['[', ']'], ignorLine = ['[', ']'],
types = ['.txt', '.json'], types = ['.txt', '.json'],
originDir = './mobileNet', originDir = './mobileNet',
resultDir = './binf', resultDir = './binf',
ext = '.dat', ext = '.dat',
formatter = 'f', formatter = 'f',
dotPrintRatio = 0, dotPrintRatio = 0,
merge = 6).convert() merge = 6).convert()
/**
* @file 打包到rd机器的配置
* @author yangmingming zhangmiao06
*/
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractLess = new ExtractTextPlugin({
filename: '[name].css'
});
module.exports = {
mode: 'development',
devtool: 'none',
optimization: {
minimize: false
},
entry: {
camera: './src/executor/camera',
index: './src/executor/runner'
},
output: {
filename: '../graphfe/src/view/common/lib/paddle/[name].js',
path: path.resolve(__dirname, './'),
library: 'panorama',
libraryTarget: 'umd',
libraryExport: 'default'
},
module: {
rules: [{
test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/,
loader: 'url-loader?limit=30000&name=[name].[ext]'
}, {
test: /\.less$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract([
{loader: 'css-loader', options: {minimize: true}},
{loader: 'less-loader'}
])
}]
},
plugins: [extractLess],
resolve: {
extensions: ['.es6', '.js', '.json']
}
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册