add histogram equalization

pull/356/head
Vladimir Mandic 2021-11-05 15:09:54 -04:00
parent 0fa9498afe
commit db63a70c8a
6 changed files with 33 additions and 8 deletions

View File

@ -2,17 +2,15 @@
## Work in Progress ## 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
<br> <br>
### Exploring ### Exploring
- Optical Flow: <https://docs.opencv.org/3.3.1/db/d7f/tutorial_js_lucas_kanade.html> - Optical Flow: <https://docs.opencv.org/3.3.1/db/d7f/tutorial_js_lucas_kanade.html>
- Histogram Equalization: Regular, Adaptive, Contrast Limited - Histogram Equalization: Regular, Adaptive, Contrast Limited, CLAHE
- TFLite Models: <https://js.tensorflow.org/api_tflite/0.0.1-alpha.4/> - TFLite Models: <https://js.tensorflow.org/api_tflite/0.0.1-alpha.4/>
- Body segmentation: `robust-video-matting` - Body segmentation: `robust-video-matting`
- TFJS incompatibility with latest `long.js` 5.0.0 due to CJS to ESM switch
<br><hr><br> <br><hr><br>

View File

@ -26,5 +26,6 @@
<pre id="status" style="position: absolute; top: 12px; right: 20px; background-color: grey; padding: 8px; box-shadow: 2px 2px black"></pre> <pre id="status" style="position: absolute; top: 12px; right: 20px; background-color: grey; padding: 8px; box-shadow: 2px 2px black"></pre>
<pre id="log" style="padding: 8px"></pre> <pre id="log" style="padding: 8px"></pre>
<div id="performance" style="position: absolute; bottom: 0; width: 100%; padding: 8px; font-size: 0.8rem;"></div> <div id="performance" style="position: absolute; bottom: 0; width: 100%; padding: 8px; font-size: 0.8rem;"></div>
<canvas id="test" style="position: absolute; bottom: 0; right: 0; width: 30%"></canvas>
</body> </body>
</html> </html>

View File

@ -122,16 +122,20 @@ export interface SegmentationConfig extends GenericConfig {
export interface FilterConfig { export interface FilterConfig {
/** @property are image filters enabled? */ /** @property are image filters enabled? */
enabled: boolean, 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 both width and height are set to 0, there is no resizing
* - if just one is set, second one is scaled automatically * - if just one is set, second one is scaled automatically
* - if both are set, values are used as-is * - if both are set, values are used as-is
* @property
*/ */
width: number, width: number,
/** Resize input height /** resize input height
* - if both width and height are set to 0, there is no resizing * - if both width and height are set to 0, there is no resizing
* - if just one is set, second one is scaled automatically * - if just one is set, second one is scaled automatically
* - if both are set, values are used as-is * - if both are set, values are used as-is
* @property
*/ */
height: number, height: number,
/** @property return processed canvas imagedata in result */ /** @property return processed canvas imagedata in result */
@ -262,6 +266,7 @@ const config: Config = {
skipAllowed: false, skipAllowed: false,
filter: { filter: {
enabled: true, enabled: true,
equalization: false,
width: 0, width: 0,
height: 0, height: 0,
flip: false, flip: false,

20
src/image/enhance.ts Normal file
View File

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

View File

@ -7,6 +7,7 @@ import * as fxImage from './imagefx';
import type { Input, AnyCanvas, Tensor, Config } from '../exports'; import type { Input, AnyCanvas, Tensor, Config } from '../exports';
import { env } from '../util/env'; import { env } from '../util/env';
import { log, now } from '../util/util'; import { log, now } from '../util/util';
import * as enhance from './enhance';
const maxSize = 2048; const maxSize = 2048;
// internal temp canvases // 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'); if (!pixels) throw new Error('cannot create tensor from input');
const casted = tf.cast(pixels, 'float32'); 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]); tf.dispose([pixels, casted]);
return { tensor, canvas: (config.filter.return ? outCanvas : null) }; return { tensor, canvas: (config.filter.return ? outCanvas : null) };
} }

2
wiki

@ -1 +1 @@
Subproject commit 5e89af1004860ea9f302e516699b5e0b4e0a825f Subproject commit 6abe315e2ae3e3aa457c4c728d9e2959d7e023db