mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2025-10-04 16:22:57 +08:00
[Other] Refactor js submodule (#415)
* Refactor js submodule * Remove change-log * Update ocr module * Update ocr-detection module * Update ocr-detection module * Remove change-log
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/detect/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/detect/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -0,0 +1 @@
|
||||
auto-install-peers=true
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./lib/index.d.ts",
|
||||
"bundledPackages": [],
|
||||
"docModel": {
|
||||
"enabled": true
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const paths = {
|
||||
root: path.join(__dirname, '../'),
|
||||
input: path.join(__dirname, '../src/index.ts'),
|
||||
lib: path.join(__dirname, '../lib'),
|
||||
}
|
||||
|
||||
export const log = {
|
||||
progress: (text: string) => {
|
||||
console.log(chalk.green(text))
|
||||
},
|
||||
error: (text: string) => {
|
||||
console.log(chalk.red(text))
|
||||
},
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
import path from 'path'
|
||||
import fse from 'fs-extra'
|
||||
import { series } from "gulp"
|
||||
import { paths, log } from "./build_package/util"
|
||||
import rollupConfig from './rollup.config'
|
||||
import { rollup } from 'rollup'
|
||||
import {
|
||||
Extractor,
|
||||
ExtractorConfig,
|
||||
ExtractorResult,
|
||||
} from '@microsoft/api-extractor'
|
||||
/**
|
||||
* 这里是由于 'conventional-changelog' 未提供类型文件
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
|
||||
interface TaskFunc {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(cb: Function): void
|
||||
}
|
||||
|
||||
const CHANGE_TRACE = ['paddlejs-models/detect', 'paddle-js-models/detect', 'paddlejs-models', 'paddle-js-models', 'all']
|
||||
|
||||
/**
|
||||
* 删除 lib 文件
|
||||
* @param cb
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clearLibFile: TaskFunc = async (cb) => {
|
||||
fse.removeSync(paths.lib)
|
||||
log.progress('Deleted lib file')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* rollup 打包
|
||||
* @param cb
|
||||
*/
|
||||
const buildByRollup: TaskFunc = async (cb) => {
|
||||
const inputOptions = {
|
||||
input: rollupConfig.input,
|
||||
external: rollupConfig.external,
|
||||
plugins: rollupConfig.plugins,
|
||||
}
|
||||
const outOptions = rollupConfig.output
|
||||
const bundle = await rollup(inputOptions)
|
||||
|
||||
// 写入需要遍历输出配置
|
||||
if (Array.isArray(outOptions)) {
|
||||
for (const outOption of outOptions) {
|
||||
await bundle.write(outOption)
|
||||
}
|
||||
cb()
|
||||
log.progress('Rollup built successfully')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* api-extractor 整理 .d.ts 文件
|
||||
* @param cb
|
||||
*/
|
||||
const apiExtractorGenerate: TaskFunc = async (cb) => {
|
||||
const apiExtractorJsonPath: string = path.join(__dirname, './api-extractor.json')
|
||||
// 加载并解析 api-extractor.json 文件
|
||||
const extractorConfig: ExtractorConfig = await ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath)
|
||||
// 判断是否存在 index.d.ts 文件,这里必须异步先访问一边,不然后面找不到会报错
|
||||
const isdtxExist: boolean = await fse.pathExists(extractorConfig.mainEntryPointFilePath)
|
||||
// 判断是否存在 etc 目录,api-extractor需要该目录存在
|
||||
const isEtcExist: boolean = await fse.pathExists('./etc')
|
||||
|
||||
if (!isdtxExist) {
|
||||
log.error('API Extractor not find index.d.ts')
|
||||
return
|
||||
}
|
||||
|
||||
if (!isEtcExist) {
|
||||
fse.mkdirSync('etc');
|
||||
log.progress('Create folder etc for API Extractor')
|
||||
}
|
||||
|
||||
// 调用 API
|
||||
const extractorResult: ExtractorResult = await Extractor.invoke(extractorConfig, {
|
||||
localBuild: true,
|
||||
// 在输出中显示信息
|
||||
showVerboseMessages: true,
|
||||
})
|
||||
|
||||
if (extractorResult.succeeded) {
|
||||
// 删除多余的 .d.ts 文件
|
||||
const libFiles: string[] = await fse.readdir(paths.lib)
|
||||
for (const file of libFiles) {
|
||||
if (file.endsWith('.d.ts') && !file.includes('index')) {
|
||||
await fse.remove(path.join(paths.lib, file))
|
||||
}
|
||||
}
|
||||
log.progress('API Extractor completed successfully')
|
||||
// api-extractor 会生成 temp 文件夹,完成后进行删除
|
||||
fse.ensureDirSync('temp')
|
||||
fse.removeSync('temp')
|
||||
cb()
|
||||
} else {
|
||||
log.error(`API Extractor completed with ${extractorResult.errorCount} errors`
|
||||
+ ` and ${extractorResult.warningCount} warnings`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
* @param cb
|
||||
*/
|
||||
const complete: TaskFunc = (cb) => {
|
||||
log.progress('---- end ----')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CHANGELOG
|
||||
* @param cb
|
||||
*/
|
||||
export const changelog: TaskFunc = async (cb) => {
|
||||
const checkTrace = (chunk: string) => {
|
||||
for (const keyWord of CHANGE_TRACE) {
|
||||
if (chunk.includes(keyWord)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const changelogPath: string = path.join(paths.root, 'CHANGELOG.md')
|
||||
// 对命令 conventional-changelog -p angular -i CHANGELOG.md -w -r 0
|
||||
const changelogPipe = await conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 0,
|
||||
})
|
||||
changelogPipe.setEncoding('utf8')
|
||||
|
||||
const resultArray = ['# 更新日志\n\n']
|
||||
changelogPipe.on('data', (chunk) => {
|
||||
// 原来的 commits 路径是进入提交列表
|
||||
chunk = chunk.replace(/\/commits\//g, '/commit/')
|
||||
/**
|
||||
* title 或 指定跟踪 才会写入CHANGELOG
|
||||
*/
|
||||
for (const log of chunk.split("\n")) {
|
||||
if (log.includes('# ') || log.includes('### ') || checkTrace(log)) {
|
||||
resultArray.push(log+"\n\n")
|
||||
}
|
||||
}
|
||||
})
|
||||
changelogPipe.on('end', async () => {
|
||||
fse.createWriteStream(changelogPath).write(resultArray.join(''))
|
||||
cb()
|
||||
log.progress('CHANGELOG generation completed')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.build = series(clearLibFile, buildByRollup, apiExtractorGenerate, complete)
|
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@paddle-js-models/detect",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.esm.js",
|
||||
"typings": "lib/index.d.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README_cn.md"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "yalc publish --push",
|
||||
"prepublish": "pnpm lint & pnpm test",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "gulp build",
|
||||
"lint": "eslint --ext .js,.ts src --fix",
|
||||
"api": "api-extractor run",
|
||||
"test": "jest --coverage --verbose -u",
|
||||
"changelog": "gulp changelog"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/d3-polygon": "^3.0.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chalk": "4.1.2",
|
||||
"commitlint": "^17.1.2",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"husky": "^8.0.1",
|
||||
"jest": "^29.0.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.34.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsify": "^5.0.4",
|
||||
"typescript": "^4.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paddlejs/paddlejs-backend-webgl": "^1.2.9",
|
||||
"@paddlejs/paddlejs-core": "^2.2.0"
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import path from 'path'
|
||||
import { RollupOptions } from 'rollup'
|
||||
import rollupTypescript from 'rollup-plugin-typescript2'
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import { eslint } from 'rollup-plugin-eslint'
|
||||
import { DEFAULT_EXTENSIONS } from '@babel/core'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { paths } from "./build_package/util";
|
||||
|
||||
|
||||
// rollup 配置项
|
||||
const rollupConfig: RollupOptions = {
|
||||
input: paths.input,
|
||||
output: [
|
||||
// 输出 commonjs 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.js'),
|
||||
format: 'cjs',
|
||||
name: pkg.name,
|
||||
},
|
||||
// 输出 es 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.esm.js'),
|
||||
format: 'es',
|
||||
name: pkg.name,
|
||||
},
|
||||
],
|
||||
external: ['@paddlejs-mediapipe/opencv',
|
||||
'@paddlejs/paddlejs-backend-webgl',
|
||||
'@paddlejs/paddlejs-core',
|
||||
'@types/node',
|
||||
'd3-polygon',
|
||||
'js-clipper',
|
||||
'number-precision'],
|
||||
// plugins 需要注意引用顺序
|
||||
plugins: [
|
||||
eslint({
|
||||
throwOnError: true,
|
||||
throwOnWarning: false,
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['node_modules/**', 'lib/**', '*.js'],
|
||||
}),
|
||||
|
||||
// 使得 rollup 支持 commonjs 规范,识别 commonjs 规范的依赖
|
||||
commonjs(),
|
||||
|
||||
// 配合 commnjs 解析第三方模块
|
||||
resolve({
|
||||
// 将自定义选项传递给解析插件
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
rollupTypescript(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
// 只转换源代码,不运行外部依赖
|
||||
exclude: 'node_modules/**',
|
||||
// babel 默认不支持 ts 需要手动添加
|
||||
extensions: [
|
||||
...DEFAULT_EXTENSIONS,
|
||||
'.ts',
|
||||
],
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export default rollupConfig
|
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @file detect model
|
||||
*/
|
||||
|
||||
import { Runner } from '@paddlejs/paddlejs-core';
|
||||
import '@paddlejs/paddlejs-backend-webgl';
|
||||
|
||||
let detectRunner = null as Runner;
|
||||
|
||||
export async function init() {
|
||||
detectRunner = new Runner({
|
||||
modelPath: 'https://paddlejs.bj.bcebos.com/models/fuse/detect/detect_fuse_activation/model.json',
|
||||
fill: '#fff',
|
||||
mean: [0.5, 0.5, 0.5],
|
||||
std: [0.5, 0.5, 0.5],
|
||||
bgr: true,
|
||||
keepRatio: false,
|
||||
webglFeedProcess: true
|
||||
});
|
||||
|
||||
await detectRunner.init();
|
||||
}
|
||||
|
||||
export async function detect(image) {
|
||||
const output = await detectRunner.predict(image);
|
||||
// 阈值
|
||||
const thresh = 0.3;
|
||||
return output.filter(item => item[1] > thresh);
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Example:', () => {
|
||||
/**
|
||||
* Example
|
||||
*/
|
||||
describe('ExampleTest', () => {
|
||||
test('Hello World!', () => {
|
||||
assert.strictEqual('Hello World!', 'Hello World!')
|
||||
})
|
||||
})
|
||||
})
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
"declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
"removeComments": false, /* Disable emitting comments. */
|
||||
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/facedetect/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/facedetect/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -0,0 +1 @@
|
||||
auto-install-peers=true
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./lib/index.d.ts",
|
||||
"bundledPackages": [],
|
||||
"docModel": {
|
||||
"enabled": true
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const paths = {
|
||||
root: path.join(__dirname, '../'),
|
||||
input: path.join(__dirname, '../src/index.ts'),
|
||||
lib: path.join(__dirname, '../lib'),
|
||||
}
|
||||
|
||||
export const log = {
|
||||
progress: (text: string) => {
|
||||
console.log(chalk.green(text))
|
||||
},
|
||||
error: (text: string) => {
|
||||
console.log(chalk.red(text))
|
||||
},
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
import path from 'path'
|
||||
import fse from 'fs-extra'
|
||||
import { series } from "gulp"
|
||||
import { paths, log } from "./build_package/util"
|
||||
import rollupConfig from './rollup.config'
|
||||
import { rollup } from 'rollup'
|
||||
import {
|
||||
Extractor,
|
||||
ExtractorConfig,
|
||||
ExtractorResult,
|
||||
} from '@microsoft/api-extractor'
|
||||
/**
|
||||
* 这里是由于 'conventional-changelog' 未提供类型文件
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
|
||||
interface TaskFunc {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(cb: Function): void
|
||||
}
|
||||
|
||||
const CHANGE_TRACE = ['paddlejs-models/facedetect', 'paddle-js-models/facedetect', 'paddlejs-models', 'paddle-js-models', 'all']
|
||||
|
||||
/**
|
||||
* 删除 lib 文件
|
||||
* @param cb
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clearLibFile: TaskFunc = async (cb) => {
|
||||
fse.removeSync(paths.lib)
|
||||
log.progress('Deleted lib file')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* rollup 打包
|
||||
* @param cb
|
||||
*/
|
||||
const buildByRollup: TaskFunc = async (cb) => {
|
||||
const inputOptions = {
|
||||
input: rollupConfig.input,
|
||||
external: rollupConfig.external,
|
||||
plugins: rollupConfig.plugins,
|
||||
}
|
||||
const outOptions = rollupConfig.output
|
||||
const bundle = await rollup(inputOptions)
|
||||
|
||||
// 写入需要遍历输出配置
|
||||
if (Array.isArray(outOptions)) {
|
||||
for (const outOption of outOptions) {
|
||||
await bundle.write(outOption)
|
||||
}
|
||||
cb()
|
||||
log.progress('Rollup built successfully')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* api-extractor 整理 .d.ts 文件
|
||||
* @param cb
|
||||
*/
|
||||
const apiExtractorGenerate: TaskFunc = async (cb) => {
|
||||
const apiExtractorJsonPath: string = path.join(__dirname, './api-extractor.json')
|
||||
// 加载并解析 api-extractor.json 文件
|
||||
const extractorConfig: ExtractorConfig = await ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath)
|
||||
// 判断是否存在 index.d.ts 文件,这里必须异步先访问一边,不然后面找不到会报错
|
||||
const isdtxExist: boolean = await fse.pathExists(extractorConfig.mainEntryPointFilePath)
|
||||
// 判断是否存在 etc 目录,api-extractor需要该目录存在
|
||||
const isEtcExist: boolean = await fse.pathExists('./etc')
|
||||
|
||||
if (!isdtxExist) {
|
||||
log.error('API Extractor not find index.d.ts')
|
||||
return
|
||||
}
|
||||
|
||||
if (!isEtcExist) {
|
||||
fse.mkdirSync('etc');
|
||||
log.progress('Create folder etc for API Extractor')
|
||||
}
|
||||
|
||||
// 调用 API
|
||||
const extractorResult: ExtractorResult = await Extractor.invoke(extractorConfig, {
|
||||
localBuild: true,
|
||||
// 在输出中显示信息
|
||||
showVerboseMessages: true,
|
||||
})
|
||||
|
||||
if (extractorResult.succeeded) {
|
||||
// 删除多余的 .d.ts 文件
|
||||
const libFiles: string[] = await fse.readdir(paths.lib)
|
||||
for (const file of libFiles) {
|
||||
if (file.endsWith('.d.ts') && !file.includes('index')) {
|
||||
await fse.remove(path.join(paths.lib, file))
|
||||
}
|
||||
}
|
||||
log.progress('API Extractor completed successfully')
|
||||
// api-extractor 会生成 temp 文件夹,完成后进行删除
|
||||
fse.ensureDirSync('temp')
|
||||
fse.removeSync('temp')
|
||||
cb()
|
||||
} else {
|
||||
log.error(`API Extractor completed with ${extractorResult.errorCount} errors`
|
||||
+ ` and ${extractorResult.warningCount} warnings`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
* @param cb
|
||||
*/
|
||||
const complete: TaskFunc = (cb) => {
|
||||
log.progress('---- end ----')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CHANGELOG
|
||||
* @param cb
|
||||
*/
|
||||
export const changelog: TaskFunc = async (cb) => {
|
||||
const checkTrace = (chunk: string) => {
|
||||
for (const keyWord of CHANGE_TRACE) {
|
||||
if (chunk.includes(keyWord)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const changelogPath: string = path.join(paths.root, 'CHANGELOG.md')
|
||||
// 对命令 conventional-changelog -p angular -i CHANGELOG.md -w -r 0
|
||||
const changelogPipe = await conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 0,
|
||||
})
|
||||
changelogPipe.setEncoding('utf8')
|
||||
|
||||
const resultArray = ['# 更新日志\n\n']
|
||||
changelogPipe.on('data', (chunk) => {
|
||||
// 原来的 commits 路径是进入提交列表
|
||||
chunk = chunk.replace(/\/commits\//g, '/commit/')
|
||||
/**
|
||||
* title 或 指定跟踪 才会写入CHANGELOG
|
||||
*/
|
||||
for (const log of chunk.split("\n")) {
|
||||
if (log.includes('# ') || log.includes('### ') || checkTrace(log)) {
|
||||
resultArray.push(log+"\n\n")
|
||||
}
|
||||
}
|
||||
})
|
||||
changelogPipe.on('end', async () => {
|
||||
fse.createWriteStream(changelogPath).write(resultArray.join(''))
|
||||
cb()
|
||||
log.progress('CHANGELOG generation completed')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.build = series(clearLibFile, buildByRollup, apiExtractorGenerate, complete)
|
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@paddle-js-models/facedetect",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.esm.js",
|
||||
"typings": "lib/index.d.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README_cn.md"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "yalc publish --push",
|
||||
"prepublish": "pnpm lint & pnpm test",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "gulp build",
|
||||
"lint": "eslint --ext .js,.ts src --fix",
|
||||
"api": "api-extractor run",
|
||||
"test": "jest --coverage --verbose -u",
|
||||
"changelog": "gulp changelog"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/d3-polygon": "^3.0.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chalk": "4.1.2",
|
||||
"commitlint": "^17.1.2",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"husky": "^8.0.1",
|
||||
"jest": "^29.0.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.34.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsify": "^5.0.4",
|
||||
"typescript": "^4.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paddlejs/paddlejs-backend-webgl": "^1.2.9",
|
||||
"@paddlejs/paddlejs-core": "^2.2.0"
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import path from 'path'
|
||||
import { RollupOptions } from 'rollup'
|
||||
import rollupTypescript from 'rollup-plugin-typescript2'
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import { eslint } from 'rollup-plugin-eslint'
|
||||
import { DEFAULT_EXTENSIONS } from '@babel/core'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { paths } from "./build_package/util";
|
||||
|
||||
|
||||
// rollup 配置项
|
||||
const rollupConfig: RollupOptions = {
|
||||
input: paths.input,
|
||||
output: [
|
||||
// 输出 commonjs 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.js'),
|
||||
format: 'cjs',
|
||||
name: pkg.name,
|
||||
},
|
||||
// 输出 es 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.esm.js'),
|
||||
format: 'es',
|
||||
name: pkg.name,
|
||||
},
|
||||
],
|
||||
external: ['@paddlejs-mediapipe/opencv',
|
||||
'@paddlejs/paddlejs-backend-webgl',
|
||||
'@paddlejs/paddlejs-core',
|
||||
'@types/node',
|
||||
'd3-polygon',
|
||||
'js-clipper',
|
||||
'number-precision'],
|
||||
// plugins 需要注意引用顺序
|
||||
plugins: [
|
||||
eslint({
|
||||
throwOnError: true,
|
||||
throwOnWarning: false,
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['node_modules/**', 'lib/**', '*.js'],
|
||||
}),
|
||||
|
||||
// 使得 rollup 支持 commonjs 规范,识别 commonjs 规范的依赖
|
||||
commonjs(),
|
||||
|
||||
// 配合 commnjs 解析第三方模块
|
||||
resolve({
|
||||
// 将自定义选项传递给解析插件
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
rollupTypescript(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
// 只转换源代码,不运行外部依赖
|
||||
exclude: 'node_modules/**',
|
||||
// babel 默认不支持 ts 需要手动添加
|
||||
extensions: [
|
||||
...DEFAULT_EXTENSIONS,
|
||||
'.ts',
|
||||
],
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export default rollupConfig
|
@@ -0,0 +1,121 @@
|
||||
import { Runner } from '@paddlejs/paddlejs-core';
|
||||
import '@paddlejs/paddlejs-backend-webgl';
|
||||
|
||||
interface FeedShape {
|
||||
fw: number;
|
||||
fh: number;
|
||||
}
|
||||
export interface ModelConfig {
|
||||
modelPath?: string;
|
||||
mean?: number[];
|
||||
std?: number[];
|
||||
feedShape?: FeedShape;
|
||||
}
|
||||
interface NaturalSize {
|
||||
naturalWidth: number;
|
||||
naturalHeight: number;
|
||||
}
|
||||
export interface TransformData {
|
||||
left: number;
|
||||
width: number;
|
||||
top: number;
|
||||
height: number;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
// 默认模型数据
|
||||
const defaultFeedShape: FeedShape = {
|
||||
fw: 1024,
|
||||
fh: 1024
|
||||
};
|
||||
const defaultModelConfig: ModelConfig = {
|
||||
modelPath: 'https://paddlejs.cdn.bcebos.com/models/fuse/facedetect_opt/model.json',
|
||||
mean: [0.407843137, 0.458823529, 0.482352941],
|
||||
std: [0.5, 0.5, 0.5]
|
||||
};
|
||||
|
||||
export class FaceDetector {
|
||||
modelConfig: ModelConfig = defaultModelConfig;
|
||||
feedShape: FeedShape = defaultFeedShape;
|
||||
runner: Runner = null;
|
||||
inputSize = {} as NaturalSize;
|
||||
scale: number | undefined;
|
||||
|
||||
constructor(modeConfig?: ModelConfig) {
|
||||
this.modelConfig = Object.assign(this.modelConfig, modeConfig);
|
||||
this.feedShape = Object.assign(this.feedShape, modeConfig?.feedShape);
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.runner = new Runner(this.modelConfig);
|
||||
await this.runner.init();
|
||||
}
|
||||
|
||||
async detect(
|
||||
input: HTMLImageElement,
|
||||
opts?
|
||||
) {
|
||||
this.inputSize.naturalWidth = input.naturalWidth;
|
||||
this.inputSize.naturalHeight = input.naturalHeight;
|
||||
const { shrink = 0.4, threshold = 0.6 } = Object.assign({}, opts);
|
||||
|
||||
const inputFeed = this.preprocess(input, shrink);
|
||||
|
||||
// 预测
|
||||
const dataOut = await this.runner.predictWithFeed(inputFeed);
|
||||
return this.postprocessor(dataOut, threshold);
|
||||
}
|
||||
|
||||
preprocess(input: HTMLImageElement, shrink: number) {
|
||||
// shrink --> scale
|
||||
const scale = this.scale = Math.min(this.inputSize.naturalWidth, this.inputSize.naturalHeight) * shrink;
|
||||
return this.runner.mediaProcessor.process(
|
||||
input,
|
||||
Object.assign({}, this.modelConfig, { scale }),
|
||||
this.feedShape
|
||||
);
|
||||
}
|
||||
|
||||
postprocessor(dataOut, threshold: number) {
|
||||
// data filter 筛掉小于阈值项
|
||||
const dataFilt = dataOut.filter(item => item[1] && item[1] > threshold);
|
||||
// data transform
|
||||
return dataFilt.map(item => this.transformData(item));
|
||||
}
|
||||
|
||||
transformData(data: Array<number>) {
|
||||
const transformRes = {} as TransformData;
|
||||
const { fw, fh } = this.feedShape;
|
||||
const { naturalWidth, naturalHeight } = this.inputSize;
|
||||
let dWidth;
|
||||
let dHeight;
|
||||
|
||||
if (naturalWidth > naturalHeight) {
|
||||
dHeight = this.scale;
|
||||
dWidth = dHeight * naturalWidth / naturalHeight;
|
||||
}
|
||||
else {
|
||||
dWidth = this.scale;
|
||||
dHeight = dWidth * naturalHeight / naturalWidth;
|
||||
}
|
||||
const dx = (fw - dWidth) / 2;
|
||||
const dy = (fh - dHeight) / 2;
|
||||
transformRes.confidence = data[1];
|
||||
transformRes.left = (data[2] * fw - dx) / dWidth;
|
||||
transformRes.width = (data[4] - data[2]) * fw / dWidth;
|
||||
transformRes.top = (data[3] * fh - dy) / dHeight;
|
||||
transformRes.height = (data[5] - data[3]) * fh / dHeight;
|
||||
return transformRes;
|
||||
}
|
||||
}
|
||||
|
||||
export function createImage(imgPath: string): Promise<HTMLImageElement> {
|
||||
return new Promise(resolve => {
|
||||
const image = new Image();
|
||||
image.crossOrigin = 'anonymous';
|
||||
image.onload = () => {
|
||||
resolve(image);
|
||||
};
|
||||
image.src = imgPath;
|
||||
});
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Example:', () => {
|
||||
/**
|
||||
* Example
|
||||
*/
|
||||
describe('ExampleTest', () => {
|
||||
test('Hello World!', () => {
|
||||
assert.strictEqual('Hello World!', 'Hello World!')
|
||||
})
|
||||
})
|
||||
})
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
"declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
"removeComments": false, /* Disable emitting comments. */
|
||||
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/gesture/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/gesture/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -0,0 +1 @@
|
||||
auto-install-peers=true
|
@@ -0,0 +1,28 @@
|
||||
[中文版](./README_cn.md)
|
||||
|
||||
# gesture
|
||||
|
||||
gesture is a gesture recognition module, including two models: gesture_detect and gesture_rec. gesture_detect model is used to identify the palm area of the person in the image. gesture_rec model is used to recognize human gesture. The interface provided by the module is simple, users only need to pass in gesture images to get the results.
|
||||
|
||||
<img src="https://img.shields.io/npm/v/@paddle-js-models/gesture?color=success" alt="version"> <img src="https://img.shields.io/bundlephobia/min/@paddle-js-models/gesture" alt="size"> <img src="https://img.shields.io/npm/dm/@paddle-js-models/gesture?color=orange" alt="downloads"> <img src="https://img.shields.io/npm/dt/@paddle-js-models/gesture" alt="downloads">
|
||||
|
||||
# Usage
|
||||
|
||||
```js
|
||||
|
||||
import * as gesture from '@paddle-js-models/gesture';
|
||||
|
||||
// Load gesture_detect model and gesture_rec model
|
||||
await gesture.load();
|
||||
|
||||
// Get the image recognition results. The results include: palm frame coordinates and recognition results
|
||||
const res = await gesture.classify(img);
|
||||
|
||||
```
|
||||
|
||||
# Online experience
|
||||
|
||||
https://paddlejs.baidu.com/gesture
|
||||
|
||||
# Performance
|
||||
<img alt="gesture" src="https://user-images.githubusercontent.com/43414102/156379706-065a4f57-cc75-4457-857a-18619589492f.gif">
|
@@ -0,0 +1,28 @@
|
||||
[English](./README.md)
|
||||
|
||||
# gesture
|
||||
|
||||
gesture 为手势识别模块,包括两个模型:gesture_detect和gesture_rec。gesture_detect模型识别图片中人物手掌区域,gesture_rec模型识别人物手势。模块提供的接口简单,使用者只需传入手势图片即可获得结果。
|
||||
|
||||
<img src="https://img.shields.io/npm/v/@paddle-js-models/gesture?color=success" alt="version"> <img src="https://img.shields.io/bundlephobia/min/@paddle-js-models/gesture" alt="size"> <img src="https://img.shields.io/npm/dm/@paddle-js-models/gesture?color=orange" alt="downloads"> <img src="https://img.shields.io/npm/dt/@paddle-js-models/gesture" alt="downloads">
|
||||
|
||||
# 使用
|
||||
|
||||
```js
|
||||
import * as gesture from '@paddle-js-models/gesture';
|
||||
|
||||
// gesture_detect、gesture_rec模型加载
|
||||
await gesture.load();
|
||||
|
||||
// 获取图片识别结果。结果包括:手掌框选坐标和识别结果
|
||||
const res = await gesture.classify(img);
|
||||
|
||||
```
|
||||
|
||||
# 在线体验
|
||||
|
||||
https://paddlejs.baidu.com/gesture
|
||||
|
||||
# 效果
|
||||
<img alt="gesture" src="https://user-images.githubusercontent.com/43414102/156379706-065a4f57-cc75-4457-857a-18619589492f.gif">
|
||||
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./lib/index.d.ts",
|
||||
"bundledPackages": [],
|
||||
"docModel": {
|
||||
"enabled": true
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const paths = {
|
||||
root: path.join(__dirname, '../'),
|
||||
input: path.join(__dirname, '../src/index.ts'),
|
||||
lib: path.join(__dirname, '../lib'),
|
||||
}
|
||||
|
||||
export const log = {
|
||||
progress: (text: string) => {
|
||||
console.log(chalk.green(text))
|
||||
},
|
||||
error: (text: string) => {
|
||||
console.log(chalk.red(text))
|
||||
},
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
import path from 'path'
|
||||
import fse from 'fs-extra'
|
||||
import { series } from "gulp"
|
||||
import { paths, log } from "./build_package/util"
|
||||
import rollupConfig from './rollup.config'
|
||||
import { rollup } from 'rollup'
|
||||
import {
|
||||
Extractor,
|
||||
ExtractorConfig,
|
||||
ExtractorResult,
|
||||
} from '@microsoft/api-extractor'
|
||||
/**
|
||||
* 这里是由于 'conventional-changelog' 未提供类型文件
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
|
||||
interface TaskFunc {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(cb: Function): void
|
||||
}
|
||||
|
||||
const CHANGE_TRACE = ['paddlejs-models/gesture', 'paddle-js-models/gesture', 'paddlejs-models', 'paddle-js-models', 'all']
|
||||
|
||||
/**
|
||||
* 删除 lib 文件
|
||||
* @param cb
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clearLibFile: TaskFunc = async (cb) => {
|
||||
fse.removeSync(paths.lib)
|
||||
log.progress('Deleted lib file')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* rollup 打包
|
||||
* @param cb
|
||||
*/
|
||||
const buildByRollup: TaskFunc = async (cb) => {
|
||||
const inputOptions = {
|
||||
input: rollupConfig.input,
|
||||
external: rollupConfig.external,
|
||||
plugins: rollupConfig.plugins,
|
||||
}
|
||||
const outOptions = rollupConfig.output
|
||||
const bundle = await rollup(inputOptions)
|
||||
|
||||
// 写入需要遍历输出配置
|
||||
if (Array.isArray(outOptions)) {
|
||||
for (const outOption of outOptions) {
|
||||
await bundle.write(outOption)
|
||||
}
|
||||
cb()
|
||||
log.progress('Rollup built successfully')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* api-extractor 整理 .d.ts 文件
|
||||
* @param cb
|
||||
*/
|
||||
const apiExtractorGenerate: TaskFunc = async (cb) => {
|
||||
const apiExtractorJsonPath: string = path.join(__dirname, './api-extractor.json')
|
||||
// 加载并解析 api-extractor.json 文件
|
||||
const extractorConfig: ExtractorConfig = await ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath)
|
||||
// 判断是否存在 index.d.ts 文件,这里必须异步先访问一边,不然后面找不到会报错
|
||||
const isdtxExist: boolean = await fse.pathExists(extractorConfig.mainEntryPointFilePath)
|
||||
// 判断是否存在 etc 目录,api-extractor需要该目录存在
|
||||
const isEtcExist: boolean = await fse.pathExists('./etc')
|
||||
|
||||
if (!isdtxExist) {
|
||||
log.error('API Extractor not find index.d.ts')
|
||||
return
|
||||
}
|
||||
|
||||
if (!isEtcExist) {
|
||||
fse.mkdirSync('etc');
|
||||
log.progress('Create folder etc for API Extractor')
|
||||
}
|
||||
|
||||
// 调用 API
|
||||
const extractorResult: ExtractorResult = await Extractor.invoke(extractorConfig, {
|
||||
localBuild: true,
|
||||
// 在输出中显示信息
|
||||
showVerboseMessages: true,
|
||||
})
|
||||
|
||||
if (extractorResult.succeeded) {
|
||||
// 删除多余的 .d.ts 文件
|
||||
const libFiles: string[] = await fse.readdir(paths.lib)
|
||||
for (const file of libFiles) {
|
||||
if (file.endsWith('.d.ts') && !file.includes('index')) {
|
||||
await fse.remove(path.join(paths.lib, file))
|
||||
}
|
||||
}
|
||||
log.progress('API Extractor completed successfully')
|
||||
// api-extractor 会生成 temp 文件夹,完成后进行删除
|
||||
fse.ensureDirSync('temp')
|
||||
fse.removeSync('temp')
|
||||
cb()
|
||||
} else {
|
||||
log.error(`API Extractor completed with ${extractorResult.errorCount} errors`
|
||||
+ ` and ${extractorResult.warningCount} warnings`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
* @param cb
|
||||
*/
|
||||
const complete: TaskFunc = (cb) => {
|
||||
log.progress('---- end ----')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CHANGELOG
|
||||
* @param cb
|
||||
*/
|
||||
export const changelog: TaskFunc = async (cb) => {
|
||||
const checkTrace = (chunk: string) => {
|
||||
for (const keyWord of CHANGE_TRACE) {
|
||||
if (chunk.includes(keyWord)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const changelogPath: string = path.join(paths.root, 'CHANGELOG.md')
|
||||
// 对命令 conventional-changelog -p angular -i CHANGELOG.md -w -r 0
|
||||
const changelogPipe = await conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 0,
|
||||
})
|
||||
changelogPipe.setEncoding('utf8')
|
||||
|
||||
const resultArray = ['# 更新日志\n\n']
|
||||
changelogPipe.on('data', (chunk) => {
|
||||
// 原来的 commits 路径是进入提交列表
|
||||
chunk = chunk.replace(/\/commits\//g, '/commit/')
|
||||
/**
|
||||
* title 或 指定跟踪 才会写入CHANGELOG
|
||||
*/
|
||||
for (const log of chunk.split("\n")) {
|
||||
if (log.includes('# ') || log.includes('### ') || checkTrace(log)) {
|
||||
resultArray.push(log+"\n\n")
|
||||
}
|
||||
}
|
||||
})
|
||||
changelogPipe.on('end', async () => {
|
||||
fse.createWriteStream(changelogPath).write(resultArray.join(''))
|
||||
cb()
|
||||
log.progress('CHANGELOG generation completed')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.build = series(clearLibFile, buildByRollup, apiExtractorGenerate, complete)
|
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@paddle-js-models/gesture",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.esm.js",
|
||||
"typings": "lib/index.d.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README_cn.md"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "yalc publish --push",
|
||||
"prepublish": "pnpm lint & pnpm test",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "gulp build",
|
||||
"lint": "eslint --ext .js,.ts src --fix",
|
||||
"api": "api-extractor run",
|
||||
"test": "jest --coverage --verbose -u",
|
||||
"changelog": "gulp changelog"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/d3-polygon": "^3.0.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chalk": "4.1.2",
|
||||
"commitlint": "^17.1.2",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"husky": "^8.0.1",
|
||||
"jest": "^29.0.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.34.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsify": "^5.0.4",
|
||||
"typescript": "^4.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paddlejs/paddlejs-backend-webgl": "^1.2.9",
|
||||
"@paddlejs/paddlejs-core": "^2.2.0"
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
import path from 'path'
|
||||
import { RollupOptions } from 'rollup'
|
||||
import { string } from "rollup-plugin-string";
|
||||
import rollupTypescript from 'rollup-plugin-typescript2'
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import { eslint } from 'rollup-plugin-eslint'
|
||||
import { DEFAULT_EXTENSIONS } from '@babel/core'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { paths } from "./build_package/util";
|
||||
|
||||
|
||||
// rollup 配置项
|
||||
const rollupConfig: RollupOptions = {
|
||||
input: paths.input,
|
||||
output: [
|
||||
// 输出 commonjs 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.js'),
|
||||
format: 'cjs',
|
||||
name: pkg.name,
|
||||
},
|
||||
// 输出 es 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.esm.js'),
|
||||
format: 'es',
|
||||
name: pkg.name,
|
||||
},
|
||||
],
|
||||
external: ['@paddlejs-mediapipe/opencv',
|
||||
'@paddlejs/paddlejs-backend-webgl',
|
||||
'@paddlejs/paddlejs-core',
|
||||
'@types/node',
|
||||
'd3-polygon',
|
||||
'js-clipper',
|
||||
'number-precision'],
|
||||
// plugins 需要注意引用顺序
|
||||
plugins: [
|
||||
eslint({
|
||||
throwOnError: true,
|
||||
throwOnWarning: false,
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['node_modules/**', 'lib/**', '*.js'],
|
||||
}),
|
||||
|
||||
// 处理txt文件
|
||||
string({
|
||||
include: "src/anchor_small.txt"
|
||||
}),
|
||||
// 使得 rollup 支持 commonjs 规范,识别 commonjs 规范的依赖
|
||||
commonjs(),
|
||||
|
||||
// 配合 commnjs 解析第三方模块
|
||||
resolve({
|
||||
// 将自定义选项传递给解析插件
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
rollupTypescript(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
// 只转换源代码,不运行外部依赖
|
||||
exclude: 'node_modules/**',
|
||||
// babel 默认不支持 ts 需要手动添加
|
||||
extensions: [
|
||||
...DEFAULT_EXTENSIONS,
|
||||
'.ts',
|
||||
],
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export default rollupConfig
|
@@ -0,0 +1,100 @@
|
||||
export default class LMProcess {
|
||||
private input_n: number;
|
||||
private input_h: number;
|
||||
private input_w: number;
|
||||
private input_c: number;
|
||||
private class_dim: number;
|
||||
private points_num: number;
|
||||
private result: any;
|
||||
private conf!: number;
|
||||
private kp: any;
|
||||
private forefinger: any;
|
||||
private output_softmax: any;
|
||||
type!: string;
|
||||
|
||||
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开始
|
||||
const offset = 1;
|
||||
const result = this.result;
|
||||
this.kp = [];
|
||||
for (let i = 0; i < this.points_num; i++) {
|
||||
const arr: number[] = [];
|
||||
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;
|
||||
const offset = 2;
|
||||
const class_dim = this.class_dim = 7;
|
||||
const result = this.result;
|
||||
const 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.9) {
|
||||
const ges = ['其他', '布', '剪刀', '石头', '1', 'ok'];
|
||||
this.type = ges[label_index];
|
||||
return;
|
||||
}
|
||||
this.type = 'other';
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,414 @@
|
||||
import WarpAffine from './warpAffine';
|
||||
|
||||
class DetectProcess {
|
||||
private modelResult: any;
|
||||
private output_size: number;
|
||||
private anchor_num: number;
|
||||
private detectResult: any;
|
||||
private hasHand: number;
|
||||
private originCanvas: HTMLCanvasElement;
|
||||
private originImageData: ImageData;
|
||||
private maxIndex!: number;
|
||||
private box: any;
|
||||
private feed: any;
|
||||
private anchors: any;
|
||||
private mtr: any;
|
||||
private source!: any[][];
|
||||
private imageData!: ImageData;
|
||||
private pxmin: any;
|
||||
private pxmax: any;
|
||||
private pymin: any;
|
||||
private pymax: any;
|
||||
private kp!: number[][];
|
||||
private tw!: number;
|
||||
private th!: number;
|
||||
private triangle: any;
|
||||
private results: any;
|
||||
|
||||
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;
|
||||
const ctx = this.originCanvas.getContext('2d');
|
||||
this.originImageData = ctx!.getImageData(0, 0, 256, 256);
|
||||
}
|
||||
|
||||
async outputBox(results) {
|
||||
this.getMaxIndex();
|
||||
if (this.maxIndex === -1) {
|
||||
this.hasHand = 0;
|
||||
return false;
|
||||
}
|
||||
this.hasHand = 1;
|
||||
await this.splitAnchors(results);
|
||||
this.decodeAnchor();
|
||||
// 求关键点
|
||||
this.decodeKp();
|
||||
// 求手势框
|
||||
this.decodeBox();
|
||||
return this.box;
|
||||
}
|
||||
|
||||
async outputFeed(paddle) {
|
||||
this.decodeTriangle();
|
||||
this.decodeSource();
|
||||
this.outputResult();
|
||||
// 提取仿射变化矩阵
|
||||
this.getaffinetransform();
|
||||
// 对图片进行仿射变换
|
||||
await this.warpAffine();
|
||||
this.allReshapeToRGB(paddle);
|
||||
return this.feed;
|
||||
}
|
||||
|
||||
async splitAnchors(results) {
|
||||
this.results = results;
|
||||
const anchors: number[][] = new Array(this.anchor_num).fill('').map(() => []);
|
||||
const anchor_num = this.anchor_num;
|
||||
for (let i = 0; i < anchor_num; i++) {
|
||||
const tmp0 = results[i * 4];
|
||||
const tmp1 = results[i * 4 + 1];
|
||||
const tmp2 = results[i * 4 + 2];
|
||||
const tmp3 = results[i * 4 + 3];
|
||||
anchors[i] = [
|
||||
tmp0 - tmp2 / 2.0,
|
||||
tmp1 - tmp3 / 2.0,
|
||||
tmp0 + tmp2 / 2.0,
|
||||
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;
|
||||
}
|
||||
}
|
||||
this.anchors = anchors;
|
||||
}
|
||||
|
||||
getMaxIndex() {
|
||||
let maxIndex = -1;
|
||||
let maxConf = -1;
|
||||
let curConf = -2.0;
|
||||
const 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() {
|
||||
const index = this.maxIndex;
|
||||
const anchors = this.anchors;
|
||||
this.pxmin = anchors[index][0];
|
||||
this.pymin = anchors[index][1];
|
||||
this.pxmax = anchors[index][2];
|
||||
this.pymax = anchors[index][3];
|
||||
}
|
||||
|
||||
decodeKp() {
|
||||
const modelResult = this.modelResult;
|
||||
const index = this.maxIndex;
|
||||
const px = (this.pxmin + this.pxmax) / 2;
|
||||
const py = (this.pymin + this.pymax) / 2;
|
||||
const pw = this.pxmax - this.pxmin;
|
||||
const ph = this.pymax - this.pymin;
|
||||
const prior_var = 0.1;
|
||||
const kp: number[][] = [[], [], []];
|
||||
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() {
|
||||
const modelResult = this.modelResult;
|
||||
const output_size = this.output_size || 10;
|
||||
const pw = this.pxmax - this.pxmin;
|
||||
const ph = this.pymax - this.pymin;
|
||||
const px = this.pxmin + pw / 2;
|
||||
const py = this.pymin + ph / 2;
|
||||
const prior_var = 0.1;
|
||||
const index = this.maxIndex;
|
||||
|
||||
const ox = modelResult[output_size * index + 2];
|
||||
const oy = modelResult[output_size * index + 3];
|
||||
const ow = modelResult[output_size * index + 4];
|
||||
const oh = modelResult[output_size * index + 5];
|
||||
|
||||
const tx = ox * prior_var * pw + px;
|
||||
const ty = oy * prior_var * ph + py;
|
||||
const tw = this.tw = Math.pow(2.71828182, prior_var * ow) * pw;
|
||||
const th = this.th = Math.pow(2.71828182, prior_var * oh) * ph;
|
||||
const box: number[][] = [[], [], [], []];
|
||||
box[0][0] = (tx - tw / 2) * 256;
|
||||
box[0][1] = (ty - th / 2) * 256;
|
||||
box[1][0] = (tx + tw / 2) * 256;
|
||||
box[1][1] = (ty - th / 2) * 256;
|
||||
box[2][0] = (tx + tw / 2) * 256;
|
||||
box[2][1] = (ty + th / 2) * 256;
|
||||
box[3][0] = (tx - tw / 2) * 256;
|
||||
box[3][1] = (ty + th / 2) * 256;
|
||||
this.box = box;
|
||||
}
|
||||
|
||||
decodeTriangle() {
|
||||
const box_enlarge = 1.04;
|
||||
const side = Math.max(this.tw * 256, this.th * 256) * (box_enlarge);
|
||||
const dir_v: number[] = [];
|
||||
const kp = this.kp;
|
||||
const triangle: number[][] = [[], [], []];
|
||||
const dir_v_r: number[] = [];
|
||||
|
||||
dir_v[0] = kp[2][0] - kp[0][0];
|
||||
dir_v[1] = kp[2][1] - kp[0][1];
|
||||
const 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() {
|
||||
const kp = this.kp;
|
||||
const box_shift = 0.0;
|
||||
const tmp0 = (kp[0][0] - kp[2][0]) * box_shift;
|
||||
const tmp1 = (kp[0][1] - kp[2][1]) * box_shift;
|
||||
const source: number[][] = [[], [], []];
|
||||
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() {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this.detectResult[i][0] = this.box[i][0];
|
||||
this.detectResult[i][1] = this.box[i][1];
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
getaffinetransform() {
|
||||
// 图像上的原始坐标点。需要对所有坐标进行归一化,_x = (x - 128) / 128, _y = (128 - y) / 128
|
||||
// 坐标矩阵
|
||||
// x1 x2 x3
|
||||
// y1 y2 y3
|
||||
// z1 z2 z3
|
||||
const originPoints = [].concat(this.detectResult[11][0] as number / 128 - 1 as never)
|
||||
.concat(this.detectResult[12][0] / 128 - 1 as number as never)
|
||||
.concat(this.detectResult[13][0] / 128 - 1 as number as never)
|
||||
.concat(1 - this.detectResult[11][1] / 128 as number as never)
|
||||
.concat(1 - this.detectResult[12][1] / 128 as number as never)
|
||||
.concat(1 - this.detectResult[13][1] / 128 as number as never)
|
||||
.concat([1, 1, 1] as never[]);
|
||||
// originPoints = [0, 0, -1, .1, 1.1, 0.1, 1, 1, 1];
|
||||
const matrixA = new Matrix(3, 3, originPoints);
|
||||
// 转化后的点[128, 128, 0, 128, 0, 128] [0, 0, -1, 0, 1, 0]
|
||||
const matrixB = new Matrix(2, 3, [0, 0, -1, 0, -1, 0]);
|
||||
// M * A = B => M = B * A逆
|
||||
let _matrixA: any = inverseMatrix(matrixA.data);
|
||||
_matrixA = new Matrix(3, 3, _matrixA);
|
||||
this.mtr = Matrix_Product(matrixB, _matrixA);
|
||||
}
|
||||
|
||||
async warpAffine() {
|
||||
const ctx = this.originCanvas.getContext('2d');
|
||||
this.originImageData = ctx!.getImageData(0, 0, 256, 256);
|
||||
const imageDataArr = await WarpAffine.main({
|
||||
imageData: this.originImageData,
|
||||
mtr: this.mtr.data,
|
||||
input: {
|
||||
width: 256,
|
||||
height: 256
|
||||
},
|
||||
output: {
|
||||
width: 224,
|
||||
height: 224
|
||||
}
|
||||
});
|
||||
this.imageData = new ImageData(Uint8ClampedArray.from(imageDataArr), 224, 224);
|
||||
}
|
||||
|
||||
allReshapeToRGB(paddle) {
|
||||
const data = paddle.mediaProcessor.allReshapeToRGB(this.imageData, {
|
||||
gapFillWith: '#000',
|
||||
mean: [0, 0, 0],
|
||||
scale: 224,
|
||||
std: [1, 1, 1],
|
||||
targetShape: [1, 3, 224, 224],
|
||||
normalizeType: 1
|
||||
});
|
||||
this.feed = [{
|
||||
data: new Float32Array(data),
|
||||
shape: [1, 3, 224, 224],
|
||||
name: 'image',
|
||||
canvas: this.originImageData
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
class Matrix {
|
||||
private row: number;
|
||||
private col: number;
|
||||
data: number[] | number[][];
|
||||
|
||||
constructor(row, col, arr: number[] | number[][] = []) {
|
||||
// 行
|
||||
this.row = row;
|
||||
// 列
|
||||
this.col = col;
|
||||
if (arr[0] && arr[0] instanceof Array) {
|
||||
this.data = arr;
|
||||
}
|
||||
else {
|
||||
this.data = [];
|
||||
const _arr: number[] = [].concat(arr as never[]);
|
||||
// 创建row个元素的空数组
|
||||
const Matrix = new Array(row);
|
||||
// 对第一层数组遍历
|
||||
for (let i = 0; i < row; i++) {
|
||||
// 每一行创建col列的空数组
|
||||
Matrix[i] = new Array(col).fill('');
|
||||
Matrix[i].forEach((_item, index, cur) => {
|
||||
cur[index] = _arr.shift() || 0;
|
||||
});
|
||||
}
|
||||
// 将矩阵保存到this.data上
|
||||
this.data = Matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Matrix_Product = (A, B) => {
|
||||
const 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;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 求行列式
|
||||
const determinant = matrix => {
|
||||
const order = matrix.length;
|
||||
let cofactor;
|
||||
let result = 0;
|
||||
if (order === 1) {
|
||||
return matrix[0][0];
|
||||
}
|
||||
for (let i = 0; i < order; i++) {
|
||||
cofactor = [];
|
||||
for (let j = 0; j < order - 1; j++) {
|
||||
cofactor[j] = [];
|
||||
for (let 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) {
|
||||
const row = matrix.length;
|
||||
const col = matrix[0].length;
|
||||
const result: number[][] = [];
|
||||
for (let i = 0; i < row; i++) {
|
||||
result[i] = [];
|
||||
for (let j = 0; j < col; j++) {
|
||||
result[i][j] = num * matrix[i][j];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 求逆矩阵
|
||||
function inverseMatrix(matrix) {
|
||||
if (determinant(matrix) === 0) {
|
||||
return false;
|
||||
}
|
||||
// 求代数余子式
|
||||
function cofactor(matrix, row, col) {
|
||||
const order = matrix.length;
|
||||
const new_matrix: number[][] = [];
|
||||
let _row;
|
||||
let _col;
|
||||
for (let i = 0; i < order - 1; i++) {
|
||||
new_matrix[i] = [];
|
||||
_row = i < row ? i : i + 1;
|
||||
for (let 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);
|
||||
}
|
||||
const order = matrix.length;
|
||||
const adjoint: number[][] = [];
|
||||
for (let i = 0; i < order; i++) {
|
||||
adjoint[i] = [];
|
||||
for (let j = 0; j < order; j++) {
|
||||
adjoint[i][j] = cofactor(matrix, j, i);
|
||||
}
|
||||
}
|
||||
return scalarMultiply(1 / determinant(matrix), adjoint);
|
||||
}
|
||||
|
||||
export default DetectProcess;
|
1
examples/application/js/package/packages/paddlejs-models/gesture/src/global.d.ts
vendored
Normal file
1
examples/application/js/package/packages/paddlejs-models/gesture/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module '*.txt';
|
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file gesture model
|
||||
*/
|
||||
|
||||
import { Runner } from '@paddlejs/paddlejs-core';
|
||||
import '@paddlejs/paddlejs-backend-webgl';
|
||||
import WarpAffine from './warpAffine';
|
||||
import DetectProcess from './detectProcess';
|
||||
import LMProcess from './LMProcess';
|
||||
import anchor from './anchor_small.txt';
|
||||
|
||||
let box = null;
|
||||
let detectRunner = null as Runner;
|
||||
let recRunner = null as Runner;
|
||||
let anchorResults = null;
|
||||
const detFeedShape = 256;
|
||||
const canvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
initCanvas();
|
||||
|
||||
function initCanvas() {
|
||||
canvas.width = detFeedShape;
|
||||
canvas.height = detFeedShape;
|
||||
canvas.style.position = 'absolute';
|
||||
canvas.style.top = '0';
|
||||
canvas.style.left = '0';
|
||||
canvas.style.zIndex = '-1';
|
||||
canvas.style.opacity = '0';
|
||||
document.body.appendChild(canvas);
|
||||
}
|
||||
|
||||
export async function load() {
|
||||
anchorResults = anchor.replace(/\s+/g, ',').split(',').map(item => +item);
|
||||
|
||||
detectRunner = new Runner({
|
||||
modelPath: 'https://paddlejs.bj.bcebos.com/models/fuse/gesture/gesture_det_fuse_activation/model.json',
|
||||
webglFeedProcess: true
|
||||
});
|
||||
const detectInit = detectRunner.init();
|
||||
|
||||
recRunner = new Runner({
|
||||
modelPath: 'https://paddlejs.bj.bcebos.com/models/fuse/gesture/gesture_rec_fuse_activation/model.json'
|
||||
});
|
||||
const recInit = recRunner.init();
|
||||
|
||||
WarpAffine.init({
|
||||
width: 224,
|
||||
height: 224
|
||||
});
|
||||
|
||||
return await Promise.all([detectInit, recInit]);
|
||||
}
|
||||
|
||||
export async function classify(image): Promise<{ box: string | number[][], type: string }> {
|
||||
canvas.getContext('2d')!.drawImage(image, 0, 0, detFeedShape, detFeedShape);
|
||||
const res = await detectRunner.predict(image);
|
||||
const post = new DetectProcess(res, canvas);
|
||||
const result = {
|
||||
box: '',
|
||||
type: ''
|
||||
};
|
||||
box = await post.outputBox(anchorResults);
|
||||
if (box) {
|
||||
// 手势框选位置
|
||||
result.box = box;
|
||||
const feed = await post.outputFeed(recRunner);
|
||||
const res2 = await recRunner.predictWithFeed(feed);
|
||||
const lmProcess = new LMProcess(res2);
|
||||
lmProcess.output();
|
||||
// 识别结果
|
||||
result.type = lmProcess.type || '';
|
||||
}
|
||||
return result;
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
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: number[][] = null as unknown as number[][];
|
||||
let gl: any = null;
|
||||
|
||||
function init(output) {
|
||||
const 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)) {
|
||||
throw new Error('initShaders false');
|
||||
}
|
||||
}
|
||||
function main(opt) {
|
||||
const {
|
||||
imageData,
|
||||
mtr,
|
||||
output
|
||||
} = 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);
|
||||
const 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');
|
||||
|
||||
const 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
|
||||
|
||||
]);
|
||||
|
||||
const 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);
|
||||
}
|
||||
|
||||
function initShaders(gl, vshader, fshader) {
|
||||
const vertexShader = createShader(gl, vshader, gl.VERTEX_SHADER);
|
||||
const fragmentShader = createShader(gl, fshader, gl.FRAGMENT_SHADER);
|
||||
|
||||
const 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
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, sourceCode);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
const info = gl.getShaderInfoLog(shader);
|
||||
throw Error('Could not compile WebGL program. \n\n' + info);
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
export default {
|
||||
main,
|
||||
init
|
||||
};
|
@@ -0,0 +1,12 @@
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Example:', () => {
|
||||
/**
|
||||
* Example
|
||||
*/
|
||||
describe('ExampleTest', () => {
|
||||
test('Hello World!', () => {
|
||||
assert.strictEqual('Hello World!', 'Hello World!')
|
||||
})
|
||||
})
|
||||
})
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
"declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
"removeComments": false, /* Disable emitting comments. */
|
||||
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/humanseg/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/humanseg/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -0,0 +1 @@
|
||||
auto-install-peers=true
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./lib/src/index.d.ts",
|
||||
"bundledPackages": [],
|
||||
"docModel": {
|
||||
"enabled": true
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const paths = {
|
||||
root: path.join(__dirname, '../'),
|
||||
input: path.join(__dirname, '../src/index.ts'),
|
||||
lib: path.join(__dirname, '../lib'),
|
||||
}
|
||||
|
||||
export const log = {
|
||||
progress: (text: string) => {
|
||||
console.log(chalk.green(text))
|
||||
},
|
||||
error: (text: string) => {
|
||||
console.log(chalk.red(text))
|
||||
},
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
import path from 'path'
|
||||
import fse from 'fs-extra'
|
||||
import { series } from "gulp"
|
||||
import { paths, log } from "./build_package/util"
|
||||
import rollupConfig from './rollup.config'
|
||||
import { rollup } from 'rollup'
|
||||
import {
|
||||
Extractor,
|
||||
ExtractorConfig,
|
||||
ExtractorResult,
|
||||
} from '@microsoft/api-extractor'
|
||||
/**
|
||||
* 这里是由于 'conventional-changelog' 未提供类型文件
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
|
||||
interface TaskFunc {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(cb: Function): void
|
||||
}
|
||||
|
||||
const CHANGE_TRACE = ['paddlejs-models/humanseg', 'paddle-js-models/humanseg', 'paddlejs-models', 'paddle-js-models', 'all']
|
||||
|
||||
/**
|
||||
* 删除 lib 文件
|
||||
* @param cb
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clearLibFile: TaskFunc = async (cb) => {
|
||||
fse.removeSync(paths.lib)
|
||||
log.progress('Deleted lib file')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* rollup 打包
|
||||
* @param cb
|
||||
*/
|
||||
const buildByRollup: TaskFunc = async (cb) => {
|
||||
const inputOptions = {
|
||||
input: rollupConfig.input,
|
||||
external: rollupConfig.external,
|
||||
plugins: rollupConfig.plugins,
|
||||
}
|
||||
const outOptions = rollupConfig.output
|
||||
const bundle = await rollup(inputOptions)
|
||||
|
||||
// 写入需要遍历输出配置
|
||||
if (Array.isArray(outOptions)) {
|
||||
for (const outOption of outOptions) {
|
||||
await bundle.write(outOption)
|
||||
}
|
||||
cb()
|
||||
log.progress('Rollup built successfully')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* api-extractor 整理 .d.ts 文件
|
||||
* @param cb
|
||||
*/
|
||||
const apiExtractorGenerate: TaskFunc = async (cb) => {
|
||||
const apiExtractorJsonPath: string = path.join(__dirname, './api-extractor.json')
|
||||
// 加载并解析 api-extractor.json 文件
|
||||
const extractorConfig: ExtractorConfig = await ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath)
|
||||
// 判断是否存在 index.d.ts 文件,这里必须异步先访问一边,不然后面找不到会报错
|
||||
const isdtxExist: boolean = await fse.pathExists(extractorConfig.mainEntryPointFilePath)
|
||||
// 判断是否存在 etc 目录,api-extractor需要该目录存在
|
||||
const isEtcExist: boolean = await fse.pathExists('./etc')
|
||||
|
||||
if (!isdtxExist) {
|
||||
log.error('API Extractor not find index.d.ts')
|
||||
return
|
||||
}
|
||||
|
||||
if (!isEtcExist) {
|
||||
fse.mkdirSync('etc');
|
||||
log.progress('Create folder etc for API Extractor')
|
||||
}
|
||||
|
||||
// 调用 API
|
||||
const extractorResult: ExtractorResult = await Extractor.invoke(extractorConfig, {
|
||||
localBuild: true,
|
||||
// 在输出中显示信息
|
||||
showVerboseMessages: true,
|
||||
})
|
||||
|
||||
if (extractorResult.succeeded) {
|
||||
// 删除多余的 .d.ts 文件
|
||||
const libFiles: string[] = await fse.readdir(paths.lib)
|
||||
for (const file of libFiles) {
|
||||
if (file.endsWith('.d.ts') && !file.includes('index')) {
|
||||
await fse.remove(path.join(paths.lib, file))
|
||||
}
|
||||
}
|
||||
log.progress('API Extractor completed successfully')
|
||||
// api-extractor 会生成 temp 文件夹,完成后进行删除
|
||||
fse.ensureDirSync('temp')
|
||||
fse.removeSync('temp')
|
||||
cb()
|
||||
} else {
|
||||
log.error(`API Extractor completed with ${extractorResult.errorCount} errors`
|
||||
+ ` and ${extractorResult.warningCount} warnings`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
* @param cb
|
||||
*/
|
||||
const complete: TaskFunc = (cb) => {
|
||||
log.progress('---- end ----')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CHANGELOG
|
||||
* @param cb
|
||||
*/
|
||||
export const changelog: TaskFunc = async (cb) => {
|
||||
const checkTrace = (chunk: string) => {
|
||||
for (const keyWord of CHANGE_TRACE) {
|
||||
if (chunk.includes(keyWord)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const changelogPath: string = path.join(paths.root, 'CHANGELOG.md')
|
||||
// 对命令 conventional-changelog -p angular -i CHANGELOG.md -w -r 0
|
||||
const changelogPipe = await conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 0,
|
||||
})
|
||||
changelogPipe.setEncoding('utf8')
|
||||
|
||||
const resultArray = ['# 更新日志\n\n']
|
||||
changelogPipe.on('data', (chunk) => {
|
||||
// 原来的 commits 路径是进入提交列表
|
||||
chunk = chunk.replace(/\/commits\//g, '/commit/')
|
||||
/**
|
||||
* title 或 指定跟踪 才会写入CHANGELOG
|
||||
*/
|
||||
for (const log of chunk.split("\n")) {
|
||||
if (log.includes('# ') || log.includes('### ') || checkTrace(log)) {
|
||||
resultArray.push(log+"\n\n")
|
||||
}
|
||||
}
|
||||
})
|
||||
changelogPipe.on('end', async () => {
|
||||
fse.createWriteStream(changelogPath).write(resultArray.join(''))
|
||||
cb()
|
||||
log.progress('CHANGELOG generation completed')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.build = series(clearLibFile, buildByRollup, apiExtractorGenerate, complete)
|
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@paddle-js-models/humanseg",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.esm.js",
|
||||
"typings": "lib/index.d.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README_cn.md"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "yalc publish --push",
|
||||
"prepublish": "pnpm lint & pnpm test",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "gulp build",
|
||||
"lint": "eslint --ext .js,.ts src --fix",
|
||||
"api": "api-extractor run",
|
||||
"test": "jest --coverage --verbose -u",
|
||||
"changelog": "gulp changelog"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/d3-polygon": "^3.0.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chalk": "4.1.2",
|
||||
"commitlint": "^17.1.2",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"husky": "^8.0.1",
|
||||
"jest": "^29.0.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.34.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsify": "^5.0.4",
|
||||
"typescript": "^4.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paddlejs/paddlejs-backend-webgl": "^1.2.9",
|
||||
"@paddlejs/paddlejs-core": "^2.2.0"
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import path from 'path'
|
||||
import { RollupOptions } from 'rollup'
|
||||
import rollupTypescript from 'rollup-plugin-typescript2'
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import { eslint } from 'rollup-plugin-eslint'
|
||||
import { DEFAULT_EXTENSIONS } from '@babel/core'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { paths } from "./build_package/util";
|
||||
|
||||
|
||||
// rollup 配置项
|
||||
const rollupConfig: RollupOptions = {
|
||||
input: paths.input,
|
||||
output: [
|
||||
// 输出 commonjs 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.js'),
|
||||
format: 'cjs',
|
||||
name: pkg.name,
|
||||
},
|
||||
// 输出 es 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.esm.js'),
|
||||
format: 'es',
|
||||
name: pkg.name,
|
||||
},
|
||||
],
|
||||
external: ['@paddlejs-mediapipe/opencv',
|
||||
'@paddlejs/paddlejs-backend-webgl',
|
||||
'@paddlejs/paddlejs-core',
|
||||
'@types/node',
|
||||
'd3-polygon',
|
||||
'js-clipper',
|
||||
'number-precision'],
|
||||
// plugins 需要注意引用顺序
|
||||
plugins: [
|
||||
eslint({
|
||||
throwOnError: true,
|
||||
throwOnWarning: false,
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['node_modules/**', 'lib/**', '*.js'],
|
||||
}),
|
||||
|
||||
// 使得 rollup 支持 commonjs 规范,识别 commonjs 规范的依赖
|
||||
commonjs(),
|
||||
|
||||
// 配合 commnjs 解析第三方模块
|
||||
resolve({
|
||||
// 将自定义选项传递给解析插件
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
rollupTypescript(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
// 只转换源代码,不运行外部依赖
|
||||
exclude: 'node_modules/**',
|
||||
// babel 默认不支持 ts 需要手动添加
|
||||
extensions: [
|
||||
...DEFAULT_EXTENSIONS,
|
||||
'.ts',
|
||||
],
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export default rollupConfig
|
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file seg or blur origin image
|
||||
*/
|
||||
|
||||
function mainFunc({
|
||||
out
|
||||
}) {
|
||||
const THRESHHOLD = 0.2;
|
||||
return `
|
||||
|
||||
#define SIGMA SIGMA 3.0
|
||||
#define BLUR_MSIZE 8
|
||||
#define MSIZE 3
|
||||
#define kernelSize 5.0
|
||||
#define weight 1.0
|
||||
|
||||
uniform int type; // 0: blurBackground 1: drawHumanseg 2: drawMask
|
||||
|
||||
void main() {
|
||||
vec2 outCoord = vCoord.xy;
|
||||
|
||||
outCoord.y = 1.0 - vCoord.y;
|
||||
|
||||
vec2 sourceTextureSize = vec2(${out.width_shape}, ${out.height_shape});
|
||||
vec2 sourceTexelSize = 1.0 / sourceTextureSize;
|
||||
|
||||
float kernel[MSIZE]; // 3
|
||||
kernel[0] = 0.12579369017522166;
|
||||
kernel[1] = 0.13298;
|
||||
kernel[2] = 0.12579369017522166;
|
||||
|
||||
float origin_alpha = 1.0 - TEXTURE2D(texture_origin, vec2(outCoord.x, outCoord.y) / 2.0).r;
|
||||
vec4 counter = TEXTURE2D(texture_counter, outCoord.xy);
|
||||
vec4 res = vec4(0.0);
|
||||
|
||||
if (type == 0) {
|
||||
// Simple Cheap Box Blur
|
||||
float pixelSizeX = 1.0 / float(${out.width_shape});
|
||||
float pixelSizeY = 1.0 / float(${out.height_shape});
|
||||
|
||||
// Horizontal Blur
|
||||
vec4 accumulation = vec4(0);
|
||||
float weightsum = 0.0;
|
||||
for (float i = -kernelSize; i <= kernelSize; i++){
|
||||
accumulation += TEXTURE2D(texture_counter, outCoord.xy + vec2(i * pixelSizeX, 0.0)) * weight;
|
||||
weightsum += weight;
|
||||
}
|
||||
// Vertical Blur
|
||||
for (float i = -kernelSize; i <= kernelSize; i++){
|
||||
accumulation += TEXTURE2D(texture_counter, outCoord.xy + vec2(0.0, i * pixelSizeY)) * weight;
|
||||
weightsum += weight;
|
||||
}
|
||||
|
||||
res = accumulation / weightsum;
|
||||
if (origin_alpha > ${THRESHHOLD}) {
|
||||
res = counter;
|
||||
}
|
||||
}
|
||||
else if (type == 1) {
|
||||
res = counter;
|
||||
res.a = origin_alpha;
|
||||
}
|
||||
else if (type == 2) {
|
||||
if (origin_alpha > ${THRESHHOLD}) {
|
||||
res = vec4(1.0);
|
||||
res.a = origin_alpha;
|
||||
}
|
||||
}
|
||||
|
||||
setPackedOutput(res);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export default {
|
||||
mainFunc,
|
||||
textureFuncConf: {
|
||||
origin: [],
|
||||
counter: []
|
||||
}
|
||||
};
|
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file add deal origin op
|
||||
*/
|
||||
|
||||
import { Transformer, env, interfaces } from '@paddlejs/paddlejs-core';
|
||||
|
||||
const IMG_ORIGIN = 'image';
|
||||
const FINAL_PACK_OP_NAME = 'fetch_pack';
|
||||
const DEFAULT_WIDTH = 500;
|
||||
const DEFAULT_HEIGHT = 280;
|
||||
|
||||
export default class DealOrigin extends Transformer {
|
||||
private width;
|
||||
private height;
|
||||
|
||||
constructor(width?: number, height?: number) {
|
||||
super('DealOrigin');
|
||||
this.width = width || DEFAULT_WIDTH;
|
||||
this.height = height || DEFAULT_HEIGHT;
|
||||
}
|
||||
|
||||
transform(...args: any) {
|
||||
if (!env.get('webgl_gpu_pipeline')) {
|
||||
return;
|
||||
}
|
||||
const [ops, vars] = args;
|
||||
const fetchOp = ops.find(item => item.type === 'fetch');
|
||||
const [inputName] = fetchOp.inputs.X;
|
||||
|
||||
const segImgOp = {
|
||||
attrs: {},
|
||||
inputs: {
|
||||
X: [inputName],
|
||||
Y: [IMG_ORIGIN]
|
||||
},
|
||||
outputs: {
|
||||
Out: [FINAL_PACK_OP_NAME]
|
||||
},
|
||||
type: 'segImg',
|
||||
isPacked: true,
|
||||
bufferType: interfaces.BufferType.ColorBuffer,
|
||||
uniform: {
|
||||
type: {
|
||||
type: '1i',
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const packOutVar = {
|
||||
name: FINAL_PACK_OP_NAME,
|
||||
shape: [1, 1, this.height, this.width],
|
||||
persistable: false
|
||||
};
|
||||
|
||||
fetchOp.inputs.X = [FINAL_PACK_OP_NAME];
|
||||
ops.push(segImgOp);
|
||||
if (vars instanceof Array) {
|
||||
vars.push(...[packOutVar]);
|
||||
}
|
||||
else {
|
||||
vars[FINAL_PACK_OP_NAME] = packOutVar;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @file humanseg model
|
||||
*/
|
||||
|
||||
import { Runner, env } from '@paddlejs/paddlejs-core';
|
||||
import '@paddlejs/paddlejs-backend-webgl';
|
||||
import WebGLImageFilter from '../thirdParty/webgl-image-filter';
|
||||
|
||||
let runner = null as Runner;
|
||||
let inputElement: any = null;
|
||||
|
||||
let WIDTH = 398;
|
||||
let HEIGHT = 224;
|
||||
|
||||
let backgroundSize: any = null;
|
||||
|
||||
const blurFilter = new WebGLImageFilter();
|
||||
blurFilter.reset();
|
||||
blurFilter.addFilter('blur', 10);
|
||||
|
||||
|
||||
export async function load(needPreheat = true, enableLightModel = false, customModel = null) {
|
||||
const modelpath = 'https://paddlejs.bj.bcebos.com/models/fuse/humanseg/humanseg_398x224_fuse_activation/model.json';
|
||||
const lightModelPath = 'https://paddlejs.bj.bcebos.com/models/fuse/humanseg/humanseg_288x160_fuse_activation/model.json';
|
||||
const path = customModel
|
||||
? customModel
|
||||
: enableLightModel ? lightModelPath : modelpath;
|
||||
if (enableLightModel) {
|
||||
WIDTH = 288;
|
||||
HEIGHT = 160;
|
||||
}
|
||||
|
||||
runner = new Runner({
|
||||
modelPath: path,
|
||||
needPreheat: needPreheat !== undefined ? needPreheat : true,
|
||||
mean: [0.5, 0.5, 0.5],
|
||||
std: [0.5, 0.5, 0.5],
|
||||
webglFeedProcess: true
|
||||
});
|
||||
|
||||
env.set('webgl_pack_channel', true);
|
||||
env.set('webgl_pack_output', true);
|
||||
|
||||
await runner.init();
|
||||
|
||||
if (runner.feedShape) {
|
||||
WIDTH = runner.feedShape.fw;
|
||||
HEIGHT = runner.feedShape.fh;
|
||||
}
|
||||
}
|
||||
|
||||
export async function preheat() {
|
||||
return await runner.preheat();
|
||||
}
|
||||
|
||||
export async function getGrayValue(input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement) {
|
||||
inputElement = input;
|
||||
const seg_values = await runner.predict(input);
|
||||
backgroundSize = genBackgroundSize();
|
||||
return {
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
data: seg_values
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function genBackgroundSize() {
|
||||
// 缩放后的宽高
|
||||
let sw = WIDTH;
|
||||
let sh = HEIGHT;
|
||||
const ratio = sw / sh;
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let bx = 0;
|
||||
let by = 0;
|
||||
let bh = inputHeight;
|
||||
let bw = inputWidth;
|
||||
const origin_ratio = inputWidth / inputHeight;
|
||||
// target的长宽比大些 就把原图的高变成target那么高
|
||||
if (ratio / origin_ratio >= 1) {
|
||||
sw = sh * origin_ratio;
|
||||
x = Math.floor((WIDTH - sw) / 2);
|
||||
bw = bh * ratio;
|
||||
bx = Math.floor((bw - inputWidth) / 2);
|
||||
}
|
||||
// target的长宽比小些 就把原图的宽变成target那么宽
|
||||
else {
|
||||
sh = sw / origin_ratio;
|
||||
y = Math.floor((HEIGHT - sh) / 2);
|
||||
bh = bw / ratio;
|
||||
by = Math.floor((bh - inputHeight) / 2);
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
sw,
|
||||
sh,
|
||||
bx,
|
||||
by,
|
||||
bw,
|
||||
bh
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {Array} seg_values seg values of the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} backgroundCanvas the background canvas draws the pixels
|
||||
*/
|
||||
export function drawHumanSeg(
|
||||
seg_values: number[],
|
||||
canvas: HTMLCanvasElement,
|
||||
backgroundCanvas?: HTMLCanvasElement | HTMLImageElement
|
||||
) {
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
canvas.width = WIDTH;
|
||||
canvas.height = HEIGHT;
|
||||
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
|
||||
const tempScaleData = ctx.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
tempContext.drawImage(inputElement, backgroundSize.x, backgroundSize.y, backgroundSize.sw, backgroundSize.sh);
|
||||
const originImageData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 100) {
|
||||
tempScaleData.data[i * 4] = originImageData.data[i * 4];
|
||||
tempScaleData.data[i * 4 + 1] = originImageData.data[i * 4 + 1];
|
||||
tempScaleData.data[i * 4 + 2] = originImageData.data[i * 4 + 2];
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i + WIDTH * HEIGHT] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
canvas.width = inputWidth;
|
||||
canvas.height = inputHeight;
|
||||
backgroundCanvas
|
||||
&& ctx.drawImage(backgroundCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
ctx.drawImage(tempCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {Array} seg_values seg_values of the input image
|
||||
*/
|
||||
export function blurBackground(seg_values: number[], dest_canvas) {
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
|
||||
const dest_ctx = dest_canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
dest_canvas.width = inputWidth;
|
||||
dest_canvas.height = inputHeight;
|
||||
|
||||
const tempScaleData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
tempContext.drawImage(inputElement, backgroundSize.x, backgroundSize.y, backgroundSize.sw, backgroundSize.sh);
|
||||
const originImageData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
|
||||
blurFilter.dispose();
|
||||
const blurCanvas = blurFilter.apply(tempCanvas);
|
||||
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 150) {
|
||||
tempScaleData.data[i * 4] = originImageData.data[i * 4];
|
||||
tempScaleData.data[i * 4 + 1] = originImageData.data[i * 4 + 1];
|
||||
tempScaleData.data[i * 4 + 2] = originImageData.data[i * 4 + 2];
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i + WIDTH * HEIGHT] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
|
||||
dest_ctx.drawImage(blurCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
dest_ctx.drawImage(tempCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw mask without human
|
||||
* @param {Array} seg_values seg_values of the input image
|
||||
* @param {HTMLCanvasElement} dest the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} canvas background canvas
|
||||
*/
|
||||
export function drawMask(seg_values: number[], dest: HTMLCanvasElement, canvas: HTMLCanvasElement) {
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
tempContext.drawImage(canvas, 0, 0, WIDTH, HEIGHT);
|
||||
const dest_ctx = dest.getContext('2d') as CanvasRenderingContext2D;
|
||||
dest.width = WIDTH;
|
||||
dest.height = HEIGHT;
|
||||
|
||||
const tempScaleData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 150) {
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
dest_ctx.drawImage(tempCanvas, 0, 0, WIDTH, HEIGHT);
|
||||
}
|
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* @file humanseg gpu pipeline
|
||||
*/
|
||||
|
||||
import { Runner, env, registerOp } from '@paddlejs/paddlejs-core';
|
||||
import { GLHelper } from '@paddlejs/paddlejs-backend-webgl';
|
||||
import segImg from './customOp/segImg';
|
||||
import AppendDealOriginOpToNN from './customTransformer/appendCustomOpToNN';
|
||||
|
||||
interface LoadOptions {
|
||||
needPreheat?: boolean,
|
||||
enableLightModel?: boolean,
|
||||
canvasWidth?: number,
|
||||
canvasHeight?: number
|
||||
}
|
||||
|
||||
let runner = null as Runner;
|
||||
|
||||
const WIDTH = 398;
|
||||
const HEIGHT = 224;
|
||||
|
||||
function registerCustomOp() {
|
||||
registerOp(segImg, 'segImg');
|
||||
}
|
||||
|
||||
registerCustomOp();
|
||||
|
||||
|
||||
const WEBGL_ATTRIBUTES = Object.assign({}, GLHelper.WEBGL_ATTRIBUTES, {
|
||||
alpha: true
|
||||
});
|
||||
|
||||
function createWebglContext(canvas: HTMLCanvasElement) {
|
||||
let gl = canvas.getContext('webgl2', WEBGL_ATTRIBUTES) as WebGLRenderingContext | null;
|
||||
if (gl) {
|
||||
env.set('webglVersion', 2);
|
||||
}
|
||||
else {
|
||||
env.set('webglVersion', 1);
|
||||
gl = (canvas.getContext('webgl', WEBGL_ATTRIBUTES)
|
||||
|| canvas.getContext('experimental-webgl', WEBGL_ATTRIBUTES)) as WebGLRenderingContext;
|
||||
}
|
||||
|
||||
return gl as WebGLRenderingContext;
|
||||
}
|
||||
|
||||
const renderCanvas = document.createElement('canvas');
|
||||
renderCanvas.width = 500;
|
||||
renderCanvas.height = 280;
|
||||
const gl = createWebglContext(renderCanvas);
|
||||
|
||||
let segImgOp = null;
|
||||
|
||||
export async function load(options: LoadOptions = {
|
||||
needPreheat: true,
|
||||
enableLightModel: false,
|
||||
canvasWidth: 500,
|
||||
canvasHeight: 280
|
||||
}) {
|
||||
const modelPath = 'https://paddlejs.cdn.bcebos.com/models/humansegv2/model.json';
|
||||
|
||||
runner = new Runner({
|
||||
modelPath: modelPath,
|
||||
needPreheat: options.needPreheat !== undefined ? options.needPreheat : true,
|
||||
feedShape: {
|
||||
fw: WIDTH,
|
||||
fh: HEIGHT
|
||||
},
|
||||
fill: '#fff',
|
||||
mean: [0.5, 0.5, 0.5],
|
||||
std: [0.5, 0.5, 0.5],
|
||||
plugins: {
|
||||
preTransforms: [new AppendDealOriginOpToNN(options.canvasWidth, options.canvasHeight)]
|
||||
}
|
||||
});
|
||||
|
||||
GLHelper.setWebGLRenderingContext(gl);
|
||||
|
||||
env.set('webgl_pack_channel', true);
|
||||
env.set('webgl_gpu_pipeline', true);
|
||||
env.set('webgl_force_half_float_texture', true);
|
||||
|
||||
await runner.init();
|
||||
}
|
||||
|
||||
export async function preheat() {
|
||||
return await runner.preheat();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} back background canvas
|
||||
*/
|
||||
export async function drawHumanSeg(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement,
|
||||
back?: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 1;
|
||||
await runner.predict(input);
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const destCtx = canvas.getContext('2d');
|
||||
if (back) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(back, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
*/
|
||||
export async function blurBackground(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 0;
|
||||
await runner.predict(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
const destCtx = canvas.getContext('2d');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human mask
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} back background canvas
|
||||
*/
|
||||
export async function drawMask(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement,
|
||||
back: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 2;
|
||||
await runner.predict(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
const destCtx = canvas.getContext('2d');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(back, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
function genBackgroundSize(inputElement) {
|
||||
// 缩放后的宽高
|
||||
let sw = WIDTH;
|
||||
let sh = HEIGHT;
|
||||
const ratio = sw / sh;
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let bx = 0;
|
||||
let by = 0;
|
||||
let bh = inputHeight;
|
||||
let bw = inputWidth;
|
||||
const origin_ratio = inputWidth / inputHeight;
|
||||
// target的长宽比大些 就把原图的高变成target那么高
|
||||
if (ratio / origin_ratio >= 1) {
|
||||
sw = sh * origin_ratio;
|
||||
x = Math.floor((WIDTH - sw) / 2);
|
||||
bw = bh * ratio;
|
||||
bx = Math.floor((bw - inputWidth) / 2);
|
||||
}
|
||||
// target的长宽比小些 就把原图的宽变成target那么宽
|
||||
else {
|
||||
sh = sw / origin_ratio;
|
||||
y = Math.floor((HEIGHT - sh) / 2);
|
||||
bh = bw / ratio;
|
||||
by = Math.floor((bh - inputHeight) / 2);
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
sw,
|
||||
sh,
|
||||
bx,
|
||||
by,
|
||||
bw,
|
||||
bh
|
||||
};
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Example:', () => {
|
||||
/**
|
||||
* Example
|
||||
*/
|
||||
describe('ExampleTest', () => {
|
||||
test('Hello World!', () => {
|
||||
assert.strictEqual('Hello World!', 'Hello World!')
|
||||
})
|
||||
})
|
||||
})
|
641
examples/application/js/package/packages/paddlejs-models/humanseg/thirdParty/webgl-image-filter.js
vendored
Normal file
641
examples/application/js/package/packages/paddlejs-models/humanseg/thirdParty/webgl-image-filter.js
vendored
Normal file
@@ -0,0 +1,641 @@
|
||||
//
|
||||
// WebGLImageFilter - MIT Licensed
|
||||
// 2013, Dominic Szablewski - phoboslab.org
|
||||
//
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
var WebGLProgram = function( gl, vertexSource, fragmentSource ) {
|
||||
|
||||
var _collect = function( source, prefix, collection ) {
|
||||
var r = new RegExp('\\b' + prefix + ' \\w+ (\\w+)', 'ig');
|
||||
source.replace(r, function(match, name) {
|
||||
collection[name] = 0;
|
||||
return match;
|
||||
});
|
||||
};
|
||||
|
||||
var _compile = function( gl, source, type ) {
|
||||
var shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
|
||||
console.log(gl.getShaderInfoLog(shader));
|
||||
return null;
|
||||
}
|
||||
return shader;
|
||||
};
|
||||
|
||||
|
||||
this.uniform = {};
|
||||
this.attribute = {};
|
||||
|
||||
var _vsh = _compile(gl, vertexSource, gl.VERTEX_SHADER);
|
||||
var _fsh = _compile(gl, fragmentSource, gl.FRAGMENT_SHADER);
|
||||
|
||||
this.id = gl.createProgram();
|
||||
gl.attachShader(this.id, _vsh);
|
||||
gl.attachShader(this.id, _fsh);
|
||||
gl.linkProgram(this.id);
|
||||
|
||||
if( !gl.getProgramParameter(this.id, gl.LINK_STATUS) ) {
|
||||
console.log(gl.getProgramInfoLog(this.id));
|
||||
}
|
||||
|
||||
gl.useProgram(this.id);
|
||||
|
||||
// Collect attributes
|
||||
_collect(vertexSource, 'attribute', this.attribute);
|
||||
for( var a in this.attribute ) {
|
||||
this.attribute[a] = gl.getAttribLocation(this.id, a);
|
||||
}
|
||||
|
||||
// Collect uniforms
|
||||
_collect(vertexSource, 'uniform', this.uniform);
|
||||
_collect(fragmentSource, 'uniform', this.uniform);
|
||||
for( var u in this.uniform ) {
|
||||
this.uniform[u] = gl.getUniformLocation(this.id, u);
|
||||
}
|
||||
};
|
||||
|
||||
var WebGLImageFilter = window.WebGLImageFilter = function (params) {
|
||||
if (!params)
|
||||
params = { };
|
||||
|
||||
var
|
||||
gl = null,
|
||||
_drawCount = 0,
|
||||
_sourceTexture = null,
|
||||
_lastInChain = false,
|
||||
_currentFramebufferIndex = -1,
|
||||
_tempFramebuffers = [null, null],
|
||||
_filterChain = [],
|
||||
_width = -1,
|
||||
_height = -1,
|
||||
_vertexBuffer = null,
|
||||
_currentProgram = null,
|
||||
_canvas = params.canvas || document.createElement('canvas');
|
||||
|
||||
// key is the shader program source, value is the compiled program
|
||||
var _shaderProgramCache = { };
|
||||
|
||||
var gl = _canvas.getContext("webgl") || _canvas.getContext("experimental-webgl");
|
||||
if( !gl ) {
|
||||
throw "Couldn't get WebGL context";
|
||||
}
|
||||
|
||||
this.addFilter = function( name ) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var filter = _filter[name];
|
||||
|
||||
_filterChain.push({func:filter, args:args});
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
_filterChain = [];
|
||||
};
|
||||
|
||||
var applied = false;
|
||||
this.dispose = function () {
|
||||
gl.deleteTexture(_sourceTexture);
|
||||
_sourceTexture = null;
|
||||
applied = false;
|
||||
};
|
||||
|
||||
this.apply = function( image ) {
|
||||
_resize( image.width, image.height );
|
||||
_drawCount = 0;
|
||||
|
||||
// Create the texture for the input image if we haven't yet
|
||||
if (!_sourceTexture)
|
||||
_sourceTexture = gl.createTexture();
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, _sourceTexture);
|
||||
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.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
if (!applied) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
applied = true;
|
||||
} else {
|
||||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
}
|
||||
|
||||
// No filters? Just draw
|
||||
if( _filterChain.length == 0 ) {
|
||||
var program = _compileShader(SHADER.FRAGMENT_IDENTITY);
|
||||
_draw();
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
for( var i = 0; i < _filterChain.length; i++ ) {
|
||||
_lastInChain = (i == _filterChain.length-1);
|
||||
var f = _filterChain[i];
|
||||
|
||||
f.func.apply(this, f.args || []);
|
||||
}
|
||||
|
||||
return _canvas;
|
||||
};
|
||||
|
||||
var _resize = function( width, height ) {
|
||||
// Same width/height? Nothing to do here
|
||||
if( width == _width && height == _height ) { return; }
|
||||
|
||||
|
||||
_canvas.width = _width = width;
|
||||
_canvas.height = _height = height;
|
||||
|
||||
// Create the context if we don't have it yet
|
||||
if( !_vertexBuffer ) {
|
||||
// Create the vertex buffer for the two triangles [x, y, u, v] * 6
|
||||
var vertices = new Float32Array([
|
||||
-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0,
|
||||
-1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0
|
||||
]);
|
||||
_vertexBuffer = gl.createBuffer(),
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
|
||||
// Note sure if this is a good idea; at least it makes texture loading
|
||||
// in Ejecta instant.
|
||||
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, _width, _height);
|
||||
|
||||
// Delete old temp framebuffers
|
||||
_tempFramebuffers = [null, null];
|
||||
};
|
||||
|
||||
var _getTempFramebuffer = function( index ) {
|
||||
_tempFramebuffers[index] =
|
||||
_tempFramebuffers[index] ||
|
||||
_createFramebufferTexture( _width, _height );
|
||||
|
||||
return _tempFramebuffers[index];
|
||||
};
|
||||
|
||||
var _createFramebufferTexture = function( width, height ) {
|
||||
var fbo = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
|
||||
var renderbuffer = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
|
||||
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
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.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
|
||||
return {fbo: fbo, texture: texture};
|
||||
};
|
||||
|
||||
var _draw = function( flags ) {
|
||||
var source = null,
|
||||
target = null,
|
||||
flipY = false;
|
||||
|
||||
// Set up the source
|
||||
if( _drawCount == 0 ) {
|
||||
// First draw call - use the source texture
|
||||
source = _sourceTexture;
|
||||
}
|
||||
else {
|
||||
// All following draw calls use the temp buffer last drawn to
|
||||
source = _getTempFramebuffer(_currentFramebufferIndex).texture;
|
||||
}
|
||||
_drawCount++;
|
||||
|
||||
|
||||
// Set up the target
|
||||
if( _lastInChain && !(flags & DRAW.INTERMEDIATE) ) {
|
||||
// Last filter in our chain - draw directly to the WebGL Canvas. We may
|
||||
// also have to flip the image vertically now
|
||||
target = null;
|
||||
flipY = _drawCount % 2 == 0;
|
||||
}
|
||||
else {
|
||||
// Intermediate draw call - get a temp buffer to draw to
|
||||
_currentFramebufferIndex = (_currentFramebufferIndex+1) % 2;
|
||||
target = _getTempFramebuffer(_currentFramebufferIndex).fbo;
|
||||
}
|
||||
|
||||
// Bind the source and target and draw the two triangles
|
||||
gl.bindTexture(gl.TEXTURE_2D, source);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
||||
|
||||
gl.uniform1f(_currentProgram.uniform.flipY, (flipY ? -1 : 1) );
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
};
|
||||
|
||||
var _compileShader = function( fragmentSource ) {
|
||||
if (_shaderProgramCache[fragmentSource]) {
|
||||
_currentProgram = _shaderProgramCache[fragmentSource];
|
||||
gl.useProgram(_currentProgram.id);
|
||||
return _currentProgram;
|
||||
}
|
||||
|
||||
// Compile shaders
|
||||
_currentProgram = new WebGLProgram( gl, SHADER.VERTEX_IDENTITY, fragmentSource );
|
||||
|
||||
var floatSize = Float32Array.BYTES_PER_ELEMENT;
|
||||
var vertSize = 4 * floatSize;
|
||||
gl.enableVertexAttribArray(_currentProgram.attribute.pos);
|
||||
gl.vertexAttribPointer(_currentProgram.attribute.pos, 2, gl.FLOAT, false, vertSize , 0 * floatSize);
|
||||
gl.enableVertexAttribArray(_currentProgram.attribute.uv);
|
||||
gl.vertexAttribPointer(_currentProgram.attribute.uv, 2, gl.FLOAT, false, vertSize, 2 * floatSize);
|
||||
|
||||
_shaderProgramCache[fragmentSource] = _currentProgram;
|
||||
return _currentProgram;
|
||||
};
|
||||
|
||||
|
||||
var DRAW = { INTERMEDIATE: 1 };
|
||||
|
||||
var SHADER = {};
|
||||
SHADER.VERTEX_IDENTITY = [
|
||||
'precision highp float;',
|
||||
'attribute vec2 pos;',
|
||||
'attribute vec2 uv;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform float flipY;',
|
||||
|
||||
'void main(void) {',
|
||||
'vUv = uv;',
|
||||
'gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
SHADER.FRAGMENT_IDENTITY = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = texture2D(texture, vUv);',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
var _filter = {};
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Color Matrix Filter
|
||||
|
||||
_filter.colorMatrix = function( matrix ) {
|
||||
// Create a Float32 Array and normalize the offset component to 0-1
|
||||
var m = new Float32Array(matrix);
|
||||
m[4] /= 255;
|
||||
m[9] /= 255;
|
||||
m[14] /= 255;
|
||||
m[19] /= 255;
|
||||
|
||||
// Can we ignore the alpha value? Makes things a bit faster.
|
||||
var shader = (1==m[18]&&0==m[3]&&0==m[8]&&0==m[13]&&0==m[15]&&0==m[16]&&0==m[17]&&0==m[19])
|
||||
? _filter.colorMatrix.SHADER.WITHOUT_ALPHA
|
||||
: _filter.colorMatrix.SHADER.WITH_ALPHA;
|
||||
|
||||
var program = _compileShader(shader);
|
||||
gl.uniform1fv(program.uniform.m, m);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.colorMatrix.SHADER = {};
|
||||
_filter.colorMatrix.SHADER.WITH_ALPHA = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform float m[20];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c = texture2D(texture, vUv);',
|
||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];',
|
||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];',
|
||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];',
|
||||
'gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];',
|
||||
'}',
|
||||
].join('\n');
|
||||
_filter.colorMatrix.SHADER.WITHOUT_ALPHA = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform float m[20];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c = texture2D(texture, vUv);',
|
||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];',
|
||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];',
|
||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];',
|
||||
'gl_FragColor.a = c.a;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
_filter.brightness = function( brightness ) {
|
||||
var b = (brightness || 0) + 1;
|
||||
_filter.colorMatrix([
|
||||
b, 0, 0, 0, 0,
|
||||
0, b, 0, 0, 0,
|
||||
0, 0, b, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.saturation = function( amount ) {
|
||||
var x = (amount || 0) * 2/3 + 1;
|
||||
var y = ((x-1) *-0.5);
|
||||
_filter.colorMatrix([
|
||||
x, y, y, 0, 0,
|
||||
y, x, y, 0, 0,
|
||||
y, y, x, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.desaturate = function() {
|
||||
_filter.saturation(-1);
|
||||
};
|
||||
|
||||
_filter.contrast = function( amount ) {
|
||||
var v = (amount || 0) + 1;
|
||||
var o = -128 * (v-1);
|
||||
|
||||
_filter.colorMatrix([
|
||||
v, 0, 0, 0, o,
|
||||
0, v, 0, 0, o,
|
||||
0, 0, v, 0, o,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.negative = function() {
|
||||
_filter.contrast(-2);
|
||||
};
|
||||
|
||||
_filter.hue = function( rotation ) {
|
||||
rotation = (rotation || 0)/180 * Math.PI;
|
||||
var cos = Math.cos(rotation),
|
||||
sin = Math.sin(rotation),
|
||||
lumR = 0.213,
|
||||
lumG = 0.715,
|
||||
lumB = 0.072;
|
||||
|
||||
_filter.colorMatrix([
|
||||
lumR+cos*(1-lumR)+sin*(-lumR),lumG+cos*(-lumG)+sin*(-lumG),lumB+cos*(-lumB)+sin*(1-lumB),0,0,
|
||||
lumR+cos*(-lumR)+sin*(0.143),lumG+cos*(1-lumG)+sin*(0.140),lumB+cos*(-lumB)+sin*(-0.283),0,0,
|
||||
lumR+cos*(-lumR)+sin*(-(1-lumR)),lumG+cos*(-lumG)+sin*(lumG),lumB+cos*(1-lumB)+sin*(lumB),0,0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.desaturateLuminance = function() {
|
||||
_filter.colorMatrix([
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sepia = function() {
|
||||
_filter.colorMatrix([
|
||||
0.393, 0.7689999, 0.18899999, 0, 0,
|
||||
0.349, 0.6859999, 0.16799999, 0, 0,
|
||||
0.272, 0.5339999, 0.13099999, 0, 0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.brownie = function() {
|
||||
_filter.colorMatrix([
|
||||
0.5997023498159715,0.34553243048391263,-0.2708298674538042,0,47.43192855600873,
|
||||
-0.037703249837783157,0.8609577587992641,0.15059552388459913,0,-36.96841498319127,
|
||||
0.24113635128153335,-0.07441037908422492,0.44972182064877153,0,-7.562075277591283,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.vintagePinhole = function() {
|
||||
_filter.colorMatrix([
|
||||
0.6279345635605994,0.3202183420819367,-0.03965408211312453,0,9.651285835294123,
|
||||
0.02578397704808868,0.6441188644374771,0.03259127616149294,0,7.462829176470591,
|
||||
0.0466055556782719,-0.0851232987247891,0.5241648018700465,0,5.159190588235296,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.kodachrome = function() {
|
||||
_filter.colorMatrix([
|
||||
1.1285582396593525,-0.3967382283601348,-0.03992559172921793,0,63.72958762196502,
|
||||
-0.16404339962244616,1.0835251566291304,-0.05498805115633132,0,24.732407896706203,
|
||||
-0.16786010706155763,-0.5603416277695248,1.6014850761964943,0,35.62982807460946,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.technicolor = function() {
|
||||
_filter.colorMatrix([
|
||||
1.9125277891456083,-0.8545344976951645,-0.09155508482755585,0,11.793603434377337,
|
||||
-0.3087833385928097,1.7658908555458428,-0.10601743074722245,0,-70.35205161461398,
|
||||
-0.231103377548616,-0.7501899197440212,1.847597816108189,0,30.950940869491138,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.polaroid = function() {
|
||||
_filter.colorMatrix([
|
||||
1.438,-0.062,-0.062,0,0,
|
||||
-0.122,1.378,-0.122,0,0,
|
||||
-0.016,-0.016,1.483,0,0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.shiftToBGR = function() {
|
||||
_filter.colorMatrix([
|
||||
0,0,1,0,0,
|
||||
0,1,0,0,0,
|
||||
1,0,0,0,0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Convolution Filter
|
||||
|
||||
_filter.convolution = function( matrix ) {
|
||||
var m = new Float32Array(matrix);
|
||||
var pixelSizeX = 1 / _width;
|
||||
var pixelSizeY = 1 / _height;
|
||||
|
||||
var program = _compileShader(_filter.convolution.SHADER);
|
||||
gl.uniform1fv(program.uniform.m, m);
|
||||
gl.uniform2f(program.uniform.px, pixelSizeX, pixelSizeY);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.convolution.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform vec2 px;',
|
||||
'uniform float m[9];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c11 = texture2D(texture, vUv - px);', // top left
|
||||
'vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y));', // top center
|
||||
'vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y));', // top right
|
||||
|
||||
'vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) );', // mid left
|
||||
'vec4 c22 = texture2D(texture, vUv);', // mid center
|
||||
'vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) );', // mid right
|
||||
|
||||
'vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) );', // bottom left
|
||||
'vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) );', // bottom center
|
||||
'vec4 c33 = texture2D(texture, vUv + px );', // bottom right
|
||||
|
||||
'gl_FragColor = ',
|
||||
'c11 * m[0] + c12 * m[1] + c22 * m[2] +',
|
||||
'c21 * m[3] + c22 * m[4] + c23 * m[5] +',
|
||||
'c31 * m[6] + c32 * m[7] + c33 * m[8];',
|
||||
'gl_FragColor.a = c22.a;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
_filter.detectEdges = function() {
|
||||
_filter.convolution.call(this, [
|
||||
0, 1, 0,
|
||||
1, -4, 1,
|
||||
0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sobelX = function() {
|
||||
_filter.convolution.call(this, [
|
||||
-1, 0, 1,
|
||||
-2, 0, 2,
|
||||
-1, 0, 1
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sobelY = function() {
|
||||
_filter.convolution.call(this, [
|
||||
-1, -2, -1,
|
||||
0, 0, 0,
|
||||
1, 2, 1
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sharpen = function( amount ) {
|
||||
var a = amount || 1;
|
||||
_filter.convolution.call(this, [
|
||||
0, -1*a, 0,
|
||||
-1*a, 1 + 4*a, -1*a,
|
||||
0, -1*a, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.emboss = function( size ) {
|
||||
var s = size || 1;
|
||||
_filter.convolution.call(this, [
|
||||
-2*s, -1*s, 0,
|
||||
-1*s, 1, 1*s,
|
||||
0, 1*s, 2*s
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Blur Filter
|
||||
|
||||
_filter.blur = function( size ) {
|
||||
var blurSizeX = (size/7) / _width;
|
||||
var blurSizeY = (size/7) / _height;
|
||||
|
||||
var program = _compileShader(_filter.blur.SHADER);
|
||||
|
||||
// Vertical
|
||||
gl.uniform2f(program.uniform.px, 0, blurSizeY);
|
||||
_draw(DRAW.INTERMEDIATE);
|
||||
|
||||
// Horizontal
|
||||
gl.uniform2f(program.uniform.px, blurSizeX, 0);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.blur.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform vec2 px;',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = vec4(0.0);',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;',
|
||||
'gl_FragColor += texture2D(texture, vUv )*0.159576912161;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Pixelate Filter
|
||||
|
||||
_filter.pixelate = function( size ) {
|
||||
var blurSizeX = (size) / _width;
|
||||
var blurSizeY = (size) / _height;
|
||||
|
||||
var program = _compileShader(_filter.pixelate.SHADER);
|
||||
|
||||
// Horizontal
|
||||
gl.uniform2f(program.uniform.size, blurSizeX, blurSizeY);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.pixelate.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform vec2 size;',
|
||||
'uniform sampler2D texture;',
|
||||
|
||||
'vec2 pixelate(vec2 coord, vec2 size) {',
|
||||
'return floor( coord / size ) * size;',
|
||||
'}',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = vec4(0.0);',
|
||||
'vec2 coord = pixelate(vUv, size);',
|
||||
'gl_FragColor += texture2D(texture, coord);',
|
||||
'}',
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
export default WebGLImageFilter;
|
||||
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
"declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
"removeComments": false, /* Disable emitting comments. */
|
||||
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/humanseg_gpu/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/humanseg_gpu/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -0,0 +1 @@
|
||||
auto-install-peers=true
|
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "./lib/src/index.d.ts",
|
||||
"bundledPackages": [],
|
||||
"docModel": {
|
||||
"enabled": true
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "./lib/index.d.ts"
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import path from "path";
|
||||
import chalk from "chalk";
|
||||
|
||||
export const paths = {
|
||||
root: path.join(__dirname, '../'),
|
||||
input: path.join(__dirname, '../src/index_gpu.ts'),
|
||||
lib: path.join(__dirname, '../lib'),
|
||||
}
|
||||
|
||||
export const log = {
|
||||
progress: (text: string) => {
|
||||
console.log(chalk.green(text))
|
||||
},
|
||||
error: (text: string) => {
|
||||
console.log(chalk.red(text))
|
||||
},
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
import path from 'path'
|
||||
import fse from 'fs-extra'
|
||||
import { series } from "gulp"
|
||||
import { paths, log } from "./build_package/util"
|
||||
import rollupConfig from './rollup.config'
|
||||
import { rollup } from 'rollup'
|
||||
import {
|
||||
Extractor,
|
||||
ExtractorConfig,
|
||||
ExtractorResult,
|
||||
} from '@microsoft/api-extractor'
|
||||
/**
|
||||
* 这里是由于 'conventional-changelog' 未提供类型文件
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import conventionalChangelog from 'conventional-changelog'
|
||||
|
||||
interface TaskFunc {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
(cb: Function): void
|
||||
}
|
||||
|
||||
const CHANGE_TRACE = ['paddlejs-models/humanseg_gpu', 'paddle-js-models/humanseg_gpu', 'paddlejs-models', 'paddle-js-models', 'all']
|
||||
|
||||
/**
|
||||
* 删除 lib 文件
|
||||
* @param cb
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clearLibFile: TaskFunc = async (cb) => {
|
||||
fse.removeSync(paths.lib)
|
||||
log.progress('Deleted lib file')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* rollup 打包
|
||||
* @param cb
|
||||
*/
|
||||
const buildByRollup: TaskFunc = async (cb) => {
|
||||
const inputOptions = {
|
||||
input: rollupConfig.input,
|
||||
external: rollupConfig.external,
|
||||
plugins: rollupConfig.plugins,
|
||||
}
|
||||
const outOptions = rollupConfig.output
|
||||
const bundle = await rollup(inputOptions)
|
||||
|
||||
// 写入需要遍历输出配置
|
||||
if (Array.isArray(outOptions)) {
|
||||
for (const outOption of outOptions) {
|
||||
await bundle.write(outOption)
|
||||
}
|
||||
cb()
|
||||
log.progress('Rollup built successfully')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* api-extractor 整理 .d.ts 文件
|
||||
* @param cb
|
||||
*/
|
||||
const apiExtractorGenerate: TaskFunc = async (cb) => {
|
||||
const apiExtractorJsonPath: string = path.join(__dirname, './api-extractor.json')
|
||||
// 加载并解析 api-extractor.json 文件
|
||||
const extractorConfig: ExtractorConfig = await ExtractorConfig.loadFileAndPrepare(apiExtractorJsonPath)
|
||||
// 判断是否存在 index.d.ts 文件,这里必须异步先访问一边,不然后面找不到会报错
|
||||
const isdtxExist: boolean = await fse.pathExists(extractorConfig.mainEntryPointFilePath)
|
||||
// 判断是否存在 etc 目录,api-extractor需要该目录存在
|
||||
const isEtcExist: boolean = await fse.pathExists('./etc')
|
||||
|
||||
if (!isdtxExist) {
|
||||
log.error('API Extractor not find index.d.ts')
|
||||
return
|
||||
}
|
||||
|
||||
if (!isEtcExist) {
|
||||
fse.mkdirSync('etc');
|
||||
log.progress('Create folder etc for API Extractor')
|
||||
}
|
||||
|
||||
// 调用 API
|
||||
const extractorResult: ExtractorResult = await Extractor.invoke(extractorConfig, {
|
||||
localBuild: true,
|
||||
// 在输出中显示信息
|
||||
showVerboseMessages: true,
|
||||
})
|
||||
|
||||
if (extractorResult.succeeded) {
|
||||
// 删除多余的 .d.ts 文件
|
||||
const libFiles: string[] = await fse.readdir(paths.lib)
|
||||
for (const file of libFiles) {
|
||||
if (file.endsWith('.d.ts') && !file.includes('index')) {
|
||||
await fse.remove(path.join(paths.lib, file))
|
||||
}
|
||||
}
|
||||
log.progress('API Extractor completed successfully')
|
||||
// api-extractor 会生成 temp 文件夹,完成后进行删除
|
||||
fse.ensureDirSync('temp')
|
||||
fse.removeSync('temp')
|
||||
cb()
|
||||
} else {
|
||||
log.error(`API Extractor completed with ${extractorResult.errorCount} errors`
|
||||
+ ` and ${extractorResult.warningCount} warnings`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成
|
||||
* @param cb
|
||||
*/
|
||||
const complete: TaskFunc = (cb) => {
|
||||
log.progress('---- end ----')
|
||||
cb()
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 CHANGELOG
|
||||
* @param cb
|
||||
*/
|
||||
export const changelog: TaskFunc = async (cb) => {
|
||||
const checkTrace = (chunk: string) => {
|
||||
for (const keyWord of CHANGE_TRACE) {
|
||||
if (chunk.includes(keyWord)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const changelogPath: string = path.join(paths.root, 'CHANGELOG.md')
|
||||
// 对命令 conventional-changelog -p angular -i CHANGELOG.md -w -r 0
|
||||
const changelogPipe = await conventionalChangelog({
|
||||
preset: 'angular',
|
||||
releaseCount: 0,
|
||||
})
|
||||
changelogPipe.setEncoding('utf8')
|
||||
|
||||
const resultArray = ['# 更新日志\n\n']
|
||||
changelogPipe.on('data', (chunk) => {
|
||||
// 原来的 commits 路径是进入提交列表
|
||||
chunk = chunk.replace(/\/commits\//g, '/commit/')
|
||||
/**
|
||||
* title 或 指定跟踪 才会写入CHANGELOG
|
||||
*/
|
||||
for (const log of chunk.split("\n")) {
|
||||
if (log.includes('# ') || log.includes('### ') || checkTrace(log)) {
|
||||
resultArray.push(log+"\n\n")
|
||||
}
|
||||
}
|
||||
})
|
||||
changelogPipe.on('end', async () => {
|
||||
fse.createWriteStream(changelogPath).write(resultArray.join(''))
|
||||
cb()
|
||||
log.progress('CHANGELOG generation completed')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
exports.build = series(clearLibFile, buildByRollup, apiExtractorGenerate, complete)
|
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@paddle-js-models/humanseg_gpu",
|
||||
"version": "3.0.1",
|
||||
"description": "",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.esm.js",
|
||||
"typings": "lib/index.d.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md",
|
||||
"README.md",
|
||||
"README_cn.md"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "yalc publish --push",
|
||||
"prepublish": "pnpm lint & pnpm test",
|
||||
"prepublishOnly": "pnpm build",
|
||||
"build": "gulp build",
|
||||
"lint": "eslint --ext .js,.ts src --fix",
|
||||
"api": "api-extractor run",
|
||||
"test": "jest --coverage --verbose -u",
|
||||
"changelog": "gulp changelog"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@microsoft/api-extractor": "^7.30.0",
|
||||
"@types/d3-polygon": "^3.0.0",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/jest": "^29.0.1",
|
||||
"@types/node": "^18.7.16",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chalk": "4.1.2",
|
||||
"commitlint": "^17.1.2",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"fs-extra": "^10.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"husky": "^8.0.1",
|
||||
"jest": "^29.0.3",
|
||||
"lint-staged": "^13.0.3",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.34.0",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsify": "^5.0.4",
|
||||
"typescript": "^4.8.3",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paddlejs/paddlejs-backend-webgl": "^1.2.9",
|
||||
"@paddlejs/paddlejs-core": "^2.2.0"
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import path from 'path'
|
||||
import { RollupOptions } from 'rollup'
|
||||
import rollupTypescript from 'rollup-plugin-typescript2'
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import { eslint } from 'rollup-plugin-eslint'
|
||||
import { DEFAULT_EXTENSIONS } from '@babel/core'
|
||||
|
||||
import pkg from './package.json'
|
||||
import { paths } from "./build_package/util";
|
||||
|
||||
|
||||
// rollup 配置项
|
||||
const rollupConfig: RollupOptions = {
|
||||
input: paths.input,
|
||||
output: [
|
||||
// 输出 commonjs 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.js'),
|
||||
format: 'cjs',
|
||||
name: pkg.name,
|
||||
},
|
||||
// 输出 es 规范的代码
|
||||
{
|
||||
file: path.join(paths.lib, 'index.esm.js'),
|
||||
format: 'es',
|
||||
name: pkg.name,
|
||||
},
|
||||
],
|
||||
external: ['@paddlejs-mediapipe/opencv',
|
||||
'@paddlejs/paddlejs-backend-webgl',
|
||||
'@paddlejs/paddlejs-core',
|
||||
'@types/node',
|
||||
'd3-polygon',
|
||||
'js-clipper',
|
||||
'number-precision'],
|
||||
// plugins 需要注意引用顺序
|
||||
plugins: [
|
||||
eslint({
|
||||
throwOnError: true,
|
||||
throwOnWarning: false,
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['node_modules/**', 'lib/**', '*.js'],
|
||||
}),
|
||||
|
||||
// 使得 rollup 支持 commonjs 规范,识别 commonjs 规范的依赖
|
||||
commonjs(),
|
||||
|
||||
// 配合 commnjs 解析第三方模块
|
||||
resolve({
|
||||
// 将自定义选项传递给解析插件
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
rollupTypescript(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
// 只转换源代码,不运行外部依赖
|
||||
exclude: 'node_modules/**',
|
||||
// babel 默认不支持 ts 需要手动添加
|
||||
extensions: [
|
||||
...DEFAULT_EXTENSIONS,
|
||||
'.ts',
|
||||
],
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
||||
export default rollupConfig
|
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file seg or blur origin image
|
||||
*/
|
||||
|
||||
function mainFunc({
|
||||
out
|
||||
}) {
|
||||
const THRESHHOLD = 0.2;
|
||||
return `
|
||||
|
||||
#define SIGMA SIGMA 3.0
|
||||
#define BLUR_MSIZE 8
|
||||
#define MSIZE 3
|
||||
#define kernelSize 5.0
|
||||
#define weight 1.0
|
||||
|
||||
uniform int type; // 0: blurBackground 1: drawHumanseg 2: drawMask
|
||||
|
||||
void main() {
|
||||
vec2 outCoord = vCoord.xy;
|
||||
|
||||
outCoord.y = 1.0 - vCoord.y;
|
||||
|
||||
vec2 sourceTextureSize = vec2(${out.width_shape}, ${out.height_shape});
|
||||
vec2 sourceTexelSize = 1.0 / sourceTextureSize;
|
||||
|
||||
float kernel[MSIZE]; // 3
|
||||
kernel[0] = 0.12579369017522166;
|
||||
kernel[1] = 0.13298;
|
||||
kernel[2] = 0.12579369017522166;
|
||||
|
||||
float origin_alpha = 1.0 - TEXTURE2D(texture_origin, vec2(outCoord.x, outCoord.y) / 2.0).r;
|
||||
vec4 counter = TEXTURE2D(texture_counter, outCoord.xy);
|
||||
vec4 res = vec4(0.0);
|
||||
|
||||
if (type == 0) {
|
||||
// Simple Cheap Box Blur
|
||||
float pixelSizeX = 1.0 / float(${out.width_shape});
|
||||
float pixelSizeY = 1.0 / float(${out.height_shape});
|
||||
|
||||
// Horizontal Blur
|
||||
vec4 accumulation = vec4(0);
|
||||
float weightsum = 0.0;
|
||||
for (float i = -kernelSize; i <= kernelSize; i++){
|
||||
accumulation += TEXTURE2D(texture_counter, outCoord.xy + vec2(i * pixelSizeX, 0.0)) * weight;
|
||||
weightsum += weight;
|
||||
}
|
||||
// Vertical Blur
|
||||
for (float i = -kernelSize; i <= kernelSize; i++){
|
||||
accumulation += TEXTURE2D(texture_counter, outCoord.xy + vec2(0.0, i * pixelSizeY)) * weight;
|
||||
weightsum += weight;
|
||||
}
|
||||
|
||||
res = accumulation / weightsum;
|
||||
if (origin_alpha > ${THRESHHOLD}) {
|
||||
res = counter;
|
||||
}
|
||||
}
|
||||
else if (type == 1) {
|
||||
res = counter;
|
||||
res.a = origin_alpha;
|
||||
}
|
||||
else if (type == 2) {
|
||||
if (origin_alpha > ${THRESHHOLD}) {
|
||||
res = vec4(1.0);
|
||||
res.a = origin_alpha;
|
||||
}
|
||||
}
|
||||
|
||||
setPackedOutput(res);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
export default {
|
||||
mainFunc,
|
||||
textureFuncConf: {
|
||||
origin: [],
|
||||
counter: []
|
||||
}
|
||||
};
|
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file add deal origin op
|
||||
*/
|
||||
|
||||
import { Transformer, env, interfaces } from '@paddlejs/paddlejs-core';
|
||||
|
||||
const IMG_ORIGIN = 'image';
|
||||
const FINAL_PACK_OP_NAME = 'fetch_pack';
|
||||
const DEFAULT_WIDTH = 500;
|
||||
const DEFAULT_HEIGHT = 280;
|
||||
|
||||
export default class DealOrigin extends Transformer {
|
||||
private width;
|
||||
private height;
|
||||
|
||||
constructor(width?: number, height?: number) {
|
||||
super('DealOrigin');
|
||||
this.width = width || DEFAULT_WIDTH;
|
||||
this.height = height || DEFAULT_HEIGHT;
|
||||
}
|
||||
|
||||
transform(...args: any) {
|
||||
if (!env.get('webgl_gpu_pipeline')) {
|
||||
return;
|
||||
}
|
||||
const [ops, vars] = args;
|
||||
const fetchOp = ops.find(item => item.type === 'fetch');
|
||||
const [inputName] = fetchOp.inputs.X;
|
||||
|
||||
const segImgOp = {
|
||||
attrs: {},
|
||||
inputs: {
|
||||
X: [inputName],
|
||||
Y: [IMG_ORIGIN]
|
||||
},
|
||||
outputs: {
|
||||
Out: [FINAL_PACK_OP_NAME]
|
||||
},
|
||||
type: 'segImg',
|
||||
isPacked: true,
|
||||
bufferType: interfaces.BufferType.ColorBuffer,
|
||||
uniform: {
|
||||
type: {
|
||||
type: '1i',
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const packOutVar = {
|
||||
name: FINAL_PACK_OP_NAME,
|
||||
shape: [1, 1, this.height, this.width],
|
||||
persistable: false
|
||||
};
|
||||
|
||||
fetchOp.inputs.X = [FINAL_PACK_OP_NAME];
|
||||
ops.push(segImgOp);
|
||||
if (vars instanceof Array) {
|
||||
vars.push(...[packOutVar]);
|
||||
}
|
||||
else {
|
||||
vars[FINAL_PACK_OP_NAME] = packOutVar;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @file humanseg model
|
||||
*/
|
||||
|
||||
import { Runner, env } from '@paddlejs/paddlejs-core';
|
||||
import '@paddlejs/paddlejs-backend-webgl';
|
||||
import WebGLImageFilter from '../thirdParty/webgl-image-filter';
|
||||
|
||||
let runner = null as Runner;
|
||||
let inputElement: any = null;
|
||||
|
||||
let WIDTH = 398;
|
||||
let HEIGHT = 224;
|
||||
|
||||
let backgroundSize: any = null;
|
||||
|
||||
const blurFilter = new WebGLImageFilter();
|
||||
blurFilter.reset();
|
||||
blurFilter.addFilter('blur', 10);
|
||||
|
||||
|
||||
export async function load(needPreheat = true, enableLightModel = false, customModel = null) {
|
||||
const modelpath = 'https://paddlejs.bj.bcebos.com/models/fuse/humanseg/humanseg_398x224_fuse_activation/model.json';
|
||||
const lightModelPath = 'https://paddlejs.bj.bcebos.com/models/fuse/humanseg/humanseg_288x160_fuse_activation/model.json';
|
||||
const path = customModel
|
||||
? customModel
|
||||
: enableLightModel ? lightModelPath : modelpath;
|
||||
if (enableLightModel) {
|
||||
WIDTH = 288;
|
||||
HEIGHT = 160;
|
||||
}
|
||||
|
||||
runner = new Runner({
|
||||
modelPath: path,
|
||||
needPreheat: needPreheat !== undefined ? needPreheat : true,
|
||||
mean: [0.5, 0.5, 0.5],
|
||||
std: [0.5, 0.5, 0.5],
|
||||
webglFeedProcess: true
|
||||
});
|
||||
|
||||
env.set('webgl_pack_channel', true);
|
||||
env.set('webgl_pack_output', true);
|
||||
|
||||
await runner.init();
|
||||
|
||||
if (runner.feedShape) {
|
||||
WIDTH = runner.feedShape.fw;
|
||||
HEIGHT = runner.feedShape.fh;
|
||||
}
|
||||
}
|
||||
|
||||
export async function preheat() {
|
||||
return await runner.preheat();
|
||||
}
|
||||
|
||||
export async function getGrayValue(input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement) {
|
||||
inputElement = input;
|
||||
const seg_values = await runner.predict(input);
|
||||
backgroundSize = genBackgroundSize();
|
||||
return {
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
data: seg_values
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function genBackgroundSize() {
|
||||
// 缩放后的宽高
|
||||
let sw = WIDTH;
|
||||
let sh = HEIGHT;
|
||||
const ratio = sw / sh;
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let bx = 0;
|
||||
let by = 0;
|
||||
let bh = inputHeight;
|
||||
let bw = inputWidth;
|
||||
const origin_ratio = inputWidth / inputHeight;
|
||||
// target的长宽比大些 就把原图的高变成target那么高
|
||||
if (ratio / origin_ratio >= 1) {
|
||||
sw = sh * origin_ratio;
|
||||
x = Math.floor((WIDTH - sw) / 2);
|
||||
bw = bh * ratio;
|
||||
bx = Math.floor((bw - inputWidth) / 2);
|
||||
}
|
||||
// target的长宽比小些 就把原图的宽变成target那么宽
|
||||
else {
|
||||
sh = sw / origin_ratio;
|
||||
y = Math.floor((HEIGHT - sh) / 2);
|
||||
bh = bw / ratio;
|
||||
by = Math.floor((bh - inputHeight) / 2);
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
sw,
|
||||
sh,
|
||||
bx,
|
||||
by,
|
||||
bw,
|
||||
bh
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {Array} seg_values seg values of the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} backgroundCanvas the background canvas draws the pixels
|
||||
*/
|
||||
export function drawHumanSeg(
|
||||
seg_values: number[],
|
||||
canvas: HTMLCanvasElement,
|
||||
backgroundCanvas?: HTMLCanvasElement | HTMLImageElement
|
||||
) {
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
canvas.width = WIDTH;
|
||||
canvas.height = HEIGHT;
|
||||
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
|
||||
const tempScaleData = ctx.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
tempContext.drawImage(inputElement, backgroundSize.x, backgroundSize.y, backgroundSize.sw, backgroundSize.sh);
|
||||
const originImageData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 100) {
|
||||
tempScaleData.data[i * 4] = originImageData.data[i * 4];
|
||||
tempScaleData.data[i * 4 + 1] = originImageData.data[i * 4 + 1];
|
||||
tempScaleData.data[i * 4 + 2] = originImageData.data[i * 4 + 2];
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i + WIDTH * HEIGHT] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
canvas.width = inputWidth;
|
||||
canvas.height = inputHeight;
|
||||
backgroundCanvas
|
||||
&& ctx.drawImage(backgroundCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
ctx.drawImage(tempCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {Array} seg_values seg_values of the input image
|
||||
*/
|
||||
export function blurBackground(seg_values: number[], dest_canvas) {
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
|
||||
const dest_ctx = dest_canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
dest_canvas.width = inputWidth;
|
||||
dest_canvas.height = inputHeight;
|
||||
|
||||
const tempScaleData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
tempContext.drawImage(inputElement, backgroundSize.x, backgroundSize.y, backgroundSize.sw, backgroundSize.sh);
|
||||
const originImageData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
|
||||
blurFilter.dispose();
|
||||
const blurCanvas = blurFilter.apply(tempCanvas);
|
||||
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 150) {
|
||||
tempScaleData.data[i * 4] = originImageData.data[i * 4];
|
||||
tempScaleData.data[i * 4 + 1] = originImageData.data[i * 4 + 1];
|
||||
tempScaleData.data[i * 4 + 2] = originImageData.data[i * 4 + 2];
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i + WIDTH * HEIGHT] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
|
||||
dest_ctx.drawImage(blurCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
dest_ctx.drawImage(tempCanvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw mask without human
|
||||
* @param {Array} seg_values seg_values of the input image
|
||||
* @param {HTMLCanvasElement} dest the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} canvas background canvas
|
||||
*/
|
||||
export function drawMask(seg_values: number[], dest: HTMLCanvasElement, canvas: HTMLCanvasElement) {
|
||||
const tempCanvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
const tempContext = tempCanvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
tempCanvas.width = WIDTH;
|
||||
tempCanvas.height = HEIGHT;
|
||||
tempContext.drawImage(canvas, 0, 0, WIDTH, HEIGHT);
|
||||
const dest_ctx = dest.getContext('2d') as CanvasRenderingContext2D;
|
||||
dest.width = WIDTH;
|
||||
dest.height = HEIGHT;
|
||||
|
||||
const tempScaleData = tempContext.getImageData(0, 0, WIDTH, HEIGHT);
|
||||
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
if (seg_values[i + WIDTH * HEIGHT] * 255 > 150) {
|
||||
tempScaleData.data[i * 4 + 3] = seg_values[i] * 255;
|
||||
}
|
||||
}
|
||||
|
||||
tempContext.putImageData(tempScaleData, 0, 0);
|
||||
dest_ctx.drawImage(tempCanvas, 0, 0, WIDTH, HEIGHT);
|
||||
}
|
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* @file humanseg gpu pipeline
|
||||
*/
|
||||
|
||||
import { Runner, env, registerOp } from '@paddlejs/paddlejs-core';
|
||||
import { GLHelper } from '@paddlejs/paddlejs-backend-webgl';
|
||||
import segImg from './customOp/segImg';
|
||||
import AppendDealOriginOpToNN from './customTransformer/appendCustomOpToNN';
|
||||
|
||||
interface LoadOptions {
|
||||
needPreheat?: boolean,
|
||||
enableLightModel?: boolean,
|
||||
canvasWidth?: number,
|
||||
canvasHeight?: number
|
||||
}
|
||||
|
||||
let runner = null as Runner;
|
||||
|
||||
const WIDTH = 398;
|
||||
const HEIGHT = 224;
|
||||
|
||||
function registerCustomOp() {
|
||||
registerOp(segImg, 'segImg');
|
||||
}
|
||||
|
||||
registerCustomOp();
|
||||
|
||||
|
||||
const WEBGL_ATTRIBUTES = Object.assign({}, GLHelper.WEBGL_ATTRIBUTES, {
|
||||
alpha: true
|
||||
});
|
||||
|
||||
function createWebglContext(canvas: HTMLCanvasElement) {
|
||||
let gl = canvas.getContext('webgl2', WEBGL_ATTRIBUTES) as WebGLRenderingContext | null;
|
||||
if (gl) {
|
||||
env.set('webglVersion', 2);
|
||||
}
|
||||
else {
|
||||
env.set('webglVersion', 1);
|
||||
gl = (canvas.getContext('webgl', WEBGL_ATTRIBUTES)
|
||||
|| canvas.getContext('experimental-webgl', WEBGL_ATTRIBUTES)) as WebGLRenderingContext;
|
||||
}
|
||||
|
||||
return gl as WebGLRenderingContext;
|
||||
}
|
||||
|
||||
const renderCanvas = document.createElement('canvas');
|
||||
renderCanvas.width = 500;
|
||||
renderCanvas.height = 280;
|
||||
const gl = createWebglContext(renderCanvas);
|
||||
|
||||
let segImgOp = null;
|
||||
|
||||
export async function load(options: LoadOptions = {
|
||||
needPreheat: true,
|
||||
enableLightModel: false,
|
||||
canvasWidth: 500,
|
||||
canvasHeight: 280
|
||||
}) {
|
||||
const modelPath = 'https://paddlejs.cdn.bcebos.com/models/humansegv2/model.json';
|
||||
|
||||
runner = new Runner({
|
||||
modelPath: modelPath,
|
||||
needPreheat: options.needPreheat !== undefined ? options.needPreheat : true,
|
||||
feedShape: {
|
||||
fw: WIDTH,
|
||||
fh: HEIGHT
|
||||
},
|
||||
fill: '#fff',
|
||||
mean: [0.5, 0.5, 0.5],
|
||||
std: [0.5, 0.5, 0.5],
|
||||
plugins: {
|
||||
preTransforms: [new AppendDealOriginOpToNN(options.canvasWidth, options.canvasHeight)]
|
||||
}
|
||||
});
|
||||
|
||||
GLHelper.setWebGLRenderingContext(gl);
|
||||
|
||||
env.set('webgl_pack_channel', true);
|
||||
env.set('webgl_gpu_pipeline', true);
|
||||
env.set('webgl_force_half_float_texture', true);
|
||||
|
||||
await runner.init();
|
||||
}
|
||||
|
||||
export async function preheat() {
|
||||
return await runner.preheat();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} back background canvas
|
||||
*/
|
||||
export async function drawHumanSeg(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement,
|
||||
back?: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 1;
|
||||
await runner.predict(input);
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const destCtx = canvas.getContext('2d');
|
||||
if (back) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(back, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human seg
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
*/
|
||||
export async function blurBackground(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 0;
|
||||
await runner.predict(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
const destCtx = canvas.getContext('2d');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw human mask
|
||||
* @param {HTMLImageElement | HTMLVideoElement | HTMLCanvasElement} input the input image
|
||||
* @param {HTMLCanvasElement} canvas the dest canvas draws the pixels
|
||||
* @param {HTMLCanvasElement} back background canvas
|
||||
*/
|
||||
export async function drawMask(
|
||||
input: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement,
|
||||
canvas: HTMLCanvasElement,
|
||||
back: HTMLCanvasElement
|
||||
) {
|
||||
if (!segImgOp) {
|
||||
segImgOp = runner.weightMap[runner.weightMap.length - 1].opData;
|
||||
}
|
||||
// todo: 底层库更新类型声明后优化这里的 ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
segImgOp.uniform.type.value = 2;
|
||||
await runner.predict(input);
|
||||
canvas.width = input.width;
|
||||
canvas.height = input.height;
|
||||
const backgroundSize = genBackgroundSize(input);
|
||||
const destCtx = canvas.getContext('2d');
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(back, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
destCtx.drawImage(gl.canvas, -backgroundSize.bx, -backgroundSize.by, backgroundSize.bw, backgroundSize.bh);
|
||||
}
|
||||
|
||||
function genBackgroundSize(inputElement) {
|
||||
// 缩放后的宽高
|
||||
let sw = WIDTH;
|
||||
let sh = HEIGHT;
|
||||
const ratio = sw / sh;
|
||||
const inputWidth = inputElement.naturalWidth || inputElement.width;
|
||||
const inputHeight = inputElement.naturalHeight || inputElement.height;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let bx = 0;
|
||||
let by = 0;
|
||||
let bh = inputHeight;
|
||||
let bw = inputWidth;
|
||||
const origin_ratio = inputWidth / inputHeight;
|
||||
// target的长宽比大些 就把原图的高变成target那么高
|
||||
if (ratio / origin_ratio >= 1) {
|
||||
sw = sh * origin_ratio;
|
||||
x = Math.floor((WIDTH - sw) / 2);
|
||||
bw = bh * ratio;
|
||||
bx = Math.floor((bw - inputWidth) / 2);
|
||||
}
|
||||
// target的长宽比小些 就把原图的宽变成target那么宽
|
||||
else {
|
||||
sh = sw / origin_ratio;
|
||||
y = Math.floor((HEIGHT - sh) / 2);
|
||||
bh = bw / ratio;
|
||||
by = Math.floor((bh - inputHeight) / 2);
|
||||
}
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
sw,
|
||||
sh,
|
||||
bx,
|
||||
by,
|
||||
bw,
|
||||
bh
|
||||
};
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Example:', () => {
|
||||
/**
|
||||
* Example
|
||||
*/
|
||||
describe('ExampleTest', () => {
|
||||
test('Hello World!', () => {
|
||||
assert.strictEqual('Hello World!', 'Hello World!')
|
||||
})
|
||||
})
|
||||
})
|
@@ -0,0 +1,641 @@
|
||||
//
|
||||
// WebGLImageFilter - MIT Licensed
|
||||
// 2013, Dominic Szablewski - phoboslab.org
|
||||
//
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
var WebGLProgram = function( gl, vertexSource, fragmentSource ) {
|
||||
|
||||
var _collect = function( source, prefix, collection ) {
|
||||
var r = new RegExp('\\b' + prefix + ' \\w+ (\\w+)', 'ig');
|
||||
source.replace(r, function(match, name) {
|
||||
collection[name] = 0;
|
||||
return match;
|
||||
});
|
||||
};
|
||||
|
||||
var _compile = function( gl, source, type ) {
|
||||
var shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
|
||||
console.log(gl.getShaderInfoLog(shader));
|
||||
return null;
|
||||
}
|
||||
return shader;
|
||||
};
|
||||
|
||||
|
||||
this.uniform = {};
|
||||
this.attribute = {};
|
||||
|
||||
var _vsh = _compile(gl, vertexSource, gl.VERTEX_SHADER);
|
||||
var _fsh = _compile(gl, fragmentSource, gl.FRAGMENT_SHADER);
|
||||
|
||||
this.id = gl.createProgram();
|
||||
gl.attachShader(this.id, _vsh);
|
||||
gl.attachShader(this.id, _fsh);
|
||||
gl.linkProgram(this.id);
|
||||
|
||||
if( !gl.getProgramParameter(this.id, gl.LINK_STATUS) ) {
|
||||
console.log(gl.getProgramInfoLog(this.id));
|
||||
}
|
||||
|
||||
gl.useProgram(this.id);
|
||||
|
||||
// Collect attributes
|
||||
_collect(vertexSource, 'attribute', this.attribute);
|
||||
for( var a in this.attribute ) {
|
||||
this.attribute[a] = gl.getAttribLocation(this.id, a);
|
||||
}
|
||||
|
||||
// Collect uniforms
|
||||
_collect(vertexSource, 'uniform', this.uniform);
|
||||
_collect(fragmentSource, 'uniform', this.uniform);
|
||||
for( var u in this.uniform ) {
|
||||
this.uniform[u] = gl.getUniformLocation(this.id, u);
|
||||
}
|
||||
};
|
||||
|
||||
var WebGLImageFilter = window.WebGLImageFilter = function (params) {
|
||||
if (!params)
|
||||
params = { };
|
||||
|
||||
var
|
||||
gl = null,
|
||||
_drawCount = 0,
|
||||
_sourceTexture = null,
|
||||
_lastInChain = false,
|
||||
_currentFramebufferIndex = -1,
|
||||
_tempFramebuffers = [null, null],
|
||||
_filterChain = [],
|
||||
_width = -1,
|
||||
_height = -1,
|
||||
_vertexBuffer = null,
|
||||
_currentProgram = null,
|
||||
_canvas = params.canvas || document.createElement('canvas');
|
||||
|
||||
// key is the shader program source, value is the compiled program
|
||||
var _shaderProgramCache = { };
|
||||
|
||||
var gl = _canvas.getContext("webgl") || _canvas.getContext("experimental-webgl");
|
||||
if( !gl ) {
|
||||
throw "Couldn't get WebGL context";
|
||||
}
|
||||
|
||||
this.addFilter = function( name ) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var filter = _filter[name];
|
||||
|
||||
_filterChain.push({func:filter, args:args});
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
_filterChain = [];
|
||||
};
|
||||
|
||||
var applied = false;
|
||||
this.dispose = function () {
|
||||
gl.deleteTexture(_sourceTexture);
|
||||
_sourceTexture = null;
|
||||
applied = false;
|
||||
};
|
||||
|
||||
this.apply = function( image ) {
|
||||
_resize( image.width, image.height );
|
||||
_drawCount = 0;
|
||||
|
||||
// Create the texture for the input image if we haven't yet
|
||||
if (!_sourceTexture)
|
||||
_sourceTexture = gl.createTexture();
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, _sourceTexture);
|
||||
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.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
if (!applied) {
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
applied = true;
|
||||
} else {
|
||||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
}
|
||||
|
||||
// No filters? Just draw
|
||||
if( _filterChain.length == 0 ) {
|
||||
var program = _compileShader(SHADER.FRAGMENT_IDENTITY);
|
||||
_draw();
|
||||
return _canvas;
|
||||
}
|
||||
|
||||
for( var i = 0; i < _filterChain.length; i++ ) {
|
||||
_lastInChain = (i == _filterChain.length-1);
|
||||
var f = _filterChain[i];
|
||||
|
||||
f.func.apply(this, f.args || []);
|
||||
}
|
||||
|
||||
return _canvas;
|
||||
};
|
||||
|
||||
var _resize = function( width, height ) {
|
||||
// Same width/height? Nothing to do here
|
||||
if( width == _width && height == _height ) { return; }
|
||||
|
||||
|
||||
_canvas.width = _width = width;
|
||||
_canvas.height = _height = height;
|
||||
|
||||
// Create the context if we don't have it yet
|
||||
if( !_vertexBuffer ) {
|
||||
// Create the vertex buffer for the two triangles [x, y, u, v] * 6
|
||||
var vertices = new Float32Array([
|
||||
-1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0,
|
||||
-1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0
|
||||
]);
|
||||
_vertexBuffer = gl.createBuffer(),
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
|
||||
// Note sure if this is a good idea; at least it makes texture loading
|
||||
// in Ejecta instant.
|
||||
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, _width, _height);
|
||||
|
||||
// Delete old temp framebuffers
|
||||
_tempFramebuffers = [null, null];
|
||||
};
|
||||
|
||||
var _getTempFramebuffer = function( index ) {
|
||||
_tempFramebuffers[index] =
|
||||
_tempFramebuffers[index] ||
|
||||
_createFramebufferTexture( _width, _height );
|
||||
|
||||
return _tempFramebuffers[index];
|
||||
};
|
||||
|
||||
var _createFramebufferTexture = function( width, height ) {
|
||||
var fbo = gl.createFramebuffer();
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
||||
|
||||
var renderbuffer = gl.createRenderbuffer();
|
||||
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
|
||||
|
||||
var texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
||||
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
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.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
|
||||
return {fbo: fbo, texture: texture};
|
||||
};
|
||||
|
||||
var _draw = function( flags ) {
|
||||
var source = null,
|
||||
target = null,
|
||||
flipY = false;
|
||||
|
||||
// Set up the source
|
||||
if( _drawCount == 0 ) {
|
||||
// First draw call - use the source texture
|
||||
source = _sourceTexture;
|
||||
}
|
||||
else {
|
||||
// All following draw calls use the temp buffer last drawn to
|
||||
source = _getTempFramebuffer(_currentFramebufferIndex).texture;
|
||||
}
|
||||
_drawCount++;
|
||||
|
||||
|
||||
// Set up the target
|
||||
if( _lastInChain && !(flags & DRAW.INTERMEDIATE) ) {
|
||||
// Last filter in our chain - draw directly to the WebGL Canvas. We may
|
||||
// also have to flip the image vertically now
|
||||
target = null;
|
||||
flipY = _drawCount % 2 == 0;
|
||||
}
|
||||
else {
|
||||
// Intermediate draw call - get a temp buffer to draw to
|
||||
_currentFramebufferIndex = (_currentFramebufferIndex+1) % 2;
|
||||
target = _getTempFramebuffer(_currentFramebufferIndex).fbo;
|
||||
}
|
||||
|
||||
// Bind the source and target and draw the two triangles
|
||||
gl.bindTexture(gl.TEXTURE_2D, source);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, target);
|
||||
|
||||
gl.uniform1f(_currentProgram.uniform.flipY, (flipY ? -1 : 1) );
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
};
|
||||
|
||||
var _compileShader = function( fragmentSource ) {
|
||||
if (_shaderProgramCache[fragmentSource]) {
|
||||
_currentProgram = _shaderProgramCache[fragmentSource];
|
||||
gl.useProgram(_currentProgram.id);
|
||||
return _currentProgram;
|
||||
}
|
||||
|
||||
// Compile shaders
|
||||
_currentProgram = new WebGLProgram( gl, SHADER.VERTEX_IDENTITY, fragmentSource );
|
||||
|
||||
var floatSize = Float32Array.BYTES_PER_ELEMENT;
|
||||
var vertSize = 4 * floatSize;
|
||||
gl.enableVertexAttribArray(_currentProgram.attribute.pos);
|
||||
gl.vertexAttribPointer(_currentProgram.attribute.pos, 2, gl.FLOAT, false, vertSize , 0 * floatSize);
|
||||
gl.enableVertexAttribArray(_currentProgram.attribute.uv);
|
||||
gl.vertexAttribPointer(_currentProgram.attribute.uv, 2, gl.FLOAT, false, vertSize, 2 * floatSize);
|
||||
|
||||
_shaderProgramCache[fragmentSource] = _currentProgram;
|
||||
return _currentProgram;
|
||||
};
|
||||
|
||||
|
||||
var DRAW = { INTERMEDIATE: 1 };
|
||||
|
||||
var SHADER = {};
|
||||
SHADER.VERTEX_IDENTITY = [
|
||||
'precision highp float;',
|
||||
'attribute vec2 pos;',
|
||||
'attribute vec2 uv;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform float flipY;',
|
||||
|
||||
'void main(void) {',
|
||||
'vUv = uv;',
|
||||
'gl_Position = vec4(pos.x, pos.y*flipY, 0.0, 1.);',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
SHADER.FRAGMENT_IDENTITY = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = texture2D(texture, vUv);',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
var _filter = {};
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Color Matrix Filter
|
||||
|
||||
_filter.colorMatrix = function( matrix ) {
|
||||
// Create a Float32 Array and normalize the offset component to 0-1
|
||||
var m = new Float32Array(matrix);
|
||||
m[4] /= 255;
|
||||
m[9] /= 255;
|
||||
m[14] /= 255;
|
||||
m[19] /= 255;
|
||||
|
||||
// Can we ignore the alpha value? Makes things a bit faster.
|
||||
var shader = (1==m[18]&&0==m[3]&&0==m[8]&&0==m[13]&&0==m[15]&&0==m[16]&&0==m[17]&&0==m[19])
|
||||
? _filter.colorMatrix.SHADER.WITHOUT_ALPHA
|
||||
: _filter.colorMatrix.SHADER.WITH_ALPHA;
|
||||
|
||||
var program = _compileShader(shader);
|
||||
gl.uniform1fv(program.uniform.m, m);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.colorMatrix.SHADER = {};
|
||||
_filter.colorMatrix.SHADER.WITH_ALPHA = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform float m[20];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c = texture2D(texture, vUv);',
|
||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[3] * c.a + m[4];',
|
||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[8] * c.a + m[9];',
|
||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[13] * c.a + m[14];',
|
||||
'gl_FragColor.a = m[15] * c.r + m[16] * c.g + m[17] * c.b + m[18] * c.a + m[19];',
|
||||
'}',
|
||||
].join('\n');
|
||||
_filter.colorMatrix.SHADER.WITHOUT_ALPHA = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform float m[20];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c = texture2D(texture, vUv);',
|
||||
'gl_FragColor.r = m[0] * c.r + m[1] * c.g + m[2] * c.b + m[4];',
|
||||
'gl_FragColor.g = m[5] * c.r + m[6] * c.g + m[7] * c.b + m[9];',
|
||||
'gl_FragColor.b = m[10] * c.r + m[11] * c.g + m[12] * c.b + m[14];',
|
||||
'gl_FragColor.a = c.a;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
_filter.brightness = function( brightness ) {
|
||||
var b = (brightness || 0) + 1;
|
||||
_filter.colorMatrix([
|
||||
b, 0, 0, 0, 0,
|
||||
0, b, 0, 0, 0,
|
||||
0, 0, b, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.saturation = function( amount ) {
|
||||
var x = (amount || 0) * 2/3 + 1;
|
||||
var y = ((x-1) *-0.5);
|
||||
_filter.colorMatrix([
|
||||
x, y, y, 0, 0,
|
||||
y, x, y, 0, 0,
|
||||
y, y, x, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.desaturate = function() {
|
||||
_filter.saturation(-1);
|
||||
};
|
||||
|
||||
_filter.contrast = function( amount ) {
|
||||
var v = (amount || 0) + 1;
|
||||
var o = -128 * (v-1);
|
||||
|
||||
_filter.colorMatrix([
|
||||
v, 0, 0, 0, o,
|
||||
0, v, 0, 0, o,
|
||||
0, 0, v, 0, o,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.negative = function() {
|
||||
_filter.contrast(-2);
|
||||
};
|
||||
|
||||
_filter.hue = function( rotation ) {
|
||||
rotation = (rotation || 0)/180 * Math.PI;
|
||||
var cos = Math.cos(rotation),
|
||||
sin = Math.sin(rotation),
|
||||
lumR = 0.213,
|
||||
lumG = 0.715,
|
||||
lumB = 0.072;
|
||||
|
||||
_filter.colorMatrix([
|
||||
lumR+cos*(1-lumR)+sin*(-lumR),lumG+cos*(-lumG)+sin*(-lumG),lumB+cos*(-lumB)+sin*(1-lumB),0,0,
|
||||
lumR+cos*(-lumR)+sin*(0.143),lumG+cos*(1-lumG)+sin*(0.140),lumB+cos*(-lumB)+sin*(-0.283),0,0,
|
||||
lumR+cos*(-lumR)+sin*(-(1-lumR)),lumG+cos*(-lumG)+sin*(lumG),lumB+cos*(1-lumB)+sin*(lumB),0,0,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.desaturateLuminance = function() {
|
||||
_filter.colorMatrix([
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0.2764723, 0.9297080, 0.0938197, 0, -37.1,
|
||||
0, 0, 0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sepia = function() {
|
||||
_filter.colorMatrix([
|
||||
0.393, 0.7689999, 0.18899999, 0, 0,
|
||||
0.349, 0.6859999, 0.16799999, 0, 0,
|
||||
0.272, 0.5339999, 0.13099999, 0, 0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.brownie = function() {
|
||||
_filter.colorMatrix([
|
||||
0.5997023498159715,0.34553243048391263,-0.2708298674538042,0,47.43192855600873,
|
||||
-0.037703249837783157,0.8609577587992641,0.15059552388459913,0,-36.96841498319127,
|
||||
0.24113635128153335,-0.07441037908422492,0.44972182064877153,0,-7.562075277591283,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.vintagePinhole = function() {
|
||||
_filter.colorMatrix([
|
||||
0.6279345635605994,0.3202183420819367,-0.03965408211312453,0,9.651285835294123,
|
||||
0.02578397704808868,0.6441188644374771,0.03259127616149294,0,7.462829176470591,
|
||||
0.0466055556782719,-0.0851232987247891,0.5241648018700465,0,5.159190588235296,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.kodachrome = function() {
|
||||
_filter.colorMatrix([
|
||||
1.1285582396593525,-0.3967382283601348,-0.03992559172921793,0,63.72958762196502,
|
||||
-0.16404339962244616,1.0835251566291304,-0.05498805115633132,0,24.732407896706203,
|
||||
-0.16786010706155763,-0.5603416277695248,1.6014850761964943,0,35.62982807460946,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.technicolor = function() {
|
||||
_filter.colorMatrix([
|
||||
1.9125277891456083,-0.8545344976951645,-0.09155508482755585,0,11.793603434377337,
|
||||
-0.3087833385928097,1.7658908555458428,-0.10601743074722245,0,-70.35205161461398,
|
||||
-0.231103377548616,-0.7501899197440212,1.847597816108189,0,30.950940869491138,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.polaroid = function() {
|
||||
_filter.colorMatrix([
|
||||
1.438,-0.062,-0.062,0,0,
|
||||
-0.122,1.378,-0.122,0,0,
|
||||
-0.016,-0.016,1.483,0,0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.shiftToBGR = function() {
|
||||
_filter.colorMatrix([
|
||||
0,0,1,0,0,
|
||||
0,1,0,0,0,
|
||||
1,0,0,0,0,
|
||||
0,0,0,1,0
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Convolution Filter
|
||||
|
||||
_filter.convolution = function( matrix ) {
|
||||
var m = new Float32Array(matrix);
|
||||
var pixelSizeX = 1 / _width;
|
||||
var pixelSizeY = 1 / _height;
|
||||
|
||||
var program = _compileShader(_filter.convolution.SHADER);
|
||||
gl.uniform1fv(program.uniform.m, m);
|
||||
gl.uniform2f(program.uniform.px, pixelSizeX, pixelSizeY);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.convolution.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform vec2 px;',
|
||||
'uniform float m[9];',
|
||||
|
||||
'void main(void) {',
|
||||
'vec4 c11 = texture2D(texture, vUv - px);', // top left
|
||||
'vec4 c12 = texture2D(texture, vec2(vUv.x, vUv.y - px.y));', // top center
|
||||
'vec4 c13 = texture2D(texture, vec2(vUv.x + px.x, vUv.y - px.y));', // top right
|
||||
|
||||
'vec4 c21 = texture2D(texture, vec2(vUv.x - px.x, vUv.y) );', // mid left
|
||||
'vec4 c22 = texture2D(texture, vUv);', // mid center
|
||||
'vec4 c23 = texture2D(texture, vec2(vUv.x + px.x, vUv.y) );', // mid right
|
||||
|
||||
'vec4 c31 = texture2D(texture, vec2(vUv.x - px.x, vUv.y + px.y) );', // bottom left
|
||||
'vec4 c32 = texture2D(texture, vec2(vUv.x, vUv.y + px.y) );', // bottom center
|
||||
'vec4 c33 = texture2D(texture, vUv + px );', // bottom right
|
||||
|
||||
'gl_FragColor = ',
|
||||
'c11 * m[0] + c12 * m[1] + c22 * m[2] +',
|
||||
'c21 * m[3] + c22 * m[4] + c23 * m[5] +',
|
||||
'c31 * m[6] + c32 * m[7] + c33 * m[8];',
|
||||
'gl_FragColor.a = c22.a;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
_filter.detectEdges = function() {
|
||||
_filter.convolution.call(this, [
|
||||
0, 1, 0,
|
||||
1, -4, 1,
|
||||
0, 1, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sobelX = function() {
|
||||
_filter.convolution.call(this, [
|
||||
-1, 0, 1,
|
||||
-2, 0, 2,
|
||||
-1, 0, 1
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sobelY = function() {
|
||||
_filter.convolution.call(this, [
|
||||
-1, -2, -1,
|
||||
0, 0, 0,
|
||||
1, 2, 1
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.sharpen = function( amount ) {
|
||||
var a = amount || 1;
|
||||
_filter.convolution.call(this, [
|
||||
0, -1*a, 0,
|
||||
-1*a, 1 + 4*a, -1*a,
|
||||
0, -1*a, 0
|
||||
]);
|
||||
};
|
||||
|
||||
_filter.emboss = function( size ) {
|
||||
var s = size || 1;
|
||||
_filter.convolution.call(this, [
|
||||
-2*s, -1*s, 0,
|
||||
-1*s, 1, 1*s,
|
||||
0, 1*s, 2*s
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Blur Filter
|
||||
|
||||
_filter.blur = function( size ) {
|
||||
var blurSizeX = (size/7) / _width;
|
||||
var blurSizeY = (size/7) / _height;
|
||||
|
||||
var program = _compileShader(_filter.blur.SHADER);
|
||||
|
||||
// Vertical
|
||||
gl.uniform2f(program.uniform.px, 0, blurSizeY);
|
||||
_draw(DRAW.INTERMEDIATE);
|
||||
|
||||
// Horizontal
|
||||
gl.uniform2f(program.uniform.px, blurSizeX, 0);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.blur.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform sampler2D texture;',
|
||||
'uniform vec2 px;',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = vec4(0.0);',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-7.0*px.x, -7.0*px.y))*0.0044299121055113265;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-6.0*px.x, -6.0*px.y))*0.00895781211794;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-5.0*px.x, -5.0*px.y))*0.0215963866053;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-4.0*px.x, -4.0*px.y))*0.0443683338718;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-3.0*px.x, -3.0*px.y))*0.0776744219933;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-2.0*px.x, -2.0*px.y))*0.115876621105;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2(-1.0*px.x, -1.0*px.y))*0.147308056121;',
|
||||
'gl_FragColor += texture2D(texture, vUv )*0.159576912161;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 1.0*px.x, 1.0*px.y))*0.147308056121;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 2.0*px.x, 2.0*px.y))*0.115876621105;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 3.0*px.x, 3.0*px.y))*0.0776744219933;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 4.0*px.x, 4.0*px.y))*0.0443683338718;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 5.0*px.x, 5.0*px.y))*0.0215963866053;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 6.0*px.x, 6.0*px.y))*0.00895781211794;',
|
||||
'gl_FragColor += texture2D(texture, vUv + vec2( 7.0*px.x, 7.0*px.y))*0.0044299121055113265;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Pixelate Filter
|
||||
|
||||
_filter.pixelate = function( size ) {
|
||||
var blurSizeX = (size) / _width;
|
||||
var blurSizeY = (size) / _height;
|
||||
|
||||
var program = _compileShader(_filter.pixelate.SHADER);
|
||||
|
||||
// Horizontal
|
||||
gl.uniform2f(program.uniform.size, blurSizeX, blurSizeY);
|
||||
_draw();
|
||||
};
|
||||
|
||||
_filter.pixelate.SHADER = [
|
||||
'precision highp float;',
|
||||
'varying vec2 vUv;',
|
||||
'uniform vec2 size;',
|
||||
'uniform sampler2D texture;',
|
||||
|
||||
'vec2 pixelate(vec2 coord, vec2 size) {',
|
||||
'return floor( coord / size ) * size;',
|
||||
'}',
|
||||
|
||||
'void main(void) {',
|
||||
'gl_FragColor = vec4(0.0);',
|
||||
'vec2 coord = pixelate(vUv, size);',
|
||||
'gl_FragColor += texture2D(texture, coord);',
|
||||
'}',
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
export default WebGLImageFilter;
|
||||
|
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
".eslintrc.js"
|
||||
]
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"DOM"
|
||||
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
"resolveJsonModule": true, /* Enable importing .json files. */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
"declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
"outDir": "./lib", /* Specify an output folder for all emitted files. */
|
||||
"removeComments": false, /* Disable emitting comments. */
|
||||
|
||||
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
|
||||
}
|
@@ -0,0 +1 @@
|
||||
node_modules
|
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser', // 使用 ts 解析器
|
||||
extends: [
|
||||
'eslint:recommended', // eslint 推荐规则
|
||||
'plugin:@typescript-eslint/recommended', // ts 推荐规则
|
||||
'plugin:jest/recommended',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'jest',
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true
|
||||
}
|
||||
},
|
||||
rules: {}
|
||||
}
|
||||
|
30
examples/application/js/package/packages/paddlejs-models/mobilenet/.gitignore
vendored
Normal file
30
examples/application/js/package/packages/paddlejs-models/mobilenet/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
etc
|
||||
lib
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user