Skip to content

Commit c42c8e6

Browse files
authored
[memory leak] fixes memory leaks (#684)
* fix memory leak in bodypix * fixes memory leak in unet * fixes memory leak in style transfer * fix memory leak in pix2pix * fixes memory leaks in kmeans * fixes memory leaks in DCGAN & adds options to constructor * fix memory leak in cvae * fixes mem leak in sentiment * fixes unet - array3DToImage destroys tensor, order matters
1 parent 6e99efc commit c42c8e6

File tree

9 files changed

+217
-118
lines changed

9 files changed

+217
-118
lines changed

src/BodyPix/index.js

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const DEFAULTS = {
2121
"multiplier": 0.75,
2222
"outputStride": 16,
2323
"segmentationThreshold": 0.5,
24-
"palette": BODYPIX_PALETTE
24+
"palette": BODYPIX_PALETTE,
25+
"returnTensors": false,
2526
}
2627

2728
class BodyPix {
@@ -40,7 +41,8 @@ class BodyPix {
4041
multiplier: options.multiplier || DEFAULTS.multiplier,
4142
outputStride: options.outputStride || DEFAULTS.outputStride,
4243
segmentationThreshold: options.segmentationThreshold || DEFAULTS.segmentationThreshold,
43-
palette: options.palette || DEFAULTS.palette
44+
palette: options.palette || DEFAULTS.palette,
45+
returnTensors: options.returnTensors || DEFAULTS.returnTensors
4446
}
4547

4648
this.ready = callCallback(this.loadModel(), callback);
@@ -126,7 +128,6 @@ class BodyPix {
126128
this.config.outputStride = segmentationOptions.outputStride || this.config.outputStride;
127129
this.config.segmentationThreshold = segmentationOptions.segmentationThreshold || this.config.segmentationThreshold;
128130

129-
// const segmentation = await this.model.estimatePersonSegmentation(imgToSegment, this.config.outputStride, this.config.segmentationThreshold)
130131
const segmentation = await this.model.estimatePartSegmentation(imgToSegment, this.config.outputStride, this.config.segmentationThreshold);
131132

132133
const bodyPartsMeta = this.bodyPartsSpec(this.config.palette);
@@ -153,13 +154,12 @@ class BodyPix {
153154
result.raw.personMask = bp.toMaskImageData(segmentation, false);
154155
result.raw.partMask = bp.toColoredPartImageData(segmentation, colorsArray);
155156

156-
let normTensor = await tf.browser.fromPixels(imgToSegment);
157-
158157
const {
159158
personMask,
160159
backgroundMask,
161160
partMask,
162161
} = tf.tidy(() => {
162+
let normTensor = tf.browser.fromPixels(imgToSegment);
163163
// create a tensor from the input image
164164
const alpha = tf.ones([segmentation.height, segmentation.width, 1]).tile([1, 1, 1]).mul(255)
165165
normTensor = normTensor.concat(alpha, 2)
@@ -198,6 +198,17 @@ class BodyPix {
198198
result.partMask = await this.convertToP5Image(partMaskPixels, segmentation.width, segmentation.height)
199199
}
200200

201+
if (!this.config.returnTensors) {
202+
personMask.dispose();
203+
backgroundMask.dispose();
204+
partMask.dispose();
205+
} else {
206+
// return tensors
207+
result.tensor.personMask = personMask;
208+
result.tensor.backgroundMask = backgroundMask;
209+
result.tensor.partMask = partMask;
210+
}
211+
201212
return result;
202213

203214
}
@@ -309,12 +320,11 @@ class BodyPix {
309320
// result.backgroundMask = bgMaskCanvas;
310321
// result.featureMask = featureMaskCanvas;
311322

312-
let normTensor = await tf.browser.fromPixels(imgToSegment);
313-
314323
const {
315324
personMask,
316325
backgroundMask
317326
} = tf.tidy(() => {
327+
let normTensor = tf.browser.fromPixels(imgToSegment);
318328
// create a tensor from the input image
319329
const alpha = tf.ones([segmentation.height, segmentation.width, 1]).tile([1, 1, 1]).mul(255)
320330
normTensor = normTensor.concat(alpha, 2)
@@ -337,16 +347,25 @@ class BodyPix {
337347
const personMaskPixels = await tf.browser.toPixels(personMask);
338348
const bgMaskPixels = await tf.browser.toPixels(backgroundMask);
339349

340-
// otherwise, return the pixels
341-
result.personMask = personMaskPixels;
342-
result.backgroundMask = bgMaskPixels;
343-
344350
// if p5 exists, convert to p5 image
345351
if (p5Utils.checkP5()) {
346352
result.personMask = await this.convertToP5Image(personMaskPixels, segmentation.width, segmentation.height)
347353
result.backgroundMask = await this.convertToP5Image(bgMaskPixels, segmentation.width, segmentation.height)
354+
} else {
355+
// otherwise, return the pixels
356+
result.personMask = personMaskPixels;
357+
result.backgroundMask = bgMaskPixels;
348358
}
349359

360+
if (!this.config.returnTensors) {
361+
personMask.dispose();
362+
backgroundMask.dispose();
363+
} else {
364+
result.tensor.personMask = personMask;
365+
result.tensor.backgroundMask = backgroundMask;
366+
}
367+
368+
350369
return result;
351370

352371
}

src/CVAE/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ class Cvae {
9797
const temp = this.model.predict([this.latentDim, input]);
9898
return temp.reshape([temp.shape[1], temp.shape[2], temp.shape[3]]);
9999
});
100+
100101

101102
const raws = await tf.browser.toPixels(res);
103+
res.dispose();
102104

103105
const canvas = document.createElement('canvas'); // consider using offScreneCanvas
104106
const ctx = canvas.getContext('2d');

src/DCGAN/index.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@ class DCGANBase {
2727
* @param {modelName} modelName - The name of the model to use.
2828
* @param {function} readyCb - A callback to be called when the model is ready.
2929
*/
30-
constructor(modelPath, callback) {
30+
constructor(modelPath, options, callback) {
3131
this.model = {};
3232
this.modelPath = modelPath;
3333
this.modelInfo = {};
3434
this.modelPathPrefix = '';
3535
this.modelReady = false;
36+
this.config = {
37+
returnTensors: options.returnTensors || false,
38+
}
3639
this.ready = callCallback(this.loadModel(), callback);
3740
}
3841

@@ -98,6 +101,7 @@ class DCGANBase {
98101
* @return {object} includes blob, raw, and tensor. if P5 exists, then a p5Image
99102
*/
100103
async generateInternal(latentVector) {
104+
101105
const {
102106
modelLatentDim
103107
} = this.modelInfo;
@@ -119,12 +123,19 @@ class DCGANBase {
119123
const result = {};
120124
result.blob = blob;
121125
result.raw = raw;
122-
result.tensor = imageTensor;
126+
123127

124128
if (p5Utils.checkP5()) {
125129
result.image = p5Image;
126130
}
127131

132+
if(!this.config.returnTensors){
133+
result.tensor = null;
134+
imageTensor.dispose();
135+
} else {
136+
result.tensor = imageTensor;
137+
}
138+
128139
return result;
129140

130141
}
@@ -138,7 +149,9 @@ class DCGANBase {
138149

139150
}
140151

141-
const DCGAN = (modelPath, cb) => {
152+
const DCGAN = (modelPath, optionsOrCb, cb) => {
153+
let callback;
154+
let options = {};
142155
if (typeof modelPath !== 'string') {
143156
throw new Error(`Please specify a path to a "manifest.json" file: \n
144157
"models/face/manifest.json" \n\n
@@ -151,10 +164,17 @@ const DCGAN = (modelPath, cb) => {
151164
}
152165
`);
153166
}
167+
168+
if(typeof optionsOrCb === 'function'){
169+
callback = optionsOrCb;
170+
} else if (typeof optionsOrCb === 'object'){
171+
options = optionsOrCb;
172+
callback = cb;
173+
}
154174

155175

156-
const instance = new DCGANBase(modelPath, cb);
157-
return cb ? instance : instance.ready;
176+
const instance = new DCGANBase(modelPath, options, callback);
177+
return callback ? instance : instance.ready;
158178

159179
}
160180

0 commit comments

Comments
 (0)