From 02efff572f2870c9e7422de53e018fc252f01a6a Mon Sep 17 00:00:00 2001 From: Na Li Date: Thu, 16 Jul 2020 18:47:45 -0700 Subject: [PATCH 1/2] Add NonMaxSuppressionV4 to converter. --- tfjs-converter/docs/supported_ops.md | 1 + .../python/tensorflowjs/op_list/dynamic.json | 50 +++++++++++++++++++ .../operations/executors/dynamic_executor.ts | 13 +++++ .../executors/dynamic_executor_test.ts | 39 ++++++++++++++- .../src/operations/op_list/dynamic.ts | 24 +++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/tfjs-converter/docs/supported_ops.md b/tfjs-converter/docs/supported_ops.md index b7692994afe..9db33accd58 100644 --- a/tfjs-converter/docs/supported_ops.md +++ b/tfjs-converter/docs/supported_ops.md @@ -157,6 +157,7 @@ |ListDiff|setdiff1dAsync| |NonMaxSuppressionV2|nonMaxSuppression| |NonMaxSuppressionV3|nonMaxSuppression| +|NonMaxSuppressionV4|nonMaxSuppression| |NonMaxSuppressionV5|nonMaxSuppression| |Where|whereAsync| diff --git a/tfjs-converter/python/tensorflowjs/op_list/dynamic.json b/tfjs-converter/python/tensorflowjs/op_list/dynamic.json index 59a68e0e2b3..9a028b50699 100644 --- a/tfjs-converter/python/tensorflowjs/op_list/dynamic.json +++ b/tfjs-converter/python/tensorflowjs/op_list/dynamic.json @@ -56,6 +56,56 @@ } ] }, + { + "tfOpName": "NonMaxSuppressionV4", + "category": "dynamic", + "inputs": [ + { + "start": 0, + "name": "boxes", + "type": "tensor" + }, + { + "start": 1, + "name": "scores", + "type": "tensor" + }, + { + "start": 2, + "name": "maxOutputSize", + "type": "number" + }, + { + "start": 3, + "name": "iouThreshold", + "type": "number" + }, + { + "start": 4, + "name": "scoreThreshold", + "type": "number" + } + ], + "attrs": [ + { + "tfName": "T", + "name": "dtype", + "type": "dtype", + "notSupported": true + }, + { + "tfName": "T_threshold", + "name": "threshold", + "type": "dtype", + "notSupported": true + }, + { + "tfName": "pad_to_max_output_size", + "name": "padToMaxOutputSize", + "type": "bool" + } + ] + }, { "tfOpName": "NonMaxSuppressionV5", "category": "dynamic", diff --git a/tfjs-converter/src/operations/executors/dynamic_executor.ts b/tfjs-converter/src/operations/executors/dynamic_executor.ts index 163cff78a29..3c0d4897d25 100644 --- a/tfjs-converter/src/operations/executors/dynamic_executor.ts +++ b/tfjs-converter/src/operations/executors/dynamic_executor.ts @@ -28,6 +28,7 @@ export const executeOp: InternalOpAsyncExecutor = async( context: ExecutionContext): Promise => { switch (node.op) { case 'NonMaxSuppressionV5': + case 'NonMaxSuppressionV4': case 'NonMaxSuppressionV3': case 'NonMaxSuppressionV2': { const boxes = @@ -52,6 +53,18 @@ export const executeOp: InternalOpAsyncExecutor = async( return [result.selectedIndices, result.selectedScores]; } + if (node.op === 'NonMaxSuppressionV4') { + const padToMaxOutputSize = + getParamValue('padToMaxOutputSize', node, tensorMap, context) as + boolean; + + const result = await tfc.image.nonMaxSuppressionPaddedAsync( + boxes as tfc.Tensor2D, scores as tfc.Tensor1D, maxOutputSize, + iouThreshold, scoreThreshold, padToMaxOutputSize); + + return [result.selectedIndices, result.validOutputs]; + } + return [await tfc.image.nonMaxSuppressionAsync( boxes as tfc.Tensor2D, scores as tfc.Tensor1D, maxOutputSize, iouThreshold, scoreThreshold)]; diff --git a/tfjs-converter/src/operations/executors/dynamic_executor_test.ts b/tfjs-converter/src/operations/executors/dynamic_executor_test.ts index 02af75b8e37..9a57b7c3837 100644 --- a/tfjs-converter/src/operations/executors/dynamic_executor_test.ts +++ b/tfjs-converter/src/operations/executors/dynamic_executor_test.ts @@ -21,7 +21,7 @@ import * as dynamic from '../op_list/dynamic'; import {Node} from '../types'; import {executeOp} from './dynamic_executor'; -import {createNumberAttrFromIndex, createTensorAttr, validateParam} from './test_helper'; +import {createBoolAttr, createNumberAttrFromIndex, createTensorAttr, validateParam} from './test_helper'; describe('dynamic', () => { let node: Node; @@ -109,6 +109,43 @@ describe('dynamic', () => { }); }); + describe('NonMaxSuppressionV4', () => { + it('should return input', () => { + node.op = 'NonMaxSuppressionV4'; + node.inputParams['boxes'] = createTensorAttr(0); + node.inputParams['scores'] = createTensorAttr(1); + node.inputParams['maxOutputSize'] = createNumberAttrFromIndex(2); + node.inputParams['iouThreshold'] = createNumberAttrFromIndex(3); + node.inputParams['scoreThreshold'] = createNumberAttrFromIndex(4); + node.attrParams['padToMaxOutputSize'] = createBoolAttr(true); + node.inputNames = ['input1', 'input2', 'input3', 'input4', 'input5']; + const input2 = [tfc.tensor1d([1])]; + const input3 = [tfc.tensor1d([1])]; + const input4 = [tfc.tensor1d([1])]; + const input5 = [tfc.tensor1d([1])]; + spyOn(tfc.image, 'nonMaxSuppressionPaddedAsync').and.returnValue({}); + const result = + executeOp(node, {input1, input2, input3, input4, input5}, context); + expect(tfc.image.nonMaxSuppressionPaddedAsync) + .toHaveBeenCalledWith(input1[0], input2[0], 1, 1, 1, 1); + expect(result instanceof Promise).toBeTruthy(); + }); + it('should match json def', () => { + node.op = 'NonMaxSuppressionV4'; + node.inputParams['boxes'] = createTensorAttr(0); + node.inputParams['scores'] = createTensorAttr(1); + node.inputParams['maxOutputSize'] = createNumberAttrFromIndex(2); + node.inputParams['iouThreshold'] = createNumberAttrFromIndex(3); + node.inputParams['scoreThreshold'] = createNumberAttrFromIndex(4); + node.inputParams['softNmsSigma'] = createNumberAttrFromIndex(5); + node.attrParams['padToMaxOutputSize'] = createBoolAttr(true); + node.inputNames = ['input1', 'input2', 'input3', 'input4', 'input5']; + + expect(validateParam(node, dynamic.json, 'NonMaxSuppressionV4')) + .toBeTruthy(); + }); + }); + describe('NonMaxSuppressionV5', () => { it('should return input', () => { node.op = 'NonMaxSuppressionV5'; diff --git a/tfjs-converter/src/operations/op_list/dynamic.ts b/tfjs-converter/src/operations/op_list/dynamic.ts index a187942470a..26dd04421b5 100644 --- a/tfjs-converter/src/operations/op_list/dynamic.ts +++ b/tfjs-converter/src/operations/op_list/dynamic.ts @@ -39,6 +39,30 @@ export const json: OpMapper[] = [ {'start': 4, 'name': 'scoreThreshold', 'type': 'number'} ] }, + { + 'tfOpName': 'NonMaxSuppressionV4', + 'category': 'dynamic', + 'inputs': [ + {'start': 0, 'name': 'boxes', 'type': 'tensor'}, + {'start': 1, 'name': 'scores', 'type': 'tensor'}, + {'start': 2, 'name': 'maxOutputSize', 'type': 'number'}, + {'start': 3, 'name': 'iouThreshold', 'type': 'number'}, + {'start': 4, 'name': 'scoreThreshold', 'type': 'number'} + ], + 'attrs': [ + {'tfName': 'T', 'name': 'dtype', 'type': 'dtype', 'notSupported': true}, { + 'tfName': 'T_threshold', + 'name': 'threshold', + 'type': 'dtype', + 'notSupported': true + }, + { + 'tfName': 'pad_to_max_output_size', + 'name': 'padToMaxOutputSize', + 'type': 'bool' + } + ] + }, { 'tfOpName': 'NonMaxSuppressionV5', 'category': 'dynamic', From 135e49c006c7c0e58b6be314e8623ceb01b723f7 Mon Sep 17 00:00:00 2001 From: Na Li Date: Thu, 16 Jul 2020 19:00:32 -0700 Subject: [PATCH 2/2] Fix test. --- .../src/operations/executors/dynamic_executor_test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tfjs-converter/src/operations/executors/dynamic_executor_test.ts b/tfjs-converter/src/operations/executors/dynamic_executor_test.ts index 9a57b7c3837..3493f9ca965 100644 --- a/tfjs-converter/src/operations/executors/dynamic_executor_test.ts +++ b/tfjs-converter/src/operations/executors/dynamic_executor_test.ts @@ -127,7 +127,7 @@ describe('dynamic', () => { const result = executeOp(node, {input1, input2, input3, input4, input5}, context); expect(tfc.image.nonMaxSuppressionPaddedAsync) - .toHaveBeenCalledWith(input1[0], input2[0], 1, 1, 1, 1); + .toHaveBeenCalledWith(input1[0], input2[0], 1, 1, 1, true); expect(result instanceof Promise).toBeTruthy(); }); it('should match json def', () => { @@ -137,7 +137,6 @@ describe('dynamic', () => { node.inputParams['maxOutputSize'] = createNumberAttrFromIndex(2); node.inputParams['iouThreshold'] = createNumberAttrFromIndex(3); node.inputParams['scoreThreshold'] = createNumberAttrFromIndex(4); - node.inputParams['softNmsSigma'] = createNumberAttrFromIndex(5); node.attrParams['padToMaxOutputSize'] = createBoolAttr(true); node.inputNames = ['input1', 'input2', 'input3', 'input4', 'input5'];