提交 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",
"description": "paddle",
"main": "index.js",
"scripts": {
"server": "parcel ./src/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",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/PaddlePaddle/paddle-mobile"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-plugin-transform-class-properties": "^6.24.1",
......@@ -24,12 +22,13 @@
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"parcel-bundler": "^1.10.3",
"axios": ">=0.18.1"
"axios": "^0.17.1"
},
"keywords": [],
"author": "",
"license": "ISC",
"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 {
constructor(model) {
this.inputs = model.inputs;
this.outputs = model.outputs;
this.attrs = model.attrs;
this.attrs = model.attrs || model['sub-attrs'];
this.type = model.type;
this.finish = false;
this.next = null;
......@@ -53,7 +53,7 @@ export default class GraphExecutor {
else if (this.type === 'fetch') {
return this.inputs.X;
}
return null;
return this.inputs.Input || this.inputs.X;
}
get outputsName() {
......@@ -68,29 +68,31 @@ export default class GraphExecutor {
return this.outputs.Y;
}
else {
return this.outputs.Out;
return this.outputs.Out || this.outputs.Output;
}
}
/**
* 将输入数据和具体op进行关联,触发执行具体每一个op
* @param inputs
* @param runtime
* @param isRendered
*/
execute(runtime) {
execute(runtime, isRendered) {
// console.log(inputs, outputs);
if (this.type !== 'feed') {
let time = +Date.now();
runtime.run(this.type, this.opData);
// let time = +Date.now();
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) {
// var result = runtime.read();
// let res = Array.prototype.slice.call(result);
// fileDownload(res, "result.csv");
// }
let length = statistic.length;
statistic[length - 1].type = this.type;
statistic[length - 1].runTime = +Date.now() - time;
// let length = statistic.length;
// statistic[length - 1].type = this.type;
// statistic[length - 1].runTime = +Date.now() - time;
// if (this.type === 'scale') {
// console.log('时间是:' + (+Date.now() - start));
// }
......
......@@ -26,6 +26,8 @@ export default class GraphModel {
this.feedOp = null;
this.feedItem = null;
this.isExecuted = false;
// 网络层数
this.iLayer = 0;
// fetch xhr jsonp
this.params = {type: 'fetch'};
// 设置分片加载model
......@@ -36,22 +38,41 @@ export default class GraphModel {
this.binaryOption = loadOptions.binaryOption;
}
}
// op runner
this.inst = Runtime.init({
'width_raw_canvas': 512,
'height_raw_canvas': 512
});
if (this.loadOptions === null) {
if (!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) {
console.time(path)
return fetch(path).then(request => {
console.timeEnd(path);
return this.fetch(path).then(request => {
return request.arrayBuffer();
})
}
fetchJson(path) {
return this.fetch(path).then(request => {
return request.json();
})
}
fetchAllData() {
// todo 兼容一下json的模式
let counts = this.binaryOption.fileCount;
......@@ -61,8 +82,6 @@ export default class GraphModel {
this.fetchOneChunk(this.modelGonfig.dir + this.binaryOption.getFileName(i))
);
}
// 1个文件
// let chunkArray = [this.fetchOneChunk('/faceModel/mergedData.dat')];
console.time('加载时间');
return Promise.all(chunkArray).then(chunks => {
console.timeEnd('加载时间');
......@@ -109,13 +128,24 @@ export default class GraphModel {
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) {
params = params || this.params;
const path = this.modelGonfig.dir + this.modelGonfig.main;
let URL_SCHEME_REGEX = /^https?:\/\//;
let load = null;
let method = params.method || 'get';
let mode = params.mode || 'cors';
// jsonp请求方式
if (params && params.type === 'jsonp') {
let json;
......@@ -139,14 +169,8 @@ export default class GraphModel {
}
// 原生fetch
else if (params.type === 'fetch') {
let myHeaders = new Headers();
load = new Promise((resolve, reject) => {
fetch(path, {
method: method,
mode: mode,
credentials: "include",
headers: myHeaders
})
this.fetch(path, params)
.then(response => response.json())
.then(responseData => resolve(responseData))
.then(err => reject(err))
......@@ -161,23 +185,13 @@ export default class GraphModel {
}
async load() {
let that = this;
console.time('生成op数据之前')
console.time('fetchModel');
const artifacts = this.handler = await this.fetchModel();
console.timeEnd('fetchModel');
if (this.multipart === true) {
console.time('6个文件准备好op数据');
await this.fetchAllData()
.then(() => this.traverse(artifacts.vars));
console.timeEnd('6个文件准备好op数据');
}
console.time('createOpsMap');
const opsMap = this.createOpsMap(artifacts.ops, artifacts.vars);
console.timeEnd('createOpsMap');
console.time('constructOpsMap');
this.weightMap = this.constructOpsMap(opsMap);
console.timeEnd('constructOpsMap');
console.timeEnd('生成op数据之前')
// 生成op数据
this.weightMap.forEach(op => {
const type = op.type;
......@@ -192,23 +206,27 @@ export default class GraphModel {
const opData = new OpData(op.type, tensor.inputs, tensor.outputs, tensor.attrs);
const name = opData.name;
const fsCode = factory.buildShader(name, opData.data);
opData.fshader = this.inst.createFragmentShader(fsCode);
opData.fsCode = fsCode;
opData.program = this.inst.createProgram(fsCode, opData.tensor['out']);
opData.renderData = opConfs[name].map(elem => {
let item = Object.assign({}, elem);
const tensorData = opData.tensor[item.tensor];
if (item.type === 'texture') {
item.data = tensorData.data;
if (this.feedOp.id === op.id) {
if (this.feedOp.id === op.id && item.tensor === 'origin') {
item.shape = tensorData.shape;
this.feedItem = item;
}
item['width_texture'] = tensorData['width_texture'];
item['height_texture'] = tensorData['height_texture'];
item['channel'] = tensorData['channel'];
} else if (item.type === 'uniform') {
item.data = tensorData[item.variable];
}
return item;
});
// console.timeEnd('opData.renderData');
opData.iLayer = this.iLayer++;
op.opData = opData;
// delete op.inputs;
// delete op.outputs;
......@@ -218,7 +236,7 @@ export default class GraphModel {
if (executor.type === 'fetch') {
return;
}
executor.execute(this.inst);
executor.execute(this.inst, this.isExecuted);
if (executor.next) {
const id = executor.next;
const next = this.getTensor(id);
......@@ -245,13 +263,12 @@ export default class GraphModel {
}
let start = +Date.now();
this.execute_(executor[0]);
console.log('总的执行时间是' + (+Date.now() - start));
this.isExecuted = true;
return this.inst;
}
updateFeed() {
this.feedItem.data = this.feed.input[0].data;
Utils.img2texture(this.feedItem);
// Utils.img2texture(this.feedItem);
}
/**
* predict enter
......@@ -282,6 +299,7 @@ export default class GraphModel {
input[key] = io.fromPixels(data, pixel);
}
else if ((key === 'Input') && (inputName === 'image' || inputName === 'x')) {
// that.feed.input[0].data = that.testData;
input[key] = that.feed.input;
that.feedOp = executor;
}
......@@ -289,6 +307,7 @@ export default class GraphModel {
input[key] = that.getTensorAttr(input[key][0]);
}
});
// console.log(input);
const tensor = {
inputs: input,
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';
export default class Factory {
constructor(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) {
......@@ -13,12 +22,16 @@ export default class Factory {
result = this.buildPrefix(opName);
result += this.buildCommon(opName);
result += this.buildOp(opName);
data.texture2d = this.texture2d;
result = this.populateData(result, data);
return result;
}
buildPrefix(opName) {
return ops.common.prefix;
if (this.webglVersion === 1) {
return ops.common.prefix;
}
return ops.common.prefix2;
}
buildCommon(opName) {
......
......@@ -2,18 +2,28 @@
import common_params from '../../shader/atom/common_params';
import common_func from '../../shader/atom/common_func';
import prefix from '../../shader/atom/prefix';
import prefix2 from '../../shader/atom/prefix2';
import suffix from '../../shader/atom/suffix';
import ivec56 from '../../shader/atom/type_ivec56';
import conv2d_params from '../../shader/conv2d/params';
import conv2d_func from '../../shader/conv2d/main';
import conv2d_conf from '../../shader/conv2d/conf';
import 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_func from '../../shader/dynamic/main';
import dynamic_conf from '../../shader/dynamic/conf';
import pool2d_params from '../../shader/pool2d/params';
import pool2d_func from '../../shader/pool2d/main';
import pool2d_conf from '../../shader/pool2d/conf';
import 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_func from '../../shader/elementwise_add/main';
import elementwise_add_conf from '../../shader/elementwise_add/conf';
......@@ -27,12 +37,21 @@ import batchnorm_params from '../../shader/batchnorm/params';
import batchnorm_func from '../../shader/batchnorm/main';
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 getArrayIndexFromTexturePos from '../../shader/atom/getArrayIndexFromTexturePos';
import getTensorPosFromArrayIndex from '../../shader/atom/getTensorPosFromArrayIndex';
import getTexturePosFromArrayIndex from '../../shader/atom/getTexturePosFromArrayIndex';
import getValueFromTexturePos from '../../shader/atom/getValueFromTexturePos';
import getValueFromTensorPos from '../../shader/atom/getValueFromTensorPos';
import getValueFromTensorPosPacked from '../../shader/atom/getValueFromTensorPosPacked';
import moveTexture2PosToReal from '../../shader/atom/moveTexture2PosToReal';
import getPixelsFromTexturePos from '../../shader/atom/getPixelsFromTexturePos';
import getRangePowSumFromArrayIndex from '../../shader/atom/getRangePowSumFromArrayIndex';
......@@ -51,6 +70,7 @@ export default {
params: common_params,
func: common_func,
prefix,
prefix2,
suffix,
ivec56
},
......@@ -60,6 +80,21 @@ export default {
func: conv2d_func,
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: {
params: dynamic_params,
func: dynamic_func,
......@@ -70,6 +105,16 @@ export default {
func: pool2d_func,
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: {
params: elementwise_add_params,
func: elementwise_add_func,
......@@ -108,6 +153,7 @@ export default {
getTexturePosFromArrayIndex,
getValueFromTexturePos,
getValueFromTensorPos,
getValueFromTensorPosPacked,
moveTexture2PosToReal,
getPixelsFromTexturePos,
getRangeSumFromArrayIndex,
......
......@@ -30,7 +30,10 @@ export default class imageFeed {
...inputs.params
};
let output = [];
if (!this.result) {
const [b, c, h, w] = params.targetShape;
this.result = new Float32Array(h * w * 3);
}
output = this.fromPixels(input, params);
return output;
};
......@@ -74,21 +77,27 @@ export default class imageFeed {
*/
allReshapeToRGB(imageData, opt, scaleSize) {
const {sw, sh} = scaleSize;
const {width, height} = opt;
const [b, c, h, w] = opt.targetShape;
let data = imageData.data;
let mean = opt.mean;
let dataLength = data.length;
let result = new Float32Array(dataLength * 3 / 4);
let offsetR = 0;
let offsetG = dataLength / 4;
let offsetB = dataLength / 2;
for (let i = 0; i < data.length; i += 4) {
result[offsetR++] = (data[i] - mean[0]) / 256;
result[offsetG++] = (data[i + 1] - mean[1]) / 256;
result[offsetB++] = (data[i + 2] - mean[2]) / 256;
// result.push((data[i] - mean[0]) / 256); // red
// result.push((data[i + 1] - mean[1]) / 256); // green
// result.push((data[i + 2] - mean[2]) / 256); // blue
// let result = new Float32Array(dataLength * 3);
let result = this.result;
// let offsetR = 0;
// let offsetG = dataLength / 4;
// let offsetB = dataLength / 2;
let offset = 0;
let size = h * w;
// h w c
for (let i = 0; i < h; ++i) {
let iw = i * w;
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;
};
......@@ -156,8 +165,10 @@ export default class imageFeed {
else {
this.fromPixels2DContext.drawImage(
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};
}
......
/**
* @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 @@
* @file io,loader相关输入输出
* @author wangqun@baidu.com
*/
export default class io {
constructor() {
this.fromPixels2DContext = document.createElement('canvas').getContext('2d');
......
/* eslint-disable */
import VSHADER from '../shader/v_shader';
import VSHADER2 from '../shader/v_shader2';
/**
* @file gpu运算
* @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 {
constructor(opts = {}) {
// 版本, 默认webgl version 2.0
this.version = 2;
this.opts = opts;
opts.width_raw_canvas = Number(opts.width_raw_canvas) || 512;
opts.height_raw_canvas = Number(opts.height_raw_canvas) || 512;
let canvas = opts.el ? opts.el : document.createElement('canvas');
canvas.width = opts.width_raw_canvas;
canvas.height = opts.height_raw_canvas;
this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
this.gl.viewport(0, 0, canvas.width, canvas.height);
// Attempt to activate the extension, returns null if unavailable
this.textureFloat = this.gl.getExtension('OES_texture_float');
// this.setOutProps();
const canvas = opts.el ? opts.el : document.createElement('canvas');
canvas.addEventListener('webglcontextlost', evt => {
evt.preventDefault();
console.log('webgl context is lost~');
}, false);
let gl = canvas.getContext('webgl2', CONF);
if (!!gl) {
// 开启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();
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() {
// 运行次数
this.times = 0;
const gl = this.gl;
// 缓存每个op的texture
// this.textures = [];
// 顶点数据
let vertices = new Float32Array([
-1.0, 1.0, 0.0, 1.0,
......@@ -40,7 +90,7 @@ export default class gpu {
// shader
this.vertexShader = null;
// 生成vertextShader
this.initShader(VSHADER);
this.initShader(this.version === 2 ? VSHADER2 : VSHADER);
this.fragmentShader = null;
// 上一个texture
this.prevTexture = null;
......@@ -49,27 +99,18 @@ export default class gpu {
// 帧缓存
this.frameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer);
// 计算texture cache, 最多3个
this.cacheTextures = [gl.createTexture(), gl.createTexture(), gl.createTexture()];
// 计算texture cache
this.cacheTextures = {};
this.uniformLocations = {};
// texture buffer
this.textureBuffer = [gl.createTexture(), gl.createTexture()];
// program
this.programs = [gl.createProgram(), gl.createProgram()];
this.program = this.programs[0];
this.textureBufferIndex = 0;
for (let i = 0; i < 2; i++) {
gl.bindTexture(gl.TEXTURE_2D, this.textureBuffer[i]);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D, null);
}
this.outTextures = [];
// pbo
this.pbo = gl.createBuffer();
}
runVertexShader() {
runVertexShader(program) {
const gl = this.gl;
let aPosition = gl.getAttribLocation(this.program, 'position');
let aPosition = gl.getAttribLocation(program, 'position');
// Turn on the position attribute
gl.enableVertexAttribArray(aPosition);
// Bind the position buffer.
......@@ -82,6 +123,7 @@ export default class gpu {
this.height_shape_out = opts.height_shape || 1;
this.width_texture_out = opts.width_texture || 1;
this.height_texture_out = opts.height_texture || 1;
this.channel = opts.channel || 0;
this.total_shape = opts.total_shape || 0;
}
......@@ -89,27 +131,63 @@ export default class gpu {
return (this.textureFloat !== null);
}
attachShader(fshader) {
createProgram(fshader, out) {
const gl = this.gl;
let index = this.textureBufferIndex % 2;
const program = this.programs[index];
const program = gl.createProgram();
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;
if (this.times < 2) {
gl.attachShader(program, this.vertexShader);
if (!isRendered) {
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;
if (!!this.fragmentShader) {
gl.detachShader(program, this.fragmentShader);
}
this.gl.attachShader(program, fshader);
this.fragmentShader = fshader;
gl.linkProgram(program);
gl.useProgram(program);
if (this.times++ < 2) {
if (this.times++ === 0) {
gl.useProgram(program);
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) {
......@@ -185,10 +263,11 @@ export default class gpu {
* @param {WebGLTexture} texture 材质
* @returns {WebGLFramebuffer} The framebuffer
*/
attachFrameBuffer(opts = {}) {
attachFrameBuffer(iLayer) {
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.currentTexture = this.outTextures[iLayer];
const gl = this.gl;
gl.framebufferTexture2D(gl.FRAMEBUFFER, // The target is always a FRAMEBUFFER.
gl.COLOR_ATTACHMENT0, // We are providing the color buffer.
......@@ -199,8 +278,14 @@ export default class gpu {
gl.viewport(
0,
0,
opts.width_texture_out || this.width_texture_out,
opts.height_texture_out || this.height_texture_out
this.width_texture_out,
this.height_texture_out
);
gl.scissor(
0,
0,
this.width_texture_out,
this.height_texture_out
);
return this.frameBuffer;
}
......@@ -243,66 +328,53 @@ export default class gpu {
return {isComplete: value, message: message};
}
/**
* 更新材质
* @param {WebGLTexture} texture 材质对象
* @param {number} type 材质类型. FLOAT, UNSIGNED_BYTE, etc.
* @param {Float32Array[]} data 材质数据
*/
refreshTexture(texture, type, data) {
const gl = this.gl;
// Bind the texture so the following methods effect it.
gl.bindTexture(gl.TEXTURE_2D, texture);
// Replace the texture data
gl.texSubImage2D(gl.TEXTURE_2D, // Target, matches bind above.
0, // Level of detail.
0, // xOffset
0, // yOffset
this.opts.width_raw_canvas, // Width - normalized to s.
this.opts.height_raw_canvas, // Height - normalized to t.
gl.RGBA, // Format for each pixel.
type, // Data type for each chanel.
data); // Image data in the described format.
// Unbind the texture.
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
/**
* 初始化材质
* @param {int} index 材质索引
* @param {string} tSampler 材质名称
* @param {Object} bufferData 数据
* @param {boolean} isRendered 是否已运行过
*/
initTexture(index, item) {
initTexture(index, item, iLayer, isRendered) {
const gl = this.gl;
let texture;
if (!item.data) {
texture = this.prevTexture;
} else {
// texture = gl.createTexture();
texture = this.cacheTextures[index];
// this.textures.push(texture);
if (isRendered && (iLayer > 0 || (iLayer === 0 && item.tensor !== 'origin'))) {
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.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_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, item.width_texture || this.opts.width_raw_canvas,
item.height_texture || this.opts.height_raw_canvas, 0,
gl.RGBA, gl.FLOAT, item.data, 0);
gl.texImage2D(gl.TEXTURE_2D, 0, this.internalFormat, item.width_texture,
item.height_texture, 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);
if (loc === null) throw `getUniformLoc ${name} err`;
// 缓存
this.uniformLocations['' + ilayer] = this.uniformLocations['' + ilayer] || {};
this.uniformLocations['' + ilayer][name] = loc;
return loc;
}
......@@ -330,16 +402,16 @@ export default class gpu {
return texture;
}
render(data = []) {
render(data = [], iLayer = 0, isRendered = false) {
const gl = this.gl;
let textureIndex = 0;
// 输入数据
data.forEach(item => {
if (item.type === 'texture') {
this.initTexture(textureIndex, item);
gl.uniform1i(this.getUniformLoc(item.variable + '_' + item.tensor), textureIndex++);
} else if (item.type === 'uniform') {
gl[item.setter](this.getUniformLoc(item.variable + '_' + item.tensor), item.data);
this.initTexture(textureIndex, item, iLayer, isRendered);
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, iLayer, isRendered), item.data);
}
});
// gl.clearColor(.0, .0, .0, 1);
......@@ -347,25 +419,127 @@ export default class gpu {
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() {
let gl = this.gl;
log.start('后处理-readinside');
const tt = +Date.now();
let pixels = new Float32Array(this.width_texture_out * this.height_texture_out * 4);
// 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);
// console.log('本次读取数据时间是' + (+Date.now() - tt2)+ ',' + (tt2 - tt));
log.end('后处理-readinside');
log.start('后处理-readloop');
let result = [];
for (let i = 0; i < this.width_texture_out * this.height_texture_out; i++) {
result.push(pixels[4 * i]);
}
log.end('后处理-readloop');
return result;
}
dispose() {
const gl = this.gl;
this.cacheTextures.forEach(texture => {
gl.deleteTexture(texture);
});
this.cacheTextures = [];
// this.cacheTextures.forEach(texture => {
// gl.deleteTexture(texture);
// });
this.cacheTextures = {};
this.programs.forEach(program => {
gl.detachShader(program, 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 */
import Gpu from '../gpu/gpu';
import getMaxUniforms from '../test/getMaxUniforms';
/**
* @file gpu运行时
* @author yangmingming
......@@ -20,10 +21,15 @@ export default {
}
},
run(opName, opData) {
let time = +Date.now();
let start = time;
let timeObj = {};
getWebglVersion() {
return this.gpu.getWebglVersion();
},
run(opName, opData, isRendered) {
// console.dir(['fscode', opData.fsCode]);
// let time = +Date.now();
// let start = time;
// let timeObj = {};
if (!opData.isPass) {
console.log('跳过当前op:' + opName);
return this;
......@@ -32,19 +38,20 @@ export default {
const gpu = this.gpu;
gpu.setOutProps(opData.tensor['out']);
// 生成帧缓存材质
gpu.makeTexure(WebGLRenderingContext.FLOAT, null);
let end = +Date.now();
gpu.attachFrameBuffer(opData.iLayer);
// let end = +Date.now();
let bufferStatus = gpu.frameBufferIsComplete();
if (bufferStatus.isComplete) {
start = +Date.now();
timeObj['buferstatus-time'] = start - end;
gpu.attachShader(opData.fshader);
end = +Date.now();
timeObj['createshader-time'] = end - start;
timeObj['jsTime'] = end - time;
statistic.push(timeObj);
// start = +Date.now();
// timeObj['buferstatus-time'] = start - end;
// gpu.attachShader(opData.fshader);
gpu.setProgram(opData.program, isRendered);
// end = +Date.now();
// timeObj['createshader-time'] = end - start;
// timeObj['jsTime'] = end - time;
// statistic.push(timeObj);
// 开始计算
this.gpu.render(opData.renderData);
this.gpu.render(opData.renderData, opData.iLayer, isRendered);
return this;
} else {
return bufferStatus.message;
......@@ -54,12 +61,34 @@ export default {
/**
* 读取op计算结果, 并返回数据
*/
read() {
return this.gpu.compute();
read2() {
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) {
return this.gpu.initShader(fsCode, 'fragment');
createProgram(fsCode, outTensor) {
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 @@
* @author yangmingming
*/
export default `
// varying变量
// 顶点shader透传的材质坐标
varying vec2 vCoord;
// 扩展shader的ivec类型
// struct ivec5 {
// int x;
// int y;
// int z;
// int w;
// int u;
// };
// struct ivec6 {
// int x;
// int y;
// int z;
// int w;
// int u;
// int v;
// };
// dynamic的input数据
const float multi_value = float(MULTI_VALUE);
const float bias_value = float(BIAS_VALUE);
......@@ -33,4 +14,5 @@ export default `
const int width_texture_out = WIDTH_TEXTURE_OUT;
const int height_texture_out = HEIGHT_TEXTURE_OUT;
const int channel_out = CHANNEL_OUT;
const int offset_y_out = OFFSET_Y_OUT;
`;
......@@ -4,11 +4,6 @@
* @author yangmingming
*/
export default `
// TENSOR_TYPE, tensor坐标的类型,ivec4
// TENSOR_NAME, tensor name
// 获取tensor坐标对应数组中的索引
// uniform int numbers_shape_TENSOR_NAME[LENGTH_SHAPE_TENSOR_NAME];
int getArrayIndexFromTensorPos_TENSOR_NAME(TENSOR_TYPE tensorPos) {
int index = 0;
......
......@@ -3,11 +3,7 @@
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, texture name
// WIDTH_TEXTURE_NAME_VALUE, texture的宽度
// 获取材质元素在数组中的索引
// const int width_TEXTURE_NAME = WIDTH_TEXTURE_NAME_VALUE;
export default `
int getArrayIndexFromTexturePos_TEXTURE_NAME(vec3 pos) {
int x = int(floor(pos.x));
......
......@@ -3,9 +3,7 @@
* @file 公共方法
* @author yangmingming
*/
// TEXTURE_NAME, tensor name
// 获取材质中的像素
// uniform sampler2D TEXTURE_NAME;
export default `
#define getPixelsFromTexturePos_TEXTURE_NAME(pos) texture2D(TEXTURE_NAME, pos)
#define getPixelsFromTexturePos_TEXTURE_NAME(pos) TEXTURE2D(TEXTURE_NAME, pos)
`;
......@@ -5,15 +5,28 @@
*/
export default `
float getValueFromTensorPos_TENSOR_NAME(int r, int g, int b, int a) {
vec4 pixels = texture2D(texture_TENSOR_NAME, vec2((float(a * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME), (float(r * height_shape_TENSOR_NAME + b) + 0.5) / float(height_texture_TENSOR_NAME)));
vec4 pixels = TEXTURE2D(texture_TENSOR_NAME,
vec2(
(float(a * channel_TENSOR_NAME + g) + 0.5) / float(width_texture_TENSOR_NAME),
(float(r * height_shape_TENSOR_NAME + b) + 0.5) / float(height_texture_TENSOR_NAME)
)
);
return pixels.r;
}
float getValueFromTensorPos_TENSOR_NAME(ivec4 pos) {
float offset = 0.5;
float width = float(pos.a * channel_TENSOR_NAME + pos.g) + offset;
float height = float(pos.r * height_shape_TENSOR_NAME + pos.b) + offset;
vec4 pixels = texture2D(texture_TENSOR_NAME, vec2(width / float(width_texture_TENSOR_NAME), height / float(height_texture_TENSOR_NAME)));
float getValueFromTensorPosLimit_TENSOR_NAME(int r, int g, int b, int a) {
float halfW = ceil(float(width_shape_TENSOR_NAME) / 2.0);
int x = int(mod(float(a), halfW));
int offsetY = 0;
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;
}
`;
/* 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 @@
// uniform sampler2D TEXTURE_NAME;
export default `
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);
if (d == 0) {
return pixels.r;
......
......@@ -11,4 +11,8 @@ export default `
precision mediump float;
precision mediump int;
#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() {
int b = int(outCoord.y / float(height_shape_out));
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 {
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_OUT',
'EPSILON',
'WIDTH_TEXTURE_SCALE',
......
......@@ -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));
float x = (o - scale[3]) / sqrt(scale[2] + epsilon);
float res = scale[0] * x + scale[1];
gl_FragColor.r = res;
setOutput(res);
}
`;
......@@ -38,6 +38,7 @@ export default {
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_OUT',
'STRIDE_HORIZONTAL',
'STRIDE_VERTICAL',
......
......@@ -6,7 +6,7 @@
export default `
// start函数
void main(void) {
ivec4 oPos = getOutputTensorPos();
ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int x = oPos.a;
int c = oPos.g;
int y = oPos.b;
......@@ -35,14 +35,14 @@ export default `
}
// channel计算
for (int j = 0; j < channel_filter; j++) {
float f = getValueFromTensorPos_filter(c, j, fy, fx);
float o = getValueFromTensorPos_origin(b, oTensorChannel + j, oy, ox);
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;
}
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 {
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_OUT',
'MULTI_VALUE',
'BIAS_VALUE',
......
......@@ -9,6 +9,6 @@ void main(void) {
// 输出数据
float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float res = ACTIVE_FUNCTION(o, multi_value, bias_value);
gl_FragColor.r = res;
setOutput(res);
}
`;
......@@ -33,6 +33,7 @@ export default {
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_OUT',
'AXIS',
'MULTI_VALUE',
......
......@@ -7,11 +7,11 @@ export default `
// start函数
void main(void) {
// 输出数据
ivec4 oPos = getOutputTensorPos();
ivec4 oPos = getOutputTensorPosLIMIT_OUT();
int index = oPos[axis];
float o = getPixelsFromTexturePos_texture_origin(vCoord).r;
float c = getValueFromCounter(index);
float res = ACTIVE_FUNCTION(o + c, multi_value, bias_value);
gl_FragColor.r = res;
setOutput(res);
}
`;
......@@ -37,7 +37,8 @@ export default {
'HEIGHT_SHAPE_OUT',
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT'
'CHANNEL_OUT',
'OFFSET_Y_OUT'
],
input: [
{
......
......@@ -13,6 +13,6 @@ void main(void) {
float o = getValueFromTensorPos_origin(out_pos[0], out_pos[1], out_pos[2], j);
res += c * o;
}
gl_FragColor.r = res;
setOutput(res);
}
`;
......@@ -29,6 +29,7 @@ export default {
'WIDTH_TEXTURE_OUT',
'HEIGHT_TEXTURE_OUT',
'CHANNEL_OUT',
'OFFSET_Y_OUT',
'STRIDES_X',
'STRIDES_Y',
......
......@@ -7,7 +7,7 @@ export default `
void main(void) {
float res = (-1.0 / exp(-20.0));
// 获取output的坐标
ivec4 out_pos = getOutputTensorPos();
ivec4 out_pos = getOutputTensorPosLIMIT_OUT();
// X、Y方向的移动步长
int count_pool = 0;
int oy_base = out_pos[2] * stride_v - padTop;
......@@ -29,7 +29,7 @@ void main(void) {
continue;
}
// 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 (curr > res) {
res = curr;
......@@ -44,6 +44,6 @@ void main(void) {
if (type_pool != 1) {
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 {
conf: [
'WIDTH_TEXTURE_ORIGIN',
'HEIGHT_TEXTURE_ORIGIN',
'TOTAL_SHAPE_ORIGIN'
'TOTAL_SHAPE_ORIGIN',
'OFFSET_Y_OUT'
],
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 = [
'height_shape',
'width_texture',
'height_texture',
'offset_x',
'offset_y',
'limit',
'channel',
'total_shape'
];
......@@ -30,6 +33,9 @@ const shaderAttrs = {
},
pool2d: {
'pooling_type': 'type_pool'
},
pool2d_winograd: {
'pooling_type': 'type_pool'
}
};
// model的名字和paddle web的tensor名字mapping
......@@ -48,7 +54,8 @@ const tensorName = {
// unique behavior
const opBehavior = {
conv2d: [
'needBatch'
'needBatch',
'isApplySeparableConv'
],
batchnorm: [
'needBatch',
......@@ -58,9 +65,15 @@ const opBehavior = {
'broadcast',
'needBatch'
],
conv2d_elementwise_add: [
'mergeAttrs',
'setActiveFunc',
'needBatch'
],
pool2d: [
'isMax',
'needBatch',
'setPacked',
'isGlobalPooling'
],
relu: [
......@@ -76,10 +89,14 @@ const opBehavior = {
'needBatch'
]
};
const mergeType = 'conv2d-elementwise_add';
export default class OpData {
constructor(name, input = {}, output = {}, attrs = {}) {
this.realName = name;
this.name = name;
this.attrs = attrs;
// 检查是否是融合op
this.checkIsMerge();
// 是否忽略当前当前op, 使用dropout
this.isPass = this.checkIsPass();
if (this.isPass) {
......@@ -133,6 +150,7 @@ export default class OpData {
});
// 生成tensor对象
tensorData.forEach(data => {
// console.log(data);
if (data) {
if (data.notTensor) {
this.tensor[data.tensorName] = {
......@@ -142,11 +160,13 @@ export default class OpData {
};
} else {
this.tensor[data.tensorName] = new Tensor({
type: data.name,
name: data.tensorName,
shape: data.shape,
data: data.data,
needBatch: data.needBatch || false,
notCompressed: data.notCompressed || false
notCompressed: data.notCompressed || false,
isPacked: data.isPacked || false
});
}
}
......@@ -195,15 +215,74 @@ export default class OpData {
}
}
broadcast(tensorData = []) {
const x = tensorData[0];
const y = tensorData[1];
let small = y;
if (x.shape.length - y.shape.length < 0) {
small = x;
mergeAttrs() {
this.attrs = this.attrs.reduce((attrs, item) => {
return Object.assign(attrs, item);
}, {});
}
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;
// mobilenet model
......@@ -227,6 +306,9 @@ export default class OpData {
isMax(tensorData = []) {
const type = this.attrs['pooling_type'] === 'max' ? 1 : 0;
this.attrs['pooling_type'] = type;
if (type === 1) {
this.name += '_max';
}
}
transToPrelu(tensorData = []) {
......@@ -240,10 +322,13 @@ export default class OpData {
this.name = 'relu';
}
setActiveFunc(tensorData = []) {
this.data['multi_value'] = '0.0';
this.data['active_function'] = 'softmax';
setActiveFunc() {
// 用于融合op
const suffix = this.realName.replace(mergeType + '-', '');
if (suffix === 'leaky_relu') {
this.data['multi_value'] = this.attrs.alpha;
this.data['active_function'] = 'leakyRelu';
}
}
reshape(tensorData = []) {
......@@ -284,6 +369,16 @@ export default class OpData {
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() {
if (this.name === 'dropout') {
if (this.attrs['dropout_implementation'] === 'downgrade_in_infer') {
......
......@@ -7,6 +7,8 @@ import Utils from './utils';
export default class Tensor {
constructor(opts = {}) {
this.opts = opts;
// 数据存储方式
this.isPacked = this.isPacked || false;
// 设置tensor名字
this.name = opts.name;
// tensor的形状
......@@ -23,36 +25,44 @@ export default class Tensor {
this.shape = shape;
}
// 获取转换到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.exceedMax = exceedMax;
this.offsetX = offsetX;
this.offsetY = offsetY;
// tensor数据
let data = [];
if (opts.data && opts.data.length) {
let data;
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) {
let b = shape[0];
let c = shape[1];
let h = shape[2];
let w = shape[3];
for (let i = 0; i < opts.data.length; i++) {
let j = Math.floor(i / (c * w));
let k = Math.floor(i % (c * w));
let b1 = Math.floor(j / h);
let h1 = Math.floor(j % h);
let c1 = Math.floor(k % c);
let w1 = Math.floor(k / c);
let 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;
data.push(opts.data[l]);
data.push(0);
data.push(0);
data.push(0);
data[i] = opts.data[l];
}
this.data = data;
} else {
// batchnorm的scale
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;
}
......@@ -106,6 +116,18 @@ export default class Tensor {
return 0;
}
get offset_x() {
return this.offsetX;
}
get offset_y() {
return this.offsetY;
}
get limit() {
return this.exceedMax ? 'Limit' : '';
}
get length_shape() {
return this.shape.length || 0;
}
......
......@@ -64,18 +64,78 @@ export default {
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个数
* @param shape {Array} tensor的形状
* @return {{shape: *[], zeroNumber: number}} {Object} texture信息
*/
getTextureInfoFromTensorShape(shape = []) {
getTextureInfoFromTensorShape(shape = [], isPacked = false) {
let b = shape[0];
let c = shape[1];
let h = shape[2];
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 {
shape: [4, b * h, c * w],
offsetX,
offsetY,
exceedMax,
shape: [4, height, width],
zeroNumber: 0
};
},
......@@ -107,21 +167,24 @@ export default {
const c = shape[1];
const h = shape[2];
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++) {
let j = Math.floor(i / (c * w));
let k = Math.floor(i % (c * w));
let b1 = Math.floor(j / h);
let h1 = Math.floor(j % h);
let c1 = Math.floor(k % c);
let w1 = Math.floor(k / c);
let 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;
data.push(renderData.data[l]);
data.push(0);
data.push(0);
data.push(0);
data[offset] = renderData.data[l];
offset += 4;
// data.push(renderData.data[l]);
// data.push(0);
// data.push(0);
// data.push(0);
}
renderData.data = new Float32Array(data);
renderData.data = data;
}
};
/* eslint-enable */
此差异已折叠。
/**
* 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:
BinaryFileConverter(
delimiter = ',',
ignorChar = ['[', ']'],
ignorLine = ['[', ']'],
types = ['.txt', '.json'],
originDir = './mobileNet',
resultDir = './binf',
ext = '.dat',
formatter = 'f',
dotPrintRatio = 0,
delimiter = ',',
ignorChar = ['[', ']'],
ignorLine = ['[', ']'],
types = ['.txt', '.json'],
originDir = './mobileNet',
resultDir = './binf',
ext = '.dat',
formatter = 'f',
dotPrintRatio = 0,
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.
先完成此消息的编辑!
想要评论请 注册