Skip to content

Commit 96217a8

Browse files
[react-native] Add option to TensorCamera to not use custom shaders to resize camera image texture (#5626)
* [react-native] Add option to not use custom shaders to resize camera image texture * fix
1 parent d421542 commit 96217a8

File tree

6 files changed

+98
-53
lines changed

6 files changed

+98
-53
lines changed

tfjs-react-native/src/camera/camera.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,15 @@ export async function toTexture(
135135
* @param texture the texture to convert into a tensor
136136
* @param sourceDims source dimensions of input texture (width, height, depth)
137137
* @param targetShape desired shape of output tensor
138+
* @param useCustomShadersToResize whether to use custom shaders to resize
139+
* texture.
138140
*
139141
* @doc {heading: 'Media', subheading: 'Camera'}
140142
*/
141143
export function fromTexture(
142144
gl: WebGL2RenderingContext, texture: WebGLTexture, sourceDims: Dimensions,
143-
targetShape: Dimensions, options: FromTextureOptions = {}): tf.Tensor3D {
145+
targetShape: Dimensions, useCustomShadersToResize = false,
146+
options: FromTextureOptions = {}): tf.Tensor3D {
144147
tf.util.assert(
145148
targetShape.depth === 3 || targetShape.depth === 4,
146149
() => 'fromTexture Error: target depth must be 3 or 4');
@@ -188,7 +191,8 @@ export function fromTexture(
188191
' "bilinear" or "nearest_neighbor"');
189192

190193
const resizedTexture = runResizeProgram(
191-
gl, texture, sourceDims, targetShape, alignCorners, interpolation);
194+
gl, texture, sourceDims, targetShape, alignCorners,
195+
useCustomShadersToResize, interpolation);
192196
const downloadedTextureData =
193197
downloadTextureData(gl, resizedTexture, targetShape);
194198

tfjs-react-native/src/camera/camera_stream.tsx

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface WrappedComponentProps {
3434
}
3535

3636
interface Props {
37+
useCustomShadersToResize: boolean;
3738
cameraTextureWidth: number;
3839
cameraTextureHeight: number;
3940
resizeWidth: number;
@@ -54,6 +55,7 @@ interface State {
5455

5556
const DEFAULT_AUTORENDER = true;
5657
const DEFAULT_RESIZE_DEPTH = 3;
58+
const DEFAULT_USE_CUSTOM_SHADERS_TO_RESIZE = false;
5759

5860
/**
5961
* A higher-order-component (HOC) that augments the [Expo.Camera](https://docs.expo.io/versions/latest/sdk/camera/)
@@ -70,10 +72,26 @@ const DEFAULT_RESIZE_DEPTH = 3;
7072
* __In addition to__ all the props taken by Expo.Camera. The returned
7173
* component takes the following props
7274
*
75+
* - __use_custom_shaders_to_resize__: boolean — whether to use custom shaders
76+
* to resize the camera image to smaller dimensions that fit the output
77+
* tensor.
78+
* - If it is set to false (default and recommended), the resize will be done
79+
* by the underlying GL system when drawing the camera image texture to the
80+
* target output texture with TEXTURE_MIN_FILTER/TEXTURE_MAG_FILTER set to
81+
* gl.LINEAR, and there is no need to provide `cameraTextureWidth` and
82+
* `cameraTextureHeight` props below.
83+
* - If it is set to true (legacy), the resize will be done by the custom
84+
* shaders defined in `resize_bilinear_program_info.ts`. Setting it to true
85+
* also requires that client provide the correct `cameraTextureWidth` and
86+
* `cameraTextureHeight` props below. Unfortunately there is no official API
87+
* to get the camera texture size programmatically so they have to be
88+
* decided empirically. From our experience, it is hard to cover all cases
89+
* in this way because different devices models and/or preview sizes might
90+
* produce different camera texture sizes.
7391
* - __cameraTextureWidth__: number — the width the camera preview texture
74-
* (see example and note below)
92+
* (see note above)
7593
* - __cameraTextureHeight__: number — the height the camera preview texture
76-
* (see example and note below)
94+
* (see note above)
7795
* - __resizeWidth__: number — the width of the output tensor
7896
* - __resizeHeight__: number — the height of the output tensor
7997
* - __resizeDepth__: number — the depth (num of channels) of the output tensor.
@@ -127,31 +145,12 @@ const DEFAULT_RESIZE_DEPTH = 3;
127145
* }
128146
*
129147
* render() {
130-
* // Currently expo does not support automatically determining the
131-
* // resolution of the camera texture used. So it must be determined
132-
* // empirically for the supported devices and preview size.
133-
*
134-
* let textureDims;
135-
* if (Platform.OS === 'ios') {
136-
* textureDims = {
137-
* height: 1920,
138-
* width: 1080,
139-
* };
140-
* } else {
141-
* textureDims = {
142-
* height: 1200,
143-
* width: 1600,
144-
* };
145-
* }
146-
*
147148
* return <View>
148149
* <TensorCamera
149150
* // Standard Camera props
150151
* style={styles.camera}
151152
* type={Camera.Constants.Type.front}
152153
* // Tensor related props
153-
* cameraTextureHeight={textureDims.height}
154-
* cameraTextureWidth={textureDims.width}
155154
* resizeHeight={200}
156155
* resizeWidth={152}
157156
* resizeDepth={3}
@@ -251,10 +250,24 @@ export function cameraWithTensors<T extends WrappedComponentProps>(
251250
resizeHeight,
252251
resizeWidth,
253252
resizeDepth,
254-
cameraTextureHeight,
255-
cameraTextureWidth,
256253
} = this.props;
257254

255+
// cameraTextureHeight and cameraTextureWidth props can be omitted when
256+
// useCustomShadersToResize is set to false. Setting a default value to
257+
// them here.
258+
const cameraTextureHeight =
259+
this.props.cameraTextureHeight != null
260+
? this.props.cameraTextureHeight
261+
: 0;
262+
const cameraTextureWidth =
263+
this.props.cameraTextureWidth != null
264+
? this.props.cameraTextureWidth
265+
: 0;
266+
const useCustomShadersToResize =
267+
this.props.useCustomShadersToResize != null
268+
? this.props.useCustomShadersToResize
269+
: DEFAULT_USE_CUSTOM_SHADERS_TO_RESIZE;
270+
258271
//
259272
// Set up a generator function that yields tensors representing the
260273
// camera on demand.
@@ -279,7 +292,8 @@ export function cameraWithTensors<T extends WrappedComponentProps>(
279292
gl,
280293
cameraTexture,
281294
textureDims,
282-
targetDims
295+
targetDims,
296+
useCustomShadersToResize,
283297
);
284298
yield imageTensor;
285299
}
@@ -329,6 +343,7 @@ export function cameraWithTensors<T extends WrappedComponentProps>(
329343
// Use this object to use typescript to check that we are removing
330344
// all the tensorCamera properties.
331345
const tensorCameraPropMap: Props = {
346+
useCustomShadersToResize: null,
332347
cameraTextureWidth: null,
333348
cameraTextureHeight: null,
334349
resizeWidth: null,

tfjs-react-native/src/camera/camera_test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describeWithFlags('toTexture', RN_ENVS, () => {
6666
const texture = await toTexture(gl, inTensor);
6767

6868
const outTensor = fromTexture(
69-
gl, texture, {width, height, depth}, {width, height, depth});
69+
gl, texture, {width, height, depth}, {width, height, depth}, true);
7070

7171
expectArraysEqual(await inTensor.data(), await outTensor.data());
7272
expectArraysEqual(inTensor.shape, outTensor.shape);
@@ -168,6 +168,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
168168
width: inShape[1],
169169
depth: inShape[2],
170170
},
171+
true,
171172
{
172173
alignCorners: false,
173174
interpolation: 'nearest_neighbor',
@@ -192,6 +193,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
192193
width: inShape[1],
193194
depth: inShape[2],
194195
},
196+
true,
195197
{
196198
alignCorners: true,
197199
interpolation: 'nearest_neighbor',
@@ -231,6 +233,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
231233
width: expectedShape[1],
232234
depth: expectedShape[2],
233235
},
236+
true,
234237
{alignCorners: false, interpolation: 'nearest_neighbor'},
235238
);
236239

@@ -267,6 +270,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
267270
width: expectedShape[1],
268271
depth: expectedShape[2],
269272
},
273+
true,
270274
{alignCorners: true, interpolation: 'nearest_neighbor'},
271275
);
272276

@@ -304,6 +308,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
304308
width: expectedShape[1],
305309
depth: expectedShape[2],
306310
},
311+
true,
307312
{alignCorners: false, interpolation: 'nearest_neighbor'},
308313
);
309314

@@ -342,6 +347,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
342347
width: expectedShape[1],
343348
depth: expectedShape[2],
344349
},
350+
true,
345351
{alignCorners: true, interpolation: 'nearest_neighbor'},
346352
);
347353

@@ -393,6 +399,7 @@ describeWithFlags('fromTexture:nearestNeighbor', RN_ENVS, () => {
393399
width: inShape[1],
394400
depth: 3,
395401
},
402+
true,
396403
{
397404
alignCorners: true,
398405
interpolation: 'nearest_neighbor',
@@ -465,6 +472,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
465472
width: inShape[1],
466473
depth: inShape[2],
467474
},
475+
true,
468476
{
469477
alignCorners: false,
470478
interpolation: 'bilinear',
@@ -489,6 +497,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
489497
width: inShape[1],
490498
depth: inShape[2],
491499
},
500+
true,
492501
{
493502
alignCorners: true,
494503
interpolation: 'bilinear',
@@ -527,6 +536,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
527536
width: expectedShape[1],
528537
depth: expectedShape[2],
529538
},
539+
true,
530540
{alignCorners: false, interpolation: 'bilinear'},
531541
);
532542

@@ -562,6 +572,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
562572
width: expectedShape[1],
563573
depth: expectedShape[2],
564574
},
575+
true,
565576
{alignCorners: true, interpolation: 'bilinear'},
566577
);
567578

@@ -599,6 +610,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
599610
width: expectedShape[1],
600611
depth: expectedShape[2],
601612
},
613+
true,
602614
{alignCorners: false, interpolation: 'bilinear'},
603615
);
604616

@@ -636,6 +648,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
636648
width: expectedShape[1],
637649
depth: expectedShape[2],
638650
},
651+
true,
639652
{alignCorners: true, interpolation: 'bilinear'},
640653
);
641654

@@ -687,6 +700,7 @@ describeWithFlags('fromTexture:bilinear', RN_ENVS, () => {
687700
width: inShape[1],
688701
depth: 3,
689702
},
703+
true,
690704
{
691705
alignCorners: true,
692706
interpolation: 'bilinear',

tfjs-react-native/src/camera/camera_webgl_util.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export function drawTexture(
151151
gl: WebGL2RenderingContext, texture: WebGLTexture,
152152
dims: {width: number, height: number}, flipHorizontal: boolean) {
153153
const {program, vao, vertices, uniformLocations} =
154-
drawTextureProgram(gl, flipHorizontal);
154+
drawTextureProgram(gl, flipHorizontal, false);
155155
gl.useProgram(program);
156156
gl.bindVertexArray(vao);
157157

@@ -176,9 +176,11 @@ export function drawTexture(
176176
export function runResizeProgram(
177177
gl: WebGL2RenderingContext, inputTexture: WebGLTexture,
178178
inputDims: Dimensions, outputDims: Dimensions, alignCorners: boolean,
179+
useCustomShadersToResize: boolean,
179180
interpolation: 'nearest_neighbor'|'bilinear') {
180-
const {program, vao, vertices, uniformLocations} =
181-
resizeProgram(gl, inputDims, outputDims, alignCorners, interpolation);
181+
const {program, vao, vertices, uniformLocations} = useCustomShadersToResize ?
182+
resizeProgram(gl, inputDims, outputDims, alignCorners, interpolation) :
183+
drawTextureProgram(gl, false, true);
182184
gl.useProgram(program);
183185
// Set up geometry
184186
webgl_util.callAndCheck(gl, () => {
@@ -191,6 +193,12 @@ export function runResizeProgram(
191193
gl.uniform1i(uniformLocations.get('inputTexture'), 1);
192194
gl.activeTexture(gl.TEXTURE0 + 1);
193195
gl.bindTexture(gl.TEXTURE_2D, inputTexture);
196+
if (!useCustomShadersToResize) {
197+
const textureFilter =
198+
interpolation === 'nearest_neighbor' ? gl.NEAREST : gl.LINEAR;
199+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, textureFilter);
200+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, textureFilter);
201+
}
194202

195203
//
196204
// Set up output texture.
@@ -295,17 +303,18 @@ function createFrameBuffer(gl: WebGL2RenderingContext): WebGLFramebuffer {
295303
return fb;
296304
}
297305

298-
function drawTextureProgram(
299-
gl: WebGL2RenderingContext, flipHorizontal: boolean): ProgramObjects {
306+
export function drawTextureProgram(
307+
gl: WebGL2RenderingContext, flipHorizontal: boolean,
308+
flipVertical: boolean): ProgramObjects {
300309
if (!programCacheByContext.has(gl)) {
301310
programCacheByContext.set(gl, new Map());
302311
}
303312
const programCache = programCacheByContext.get(gl);
304313

305-
const cacheKey = `drawTexture_${flipHorizontal}`;
314+
const cacheKey = `drawTexture_${flipHorizontal}_${flipVertical}`;
306315
if (!programCache.has(cacheKey)) {
307316
const vertSource =
308-
drawTextureProgramInfo.vertexShaderSource(flipHorizontal);
317+
drawTextureProgramInfo.vertexShaderSource(flipHorizontal, flipVertical);
309318
const fragSource = drawTextureProgramInfo.fragmentShaderSource();
310319

311320
const vertices = drawTextureProgramInfo.vertices();

tfjs-react-native/src/camera/draw_texture_program_info.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
* =============================================================================
1616
*/
1717

18-
export function vertexShaderSource(flipHorizontal: boolean) {
18+
export function vertexShaderSource(
19+
flipHorizontal: boolean, flipVertical: boolean) {
1920
const horizontalScale = flipHorizontal ? -1 : 1;
21+
const verticalScale = flipVertical ? -1 : 1;
2022
return `#version 300 es
2123
precision highp float;
2224
@@ -27,7 +29,8 @@ out vec2 uv;
2729
2830
void main() {
2931
// Invert geometry to match the image orientation from the camera.
30-
gl_Position = vec4(position * vec2(${horizontalScale}., -1.), 0, 1);
32+
gl_Position = vec4(position * vec2(${horizontalScale}., ${
33+
verticalScale}. * -1.), 0, 1);
3134
uv = texCoords;
3235
}`;
3336
}

0 commit comments

Comments
 (0)