提交 55dbbb8f 编写于 作者: W wangqun

[change][d]修复了一些兼容性问题,比如精度问题

修复了一些兼容性问题,比如精度问题,多余demo删除
上级 e24f59e9
...@@ -29,57 +29,33 @@ Currently Paddle.js only supports a limited set of Paddle Ops. See the full list ...@@ -29,57 +29,33 @@ Currently Paddle.js only supports a limited set of Paddle Ops. See the full list
## Loading and running in the browser ## Loading and running in the browser
If the original model was a SavedModel, use paddle.load().
```bash ```bash
import Paddle from 'paddlejs'; import {runner as Paddlejs} from 'paddlejs';
let feed = io.process({ const paddlejs = new Paddlejs({
input: document.getElementById('image'), modelPath: 'model/mobilenetv2', // model path
params: { fileCount: 4, // model data file count
gapFillWith: '#000', // What to use to fill the square part after zooming feedShape: { // input shape
targetSize: { fw: 256,
height: fw, fh: 256
width: fh
}, },
targetShape: [1, 3, fh, fw], // Target shape changed its name to be compatible with previous logic fetchShape: [1, 1, 1920, 10], // output shape
// shape: [3, 608, 608], // Preset sensor shape fill: '#fff', // fill color when resize image
mean: [117.001, 114.697, 97.404], // Preset mean needBatch: true, // whether need to complete the shape to 4 dimension
// std: [0.229, 0.224, 0.225] // Preset std inputType: 'image' // whether is image or video
} });
});
// load paddlejs model and preheat
const MODEL_CONFIG = { await paddlejs.loadModel();
dir: `/${path}/`, // model URL
main: 'model.json', // main graph // run model
}; await paddlejs.predict(img, postProcess);
const paddle = new Paddle({ function postProcee(data) {
urlConf: MODEL_CONFIG, // data is predicted result
options: { console.log(data);
multipart: true, }
dataType: 'binary',
options: {
fileCount: 1, // How many model have been cut
getFileName(i) {
return 'chunk_' + i + '.dat';
}
}
}
});
model = await paddle.load();
//
let inst = model.execute({
input: feed
});
// There should be a fetch execution call or a fetch output
let result = await inst.read();
``` ```
......
...@@ -28,52 +28,31 @@ Paddle.js项目基于Atom系统构建,该系统是一个通用框架,可支 ...@@ -28,52 +28,31 @@ Paddle.js项目基于Atom系统构建,该系统是一个通用框架,可支
```bash ```bash
import Paddle from 'paddlejs'; import {runner as Paddlejs} from 'paddlejs';
let feed = io.process({ const paddlejs = new Paddlejs({
input: document.getElementById('image'), modelPath: 'model/mobilenetv2', // model path
params: { fileCount: 4, // model data file count
gapFillWith: '#000', // What to use to fill the square part after zooming feedShape: { // input shape
targetSize: { fw: 256,
height: fw, fh: 256
width: fh
}, },
targetShape: [1, 3, fh, fw], // Target shape changed its name to be compatible with previous logic fetchShape: [1, 1, 1920, 10], // output shape
// shape: [3, 608, 608], // Preset sensor shape fill: '#fff', // fill color when resize image
mean: [117.001, 114.697, 97.404], // Preset mean needBatch: true, // whether need to complete the shape to 4 dimension
// std: [0.229, 0.224, 0.225] // Preset std inputType: 'image' // whether is image or video
} });
});
const MODEL_CONFIG = {
dir: `/${path}/`, // model URL
main: 'model.json', // main graph
};
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: true,
dataType: 'binary',
options: {
fileCount: 1, // How many model have been cut
getFileName(i) {
return 'chunk_' + i + '.dat';
}
}
}
});
model = await paddle.load();
// run model // load paddlejs model and preheat
let inst = model.execute({ await paddlejs.loadModel();
input: feed
});
// There should be a fetch execution call or a fetch output // run model
let result = await inst.read(); await paddlejs.predict(img, postProcess);
function postProcee(data) {
// data为预测结果
console.log(data);
}
``` ```
...@@ -85,7 +64,6 @@ let result = await inst.read(); ...@@ -85,7 +64,6 @@ let result = await inst.read();
## 运行Paddle.js提供的转换器脚本 ## 运行Paddle.js提供的转换器脚本
模型转换器需要输入一个Paddle格式的model,可以是Paddle Hub中的model,运行转换器将会得到paddle.js的JSON格式model。 模型转换器需要输入一个Paddle格式的model,可以是Paddle Hub中的model,运行转换器将会得到paddle.js的JSON格式model。
[查看转换工具使用方法](./tools/ModelConverter/README_cn.md)
## Web友好的model格式 ## Web友好的model格式
......
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
]
};
<template lang="pug">
div
config
</template>
<script lang="ts">
import Vue from "vue";
import config from "./config.vue";
export default Vue.extend({
data() {
return {
bundler: "Parcel",
};
},
components: {
'config': config
},
methods() {
}
});
</script>
<style lang="less" scoped>
.container {
color: green;
}
.text {
color: red;
}
</style>
<template lang="pug">
div
el-row(type=flex justify="start" align="middle")
el-col(:span="8")
el-row.model-row(align="middle" type="flex")
el-col(:span="6")
div model:
el-col(:span="18")
el-select(v-model="modelSeleted" placeHolder="please selete" @change="changeModel")
el-option(v-for="(modelItem, index) in modelList" :key="index" :label="modelItem.name" :value="modelItem")
el-row.model-row(align="middle" type="flex")
el-col(:span="6")
div run times:
el-col(:span="18")
el-input(v-model="times" placeHolde="请输入运行次数" @change="getTimes")
el-button.model-row(type="primary" @click="run") run
el-col.model-row.model-stage(:span="12")
el-steps(align-center :space="200" :active="stage" finish-status="success")
el-step(title="start")
el-step(title="load model")
el-step(title="preheat")
el-step(title="run model")
el-row.model-table(type=flex justify="space-between" :gutter="30")
el-col(:span="7")
header.model-header general data
el-table.model-row(:data="tableData" max-height="100%" border)
el-table-column(prop="name" label="type")
el-table-column(prop="t" label="name")
el-col(:span="7")
header.model-header op data
el-table.model-row(:data="ops" max-height="100%" border)
el-table-column(prop="name" label="type")
el-table-column(prop="time" label="time")
el-col.model-detail(:span="7")
header.model-header details
//- el-divider(direction="vertical" content-position="left")
el-table.model-row(:data="progress" max-height="1500" border :row-class-name="tableRowClassName")
el-table-column(prop="index" label="index")
el-table-column(prop="name" label="name")
el-table-column(prop="time" label="time")
</template>
<script>
import Vue from 'vue';
import 'regenerator-runtime/runtime'
import 'babel-polyfill'
import Paddle from '../../src/executor/runner';
import Utils from '../../src/utils/utils';
export default Vue.extend({
data() {
return {
modelList: [
{
name: 'mobileNetV2',
fileCount: 4,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 1000, 10, 1],
fill: '#fff',
targetSize: { height: 224, width: 224 },
},
{
name: 'mobileNetV2Opt',
fileCount: 4,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 1000, 10, 1],
fill: '#fff',
targetSize: { height: 224, width: 224 },
},
{
name: 'wine',
fileCount: 3,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 40, 10, 1],
fill: '#fff',
targetSize: { height: 224, width: 224 },
},
{
name: 'gesture_detect',
fileCount: 2,
feedShape: {
fw: 256,
fh: 256
},
fetchShape: [1, 1920, 10 , 1],
fill: '#fff',
targetSize: { height: 256, width: 256 },
},
{
name: 'gesture_rec',
fileCount: 1,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 9, 1, 1],
fill: '#fff',
targetSize: { height: 224, width: 224 },
},
{
name: 'humanseg',
fileCount: 1,
feedShape: {
fw: 192,
fh: 192
},
fetchShape: [1, 2, 192, 192],
fill: '#fff',
targetSize: { height: 224, width: 224 },
}
],
modelSeleted: '',
modelPathPrefix: 'https://paddlejs.cdn.bcebos.com/models/',
modelPath: '',
curModel: null,
progressText: '',
times: null,
stage: null,
loadT: '--',
preheatT: '--',
remainOthersT: '--',
bestT: '--',
progress: [],
ops: [],
paddle: null,
gl: null,
test: null,
opCount: '--'
}
},
computed: {
tableData() {
return [
{name: 'load time', t: this.loadT},
{name: 'preheat time', t: this.preheatT},
{name: 'subsequent average time', t: this.remainOthersT},
{name: 'best time', t: this.bestT},
{name: 'op count', t: this.opCount},
]
}
},
methods: {
tableRowClassName({row}) {
return row.name === 'total' && 'detail-index-row';
},
// arraySpanMethod({row, columnIndex}) {
// return [row.name === 'total' && columnIndex === 0 ? this.opCount: 1, 1];
// },
changeModel(value) {
console.log(value);
this.modelSeleted = value.name;
this.modelPath = this.modelPathPrefix + value.name;
console.log(this.modelPath);
this.curModel = {...value, modelPath: this.modelPath};
},
async loadModel() {
this.stage = 1;
this.progressText = '模型加载中';
const paddle = this.paddle = new Paddle({
...this.curModel,
needPostProcess: false,
needPreheat: false,
fileDownload: false,
});
const start = Date.now();
await paddle.loadModel();
this.loadT = Date.now() - start;
this.stage = 2;
this.gl = paddle.model.graph.inst.gpu.gl;
},
async preheat() {
const start = Date.now();
await this.paddle.preheat();
this.preheatT = Date.now() - start;
this.stage = 3;
},
getTimes(val) {
if (!val || !this.curModel) {
return;
}
},
clear() {
this.loadT = '--' ;
this.preheatT = '--' ;
this.remainOthersT = '--' ;
this.bestT = '--' ;
this.progress = [] ;
this.ops = [] ;
this.opCount = '--';
},
async predict() {
const totalTimes = +this.times;
let curTimes = 1;
let remainWholeT = 0;
let progress = [];
let totaltimeList = [];
let opCount = 0;
const ops = [];
while(curTimes <= totalTimes) {
const start = Date.now();
await this.paddle.runWithFeed(this.paddle.preheatFeed);
const t = Date.now() - start;
remainWholeT += t;
// this.remainOthersT = +(remainWholeT / (curTimes - 2).toFixed(4));
const {queryList} = this.paddle.model.graph;
const quertyResults = this.query(queryList);
if (!opCount) {
opCount = quertyResults.length;
}
progress.push({
index: curTimes,
time: t,
name: 'total'
});
for (let item of quertyResults) {
progress.push({
index: curTimes,
time: item.time,
name: item.name
});
}
totaltimeList.push(t);
ops.push(this.getOpPerf(quertyResults, this.aggregate, {}));
curTimes++;
}
const len = totaltimeList.length;
totaltimeList.sort((a, b) => a - b);
this.bestT = totaltimeList[0];
const opInfos = this.getOpPerf(ops, this.merge);
this.opCount = opCount;
this.remainOthersT = (remainWholeT / this.times).toFixed(4);
this.progress = progress;
this.ops = Object.keys(opInfos).map(opname => {
return {
name: opname,
time: opInfos[opname].time
};
});
this.stage = 4;
},
async run() {
this.clear();
await this.loadModel();
await this.preheat();
await this.predict();
},
query(queryList) {
const result = queryList.map(item => {
const {name, query, count} = item;
const time = Utils.getQueryTime(this.gl, query) / 1000000;
return {name, count, time: +(time.toFixed(4))};
})
return result.filter(item => item.name !== 'feed');
},
aggregate(acc, cur) {
const {name, time, count} = cur;
let now = {...acc};
if (!acc[name]) {
now[name] = {time, count};
return now;
}
const item = now[cur.name];
item.time += time;
item.count++;
return now;
},
getOpPerf(queryList, reduceF, acc) {
let timeRes = acc ? queryList.reduce(reduceF, acc) : queryList.reduce(reduceF);
for(let key of Object.keys(timeRes)) {
const item = timeRes[key];
const {time, count, name} = item;
if (name === 'feed') {
return item;
}
item.time = +(time / count).toFixed(4);
item.count = 1;
}
return timeRes;
},
merge(acc, cur) {
for (let name of Object.keys(cur)) {
const {time, count} = cur[name];
acc[name].time += time;
acc[name].count += count;
}
return acc;
}
}
})
</script>
<style lang="less" scoped>
.model-row {
margin-top: 20px;
}
.model-stage {
margin-top: 50px;
}
.model-header {
text-align: center;
}
.model-table {
margin-top: 50px;
}
</style>
<style>
.el-table .detail-index-row {
background-color: #f0f9eb;
}
</style>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PaddleJS benchmark</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
// index.js
import Vue from 'vue'
import App from './app.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI);
const vm = new Vue({render: h => h(App)})
vm.$mount('#app')
import WarpAffine from './warpAffine';
class DetectProcess {
constructor(result, canvas) {
this.modelResult = result;
this.output_size = 10;
this.anchor_num = 1920;
this.detectResult = new Array(14).fill('').map(() => []);
this.hasHand = 0;
this.originCanvas = canvas;
let ctx = this.originCanvas.getContext('2d');
this.originImageData = ctx.getImageData(0, 0, 256, 256);
}
async outputBox(results) {
let splitT = Date.now();
this.getMaxIndex();
// console.info('getMaxIndex:', Date.now() - splitT);
// debugger;
splitT = Date.now();
if (this.maxIndex === -1) {
this.hasHand = 0;
return false;
}
this.hasHand = 1;
await this.splitAnchors(results);
// console.info('splitAnchors:', Date.now() - splitT);
// debugger;
splitT = Date.now();
this.decodeAnchor();
this.decodeKp(); // 求关键点
this.decodeBox(); // 求手势框
// console.info('求关键点跟手势框', Date.now() - splitT);
// debugger;
return this.box;
}
async outputFeed(paddle) {
let oft = Date.now();
this.decodeTriangle();
// console.info('decodeTriangle', Date.now() - oft);
oft = Date.now();
this.decodeSource();
// console.info('decodeSource', Date.now() - oft);
oft = Date.now();
this.outputResult();
// console.info('outputResult', Date.now() - oft);
oft = Date.now();
this.getaffinetransform(); // 提取仿射变化矩阵
// console.info('getaffinetransform', Date.now() - oft);
oft = Date.now();
await this.warpAffine(); // 对图片进行仿射变换
// console.info('warpAffine', Date.now() - oft);
oft = Date.now();
this.allReshapeToRGB(paddle);
// console.info('allReshapeToRGB', Date.now() - oft);
oft = Date.now();
return this.feed;
}
async splitAnchors(results) {
window.results = results;
let anchors = new Array(this.anchor_num).fill('').map(() => []);
let output_size = this.output_size;
let anchor_num = this.anchor_num;
for (let i = 0; i < anchor_num; i++) {
anchors[i] = [];
let tmp0 = results[i * 4];
let tmp1 = results[i * 4 + 1];
let tmp2 = results[i * 4 + 2];
let tmp3 = results[i * 4 + 3];
anchors[i][0] = tmp0 - tmp2 / 2.0;
anchors[i][1] = tmp1 - tmp3 / 2.0;
anchors[i][2] = tmp0 + tmp2 / 2.0;
anchors[i][3] = tmp1 + tmp3 / 2.0;
if (anchors[i][0] < 0) anchors[i][0] = 0;
if (anchors[i][0] > 1) anchors[i][0] = 1;
if (anchors[i][1] < 0) anchors[i][1] = 0;
if (anchors[i][1] > 1) anchors[i][1] = 1;
if (anchors[i][2] < 0) anchors[i][2] = 0;
if (anchors[i][2] > 1) anchors[i][2] = 1;
if (anchors[i][3] < 0) anchors[i][3] = 0;
if (anchors[i][3] > 1) anchors[i][3] = 1;
}
window.anchors = this.anchors = anchors;
}
getMaxIndex() {
let maxIndex = -1;
let maxConf = -1;
let curConf = -2.0;
let output_size = 10;
for (let i = 0; i < this.anchor_num; i++) {
curConf = sigm(this.modelResult[i * output_size + 1]);
if (curConf > 0.55 && curConf > maxConf) {
maxConf = curConf;
maxIndex = i;
}
}
this.maxIndex = maxIndex;
function sigm(value) {
return 1.0 / (1.0 + Math.exp(0.0 - value));
}
}
decodeAnchor() {
let index = this.maxIndex;
let anchors = this.anchors;
this.pxmin = anchors[index][0];
this.pymin = anchors[index][1];
this.pxmax = anchors[index][2];
this.pymax = anchors[index][3];
}
decodeKp() {
let modelResult = this.modelResult;
let index = this.maxIndex;
let px = (this.pxmin + this.pxmax) / 2;
let py = (this.pymin + this.pymax) / 2;
let pw = this.pxmax - this.pxmin;
let ph = this.pymax - this.pymin;
let prior_var = 0.1
let kp = [[], [], []];
kp[0][0] = (modelResult[index * this.output_size + 6] * pw * prior_var + px) * 256;
kp[0][1] = (modelResult[index * this.output_size + 8] * ph * prior_var + py) * 256;
kp[2][0] = (modelResult[index *this. output_size + 7] * pw * prior_var + px) * 256;
kp[2][1] = (modelResult[index * this.output_size + 9] * ph * prior_var + py) * 256;
this.kp = kp;
}
decodeBox() {
let modelResult = this.modelResult;
let output_size = this.output_size || 10;
let pw = this.pxmax - this.pxmin;
let ph = this.pymax - this.pymin;
let px = this.pxmin + pw / 2;
let py = this.pymin + ph / 2;
let prior_var = 0.1;
let index = this.maxIndex;
let ox = modelResult[output_size * index + 2];
let oy = modelResult[output_size * index + 3];
let ow = modelResult[output_size * index + 4];
let oh = modelResult[output_size * index + 5];
let tx = ox * prior_var * pw + px;
let ty = oy * prior_var * ph + py;
let tw = this.tw = Math.pow(2.71828182, prior_var * ow) * pw;
let th = this.th = Math.pow(2.71828182, prior_var * oh) * ph;
let box = [[], [], [], []];
box[0][0] = parseInt((tx - tw / 2) * 256);
box[0][1] = parseInt((ty - th / 2) * 256);
box[1][0] = parseInt((tx + tw / 2) * 256);
box[1][1] = parseInt((ty - th / 2) * 256);
box[2][0] = parseInt((tx + tw / 2) * 256);
box[2][1] = parseInt((ty + th / 2) * 256);
box[3][0] = parseInt((tx - tw / 2) * 256);
box[3][1] = parseInt((ty + th / 2) * 256);
this.box = box;
}
decodeTriangle() {
let box_enlarge = 1.04;
let side = Math.max(this.tw * 256, this.th * 256) * (box_enlarge);
let dir_v = [[], []];
let kp = this.kp;
let triangle = [[], [], []];
let dir_v_r = [];
dir_v[0] = kp[2][0] - kp[0][0];
dir_v[1] = kp[2][1] - kp[0][1];
let sq = Math.sqrt(Math.pow(dir_v[0], 2) + Math.pow(dir_v[1], 2)) || 1;
dir_v[0] = dir_v[0] / sq;
dir_v[1] = dir_v[1] / sq;
dir_v_r[0] = dir_v[0] * 0 + dir_v[1] * 1;
dir_v_r[1] = dir_v[0] * -1 + dir_v[1] * 0;
triangle[0][0] = kp[2][0];
triangle[0][1] = kp[2][1];
triangle[1][0] = kp[2][0] + dir_v[0] * side;
triangle[1][1] = kp[2][1] + dir_v[1] * side;
triangle[2][0] = kp[2][0] + dir_v_r[0] * side;
triangle[2][1] = kp[2][1] + dir_v_r[1] * side;
this.triangle = triangle;
}
decodeSource() {
let kp = this.kp;
let box_shift = 0.0; // 为什么c语言是乘以0.0
let tmp0 = (kp[0][0] - kp[2][0]) * box_shift;
let tmp1 = (kp[0][1] - kp[2][1]) * box_shift;
let source = [[], [], []];
for (let i = 0; i < 3; i++) {
source[i][0] = this.triangle[i][0] - tmp0;
source[i][1] = this.triangle[i][1] - tmp1;
}
this.source = source;
}
outputResult(){
// let detectResult = [[], [], [], []];
for (let i = 0; i < 4; i++) {
this.detectResult[i][0] = this.box[i][0];
this.detectResult[i][1] = this.box[i][1];
}
// 王超的检测模型只保留0、2两个点。
this.detectResult[4][0] = this.kp[0][0];
this.detectResult[4][1] = this.kp[0][1];
this.detectResult[6][0] = this.kp[2][0];
this.detectResult[6][1] = this.kp[2][1];
for (let i = 0; i < 3; i++) {
this.detectResult[i + 11][0] = this.source[i][0];
this.detectResult[i + 11][1] = this.source[i][1];
}
// let point1 = document.getElementById('point1');
// let point2 = document.getElementById('point2');
// let point3 = document.getElementById('point3');
// [point1, point2, point3].forEach((item, index) => {
// item.style.top = this.detectResult[11 + index][1] + 'px';
// item.style.left = this.detectResult[11 + index][0] + 'px';
// })
}
getaffinetransform() {
/*
* 图像上的原始坐标点。需要对所有坐标进行归一化,_x = (x - 128) / 128, _y = (128 - y) / 128
* 坐标矩阵
* x1 x2 x3
* y1 y2 y3
* z1 z2 z3
*/
let originPoints = [].concat(this.detectResult[11][0] / 128 -1)
.concat(this.detectResult[12][0] / 128 -1)
.concat(this.detectResult[13][0] / 128 -1)
.concat(1 - this.detectResult[11][1] / 128)
.concat(1 - this.detectResult[12][1] / 128)
.concat(1 - this.detectResult[13][1] / 128)
.concat([1, 1, 1]);
// originPoints = [0, 0, -1, .1, 1.1, 0.1, 1, 1, 1];
let matrixA = new Matrix(3, 3, originPoints);
// 转化后的点[128, 128, 0, 128, 0, 128] [0, 0, -1, 0, 1, 0]
let matrixB = new Matrix(2, 3, [0, 0, -1, 0, -1, 0]);
// M * A = B => M = B * A逆
let _matrixA = inverseMatrix(matrixA.data);
_matrixA = new Matrix(3, 3, _matrixA);
let M = Matrix_Product(matrixB, _matrixA);
this.mtr = M;
}
async warpAffine() {
let wat = Date.now();
let ctx = this.originCanvas.getContext('2d');
this.originImageData = ctx.getImageData(0, 0, 256, 256);
// console.info('=====before getImageData:', Date.now() - wat);
wat = Date.now();
let imageDataArr = await WarpAffine.main({
imageData: this.originImageData,
mtr: this.mtr.data,
input: {
width: 256,
height: 256
},
output: {
width: 224,
height: 224
}
});
// console.info('=====warpAffine main:', Date.now() - wat);
wat = Date.now();
this.imageData = new ImageData(Uint8ClampedArray.from(imageDataArr), 224, 224)
// console.info('=====after getImageData:', Date.now() - wat);
}
allReshapeToRGB(paddle) {
let data = paddle.io.allReshapeToRGB(this.imageData, {
gapFillWith: "#000",
mean: [0, 0, 0],
scale: 224,
std: [1, 1, 1],
targetShape: [1, 3, 224, 224],
targetSize: {
width: 224,
height: 224
},
normalizeType: 1
})
this.feed = [{
data: data,
shape: [1, 3, 224, 224],
name: 'image',
canvas: this.originImageData
}];
}
}
class Matrix {
constructor(row, col, arr = []){
this.row = row; //行
this.col = col; //列
if (arr[0] && arr[0] instanceof Array) {
this.data = arr;
}
else {
this.data = [];
let _arr = [].concat(arr);
let Matrix = new Array(row); //创建row个元素的空数组
for(let i = 0; i < row; i++){ //对第一层数组遍历
Matrix[i] = new Array(col).fill(''); //每一行创建col列的空数组
Matrix[i].forEach((item, index, cur) => {
cur[index] = _arr.shift() || 0;
});
}
this.data = Matrix; //将矩阵保存到this.data上
}
}
}
function Matrix_Product(A, B){
let tempMatrix = new Matrix(A.row, B.col);
if(A.col == B.row){
for(let i = 0;i < A.row;i++){
for(let j = 0; j < B.col; j++){
tempMatrix.data[i][j] = 0;
for(let n = 0; n < A.col; n++){
tempMatrix.data[i][j] += A.data[i][n] * B.data[n][j];
}
tempMatrix.data[i][j] = tempMatrix.data[i][j].toFixed(5);
}
}
return tempMatrix;
}
}
// 求行列式
function determinant(matrix) {
var order = matrix.length,
cofactor,
result = 0;
if (order == 1) {
return matrix[0][0];
}
for (var i = 0; i < order; i++) {
cofactor = [];
for (var j = 0; j < order-1; j++) {
cofactor[j] = [];
for (var k = 0; k < order - 1; k++) {
cofactor[j][k] = matrix[j+1][ k<i ? k : k+1 ];
}
}
result += matrix[0][i] * Math.pow(-1, i) * determinant(cofactor);
}
return result;
}
// 矩阵数乘
function scalarMultiply(num, matrix) {
var row = matrix.length,
col = matrix[0].length,
result = [];
for (var i = 0; i < row; i++) {
result[i] = [];
for (var j = 0; j < col; j++) {
result[i][j] = num * matrix[i][j];
}
}
return result;
}
// 矩阵转置
function transpose(matrix) {
var row = matrix.length,
col = matrix[0].length,
result = [];
for (var i = 0; i < col; i++) {
result[i] = [];
for (var j = 0; j < row; j++) {
result[i][j] = matrix[j][i];
}
}
return result;
}
// 矢量内积
function dotProduct(vector1, vector2) {
var n = Math.min(vector1.length, vector2.length),
result = 0;
for (var i = 0; i < n; i++) {
result += vector1[i] * vector2[i];
}
return result;
}
// 矩阵乘法
function multiply(matrix1, matrix2) {
if (matrix1[0].length !== matrix2.length) {
return false;
}
var row = matrix1.length,
col = matrix2[0].length,
matrix2_t = transpose(matrix2);
result = [];
for (var i = 0; i < row; i++) {
result[i] = [];
for (var j = 0; j < col; j++) {
result[i][j] = dotProduct(matrix1[i], matrix2_t[j]);
}
}
return result;
}
// 求逆矩阵
function inverseMatrix(matrix) {
if (determinant(matrix) === 0) {
return false;
}
// 求代数余子式
function cofactor(matrix, row, col) {
var order = matrix.length,
new_matrix = [],
_row, _col;
for (var i = 0; i < order-1; i++) {
new_matrix[i] = [];
_row = i < row ? i : i + 1;
for (var j = 0; j < order-1; j++) {
_col = j < col ? j : j + 1;
new_matrix[i][j] = matrix[_row][_col];
}
}
return Math.pow(-1, row + col) * determinant(new_matrix);
}
var order = matrix.length,
adjoint = [];
for (var i = 0; i < order; i++) {
adjoint[i] = [];
for (var j = 0; j < order; j++) {
adjoint[i][j] = cofactor(matrix, j, i);
}
}
return scalarMultiply(1/determinant(matrix), adjoint);
}
export default DetectProcess;
export default class LMProcess {
constructor(result) {
/*
* result[0] :是否为手
* result[1:43]: 手的21个关键点
* result[43:49]: 几中手势分类,包含但不限于石头剪刀布,为了提升准确度
* this.kp decode result得出的21个手指关键点,this.kp[8]为食指
* this.conf 是否为手,越大,越可能是手
*/
this.input_n = 1;
this.input_h = 224;
this.input_w = 224;
this.input_c = 3;
this.class_dim = 6;
this.points_num = 1;
this.result = result;
}
sigm(value) {
return 1.0 / (1.0 + Math.exp(0.0 - value));
}
decodeConf() {
this.conf = this.sigm(this.result[0]);
}
decodeKp() {
// 21个关键点,下标1开始
let offset = 1;
let result = this.result;
this.kp = [];
for (let i = 0; i < this.points_num; i++) {
let arr = [];
arr.push((result[offset + i * 2] + 0.5) * this.input_h);
arr.push((result[offset + i * 2 + 1] + 0.5) * this.input_h);
this.kp.push(arr);
}
this.forefinger = this.kp[0];
}
softMax() {
let max = 0;
let sum = 0;
let offset = 2;
let class_dim = this.class_dim = 7;
let result = this.result;
let output_softmax = new Array(7).fill(null);
for (let i = 0; i < class_dim; i++) {
if (max < result[i + offset])
max = result[i + offset];
}
for (let i = 0; i < class_dim; i++) {
output_softmax[i] = Math.exp(result[i + offset] - max);
sum += output_softmax[i];
}
for (let i = 0; i < class_dim; i++) {
output_softmax[i] /= sum;
}
this.output_softmax = output_softmax;
}
output() {
this.decodeKp();
this.softMax();
let label_index = 0;
let max_pro = this.output_softmax[0];
for (let i = 1; i < this.class_dim; i++) {
if (max_pro < this.output_softmax[i]) {
label_index = i;
max_pro = this.output_softmax[i];
}
}
// 最后一位:有无手
if (label_index != 0 && label_index != this.class_dim - 1 && max_pro > 0.95) {
let ges = ['其他', '布', '剪刀', '石头', '1', 'ok'];
this.type = ges[label_index];
} else {
this.type = 'other';
}
}
}
此差异已折叠。
// import 'babel-polyfill';
// import Paddlejs from '../../src/executor/runner';
import {runner as Paddlejs} from 'paddlejs';
import Camera from '../../src/executor/camera';
import DetectProcess from './DetectProcess';
import LMProcess from './LMProcess';
import WarpAffine from './warpAffine';
let image = '';
let camera = null;
let anchorResults = null;
const TYPE = 'video';
let rect = null;
let paddlejs = null;
let paddlejs2 = null;
const rectDom = document.getElementById('rect');
let video = document.getElementById('video');
const videoSelect = document.getElementById('videoSelect');
let upload = document.getElementById("uploadImg");
let loadingDom = document.getElementById("isLoading");
async function init() {
paddlejs = new Paddlejs({
modelPath: 'https://paddlejs.cdn.bcebos.com/models/gesture/gesture_detect',
fileCount: 2,
feedShape: {
fw: 256,
fh: 256
},
fetchShape: [1, 1, 1920, 10],
needBatch: true,
fill: '#fff',
targetSize: { height: 256, width: 256 },
needPostProcess: false
});
const waitPaddle1 = paddlejs.loadModel();
paddlejs2 = new Paddlejs({
modelPath: 'https://paddlejs.cdn.bcebos.com/models/gesture/gesture_rec',
fileCount: 1,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 1, 1, 9],
fill: '#fff',
targetSize: { height: 224, width: 224 },
needBatch: true,
needPostProcess: false
})
const waitPaddle2 = paddlejs2.loadModel();
WarpAffine.init({
width: 224,
height: 224
});
const waitAnchor = fetch(require('./anchor_small.txt')).then(async (res) => {
anchorResults = await res.text();
anchorResults = anchorResults.replace(/\s+/g, ',').split(',').map(item => +item);
});
Promise.all([waitPaddle1, waitPaddle2, waitAnchor]).then(() => {
loadingDom.classList.add('hidden');
})
}
init();
const imageDom = document.getElementById('image');
const content = document.getElementById('txt');
const videoToolDom = document.getElementById('video-tool');
(function main() {
// 采用图片模式
if (TYPE === 'image') {
video.remove();
videoToolDom.remove();
loadingDom.remove();
video = null;
}
// 采用视频模式
else if (TYPE === 'video') {
imageDom.remove();
upload.remove();
upload = null;
}
})()
if (video) {
camera = new Camera({
// 用来显示摄像头图像的dom
videoDom: video,
});
/* 获取摄像头并开始视频流 */
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();
}
});
}
/* 循环跑模型 */
let loopFlag = true;
async function loop() {
let input = camera.curVideo;
if (loopFlag) {
// setTimeout(async () => {
await run(input);
// }, 100);
}
}
const startBtn = document.getElementById('start');
const endBtn = document.getElementById('end');
/* 视频开始播放时,开始模型预热 */
video && video.addEventListener('play', async function (e) {
startBtn.disabled = false;
startBtn.innerText = '开始测试';
});
/* 点击开始按钮,开始正式跑模型 */
document.getElementById('tool').addEventListener('click', function(e) {
if (e.target.id === 'start') {
loopFlag = true;
startBtn.disabled = true;
endBtn.disabled = false;
loop();
}
if (e.target.id === 'end') {
loopFlag = false;
endBtn.disabled = true;
startBtn.disabled = false;
}
})
/* 模型执行函数 */
async function run(input) {
rect = video.getBoundingClientRect();
await paddlejs.predict(input, postProcess);
}
function selectImage(file) {
if (!file.files || !file.files[0]) {
return;
}
content.innerText = "识别中。。。";
let reader = new FileReader();
reader.onload = function (evt) {
let img = document.getElementById('image');
img.src = evt.target.result;
img.onload = async function() {
rect = img.getBoundingClientRect();
paddlejs.predict(img, postProcess);
};
image = evt.target.result;
}
reader.readAsDataURL(file.files[0]);
}
// 第一个模型的后处理
async function postProcess(data) {
let post = new DetectProcess(data, paddlejs.io.fromPixels2DContext.canvas);
let box = await post.outputBox(anchorResults);
if (!box) {
console.info('box为空')
rectDom.style.border = 'none';
content.innerText = "识别不到手";
TYPE === 'video' && loop();
return;
}
content.innerText = "...";
calculateBox();
// TYPE === 'video' && loop();
let feed = await post.outputFeed(paddlejs);
// 第一个模型的后处理可以直接拿到feed
paddlejs2.runWithFeed(feed, function (data) {
// 开始第二个模型的后处理
TYPE === 'video' && loop();
let lmProcess = new LMProcess(data);
lmProcess.output();
content.innerText = "您出的是" + lmProcess.type;
});
function calculateBox() {
let offsetX = 0;
let offsetY = 0;
if (rect.width < rect.height) {
offsetX = rect.height - rect.width;
}
if (rect.width > rect.height) {
offsetY = rect.width - rect.height;
}
let height = Math.abs(box[3][1] - box[0][1]);
let width = Math.abs(box[1][0] - box[0][0]);
let widthRatio = (rect.width + offsetX) / 256;
let heightRatio = (rect.height + offsetY) / 256;
let transformStr = `translate(${box[0][0] * widthRatio - offsetX / 2}px,
${box[0][1] * heightRatio - offsetY / 2}px) scale(${widthRatio * width / 100},
${heightRatio * height / 100})
`;
let cssObj = rectDom.style;
cssObj.transform = transformStr;
cssObj.border = "2px solid red";
let rectStyle = {
width: width + 'px',
height: height + 'px',
left: box[0][0] + 'px',
top: box[0][1] + 'px',
}
}
}
if (upload) {
upload.onchange = async function () {
selectImage(this);
};
}
<!DOCTYPE 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>
body {
margin: 0 auto;
position: fixed;
width: 100%;
height: 100%;
}
#rect {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
transform-origin: 0 0;
/* transition: transform .1s linear; */
}
.wrapper {
position: relative;
width: 100%;
height: 100%;
margin: 0 auto;
}
video {
width: 100%;
}
.point {
position: absolute;
width: 5px;
height: 5px;
}
#camera-btn {
display: none;
}
#videoSelect::before {
content: "请选择设备摄像头:";
}
#video-tool {
display: flex;
justify-content: space-around;
}
#isLoading {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .5);
}
#isLoading .loading-text {
color: #fff;
font-size: 24px;
}
.center {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.hidden {
display: none;
}
.show {
display: block;
}
.flex {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: -moz-box;
display: -moz-flex;
display: flex;
}
.flex-space-around {
-webkit-box-pack: justify;
-moz-box-pack: justify;
-ms-box-pack: justify;
-webkit-justify-content: space-around;
-moz-justify-content: space-around;
justify-content: space-around;
}
</style>
</head>
<body>
<div class="wrapper center">
<img id="image" src="https://m.baidu.com/se/static/img/iphone/logo.png" width="256">
<video id="video"></video>
<div id="rect"></div>
<div id="tool">
<input type="file" id="uploadImg">
<div id="video-tool" class="flex">
<div>设备摄像头:<select id="videoSelect"></select></div>
<button id="start" type="btn" disabled>模型loading中</button>
<button id="end" type="btn" disabled>停止测试</button>
</div>
</div>
<div id="txt">...</div>
</div>
<div id="isLoading">
<p class="loading-text center">loading中……</p>
</div>
<!-- <div style="position:relative;display:inline-block;;">
<canvas id="canvas-square" style="display:block;border: 1px solid black;"></canvas>
<div id="rect2" style="position:absolute;"></div>
<div id="point1" class="point" style="background:#ff0000;"></div>
<div id="point2" class="point" style="background:#00ff00;"></div>
<div id="point3" class="point" style="background:#0000ff;"></div>
</div> -->
<!-- <canvas id="canvas-warpAffine" style="display:inline-block;"></canvas> -->
<script src="index.es6"></script>
</body>
</html>
\ No newline at end of file
const VSHADER_SOURCE = `attribute vec4 a_Position;
attribute vec2 a_TexCoord;
uniform mat4 translation;
varying vec2 v_TexCoord;
void main() {
gl_Position = translation * a_Position;
v_TexCoord = a_TexCoord;
}`;
const FSHADER_SOURCE = `precision mediump float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}`;
let imgData = null;
let mtrData = null;
let gl = null;
function init(output) {
var canvas = document.createElement('canvas');
canvas.width = output.width;
canvas.height = output.height;
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
// 初始化之前先加载图片
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
return;
}
}
function main(opt) {
const {
imageData,
mtr,
output,
input
} = opt;
mtrData = mtr;
imgData = imageData;
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const n = initVertexBuffers(gl);
initTextures(gl, n, 0);
let outputData = new Uint8Array(output.width * output.height * 4);
gl.readPixels(0, 0, output.width, output.height, gl.RGBA, gl.UNSIGNED_BYTE, outputData);
return outputData;
}
function initVertexBuffers(gl) {
// 顶点坐标和纹理图像坐标
const vertices = new Float32Array([
// webgl坐标,纹理坐标
-1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, 1.0, 0.0, 1.0, 1.0,
1.0, -1.0, 0.0, 1.0, 0.0
]);
const FSIZE = vertices.BYTES_PER_ELEMENT;
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const aPosition = gl.getAttribLocation(gl.program, 'a_Position');
const aTexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
var xformMatrix = new Float32Array([
mtrData[0][0], mtrData[1][0], 0.0, 0.0,
mtrData[0][1], mtrData[1][1], 0.0, 0.0,
0.0, 0.0, 0.0, 1.0,
mtrData[0][2], mtrData[1][2], 0.0, 1.0
]);
var translation = gl.getUniformLocation(gl.program, 'translation');
gl.uniformMatrix4fv(translation, false, xformMatrix);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, FSIZE * 5, 0);
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aTexCoord, 2, gl.FLOAT, false, FSIZE * 5, FSIZE * 3);
gl.enableVertexAttribArray(aTexCoord);
return 4;
}
function initTextures(gl, n, index) {
// 创建纹理对象
const texture = gl.createTexture();
const uSampler = gl.getUniformLocation(gl.program, 'u_Sampler');
// WebGL纹理坐标中的纵轴方向和PNG,JPG等图片容器的Y轴方向是反的,所以先反转Y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
// 激活纹理单元,开启index号纹理单元
gl.activeTexture(gl[`TEXTURE${index}`]);
// 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 配置纹理对象的参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 将纹理图像分配给纹理对象
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgData);
// 将纹理单元编号传给着色器
gl.uniform1i(uSampler, index);
// 绘制
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
return;
}
function initShaders(gl, vshader, fshader) {
var vertexShader = createShader(gl, vshader, gl.VERTEX_SHADER);
var fragmentShader = createShader(gl, fshader, gl.FRAGMENT_SHADER);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
if (!program) {
console.log('无法创建程序对象');
return false;
}
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;
return true;
}
function createShader(gl, sourceCode, type) {
// Compiles either a shader of type gl.VERTEX_SHADER or gl.FRAGMENT_SHADER
var shader = gl.createShader(type);
gl.shaderSource(shader, sourceCode);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
var info = gl.getShaderInfoLog(shader);
throw 'Could not compile WebGL program. \n\n' + info;
}
return shader;
}
export default {
main,
init
};
.wrapper {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin-bottom: 10px;
width: 100%;
}
#video {
width: 45%;
min-width: 310px;
border: 2px solid #aaa;
transform: rotateY(180deg);
}
#myCanvas2 {
width: 45%;
min-width: 310px;
border: 2px solid #aaa;
transform: rotateY(180deg);
}
import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed';
import Utils from '../../src/utils/utils';
import Camera from '../../src/executor/camera';
import Runner from '../../src/executor/runner';
import cv from '../../opencv.js';
function thresholdMask(img, threshBg, threshFg) {
for (let i = 0; i < img.data.length / 4; i++) {
let tmp = (img.data[i * 4 + 3] - threshBg * 255.0) / (threshFg - threshBg);
if (tmp < 0) {
img.data[i * 4 + 3] = 0;
}
else if (tmp > 255) {
img.data[i * 4 + 3] = 255;
}
else {
img.data[i * 4 + 3] = tmp;
}
}
}
if (location.protocol === 'http:') {
location.href = location.href.replace('http://', 'https://');
}
/**
* @file model demo 入口文件
* @author zhuxingyu01@baidu.com
*
*/
// 模型feed数据
const feedShape = {
'humanseg': {
fw: 192,
fh: 192
}
};
// 模型fetch数据
const fetchShape = {
'humanseg': [1, 2, 192, 192]
};
const modelType = 'humanseg';
const {fw, fh} = feedShape[modelType];
const outputShape = fetchShape[modelType];
// 统计参数
let loaded = false;
let model = {};
window.statistic = [];
var video = document.getElementById('video');
const videoSelect = document.getElementById('videoSelect');
let camera = new Camera({
// 用来显示摄像头图像的dom
videoDom: video,
constraints: {
video: {
width: {min: 200, ideal: 480, max: 1080},
height: {min: 300, ideal: 720, max: 1620}
}
}
});
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();
}
});
let loopFlag = true;
async function loop() {
let input = camera.curVideo;
if (loopFlag) {
await run(input);
await loop();
}
}
let startBtn = document.getElementById('start');
let endBtn = document.getElementById('end');
video.addEventListener('play', async function (e) {
await run();
startBtn.disabled = false;
startBtn.innerText = '开始测试';
})
// document.addEventListener("DOMContentLoaded", async function () {
// // await run();
// // startBtn.disabled = false;
// });
document.getElementById('tool').addEventListener('click', function(e) {
if (e.target.id === 'start') {
loopFlag = true;
startBtn.disabled = true;
endBtn.disabled = false;
loop();
}
if (e.target.id === 'end') {
loopFlag = false;
endBtn.disabled = true;
startBtn.disabled = false;
}
})
async function run(input) {
let start = Date.now();
const io = new IO();
let feed = input
? io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
scale: 192, // 缩放尺寸
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
mean: [122.675, 116.669, 104.008],
std: [1.0, 1.0, 1.0],
bgr: true
}
})
: [{
data: new Float32Array(3 * fh * fw),
name: 'image',
shape: [1, 3, fh, fw]
}];
const path = 'https://paddlejs.cdn.bcebos.com/models/humanseg';
if (!loaded) {
const MODEL_CONFIG = {
dir: `${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: true,
dataType: 'binary',
options: {
fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_' + i + '.dat';
}
},
feed
}
});
model = await paddle.load();
}
let inst = model.execute({
input: feed
});
if (!input) {
return;
}
let result = await inst.read();
let N = outputShape[0];
let C = outputShape[1];
let H = outputShape[2];
let W = outputShape[3];
let nhwcShape = [N, H, W, C];
// console.dir(result);
let nchwData = Utils.nhwc2nchw(result, nhwcShape);
// Utils.stridePrint(nchwData);
// Utils.continuousPrint(nchwData);
let myCanvas = document.getElementById('myCanvas');
let img = document.getElementById('video');
myCanvas.width = 192;
myCanvas.height = 192;
let ctx = myCanvas.getContext("2d");
ctx.drawImage(img, 0, 0, 192, 192);
let imageData = ctx.getImageData(0,0,192,192);
let myCanvas2 = document.getElementById('myCanvas2');
myCanvas2.width = img.videoWidth;
myCanvas2.height = img.videoHeight;
let ctx2 = myCanvas2.getContext("2d");
var myImageData = ctx2.createImageData(192, 192);
for (let i = 0; i < 36864; i++) {
myImageData.data[i * 4] = (imageData.data[i * 4] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 1] = (imageData.data[i * 4 + 1] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 2] = (imageData.data[i * 4 + 2] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 3] = nchwData[36864 + i] * 255;
}
ctx.putImageData(myImageData, 0, 0);
let logit = cv.imread(myCanvas);
let dst = new cv.Mat();
let ksize = new cv.Size(5, 5);
let anchor = new cv.Point(-1, -1);
cv.blur(logit, dst, ksize, anchor, cv.BORDER_DEFAULT);
thresholdMask(dst, 0.2, 0.8);
for (let i = 0; i < 36864; i++) {
myImageData.data[i * 4 + 3] = dst.data[i * 4 + 3];
}
ctx.putImageData(myImageData, 0, 0);
ctx2.drawImage(myCanvas, 0, 0, img.videoWidth, img.videoHeight);
let temp = ctx2.getImageData(0, 0, img.videoWidth, img.videoHeight);
myCanvas.width = img.videoWidth;
myCanvas.height = img.videoHeight;
ctx.drawImage(img, 0, 0);
let origin = ctx.getImageData(0, 0, img.videoWidth, img.videoHeight);
for (let i = 0; i < img.videoHeight * img.videoWidth; i++) {
temp.data[i * 4] = origin.data[i * 4];
temp.data[i * 4 + 1] = origin.data[i * 4 + 1];
temp.data[i * 4 + 2] = origin.data[i * 4 + 2];
}
ctx.clearRect(0, 0, img.videoWidth, img.videoHeight);
ctx2.clearRect(0, 0, img.videoWidth, img.videoHeight);
ctx2.putImageData(temp, 0, 0);
};
<!DOCTYPE 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">
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="wrapper">
<video id="video"></video>
<canvas id="myCanvas2"></canvas>
</div>
<div id="tool">
<select id="videoSelect"></select>
<button id="start" type="btn" disabled>模型loading中</button>
<button id="end" type="btn" disabled>停止测试</button>
</div>
<canvas style="display: none;" id="myCanvas"></canvas>
<script src="index.es6"></script>
</body>
</html>
import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed';
import Utils from '../../src/utils/utils';
import cv from '../../opencv.js';
const fileDownload = require('js-file-download');
/**
* @file model demo 入口文件
* @author chenhaoze@baidu.com
*
*/
// 模型feed数据
const feedShape = {
'humanseg': {
fw: 192,
fh: 192
}
};
// 模型fetch数据
const fetchShape = {
'humanseg': [1, 2, 192, 192]
};
const modelType = 'humanseg';
const {fw, fh} = feedShape[modelType];
const outputShape = fetchShape[modelType];
let canvas1;
let canvas2;
let ctx;
let ctx2;
// 统计参数
let loaded = false;
let model = {};
window.statistic = [];
async function run(input) {
const io = new IO();
let feed = io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
scale: 192, // 缩放尺寸
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
mean: [122.675, 116.669, 104.008],
std: [1.0, 1.0, 1.0],
bgr: true
}
});
const path = 'https://paddlejs.cdn.bcebos.com/models/humanseg';
if (!loaded) {
const MODEL_CONFIG = {
dir: `${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: true,
dataType: 'binary',
options: {
fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_' + i + '.dat';
}
},
feed
}
});
model = await paddle.load();
}
let inst = model.execute({
input: feed
});
let result = await inst.read();
let N = outputShape[0];
let C = outputShape[1];
let H = outputShape[2];
let W = outputShape[3];
let nhwcShape = [N, H, W, C];
// console.dir(result);
let nchwData = Utils.nhwc2nchw(result, nhwcShape);
// Utils.stridePrint(nchwData);
// Utils.continuousPrint(nchwData);
// let [w1, h1, width, height] = calSize(img);
let img = document.getElementById('image');
canvas1.width = 192;
canvas1.height = 192;
ctx.drawImage(img, 0, 0, 192, 192);
let imageData = ctx.getImageData(0, 0, 192, 192);
canvas2.width = img.naturalWidth;
canvas2.height = img.naturalHeight;
var myImageData = ctx2.createImageData(192, 192);
for (let i = 0; i < 36864; i++) {
myImageData.data[i * 4] = (imageData.data[i * 4] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 1] = (imageData.data[i * 4 + 1] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 2] = (imageData.data[i * 4 + 2] - 255) * nchwData[36864 + i] + 255;
myImageData.data[i * 4 + 3] = nchwData[36864 + i] * 255;
}
ctx.putImageData(myImageData, 0, 0);
let logit = cv.imread(myCanvas);
let dst = new cv.Mat();
let ksize = new cv.Size(5, 5);
let anchor = new cv.Point(-1, -1);
cv.blur(logit, dst, ksize, anchor, cv.BORDER_DEFAULT);
thresholdMask(dst, 0.4, 0.8);
for (let i = 0; i < 36864; i++) {
myImageData.data[i * 4 + 3] = dst.data[i * 4 + 3];
}
ctx.putImageData(myImageData, 0, 0);
ctx2.drawImage(myCanvas, 0, 0, img.naturalWidth, img.naturalHeight);
let temp = ctx2.getImageData(0, 0, img.naturalWidth, img.naturalHeight);
myCanvas.width = img.naturalWidth;
myCanvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
let origin = ctx.getImageData(0, 0, img.naturalWidth, img.naturalHeight);
for (let i = 0; i < img.naturalHeight * img.naturalWidth; i++) {
temp.data[i * 4] = origin.data[i * 4];
temp.data[i * 4 + 1] = origin.data[i * 4 + 1];
temp.data[i * 4 + 2] = origin.data[i * 4 + 2];
}
ctx.clearRect(0, 0, img.naturalWidth, img.naturalHeight);
ctx2.putImageData(temp, 0, 0);
}
function thresholdMask(img, threshBg, threshFg) {
for (let i = 0; i < img.data.length; i++) {
let tmp = (img.data[i] - threshBg * 255.0) / (threshFg - threshBg);
if (tmp < 0) {
img.data[i] = 0;
}
else if (tmp > 255) {
img.data[i] = 255;
}
else {
img.data[i] = tmp;
}
}
}
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 () {
run(img);
};
image = evt.target.result;
};
reader.readAsDataURL(file.files[0]);
}
// selectImage
document.getElementById('uploadImg').onchange = function () {
selectImage(this);
};
window.onload = function () {
canvas1 = document.getElementById('myCanvas');
ctx = canvas1.getContext('2d');
canvas2 = document.getElementById('myCanvas2');
ctx2 = canvas2.getContext('2d');
};
\ No newline at end of file
...@@ -4,31 +4,14 @@ ...@@ -4,31 +4,14 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>paddle web demo</title> <title>paddle web demo</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <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 #f71111;
box-sizing: border-box;
}
</style>
</head> </head>
<body> <body>
<div class="image-wrap"> <img id="image" src="https://m.baidu.com/se/static/img/iphone/logo.png" style="max-width: 100%;"><canvas id="myCanvas2"></canvas>
<img id="mobilenet"> <input type="file" id="uploadImg">
</div>
<p>原图片</p>
<div class="image-wrap">
<img id="image" src="pic.png">
<div id="myDiv"></div>
</div>
<p>画布</p> <p>画布</p>
<canvas id="myCanvas"></canvas> <canvas id="myCanvas"></canvas>
<input type="file" id="uploadImg">
<div id="txt"></div> <div id="txt"></div>
<script src="index.es6"></script> <script src="index.es6"></script>
</body> </body>
</html> </html>
import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed';
/**
* @file model demo mnist 入口文件
* @author wangqun@baidu.com
*
*/
const pic = document.getElementById('pic');
const io = new IO();
let model = {};
async function run() {
let feed = io.process({
input: pic,
params: {
targetShape: [1, 3, 320, 320], // 目标形状 为了兼容之前的逻辑所以改个名
scale: 256, // 缩放尺寸
width: 224, height: 224, // 压缩宽高
shape: [3, 224, 224], // 预设tensor形状
mean: [0.485, 0.456, 0.406], // 预设期望
std: [0.229, 0.224, 0.225] // 预设方差
}});
console.dir(['feed', feed]);
const path = 'model/mnist';
const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: false,
dataType: 'json'
}
});
model = await paddle.load();
let inst = model.execute({
input: feed
});
// 其实这里应该有个fetch的执行调用或者fetch的输出
let result = await inst.read();
// let inst = model.execute({input: cat});
// let res = inst.read();
console.dir(['result', result]);
// var fileDownload = require('js-file-download');
// fileDownload(res, 'result.csv');
}
run();
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>paddle web demo</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>
<div>
<img id="pic" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+vTPDHwP8TeJ9DtdXiuLCzt7kbo0uWcOU7NgKRgjkc81i+O/hvrPgW8xco1zp7ELHfIm1HYqCRjJIPUc9cHFcbSgEnABJ9BXaafH8Rrrw3NpdjBrkmjohLQLE/l7c5OOPUHgV6Fcw3um/sxXNt4hZo7qW5X7FDdLtlRfOU7QG5zgSH/dPpXhFel/Bzxj4a8H6vfzeILZy86ILe6WLzPI27i3HUZ+XkA9PQ16Pc/Hfw7pM91LaXusa20wDRxSQRQww9eAdob35DfWuNg+Ny67Dfab430SDUNLuQxjW2UK8BwcAZPPOPmyCOvPSvH6KKKK//9k=" >
</div>
<script src="index.es6"></script>
</body>
</html>
...@@ -51,11 +51,11 @@ async function run(input) { ...@@ -51,11 +51,11 @@ async function run(input) {
} }
}); });
const path = 'model/mobileNet'; const path = 'https://paddlejs.cdn.bcebos.com/models/mobileNetV2';
if (!loaded) { if (!loaded) {
const MODEL_CONFIG = { const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹 dir: `${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件 main: 'model.json', // 主文件
}; };
loaded = true; loaded = true;
...@@ -86,10 +86,8 @@ async function run(input) { ...@@ -86,10 +86,8 @@ async function run(input) {
let C = outputShape[1]; let C = outputShape[1];
let H = outputShape[2]; let H = outputShape[2];
let W = outputShape[3]; let W = outputShape[3];
console.log(outputShape);
let nhwcShape = [N, H, W, C]; let nhwcShape = [N, H, W, C];
console.log(nhwcShape); console.log(nhwcShape);
console.log(result.length);
let nchwData = Utils.nhwc2nchw(result, nhwcShape); let nchwData = Utils.nhwc2nchw(result, nhwcShape);
Utils.stridePrint(nchwData); Utils.stridePrint(nchwData);
......
import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed';
import Utils from '../../src/utils/utils';
// 获取map表
import Map from '../../test/data/map';
/**
* @file model demo 入口文件
* @author wangqun@baidu.com
*
*/
// 模型feed数据
const feedShape = {
'mobilenetv2': {
fw: 224,
fh: 224
}
};
// 模型fetch数据
const fetchShape = {
'mobilenetv2': [1, 1000, 1, 1]
};
const modelType = 'mobilenetv2';
const {fw, fh} = feedShape[modelType];
const outputShape = fetchShape[modelType];
// 统计参数
let loaded = false;
let model = {};
window.statistic = [];
async function run(input) {
// const input = document.getElementById('mobilenet');
const io = new IO();
let feed = io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
targetSize: {
height: fh,
width: fw
},
scale: 256, // 缩放尺寸
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
mean: [0.485, 0.456, 0.406],
std: [0.229, 0.224, 0.225]
}
});
const path = 'https://paddlejs.cdn.bcebos.com/models/mobileNetV2Opt';
if (!loaded) {
const MODEL_CONFIG = {
dir: `${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: true,
dataType: 'binary',
options: {
fileCount: 4, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_' + i + '.dat';
}
},
feed
}
});
model = await paddle.load();
}
let inst = model.execute({
input: feed
});
let result = await inst.read();
let N = outputShape[0];
let C = outputShape[1];
let H = outputShape[2];
let W = outputShape[3];
let nhwcShape = [N, H, W, C];
console.log(nhwcShape);
let nchwData = Utils.nhwc2nchw(result, nhwcShape);
Utils.stridePrint(nchwData);
Utils.continuousPrint(nchwData);
// for test
// fileDownload(nchwData, "paddlejs-0.txt");
let maxItem = Utils.getMaxItem(nchwData);
console.log(maxItem);
document.getElementById('txt').innerHTML = Map['' + maxItem.index];
console.log('识别出的结果是' + Map['' + maxItem.index]);
};
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() {
run(img);
};
image = evt.target.result;
}
reader.readAsDataURL(file.files[0]);
}
// selectImage
document.getElementById("uploadImg").onchange = function () {
selectImage(this);
};
.detector-area {
width: 580px;
height: 400px;
background-color: #fafafa;
overflow: hidden;
text-align: center;
position: relative;
img {
height: 100%;
max-width: 100%;
}
.detector-result-container {
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: #00000080;
display: none;
.result {
opacity: 0.8;
color: #fff;
font-size: 40px;
text-align: center;
#name {
padding: 30px;
display: inline-block;
border: 2px #fff solid;
margin-top: 80px;
}
#percent {
padding-top: 20px;
}
}
}
}
#txt {
font-size: 30px;
color: #fff;
}
/**
* @file terrorModel 验证 fluid
* @author zhangjingyuan02
*
*/
import 'babel-polyfill'; import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle'; import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed'; import IO from '../../src/feed/imageFeed';
import Utils from '../../src/utils/utils'; import Utils from '../../src/utils/utils';
// 获取map表 const fileDownload = require('js-file-download');
import Map from '../../test/data/map';
/**
* @file model demo 入口文件
* @author wangqun@baidu.com
*
*/
// 模型feed数据 // 模型feed数据
const feedShape = { const feedShape = {
'608': { '608': {
...@@ -28,12 +29,20 @@ const feedShape = { ...@@ -28,12 +29,20 @@ const feedShape = {
fh: 320 fh: 320
} }
}; };
const modelType = 'separate'; const modelType = 'separate';
const {fw, fh} = feedShape[modelType]; const {fw, fh} = feedShape[modelType];
// 统计参数 // 统计参数
let loaded = false; let loaded = false;
let model = {}; let model = null;
window.statistic = [];
// 模型名字
const modelName = 'terrorModel';
// 模型网络输出shape
const outputShape = [1, 15, 1, 1];
// 模型分片
const fileCount = 3;
async function run(input) { async function run(input) {
// const input = document.getElementById('mobilenet'); // const input = document.getElementById('mobilenet');
const io = new IO(); const io = new IO();
...@@ -42,14 +51,18 @@ async function run(input) { ...@@ -42,14 +51,18 @@ async function run(input) {
params: { params: {
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名 targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
scale: 256, // 缩放尺寸 scale: 256, // 缩放尺寸
width: 224, height: 224, // 压缩宽高 width: 224,
height: 224, // 压缩宽高
shape: [3, 224, 224], // 预设tensor形状 shape: [3, 224, 224], // 预设tensor形状
mean: [0.485, 0.456, 0.406], // 预设期望 mean: [0.485, 0.456, 0.406], // 预设期望
std: [0.229, 0.224, 0.225] // 预设方差 std: [0.229, 0.224, 0.225] // 预设方差
}}); }
});
// 生成 fluid 数据
generareFluidData(feed);
console.dir(['feed', feed]); const path = `model/${modelName}`;
const path = 'model/huangfan';
if (!loaded) { if (!loaded) {
const MODEL_CONFIG = { const MODEL_CONFIG = {
...@@ -57,38 +70,55 @@ async function run(input) { ...@@ -57,38 +70,55 @@ async function run(input) {
main: 'model.json', // 主文件 main: 'model.json', // 主文件
}; };
loaded = true; loaded = true;
const paddle = new Paddle({ const paddle = new Paddle({
urlConf: MODEL_CONFIG, urlConf: MODEL_CONFIG,
options: { options: {
multipart: false, multipart: true,
dataType: 'json' dataType: 'binary',
options: {
fileCount: 3, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_' + i + '.dat';
}
},
feed
} }
}); });
model = await paddle.load(); model = await paddle.load();
} }
let inst = model.execute({ let inst = model.execute({
input: feed input: feed
}); });
// 其实这里应该有个fetch的执行调用或者fetch的输出
let result = await inst.read(); let result = await inst.read();
console.dir(['result', result]); let shape = model.graph.currentShape;
let maxItem = Utils.getMaxItem(result); shape = [1, 15, 1, 1];
document.getElementById('txt').innerHTML = Map['' + maxItem.index]; shape = Utils.padToFourDimShape(shape);
console.log('识别出的结果是' + Map['' + maxItem.index]); let N = shape[0];
// console.dir(['每个op耗时', window.statistic]); let C = shape[1];
// let total = statistic.reduce((all, cur) => { let H = shape[2];
// return all + cur.runTime; let W = shape[3];
// }, 0); const nhwc = [N, H, W, C];
// console.log('op total = ' + total);
let nchwData = Utils.nhwc2nchw(result, nhwc);
const formatData = Utils.formatReadData(nchwData, shape);
fileDownload(formatData, 'data.txt');
}
function generareFluidData(feed) {
let data = new Float32Array(3 * 224 * 224);
for (let i = 0; i < 3 * 224 * 224; i++) {
data = 1.0 + 0.0;
}
data = Utils.nchw2nhwc(data, [1, 3, 224, 224]);
for (let i = 0; i < 3 * 224 * 224; i++) {
feed[0].data[i] = 1;
}
} }
var image = '';
function selectImage(file) { function selectImage(file) {
if (!file.files || !file.files[0]) { if (!file.files || !file.files[0]) {
return; return;
...@@ -100,7 +130,6 @@ function selectImage(file) { ...@@ -100,7 +130,6 @@ function selectImage(file) {
img.onload = function () { img.onload = function () {
run(img); run(img);
}; };
image = evt.target.result;
}; };
reader.readAsDataURL(file.files[0]); reader.readAsDataURL(file.files[0]);
} }
......
import 'babel-polyfill';
import Paddle from '../../src/paddle/paddle';
import IO from '../../src/feed/imageFeed';
import Utils from '../../src/utils/utils';
const fileDownload = require('js-file-download');
/**
* @file Terror model 入口文件
* @author zhangjingyuan02
*
*/
// 模型feed数据
const feedShape = {
'terrorModel': {
fw: 224,
fh: 224
}
};
// 统计参数
let loaded = false;
let model = null;
// 模型名字
const modelName = 'terrorModel';
// 模型网络输出shape
const outputShape = [1, 15, 1, 1];
// 模型分片
const fileCount = 3;
const {fw, fh} = feedShape[modelName];
async function run(input) {
// const input = document.getElementById('mobilenet');
const io = new IO();
let feed = io.process({
input: input,
params: {
gapFillWith: '#000', // 缩放后用什么填充不足方形部分
targetSize: {
height: fh,
width: fw
},
scale: 256, // 缩放尺寸
targetShape: [1, 3, fh, fw], // 目标形状 为了兼容之前的逻辑所以改个名
mean: [0.485, 0.456, 0.406],
std: [0.229, 0.224, 0.225]
}
});
const path = `model/${modelName}`;
if (!loaded) {
const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件
};
loaded = true;
const paddle = new Paddle({
urlConf: MODEL_CONFIG,
options: {
multipart: true,
dataType: 'binary',
options: {
fileCount: fileCount, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称
return 'chunk_' + i + '.dat';
}
},
feed
}
});
model = await paddle.load();
}
let inst = model.execute({
input: feed
});
let result = await inst.read();
let N = outputShape[0];
let C = outputShape[1];
let H = outputShape[2];
let W = outputShape[3];
let nhwcShape = [N, H, W, C];
let nchwData = Utils.nhwc2nchw(result, nhwcShape);
// 模型后置处理, infer的时候需要
nchwData = Utils.softmax(nchwData);
// 打印数据
Utils.stridePrint(nchwData);
Utils.continuousPrint(nchwData);
// 生成详细类别得分
const score12List = convertTo12class(nchwData);
const detailClass = [
'正常', '警察部队', '血腥', '尸体', '爆炸火灾', '杀人', '暴乱',
'暴恐人物', '军事武器', '暴恐旗帜', '血腥动物或动物尸体', '车祸'
];
// 详细分类得分
const class12Score = [];
detailClass.forEach((name, index) => {
class12Score.push({
name,
score: score12List[index]
});
});
// 生成简单类别得分
const twoClass = ['正常', '暴恐'];
const score2List = convertTo2class(nchwData);
// 简单分类得分
const class2Score = [];
twoClass.forEach((name, index) => {
class2Score.push({
name,
score: score2List[index]
});
});
// 展示结果
const maxItem = Utils.getMaxItem(score12List);
const resName = detailClass[maxItem.index];
const resPercent = toPercent(maxItem.value);
document.querySelector('.result #name').innerHTML = resName;
document.querySelector('.result #percent').innerHTML = resPercent;
document.querySelector('.detector-result-container').style.display = 'block';
}
// 小数转百分比
function toPercent(data) {
let str = Number(data * 100).toFixed(3);
return str += '%';
}
function convertTo12class(scores) {
return [
scores[0] + scores[12], scores[10], scores[3], scores[2],
scores[8], scores[1] + scores[7], scores[9], scores[5] + scores[6],
scores[11], scores[4], scores[13], scores[14]
];
}
function convertTo2class(scores) {
const totalScore = scores.reduce((acc, cur) => acc += cur, 0);
let normalScore = scores[0] + scores[10] + scores[11] + scores[12] + scores[13];
let terrorScore = totalScore - normalScore;
return [
normalScore,
terrorScore
];
}
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 () {
run(img);
};
};
reader.readAsDataURL(file.files[0]);
}
// selectImage
document.getElementById('uploadImg').onchange = function () {
selectImage(this);
};
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>paddle web demo - terrorModel</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<link href="custom.less" rel="stylesheet">
</head>
<body>
<div class="detector-area">
<img id="image" src="https://m.baidu.com/se/static/img/iphone/logo.png" >
<div class="detector-result-container">
<div class="result">
<div id="name"></div>
<div id="percent"></div>
</div>
</div>
</div>
<input type="file" id="uploadImg">
<script src="index.es6"></script>
</body>
</html>
...@@ -54,7 +54,7 @@ const feedShape = { ...@@ -54,7 +54,7 @@ const feedShape = {
}; };
// 模型路径 // 模型路径
const modelPath = { const modelPath = {
'tinyYolo': 'model/tinyYolo' 'tinyYolo': 'https://paddlejs.cdn.bcebos.com/models/tinyYolo'
}; };
const modelType = 'tinyYolo'; const modelType = 'tinyYolo';
const path = modelPath[modelType]; const path = modelPath[modelType];
...@@ -86,7 +86,7 @@ async function run(input) { ...@@ -86,7 +86,7 @@ async function run(input) {
// log.end('预处理'); // log.end('预处理');
if (!loaded) { if (!loaded) {
const MODEL_CONFIG = { const MODEL_CONFIG = {
dir: `/${path}/`, // 存放模型的文件夹 dir: `${path}/`, // 存放模型的文件夹
main: 'model.json', // 主文件 main: 'model.json', // 主文件
}; };
loaded = true; loaded = true;
...@@ -98,7 +98,7 @@ async function run(input) { ...@@ -98,7 +98,7 @@ async function run(input) {
options: { options: {
fileCount: 1, // 切成了多少文件 fileCount: 1, // 切成了多少文件
getFileName(i) { // 获取第i个文件的名称 getFileName(i) { // 获取第i个文件的名称
return 'chunk_0.dat'; return `chunk_${i}.dat`;
} }
} }
} }
......
...@@ -45,7 +45,7 @@ const feedShape = { ...@@ -45,7 +45,7 @@ const feedShape = {
} }
}; };
const modelPath = { const modelPath = {
'tinyYolo': 'model/tinyYolo' 'tinyYolo': 'https://paddlejs.cdn.bcebos.com/models/tinyYolo'
}; };
const modelType = 'tinyYolo'; const modelType = 'tinyYolo';
const path = modelPath[modelType]; const path = modelPath[modelType];
...@@ -55,7 +55,8 @@ const runner = new Runner({ ...@@ -55,7 +55,8 @@ const runner = new Runner({
modelName: modelType, // '608' | '320' | '320fused' | 'separate' modelName: modelType, // '608' | '320' | '320fused' | 'separate'
modelPath: path, modelPath: path,
feedShape: feedShape[modelType], feedShape: feedShape[modelType],
outputShapes: outputShapes[modelType] outputShapes: outputShapes[modelType],
inputType: 'video'
}); });
startBtn.disabled = true; startBtn.disabled = true;
runner.preheat() runner.preheat()
...@@ -95,7 +96,7 @@ const handleDiv = function (data) { ...@@ -95,7 +96,7 @@ const handleDiv = function (data) {
} }
startBtn.addEventListener('click', function () { startBtn.addEventListener('click', function () {
startBtn.disabled = true; startBtn.disabled = true;
runner.startStream(() => camera.curVideo, handleDiv); runner.predict(() => camera.curVideo, handleDiv);
}); });
stopBtn.addEventListener('click', function () { stopBtn.addEventListener('click', function () {
startBtn.disabled = false; startBtn.disabled = false;
......
<template lang="pug">
div
page
</template>
<script lang="ts">
import Vue from "vue";
import page from "./page.vue";
export default Vue.extend({
data() {
return {
bundler: "Parcel",
};
},
components: {
'page': page
}
});
</script>
<style lang="less" scoped>
.container {
color: green;
}
.text {
color: red;
}
</style>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PaddleJS benchmark</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
import Vue from 'vue';
import App from './app.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
const vm = new Vue({render: h => h(App)});
vm.$mount('#app');
<template lang="pug">
div.page
div.imglist
el-carousel(:interval="4000" type="card" @change="change" height="400px" :autoplay="autoPlay")
el-carousel-item(v-for="(item, index) in imgList" :key="item")
img.img(:src="item" :ref="'img' + index")
span.result-text(v-if="result[index]") {{ result[index] }}
el-button(type="primary" @click="predict").predict 点击识别此图
div.loading(v-if="isLoading || isPredicting")
span.loading-text {{ isLoading ? 'loading...' : '识别中……'}}
</template>
<script>
import Vue from 'vue';
import 'regenerator-runtime/runtime'
import 'babel-polyfill'
import Paddlejs from '../../src/executor/runner';
import Utils from '../../src/utils/utils';
import postProcess from './postProcess'
export default Vue.extend({
data() {
return {
modelConf: {
modelPath: 'https://paddlejs.cdn.bcebos.com/models/wine',
fileCount: 3,
feedShape: {
fw: 224,
fh: 224
},
fetchShape: [1, 40, 1, 1],
fill: '#000',
scale: 256,
targetSize: { height: 224, width: 224 },
mean: [0.485, 0.456, 0.406],
std: [0.229, 0.224, 0.225],
needBatch: true
},
imgList: [
require('./imgs/1.jpg'),
require('./imgs/2.jpg'),
require('./imgs/3.jpg'),
require('./imgs/4.jpg'),
require('./imgs/5.jpg'),
require('./imgs/6.jpg'),
require('./imgs/7.jpg')
],
result: {},
curImgIndex: 0,
autoPlay: true,
isLoading: true,
isPredicting: false,
paddlejs: null
}
},
methods: {
change(index) {
this.curImgIndex = index;
},
async predict() {
console.log('开始预测')
this.autoPlay = false;
this.isPredicting = true;
const curIndex = this.curImgIndex;
const imgDom = this.$refs['img' + this.curImgIndex][0];
await this.predictModel(imgDom, curIndex);
this.isPredicting = false;
},
load() {
const paddlejs = this.paddlejs = new Paddlejs(this.modelConf);
paddlejs.loadModel().then(() => {
this.isLoading = false;
})
},
async predictModel(img, curIndex) {
await this.paddlejs.predict(img, (data) => this.process(data, curIndex));
},
process(data, curIndex) {
const result = postProcess(data);
console.log(result)
this.result[curIndex] = result || '无结果'
}
},
mounted() {
this.load();
}
})
</script>
<style>
.page {
padding-top: 50px;
text-align: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 1px solid #f1f1f1;
}
.img {
width: 100%;
}
.loading {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .7);
color: #fff;
z-index: 2;
font-size: 32px;
}
.imglist {
margin-bottom: 50px;
}
.predict {
}
.result-text {
font-weight: bold;
}
</style>
\ No newline at end of file
// index.js
import Map from '../../test/data/wine.map.json';
import Utils from '../../src/utils/utils';
export default function postProcess(data) {
let maxItem = Utils.getMaxItem(data);
return Map[maxItem.index];
}
此差异已折叠。
module.exports = {
testEnvironment: 'node',
transform: {
// 将.js后缀的文件使用babel-jest处理
'^.+\\.(js|es6)$': 'babel-jest'
},
moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'es6'],
testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)*(spec|test).[jt]s?(x)" ]
};
...@@ -5,24 +5,31 @@ ...@@ -5,24 +5,31 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"server": "parcel ./src/index.html", "server": "parcel ./src/index.html",
"mnistdemo": "parcel ./examples/mnist/index.html",
"mobilenet": "parcel ./examples/mobileNet/index.html", "mobilenet": "parcel ./examples/mobileNet/index.html",
"mobilenetOpt": "parcel ./examples/mobileNetOpt/index.html",
"wine": "parcel ./examples/wine/index.html --port 1234",
"tinyYolo": "parcel ./examples/tinyYolo/index.html", "tinyYolo": "parcel ./examples/tinyYolo/index.html",
"huangfan": "parcel ./examples/huangfan/index.html", "huangfan": "parcel ./examples/huangfan/index.html",
"terrorModel": "parcel ./examples/terrorModel/index.html", "terrorModel": "parcel ./examples/terrorModel/index.html",
"humanseg": "parcel ./examples/humanseg/index.html", "humanseg": "parcel ./examples/humanseg/index.html",
"humanStream": "parcel ./examples/humanStream/index.html --port 1234 --https", "humanStream": "parcel ./examples/humanStream/index.html --port 1234 --https",
"gesture": "parcel ./examples/gesture/index.html --port 8888 --https",
"benchmark": "parcel ./examples/benchmark/index.html",
"yolo": "parcel ./examples/yolo/index.html", "yolo": "parcel ./examples/yolo/index.html",
"videoDemo": "parcel ./examples/videoDemo.html --port 8123 --https", "videoDemo": "parcel ./examples/tinyYolo/videoDemo.html --port 8123 --https",
"unitTest": "parcel ./test/unitTest.html", "unitTest": "parcel ./test/unitTest.html",
"test": "echo \"Error: no test specified\" && exit 1", "test": "jest",
"build": "webpack -w" "build": "parcel build ./examples/humanStream/index.html",
"demo": "parcel ./examples/demo/index.html",
"pub": "webpack"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.7.2", "@babel/core": "^7.9.6",
"@babel/preset-env": "^7.7.1", "@babel/preset-env": "^7.9.6",
"axios": "^0.19.2", "@vue/component-compiler-utils": "^3.1.2",
"axios": "^0.17.1",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"babel-jest": "^26.0.1",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-decorators-legacy": "^1.3.5",
...@@ -32,19 +39,32 @@ ...@@ -32,19 +39,32 @@
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1", "babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"gl": "^4.5.0",
"jest": "^26.0.1",
"less": "^3.11.1", "less": "^3.11.1",
"parcel-bundler": "^1.10.3", "parcel-bundler": "^1.10.3",
"pre-push": "^0.1.1",
"pug": "^3.0.0",
"typescript": "^3.9.5",
"vue-template-compiler": "^2.6.11",
"webpack-cli": "^3.3.6" "webpack-cli": "^3.3.6"
}, },
"keywords": [], "keywords": [],
"pre-push": [
"test"
],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@babel/plugin-transform-runtime": "^7.6.2", "@babel/plugin-transform-runtime": "^7.6.2",
"@babel/runtime": "^7.7.2", "@babel/runtime": "^7.7.2",
"element-ui": "^2.13.2",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"js-file-download": "^0.4.10", "js-file-download": "^0.4.10",
"paddlejs": "^1.0.0-beta.0",
"vconsole": "^3.3.2", "vconsole": "^3.3.2",
"vue": "^2.6.11",
"vue-hot-reload-api": "^2.3.4",
"webpack": "^4.39.2", "webpack": "^4.39.2",
"webpack-zepto": "0.0.1" "webpack-zepto": "0.0.1"
} }
......
...@@ -23,6 +23,9 @@ export default class Camera { ...@@ -23,6 +23,9 @@ export default class Camera {
if(navigator.mediaDevices) { if(navigator.mediaDevices) {
this.haveDevice = true; this.haveDevice = true;
} }
if (option.constraints) {
this.constraints = option.constraints;
}
} }
// 访问用户媒体设备的兼容方法 // 访问用户媒体设备的兼容方法
...@@ -49,6 +52,9 @@ export default class Camera { ...@@ -49,6 +52,9 @@ export default class Camera {
video: true video: true
}; };
} }
else if (this.constraints) {
constraints = this.constraints;
}
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// 最新的标准API // 最新的标准API
......
此差异已折叠。
...@@ -7,6 +7,7 @@ export default class Factory { ...@@ -7,6 +7,7 @@ export default class Factory {
constructor(opts) { constructor(opts) {
this.defaultOpts = Object.assign({}, opts); this.defaultOpts = Object.assign({}, opts);
this.webglVersion = 2; this.webglVersion = 2;
this.isFrameBufferSupportFloat = true;
this.texture2d = 'texture'; this.texture2d = 'texture';
} }
...@@ -17,6 +18,10 @@ export default class Factory { ...@@ -17,6 +18,10 @@ export default class Factory {
} }
} }
setIsFrameBufferSupportFloat(res = true) {
this.isFrameBufferSupportFloat = res;
}
buildShader(opName, data, runtime = undefined) { buildShader(opName, data, runtime = undefined) {
let result = ''; let result = '';
result = this.buildPrefix(opName); result = this.buildPrefix(opName);
...@@ -30,7 +35,7 @@ export default class Factory { ...@@ -30,7 +35,7 @@ export default class Factory {
buildPrefix(opName) { buildPrefix(opName) {
if (this.webglVersion === 1) { if (this.webglVersion === 1) {
return ops.common.prefix; return this.isFrameBufferSupportFloat ? ops.common.prefix : ops.common.prefixHalf;
} }
return ops.common.prefix2; return ops.common.prefix2;
} }
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册