From db63a70c8a2fd9899e7465cd4aa633ecab4348b3 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 5 Nov 2021 15:09:54 -0400 Subject: [PATCH] add histogram equalization --- TODO.md | 6 ++---- demo/typescript/index.html | 1 + src/config.ts | 9 +++++++-- src/image/enhance.ts | 20 ++++++++++++++++++++ src/image/image.ts | 3 ++- wiki | 2 +- 6 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 src/image/enhance.ts diff --git a/TODO.md b/TODO.md index b0ccdfd3..41260b76 100644 --- a/TODO.md +++ b/TODO.md @@ -2,17 +2,15 @@ ## Work in Progress -- Switch to custom `tfjs` for main `human` ESM bundle -- Sort out incompatibility with latest `long.js` 5.0.0 due to CJS to ESM switch -
### Exploring - Optical Flow: -- Histogram Equalization: Regular, Adaptive, Contrast Limited +- Histogram Equalization: Regular, Adaptive, Contrast Limited, CLAHE - TFLite Models: - Body segmentation: `robust-video-matting` +- TFJS incompatibility with latest `long.js` 5.0.0 due to CJS to ESM switch


diff --git a/demo/typescript/index.html b/demo/typescript/index.html index a74a14bc..3b7d0ba4 100644 --- a/demo/typescript/index.html +++ b/demo/typescript/index.html @@ -26,5 +26,6 @@

     

     
+ diff --git a/src/config.ts b/src/config.ts index 502b1ae8..e89c9601 100644 --- a/src/config.ts +++ b/src/config.ts @@ -122,16 +122,20 @@ export interface SegmentationConfig extends GenericConfig { export interface FilterConfig { /** @property are image filters enabled? */ enabled: boolean, - /** Resize input width + /** @property perform image histogram equalization */ + equalization: boolean, + /** resize input width * - if both width and height are set to 0, there is no resizing * - if just one is set, second one is scaled automatically * - if both are set, values are used as-is + * @property */ width: number, - /** Resize input height + /** resize input height * - if both width and height are set to 0, there is no resizing * - if just one is set, second one is scaled automatically * - if both are set, values are used as-is + * @property */ height: number, /** @property return processed canvas imagedata in result */ @@ -262,6 +266,7 @@ const config: Config = { skipAllowed: false, filter: { enabled: true, + equalization: false, width: 0, height: 0, flip: false, diff --git a/src/image/enhance.ts b/src/image/enhance.ts new file mode 100644 index 00000000..8fb55782 --- /dev/null +++ b/src/image/enhance.ts @@ -0,0 +1,20 @@ +/** + * Image enhancements + */ + +import * as tf from '../../dist/tfjs.esm.js'; +import type { Tensor } from '../exports'; + +export function histogramEqualization(input: Tensor): Tensor { + const channels = tf.split(input, 3, 2); + const min: Tensor[] = [tf.min(channels[0]), tf.min(channels[1]), tf.min(channels[2])]; + const max: Tensor[] = [tf.max(channels[0]), tf.max(channels[1]), tf.max(channels[2])]; + const sub = [tf.sub(channels[0], min[0]), tf.sub(channels[1], min[1]), tf.sub(channels[2], min[2])]; + const range = [tf.sub(max[0], min[0]), tf.sub(max[1], min[1]), tf.sub(max[2], min[2])]; + const fact = [tf.div(255, range[0]), tf.div(255, range[1]), tf.div(255, range[2])]; + const enh = [tf.mul(sub[0], fact[0]), tf.mul(sub[1], fact[1]), tf.mul(sub[2], fact[2])]; + const rgb = tf.stack([enh[0], enh[1], enh[2]], 2); + const reshape = tf.reshape(rgb, [1, input.shape[0], input.shape[1], 3]); + tf.dispose([...channels, ...min, ...max, ...sub, ...range, ...fact, ...enh, rgb]); + return reshape; +} diff --git a/src/image/image.ts b/src/image/image.ts index 97330b40..ad8f53b6 100644 --- a/src/image/image.ts +++ b/src/image/image.ts @@ -7,6 +7,7 @@ import * as fxImage from './imagefx'; import type { Input, AnyCanvas, Tensor, Config } from '../exports'; import { env } from '../util/env'; import { log, now } from '../util/util'; +import * as enhance from './enhance'; const maxSize = 2048; // internal temp canvases @@ -201,7 +202,7 @@ export function process(input: Input, config: Config, getTensor: boolean = true) } if (!pixels) throw new Error('cannot create tensor from input'); const casted = tf.cast(pixels, 'float32'); - const tensor = tf.expandDims(casted, 0); + const tensor = config.filter.equalization ? enhance.histogramEqualization(casted) : tf.expandDims(casted, 0); tf.dispose([pixels, casted]); return { tensor, canvas: (config.filter.return ? outCanvas : null) }; } diff --git a/wiki b/wiki index 5e89af10..6abe315e 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 5e89af1004860ea9f302e516699b5e0b4e0a825f +Subproject commit 6abe315e2ae3e3aa457c4c728d9e2959d7e023db