Skip to content

Commit c60a337

Browse files
author
Nikhil Thorat
authored
Make tf.browser.fromPixels and route tf.fromPixels to it. (tensorflow#1540)
MISC We will delete tf.fromPixels in the 1.0 branch. Fixes tensorflow/tfjs#669
1 parent 99ef32e commit c60a337

File tree

7 files changed

+234
-164
lines changed

7 files changed

+234
-164
lines changed

src/engine_test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describeWithFlags('fromPixels + regular math op', WEBGL_ENVS, () => {
3232
pixels.data[i] = 250;
3333
}
3434

35-
const a = tf.fromPixels(pixels, 4);
35+
const a = tf.browser.fromPixels(pixels, 4);
3636
const b = tf.scalar(20, 'int32');
3737

3838
const res = tf.add(a, b);
@@ -622,12 +622,12 @@ describeWithFlags('Switching WebGL + CPU backends', WEBGL_ENVS, () => {
622622

623623
it('fromPixels with mixed backends works', () => {
624624
tf.setBackend('webgl1');
625-
const a =
626-
tf.fromPixels(new ImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1));
625+
const a = tf.browser.fromPixels(
626+
new ImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1));
627627

628628
tf.setBackend('webgl2');
629-
const b =
630-
tf.fromPixels(new ImageData(new Uint8ClampedArray([5, 6, 7, 8]), 1, 1));
629+
const b = tf.browser.fromPixels(
630+
new ImageData(new Uint8ClampedArray([5, 6, 7, 8]), 1, 1));
631631

632632
expectArraysClose(tf.add(a, b), [6, 8, 10]);
633633
});

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {deprecationWarn, disableDeprecationWarnings, enableProdMode, Environment
2727
// Serialization.
2828
import * as io from './io/io';
2929
import * as math from './math';
30+
import * as browser from './ops/browser';
3031
import * as serialization from './serialization';
3132
import {setOpHandler} from './tensor';
3233
import * as test_util from './test_util';
@@ -69,7 +70,7 @@ export {version as version_core};
6970
export {nextFrame, enableProdMode, disableDeprecationWarnings, deprecationWarn};
7071

7172
// Second level exports.
72-
export {environment, io, math, serialization, test_util, util, webgl};
73+
export {browser, environment, io, math, serialization, test_util, util, webgl};
7374

7475
// Backend specific.
7576
export {KernelBackend, BackendTimingInfo, DataMover, DataStorage} from './kernels/backend';

src/kernels/backend_cpu.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ export class MathBackendCPU implements KernelBackend {
115115
pixels: ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement,
116116
numChannels: number): Tensor3D {
117117
if (pixels == null) {
118-
throw new Error('pixels passed to tf.fromPixels() can not be null');
118+
throw new Error(
119+
'pixels passed to tf.browser.fromPixels() can not be null');
119120
}
120121
let vals: Uint8ClampedArray;
121122
// tslint:disable-next-line:no-any
@@ -150,7 +151,7 @@ export class MathBackendCPU implements KernelBackend {
150151
.data;
151152
} else {
152153
throw new Error(
153-
'pixels passed to tf.fromPixels() must be either an ' +
154+
'pixels passed to tf.browser.fromPixels() must be either an ' +
154155
`HTMLVideoElement, HTMLImageElement, HTMLCanvasElement or ` +
155156
`ImageData, but was ${(pixels as {}).constructor.name}`);
156157
}

src/kernels/backend_webgl.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ export class MathBackendWebGL implements KernelBackend {
207207
pixels: ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement,
208208
numChannels: number): Tensor3D {
209209
if (pixels == null) {
210-
throw new Error('pixels passed to tf.fromPixels() can not be null');
210+
throw new Error(
211+
'pixels passed to tf.browser.fromPixels() can not be null');
211212
}
212213
const texShape: [number, number] = [pixels.height, pixels.width];
213214
const outShape = [pixels.height, pixels.width, numChannels];
@@ -218,17 +219,18 @@ export class MathBackendWebGL implements KernelBackend {
218219
!(pixels instanceof HTMLCanvasElement) &&
219220
!(pixels instanceof ImageData)) {
220221
throw new Error(
221-
'pixels passed to tf.fromPixels() must be either an ' +
222+
'pixels passed to tf.browser.fromPixels() must be either an ' +
222223
`HTMLVideoElement, HTMLImageElement, HTMLCanvasElement or ` +
223224
`ImageData, but was ${(pixels as {}).constructor.name}`);
224225
}
225226
if (pixels instanceof HTMLVideoElement) {
226227
if (this.fromPixels2DContext == null) {
227228
if (document.readyState !== 'complete') {
228229
throw new Error(
229-
'The DOM is not ready yet. Please call tf.fromPixels() ' +
230-
'once the DOM is ready. One way to do that is to add an ' +
231-
'event listener for `DOMContentLoaded` on the document object');
230+
'The DOM is not ready yet. Please call ' +
231+
'tf.browser.fromPixels() once the DOM is ready. One way to ' +
232+
'do that is to add an event listener for `DOMContentLoaded` ' +
233+
'on the document object');
232234
}
233235
this.fromPixels2DContext =
234236
document.createElement('canvas').getContext('2d');

src/ops/array_ops.ts

Lines changed: 7 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import {Tensor, Tensor1D, Tensor2D, Tensor3D, Tensor4D, TensorBuffer} from '../t
2020
import {convertToTensor, convertToTensorArray} from '../tensor_util_env';
2121
import {DataType, DataTypeMap, Rank, ShapeMap, TensorLike, TensorLike4D} from '../types';
2222
import * as util from '../util';
23+
2324
import {getAxesPermutation, getInnerMostAxes} from './axis_util';
25+
import {fromPixels as browserFromPixels, toPixels as browserToPixels} from './browser';
2426
import {concat} from './concat_split';
2527
import {op} from './operation';
2628
import {MPRandGauss} from './rand';
@@ -306,17 +308,7 @@ function oneHot_(
306308
}
307309

308310
/**
309-
* Creates a `tf.Tensor` from an image.
310-
*
311-
* ```js
312-
* const image = new ImageData(1, 1);
313-
* image.data[0] = 100;
314-
* image.data[1] = 150;
315-
* image.data[2] = 200;
316-
* image.data[3] = 255;
317-
*
318-
* tf.fromPixels(image).print();
319-
* ```
311+
* Deprecated. Use `tf.browser.fromPixels`.
320312
*
321313
* @param pixels The input image to construct the tensor from. The
322314
* supported image types are all 4-channel.
@@ -328,22 +320,11 @@ function oneHot_(
328320
function fromPixels_(
329321
pixels: ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement,
330322
numChannels = 3): Tensor3D {
331-
if (numChannels > 4) {
332-
throw new Error(
333-
'Cannot construct Tensor with more than 4 channels from pixels.');
334-
}
335-
return ENV.engine.fromPixels(pixels, numChannels);
323+
return browserFromPixels(pixels, numChannels);
336324
}
337325

338326
/**
339-
* Draws a `tf.Tensor` of pixel values to a byte array or optionally a
340-
* canvas.
341-
*
342-
* When the dtype of the input is 'float32', we assume values in the range
343-
* [0-1]. Otherwise, when input is 'int32', we assume values in the range
344-
* [0-255].
345-
*
346-
* Returns a promise that resolves when the canvas has been drawn to.
327+
* Deprecated. Use `tf.browser.toPixels`.
347328
*
348329
* @param img A rank-2 or rank-3 tensor. If rank-2, draws grayscale. If
349330
* rank-3, must have depth of 1, 3 or 4. When depth of 1, draws
@@ -356,89 +337,7 @@ function fromPixels_(
356337
async function toPixels(
357338
img: Tensor2D|Tensor3D|TensorLike,
358339
canvas?: HTMLCanvasElement): Promise<Uint8ClampedArray> {
359-
let $img = convertToTensor(img, 'img', 'toPixels');
360-
if (!(img instanceof Tensor)) {
361-
// Assume int32 if user passed a native array.
362-
$img = $img.toInt();
363-
}
364-
if ($img.rank !== 2 && $img.rank !== 3) {
365-
throw new Error(
366-
`toPixels only supports rank 2 or 3 tensors, got rank ${$img.rank}.`);
367-
}
368-
const [height, width] = $img.shape.slice(0, 2);
369-
const depth = $img.rank === 2 ? 1 : $img.shape[2];
370-
371-
if (depth > 4 || depth === 2) {
372-
throw new Error(
373-
`toPixels only supports depth of size ` +
374-
`1, 3 or 4 but got ${depth}`);
375-
}
376-
377-
const minTensor = $img.min();
378-
const maxTensor = $img.max();
379-
const min = (await minTensor.data())[0];
380-
const max = (await maxTensor.data())[0];
381-
minTensor.dispose();
382-
maxTensor.dispose();
383-
if ($img.dtype === 'float32') {
384-
if (min < 0 || max > 1) {
385-
throw new Error(
386-
`Tensor values for a float32 Tensor must be in the ` +
387-
`range [0 - 1] but got range [${min} - ${max}].`);
388-
}
389-
} else if ($img.dtype === 'int32') {
390-
if (min < 0 || max > 255) {
391-
throw new Error(
392-
`Tensor values for a int32 Tensor must be in the ` +
393-
`range [0 - 255] but got range [${min} - ${max}].`);
394-
}
395-
} else {
396-
throw new Error(
397-
`Unsupported type for toPixels: ${$img.dtype}.` +
398-
` Please use float32 or int32 tensors.`);
399-
}
400-
401-
const data = await $img.data();
402-
const multiplier = $img.dtype === 'float32' ? 255 : 1;
403-
const bytes = new Uint8ClampedArray(width * height * 4);
404-
405-
for (let i = 0; i < height * width; ++i) {
406-
let r, g, b, a;
407-
if (depth === 1) {
408-
r = data[i] * multiplier;
409-
g = data[i] * multiplier;
410-
b = data[i] * multiplier;
411-
a = 255;
412-
} else if (depth === 3) {
413-
r = data[i * 3] * multiplier;
414-
g = data[i * 3 + 1] * multiplier;
415-
b = data[i * 3 + 2] * multiplier;
416-
a = 255;
417-
} else if (depth === 4) {
418-
r = data[i * 4] * multiplier;
419-
g = data[i * 4 + 1] * multiplier;
420-
b = data[i * 4 + 2] * multiplier;
421-
a = data[i * 4 + 3] * multiplier;
422-
}
423-
424-
const j = i * 4;
425-
bytes[j + 0] = Math.round(r);
426-
bytes[j + 1] = Math.round(g);
427-
bytes[j + 2] = Math.round(b);
428-
bytes[j + 3] = Math.round(a);
429-
}
430-
431-
if (canvas != null) {
432-
canvas.width = width;
433-
canvas.height = height;
434-
const ctx = canvas.getContext('2d');
435-
const imageData = new ImageData(bytes, width, height);
436-
ctx.putImageData(imageData, 0, 0);
437-
}
438-
if ($img !== img) {
439-
$img.dispose();
440-
}
441-
return bytes;
340+
return browserToPixels(img, canvas);
442341
}
443342

444343
/**
@@ -1210,7 +1109,7 @@ export const cumsum = op({cumsum_});
12101109
export const depthToSpace = op({depthToSpace_});
12111110
export const expandDims = op({expandDims_});
12121111
export const eye = op({eye_});
1213-
export const fromPixels = op({fromPixels_});
1112+
export const fromPixels = fromPixels_;
12141113
export const multinomial = op({multinomial_});
12151114
export const oneHot = op({oneHot_});
12161115
export const pad = op({pad_});

0 commit comments

Comments
 (0)