[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:
chenqianhe
2022-10-23 14:05:13 +08:00
committed by GitHub
parent 30971cf3fd
commit f2619b0546
273 changed files with 14697 additions and 5088 deletions

View File

@@ -0,0 +1,10 @@
{
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
]
}

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'], // 使用预设的配置 https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/config-conventional/index.js
}

View File

@@ -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: {}
}

View 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?

View File

@@ -0,0 +1 @@
auto-install-peers=true

View File

@@ -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"
}
}

View File

@@ -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))
},
}

View File

@@ -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)

View File

@@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
}

View File

@@ -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"
}
}

View File

@@ -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

View File

@@ -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: []
}
};

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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
};
}

View File

@@ -0,0 +1,12 @@
import assert from 'assert'
describe('Example:', () => {
/**
* Example
*/
describe('ExampleTest', () => {
test('Hello World!', () => {
assert.strictEqual('Hello World!', 'Hello World!')
})
})
})

View 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;

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"baseUrl": "./",
"resolveJsonModule": true
},
"include": [
"**/*.ts",
"**/*.js",
".eslintrc.js"
]
}

View File

@@ -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"
]
}