diff --git a/pose-detection/demos/README.md b/pose-detection/demos/README.md index dbb5cdeba7..637538dc3e 100644 --- a/pose-detection/demos/README.md +++ b/pose-detection/demos/README.md @@ -48,3 +48,48 @@ If you want to run any of the demos locally, follow these steps: 5. Run the demo. `yarn watch` 6. The demo runs at `localhost:1234`. (Remember to provide URL model parameter e. g. `localhost:1234/?model=movenet`) + +### Windows local setup notes + +On Windows, this repository may check out `pose-detection/src/shared` and +`pose-detection/test_data/shared` as plain text files instead of links. If +`yarn build-dep` fails with errors such as `Cannot find module '../shared/...`, +replace those files with junctions from the `pose-detection` directory: + +```powershell +cd D:\HAOJING_code\stu-project\tfjs-models\pose-detection + +Remove-Item .\src\shared -Force +New-Item -ItemType Junction -Path .\src\shared -Target ..\shared + +Remove-Item .\test_data\shared -Force +New-Item -ItemType Junction -Path .\test_data\shared -Target ..\shared\test_data +``` + +This demo uses older Parcel 1 dependencies. If your system Node.js is newer +(for example Node 18, 20, or 24) and dependency install or startup fails around +`deasync`, run the demo with Node 16 without changing the system Node install: + +```powershell +cd D:\HAOJING_code\stu-project\tfjs-models\pose-detection\demos\live_video + +npx -y -p node@16 -p yarn@1.22.22 yarn build-dep +npx -y -p node@16 -p yarn@1.22.22 yarn install +npx -y -p node@16 node .\node_modules\parcel-bundler\bin\cli.js index.html --no-hmr -p 1234 --host 127.0.0.1 +``` + +Then open the MoveNet demo at: + +```text +http://127.0.0.1:1234/?model=movenet +``` + +Use `127.0.0.1` instead of `localhost` if the browser reports +`ERR_CONNECTION_REFUSED` while the server is running. + +You can also run the same Windows setup and MoveNet demo startup with: + +```powershell +cd D:\HAOJING_code\stu-project\tfjs-models\pose-detection\demos +.\run_movenet_local.bat +``` diff --git a/pose-detection/demos/live_video/parcel-local.err.log b/pose-detection/demos/live_video/parcel-local.err.log new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pose-detection/demos/live_video/parcel-local.log b/pose-detection/demos/live_video/parcel-local.log new file mode 100644 index 0000000000..b2f07de3d9 --- /dev/null +++ b/pose-detection/demos/live_video/parcel-local.log @@ -0,0 +1,3 @@ +Server running at http://127.0.0.1:1234 +√ Built in 3.64s. +Static path (file or directory) 'D:\HAOJING_code\stu-project\tfjs-models\pose-detection\demos\live_video\static' does not exist. Skipping. diff --git a/pose-detection/demos/run_movenet_local.bat b/pose-detection/demos/run_movenet_local.bat new file mode 100644 index 0000000000..d7dcf2fa02 --- /dev/null +++ b/pose-detection/demos/run_movenet_local.bat @@ -0,0 +1,43 @@ +@echo off +setlocal + +set "REPO_ROOT=%~dp0..\.." +set "POSE_ROOT=%REPO_ROOT%\pose-detection" +set "DEMO_ROOT=%POSE_ROOT%\demos\live_video" + +echo [1/5] Checking shared directory junctions... +cd /d "%POSE_ROOT%" || exit /b 1 + +if not exist "%POSE_ROOT%\src\shared\calculators" ( + if exist "%POSE_ROOT%\src\shared" del /f /q "%POSE_ROOT%\src\shared" >nul 2>nul + if exist "%POSE_ROOT%\src\shared" rmdir /s /q "%POSE_ROOT%\src\shared" >nul 2>nul + mklink /J "%POSE_ROOT%\src\shared" "%REPO_ROOT%\shared" || exit /b 1 +) + +if not exist "%POSE_ROOT%\test_data\shared" ( + if exist "%POSE_ROOT%\test_data\shared" del /f /q "%POSE_ROOT%\test_data\shared" >nul 2>nul + if exist "%POSE_ROOT%\test_data\shared" rmdir /s /q "%POSE_ROOT%\test_data\shared" >nul 2>nul + mklink /J "%POSE_ROOT%\test_data\shared" "%REPO_ROOT%\shared\test_data" || exit /b 1 +) + +echo [2/5] Checking npx... +where npx >nul 2>nul +if errorlevel 1 ( + echo npx was not found. Please install Node.js first. + exit /b 1 +) + +echo [3/5] Building pose-detection with Node 16 and Yarn 1... +cd /d "%DEMO_ROOT%" || exit /b 1 +call npx -y -p node@16 -p yarn@1.22.22 yarn build-dep || exit /b 1 + +echo [4/5] Installing live_video demo dependencies... +call npx -y -p node@16 -p yarn@1.22.22 yarn install || exit /b 1 + +echo [5/5] Starting MoveNet demo... +echo Open this URL in your browser: +echo http://127.0.0.1:1234/?model=movenet +echo. +call npx -y -p node@16 node .\node_modules\parcel-bundler\bin\cli.js index.html --no-hmr -p 1234 --host 127.0.0.1 + +endlocal diff --git a/pose-detection/src/shared b/pose-detection/src/shared deleted file mode 120000 index 0ab0cb2b3a..0000000000 --- a/pose-detection/src/shared +++ /dev/null @@ -1 +0,0 @@ -../../shared \ No newline at end of file diff --git a/pose-detection/src/shared/calculators/association_norm_rect.ts b/pose-detection/src/shared/calculators/association_norm_rect.ts new file mode 100644 index 0000000000..857e2a583c --- /dev/null +++ b/pose-detection/src/shared/calculators/association_norm_rect.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {BoundingBox, Rect} from './interfaces/shape_interfaces'; + +function area(rect: BoundingBox) { + return rect.width * rect.height; +} + +function intersects(rect1: BoundingBox, rect2: BoundingBox) { + return !( + rect1.xMax < rect2.xMin || rect2.xMax < rect1.xMin || + rect1.yMax < rect2.yMin || rect2.yMax < rect1.yMin); +} + +function intersect(rect1: BoundingBox, rect2: BoundingBox) { + const xMin = Math.max(rect1.xMin, rect2.xMin); + const xMax = Math.min(rect1.xMax, rect2.xMax); + const yMin = Math.max(rect1.yMin, rect2.yMin); + const yMax = Math.min(rect1.yMax, rect2.yMax); + const width = Math.max(xMax - xMin, 0); + const height = Math.max(yMax - yMin, 0); + + return {xMin, xMax, yMin, yMax, width, height}; +} + +export function getBoundingBox(rect: Rect): BoundingBox { + const xMin = rect.xCenter - rect.width / 2; + const xMax = xMin + rect.width; + const yMin = rect.yCenter - rect.height / 2; + const yMax = yMin + rect.height; + return {xMin, xMax, yMin, yMax, width: rect.width, height: rect.height}; +} + +function overlapSimilarity(rect1: Rect, rect2: Rect): number { + const bbox1 = getBoundingBox(rect1); + const bbox2 = getBoundingBox(rect2); + if (!intersects(bbox1, bbox2)) { + return 0; + } + const intersectionArea = area(intersect(bbox1, bbox2)); + const normalization = area(bbox1) + area(bbox2) - intersectionArea; + return normalization > 0 ? intersectionArea / normalization : 0; +} + +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/association_norm_rect_calculator.cc +// Propgating ids from previous to current is not performed by this code. +export function calculateAssociationNormRect( + rectsArray: Rect[][], minSimilarityThreshold: number): Rect[] { + let result: Rect[] = []; + + // rectsArray elements are interpreted to be sorted in reverse priority order, + // so later elements are higher in priority. This means that if there's a + // large overlap, the later rect will be added and the older rect will be + // removed. + rectsArray.forEach(rects => rects.forEach(curRect => { + result = result.filter( + prevRect => + overlapSimilarity(curRect, prevRect) <= minSimilarityThreshold); + result.push(curRect); + })); + + return result; +} diff --git a/pose-detection/src/shared/calculators/association_norm_rect_test.ts b/pose-detection/src/shared/calculators/association_norm_rect_test.ts new file mode 100644 index 0000000000..8e6c0b1240 --- /dev/null +++ b/pose-detection/src/shared/calculators/association_norm_rect_test.ts @@ -0,0 +1,142 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {calculateAssociationNormRect} from './association_norm_rect'; +import {Rect} from './interfaces/shape_interfaces'; + +// 0.4 ================ +// | | | | +// 0.3 ===================== | NR2 | | +// | | | NR1 | | | NR4 | +// 0.2 | NR0 | =========== ================ +// | | | | | | +// 0.1 =====|=============== | +// | NR3 | | | +// 0.0 ================ | +// | NR5 | +// -0.1 =========== +// 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 + +// NormalizedRect nr0. +const nr0: Rect = { + xCenter: 0.2, + yCenter: 0.2, + width: 0.2, + height: 0.2 +}; + +// NormalizedRect nr1. +const nr1: Rect = { + xCenter: 0.4, + yCenter: 0.2, + width: 0.2, + height: 0.2 +}; + +// NormalizedRect nr2. +const nr2: Rect = { + xCenter: 1.0, + yCenter: 0.3, + width: 0.2, + height: 0.2 +}; + +// NormalizedRect nr3. +const nr3: Rect = { + xCenter: 0.35, + yCenter: 0.15, + width: 0.3, + height: 0.3 +}; + +// NormalizedRect nr4. +const nr4: Rect = { + xCenter: 1.1, + yCenter: 0.3, + width: 0.2, + height: 0.2 +}; + +// NormalizedRect nr5. +const nr5: Rect = { + xCenter: 0.45, + yCenter: 0.05, + width: 0.3, + height: 0.3 +}; + +describe('calculateAssociationNormRect', () => { + it('3 inputs.', async () => { + const minSimilarityThreshold = 0.1; + const inputList0 = [nr0, nr1, nr2]; + const inputList1 = [nr3, nr4]; + const inputList2 = [nr5]; + + const result = calculateAssociationNormRect( + [inputList0, inputList1, inputList2], minSimilarityThreshold); + + // nr3 overlaps with nr0, nr1 and nr5 overlaps with nr3. Since nr5 is + // in the highest priority, we remove other rects. + // nr4 overlaps with nr2, and nr4 is higher priority, so we keep it. + // The final output therefore contains 2 elements. + expect(result.length).toBe(2); + // Outputs are in order of inputs, so nr4 is before nr5 in output vector. + + // det_4 overlaps with det_2. + expect(result[0]).toBe(nr4); + + // det_3 overlaps with det_0. + // det_3 overlaps with det_1. + // det_5 overlaps with det_3. + expect(result[1]).toBe(nr5); + }); + + it('3 inputs reverse.', async () => { + const minSimilarityThreshold = 0.1; + const inputList0 = [nr5]; + const inputList1 = [nr3, nr4]; + const inputList2 = [nr0, nr1, nr2]; + + const result = calculateAssociationNormRect( + [inputList0, inputList1, inputList2], minSimilarityThreshold); + + // nr3 overlaps with nr5, so nr5 is removed. nr0 overlaps with nr3, so + // nr3 is removed as nr0 is in higher priority for keeping. nr2 overlaps + // with nr4 so nr4 is removed as nr2 is higher priority for keeping. + // The final output therefore contains 3 elements. + expect(result.length).toBe(3); + // Outputs are in order of inputs, so nr4 is before nr5 in output vector. + + // Outputs are in same order as inputs. + expect(result[0]).toBe(nr0); + expect(result[1]).toBe(nr1); + expect(result[2]).toBe(nr2); + }); + + it('single input.', async () => { + const minSimilarityThreshold = 0.1; + const inputList0 = [nr3, nr5]; + + const result = + calculateAssociationNormRect([inputList0], minSimilarityThreshold); + + // nr5 overlaps with nr3. Since nr5 is after nr3 in the same input + // stream we remove nr3 and keep nr5. The final output therefore contains + // 1 elements. + expect(result.length).toBe(1); + expect(result[0]).toBe(nr5); + }); +}); diff --git a/pose-detection/src/shared/calculators/calculate_alignment_points_rects.ts b/pose-detection/src/shared/calculators/calculate_alignment_points_rects.ts new file mode 100644 index 0000000000..a016e76485 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_alignment_points_rects.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {computeRotation} from './detection_to_rect'; +import {ImageSize} from './interfaces/common_interfaces'; +import {DetectionToRectConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; +import {Detection} from './interfaces/shape_interfaces'; + +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/alignment_points_to_rects_calculator.cc +export function calculateAlignmentPointsRects( + detection: Detection, imageSize: ImageSize, + config: DetectionToRectConfig): Rect { + const startKeypoint = config.rotationVectorStartKeypointIndex; + const endKeypoint = config.rotationVectorEndKeypointIndex; + + const locationData = detection.locationData; + const xCenter = + locationData.relativeKeypoints[startKeypoint].x * imageSize.width; + const yCenter = + locationData.relativeKeypoints[startKeypoint].y * imageSize.height; + + const xScale = + locationData.relativeKeypoints[endKeypoint].x * imageSize.width; + const yScale = + locationData.relativeKeypoints[endKeypoint].y * imageSize.height; + + // Bounding box size as double distance from center to scale point. + const boxSize = Math.sqrt( + (xScale - xCenter) * (xScale - xCenter) + + (yScale - yCenter) * (yScale - yCenter)) * + 2; + + const rotation = computeRotation(detection, imageSize, config); + + // Set resulting bounding box. + return { + xCenter: xCenter / imageSize.width, + yCenter: yCenter / imageSize.height, + width: boxSize / imageSize.width, + height: boxSize / imageSize.height, + rotation + }; +} diff --git a/pose-detection/src/shared/calculators/calculate_inverse_matrix.ts b/pose-detection/src/shared/calculators/calculate_inverse_matrix.ts new file mode 100644 index 0000000000..e88604f8d5 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_inverse_matrix.ts @@ -0,0 +1,88 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +export type Matrix4x4Row = [number, number, number, number]; +export type Matrix4x4 = + [Matrix4x4Row, Matrix4x4Row, Matrix4x4Row, Matrix4x4Row]; + +export function matrix4x4ToArray(matrix: Matrix4x4): number[] { + return [].concat.apply([], matrix); +} + +export function arrayToMatrix4x4(array: ArrayLike): Matrix4x4 { + if (array.length !== 16) { + throw new Error(`Array length must be 16 but got ${array.length}`); + } + return [ + [array[0], array[1], array[2], array[3]], + [array[4], array[5], array[6], array[7]], + [array[8], array[9], array[10], array[11]], + [array[12], array[13], array[14], array[15]], + ]; +} + +function generalDet3Helper( + matrix: Matrix4x4, i1: number, i2: number, i3: number, j1: number, + j2: number, j3: number) { + return matrix[i1][j1] * + (matrix[i2][j2] * matrix[i3][j3] - matrix[i2][j3] * matrix[i3][j2]); +} + +function cofactor4x4(matrix: Matrix4x4, i: number, j: number) { + const i1 = (i + 1) % 4, i2 = (i + 2) % 4, i3 = (i + 3) % 4, j1 = (j + 1) % 4, + j2 = (j + 2) % 4, j3 = (j + 3) % 4; + return generalDet3Helper(matrix, i1, i2, i3, j1, j2, j3) + + generalDet3Helper(matrix, i2, i3, i1, j1, j2, j3) + + generalDet3Helper(matrix, i3, i1, i2, j1, j2, j3); +} +/** + * Calculates inverse of an invertible 4x4 matrix. + * @param matrix 4x4 matrix to invert. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/inverse_matrix_calculator.cc +// https://gitlab.com/libeigen/eigen/-/blob/master/Eigen/src/LU/InverseImpl.h +export function calculateInverseMatrix(matrix: Matrix4x4): Matrix4x4 { + const inverse = arrayToMatrix4x4(new Array(16).fill(0)); + + inverse[0][0] = cofactor4x4(matrix, 0, 0); + inverse[1][0] = -cofactor4x4(matrix, 0, 1); + inverse[2][0] = cofactor4x4(matrix, 0, 2); + inverse[3][0] = -cofactor4x4(matrix, 0, 3); + inverse[0][2] = cofactor4x4(matrix, 2, 0); + inverse[1][2] = -cofactor4x4(matrix, 2, 1); + inverse[2][2] = cofactor4x4(matrix, 2, 2); + inverse[3][2] = -cofactor4x4(matrix, 2, 3); + inverse[0][1] = -cofactor4x4(matrix, 1, 0); + inverse[1][1] = cofactor4x4(matrix, 1, 1); + inverse[2][1] = -cofactor4x4(matrix, 1, 2); + inverse[3][1] = cofactor4x4(matrix, 1, 3); + inverse[0][3] = -cofactor4x4(matrix, 3, 0); + inverse[1][3] = cofactor4x4(matrix, 3, 1); + inverse[2][3] = -cofactor4x4(matrix, 3, 2); + inverse[3][3] = cofactor4x4(matrix, 3, 3); + + const scale = matrix[0][0] * inverse[0][0] + matrix[1][0] * inverse[0][1] + + matrix[2][0] * inverse[0][2] + matrix[3][0] * inverse[0][3]; + + for (let i = 0; i < inverse.length; i++) { + for (let j = 0; j < inverse.length; j++) { + inverse[i][j] /= scale; + } + } + return inverse; +} diff --git a/pose-detection/src/shared/calculators/calculate_inverse_matrix_test.ts b/pose-detection/src/shared/calculators/calculate_inverse_matrix_test.ts new file mode 100644 index 0000000000..fc057c5dc9 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_inverse_matrix_test.ts @@ -0,0 +1,129 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +// tslint:disable-next-line: no-imports-from-dist +import {expectArraysClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {arrayToMatrix4x4, calculateInverseMatrix, Matrix4x4, matrix4x4ToArray} from './calculate_inverse_matrix'; + +describe('calculateInverseMatrix', () => { + const identity: Matrix4x4 = + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + + it('identity matrix.', async () => { + await tf.ready(); + const inverse = calculateInverseMatrix(identity); + + expectArraysClose(matrix4x4ToArray(inverse), matrix4x4ToArray(identity)); + }); + + it('translation.', async () => { + await tf.ready(); + const matrix: Matrix4x4 = [ + [1.0, 0.0, 0.0, 2.0], + [0.0, 1.0, 0.0, -5.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + const inverse = calculateInverseMatrix(matrix); + + const expectedInverse: Matrix4x4 = [ + [1.0, 0.0, 0.0, -2.0], + [0.0, 1.0, 0.0, 5.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + expectArraysClose( + matrix4x4ToArray(inverse), matrix4x4ToArray(expectedInverse)); + }); + + it('scale.', async () => { + await tf.ready(); + const matrix: Matrix4x4 = [ + [5.0, 0.0, 0.0, 0.0], + [0.0, 2.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + const inverse = calculateInverseMatrix(matrix); + + const expectedInverse: Matrix4x4 = [ + [0.2, 0.0, 0.0, 0.0], + [0.0, 0.5, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + expectArraysClose( + matrix4x4ToArray(inverse), matrix4x4ToArray(expectedInverse)); + }); + + it('rotation90.', async () => { + await tf.ready(); + const matrix: Matrix4x4 = [ + [0.0, -1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + const inverse = calculateInverseMatrix(matrix); + + const expectedInverse: Matrix4x4 = [ + [0.0, 1.0, 0.0, 0.0], + [-1.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + ]; + + expectArraysClose( + matrix4x4ToArray(inverse), matrix4x4ToArray(expectedInverse)); + }); + + it('precision.', async () => { + await tf.ready(); + const matrix: Matrix4x4 = [ + [0.00001, 0.0, 0.0, 0.0], [0.0, 0.00001, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0] + ]; + + const inverse = calculateInverseMatrix(matrix); + + const expectedInverse: Matrix4x4 = [ + [100000.0, 0.0, 0.0, 0.0], [0.0, 100000.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0] + ]; + + expectArraysClose( + matrix4x4ToArray(inverse), matrix4x4ToArray(expectedInverse)); + }); + + it('random matrix.', async () => { + for (let seed = 1; seed <= 5; ++seed) { + await tf.ready(); + const matrix = tf.randomUniform([4, 4], 0, 10, 'float32', seed); + const inverse = + calculateInverseMatrix(arrayToMatrix4x4(matrix.dataSync())); + const product = tf.matMul(matrix, inverse); + + expectArraysClose(product.dataSync(), matrix4x4ToArray(identity)); + } + }); +}); diff --git a/pose-detection/src/shared/calculators/calculate_landmark_projection.ts b/pose-detection/src/shared/calculators/calculate_landmark_projection.ts new file mode 100644 index 0000000000..b818e56b35 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_landmark_projection.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './interfaces/common_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +/** + * Projects normalized landmarks in a rectangle to its original coordinates. The + * rectangle must also be in normalized coordinates. + * @param landmarks A normalized Landmark list representing landmarks in a + * normalized rectangle. + * @param inputRect A normalized rectangle. + * @param config Config object has one field ignoreRotation, default to false. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmark_projection_calculator.cc +export function calculateLandmarkProjection( + landmarks: Keypoint[], inputRect: Rect, + config: {ignoreRotation: boolean} = { + ignoreRotation: false + }) { + const outputLandmarks: Keypoint[] = []; + for (const landmark of landmarks) { + const x = landmark.x - 0.5; + const y = landmark.y - 0.5; + const angle = config.ignoreRotation ? 0 : inputRect.rotation; + let newX = Math.cos(angle) * x - Math.sin(angle) * y; + let newY = Math.sin(angle) * x + Math.cos(angle) * y; + + newX = newX * inputRect.width + inputRect.xCenter; + newY = newY * inputRect.height + inputRect.yCenter; + + const newZ = landmark.z * inputRect.width; // Scale Z coordinate as x. + + const newLandmark = {...landmark}; + + newLandmark.x = newX; + newLandmark.y = newY; + newLandmark.z = newZ; + + outputLandmarks.push(newLandmark); + } + + return outputLandmarks; +} diff --git a/pose-detection/src/shared/calculators/calculate_landmark_projection_test.ts b/pose-detection/src/shared/calculators/calculate_landmark_projection_test.ts new file mode 100644 index 0000000000..9837c222f5 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_landmark_projection_test.ts @@ -0,0 +1,53 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {calculateLandmarkProjection} from './calculate_landmark_projection'; + +describe('calculateLandmarkProjection', () => { + it('default rectangle.', async () => { + const landmarks = [{x: 10, y: 20, z: -0.5}]; + const rect = { + xCenter: 0.5, + yCenter: 0.5, + height: 1.0, + width: 1.0, + rotation: 0, + }; + + const result = calculateLandmarkProjection(landmarks, rect); + + expect(result[0].x).toBe(10); + expect(result[0].y).toBe(20); + expect(result[0].z).toBe(-0.5); + }); + + it('cropped rectangle.', async () => { + const landmarks = [{x: 1, y: 1, z: -0.5}]; + const rect = { + xCenter: 0.5, + yCenter: 0.5, + height: 2, + width: 0.5, + rotation: 0, + }; + + const result = calculateLandmarkProjection(landmarks, rect); + + expect(result[0].x).toBe(0.75); + expect(result[0].y).toBe(1.5); + expect(result[0].z).toBe(-0.25); + }); +}); diff --git a/pose-detection/src/shared/calculators/calculate_score_copy.ts b/pose-detection/src/shared/calculators/calculate_score_copy.ts new file mode 100644 index 0000000000..e9d5160ad0 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_score_copy.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './interfaces/common_interfaces'; + +/** + * A calculator to copy score between landmarks. + * + * Landmarks to copy from and to copy to can be of different type (normalized or + * non-normalized), but landmarks to copy to and output landmarks should be of + * the same type. + * @param landmarksFrom A list of landmarks. + * to copy from. + * @param landmarksTo A list of landmarks. + * to copy to. + * @param copyScore Copy the score from the `landmarksFrom` parameter. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/visibility_copy_calculator.cc +export function calculateScoreCopy( + landmarksFrom: Keypoint[], landmarksTo: Keypoint[], + copyScore = true): Keypoint[] { + const outputLandmarks = []; + for (let i = 0; i < landmarksFrom.length; i++) { + // Create output landmark and copy all fields from the `to` landmarks + const newLandmark = {...landmarksTo[i]}; + + // Copy score from the `from` landmark. + if (copyScore) { + newLandmark.score = landmarksFrom[i].score; + } + + outputLandmarks.push(newLandmark); + } + + return outputLandmarks; +} diff --git a/pose-detection/src/shared/calculators/calculate_world_landmark_projection.ts b/pose-detection/src/shared/calculators/calculate_world_landmark_projection.ts new file mode 100644 index 0000000000..ff09e760f4 --- /dev/null +++ b/pose-detection/src/shared/calculators/calculate_world_landmark_projection.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './interfaces/common_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +/** + * Projects world landmarks from the rectangle to original coordinates. + * + * World landmarks are predicted in meters rather than in pixels of the image + * and have origin in the middle of the hips rather than in the corner of the + * pose image (cropped with given rectangle). Thus only rotation (but not scale + * and translation) is applied to the landmarks to transform them back to + * original coordinates. + * @param worldLandmarks A Landmark list representing world landmarks in the + * rectangle. + * @param inputRect A normalized rectangle. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmark_projection_calculator.cc +export function calculateWorldLandmarkProjection( + worldLandmarks: Keypoint[], inputRect: Rect): Keypoint[] { + const outputLandmarks = []; + for (const worldLandmark of worldLandmarks) { + const x = worldLandmark.x; + const y = worldLandmark.y; + const angle = inputRect.rotation; + const newX = Math.cos(angle) * x - Math.sin(angle) * y; + const newY = Math.sin(angle) * x + Math.cos(angle) * y; + + const newLandmark = {...worldLandmark}; + + newLandmark.x = newX; + newLandmark.y = newY; + + outputLandmarks.push(newLandmark); + } + + return outputLandmarks; +} diff --git a/pose-detection/src/shared/calculators/constants.ts b/pose-detection/src/shared/calculators/constants.ts new file mode 100644 index 0000000000..43b1e687c1 --- /dev/null +++ b/pose-detection/src/shared/calculators/constants.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +export const MICRO_SECONDS_TO_SECOND = 1e-6; +export const SECOND_TO_MICRO_SECONDS = 1e6; +export const MILLISECOND_TO_MICRO_SECONDS = 1000; diff --git a/pose-detection/src/shared/calculators/convert_image_to_tensor.ts b/pose-detection/src/shared/calculators/convert_image_to_tensor.ts new file mode 100644 index 0000000000..70591bb056 --- /dev/null +++ b/pose-detection/src/shared/calculators/convert_image_to_tensor.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +import {Matrix4x4} from './calculate_inverse_matrix'; +import {getRotatedSubRectToRectTransformMatrix} from './get_rotated_sub_rect_to_rect_transformation_matrix'; +import {getImageSize, getProjectiveTransformMatrix, getRoi, padRoi, toImageTensor} from './image_utils'; +import {Padding, PixelInput} from './interfaces/common_interfaces'; +import {ImageToTensorConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; +import {shiftImageValue} from './shift_image_value'; + +/** + * Convert an image or part of it to an image tensor. + * + * @param image An image, video frame or image tensor. + * @param config + * inputResolution: The target height and width. + * keepAspectRatio?: Whether target tensor should keep aspect ratio. + * @param normRect A normalized rectangle, representing the subarea to crop from + * the image. If normRect is provided, the returned image tensor represents + * the subarea. + * @returns A map with the following properties: + * - imageTensor + * - padding: Padding ratio of left, top, right, bottom, based on the output + * dimensions. + * - transformationMatrix: Projective transform matrix used to transform + * input image to transformed image. + */ +export function convertImageToTensor( + image: PixelInput, config: ImageToTensorConfig, normRect?: Rect): { + imageTensor: tf.Tensor4D, + padding: Padding, + transformationMatrix: Matrix4x4 +} { + const { + outputTensorSize, + keepAspectRatio, + borderMode, + outputTensorFloatRange + } = config; + + // Ref: + // https://github.com/google/mediapipe/blob/master/mediapipe/calculators/tensor/image_to_tensor_calculator.cc + const imageSize = getImageSize(image); + const roi = getRoi(imageSize, normRect); + const padding = padRoi(roi, outputTensorSize, keepAspectRatio); + const transformationMatrix = getRotatedSubRectToRectTransformMatrix( + roi, imageSize.width, imageSize.height, false); + + const imageTensor = tf.tidy(() => { + const $image = toImageTensor(image); + + const transformMatrix = tf.tensor2d( + getProjectiveTransformMatrix( + transformationMatrix, imageSize, outputTensorSize), + [1, 8]); + + const fillMode = borderMode === 'zero' ? 'constant' : 'nearest'; + + const imageTransformed = tf.image.transform( + // tslint:disable-next-line: no-unnecessary-type-assertion + tf.expandDims(tf.cast($image, 'float32')) as tf.Tensor4D, + transformMatrix, 'bilinear', fillMode, 0, + [outputTensorSize.height, outputTensorSize.width]); + + const imageShifted = outputTensorFloatRange != null ? + shiftImageValue(imageTransformed, outputTensorFloatRange) : + imageTransformed; + + return imageShifted; + }); + + return {imageTensor, padding, transformationMatrix}; +} diff --git a/pose-detection/src/shared/calculators/convert_image_to_tensor_test.ts b/pose-detection/src/shared/calculators/convert_image_to_tensor_test.ts new file mode 100644 index 0000000000..5a61f4ded5 --- /dev/null +++ b/pose-detection/src/shared/calculators/convert_image_to_tensor_test.ts @@ -0,0 +1,339 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import * as tf from '@tensorflow/tfjs-core'; +// tslint:disable-next-line: no-imports-from-dist +import {expectArraysClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {loadImage} from '../test_util'; +import {convertImageToTensor} from './convert_image_to_tensor'; +import {transformValueRange} from './image_utils'; +import {ImageToTensorConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; +import {toImageDataLossy} from './mask_util'; + +// Measured in pixels. +const EPS = 7; + +describe('ImageToTensorCalculator', () => { + let input: HTMLImageElement; + let timeout: number; + + beforeAll(async () => { + await tf.ready(); + timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000; // 2mins + + input = await loadImage('shared/image_to_tensor_input.jpg', 64, 128); + }); + + afterAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout; + }); + + async function runTest( + roi: Rect, config: ImageToTensorConfig, expectedFileName: string) { + const {imageTensor} = convertImageToTensor(input, config, roi); + + // Shift to [0, 255] range. + const [rangeMin, rangeMax] = config.outputTensorFloatRange; + const {scale, offset} = transformValueRange(rangeMin, rangeMax, 0, 255); + const actualResult = + tf.tidy(() => tf.add(tf.mul(imageTensor, scale), offset)); + tf.dispose(imageTensor); + + const expectedResultRGBA = + await loadImage( + `shared/${expectedFileName}`, config.outputTensorSize.width, + config.outputTensorSize.height) + .then(image => toImageDataLossy(image)); + const expectedResultRGB = + expectedResultRGBA.data.filter((_, index) => index % 4 !== 3); + + expectArraysClose( + new Uint8ClampedArray(actualResult.dataSync()), expectedResultRGB, EPS); + } + + it('medium sub rect keep aspect.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: true, + borderMode: 'replicate' + }, + 'medium_sub_rect_keep_aspect.png', + ); + }); + + it('medium sub rect keep aspect border zero.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: true, + borderMode: 'zero' + }, + + 'medium_sub_rect_keep_aspect_border_zero.png', + ); + }); + + it('medium sub rect keep aspect with rotation.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: Math.PI * 90 / 180, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: true, + borderMode: 'replicate' + }, + + 'medium_sub_rect_keep_aspect_with_rotation.png', + ); + }); + + it('medium sub rect keep aspect with rotation border zero.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: Math.PI * 90 / 180, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: true, + borderMode: 'zero' + }, + + 'medium_sub_rect_keep_aspect_with_rotation_border_zero.png', + ); + }); + + it('medium sub rect with rotation.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: Math.PI * -45 / 180, + }, + { + outputTensorFloatRange: [-1, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: false, + borderMode: 'replicate' + }, + 'medium_sub_rect_with_rotation.png', + ); + }); + + it('medium sub rect with rotation border zero.', async () => { + await runTest( + { + xCenter: 0.65, + yCenter: 0.4, + width: 0.5, + height: 0.5, + rotation: Math.PI * -45 / 180, + }, + { + outputTensorFloatRange: [-1, 1], + outputTensorSize: {width: 256, height: 256}, + keepAspectRatio: false, + borderMode: 'zero' + }, + + 'medium_sub_rect_with_rotation_border_zero.png', + ); + }); + + it('large sub rect.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: false, + borderMode: 'replicate' + }, + 'large_sub_rect.png', + ); + }); + + it('large sub rect border zero.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: false, + borderMode: 'zero' + }, + 'large_sub_rect_border_zero.png', + ); + }); + + it('large sub keep aspect.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: true, + borderMode: 'replicate' + }, + 'large_sub_rect_keep_aspect.png', + ); + }); + + it('large sub keep aspect border zero.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: true, + borderMode: 'zero' + }, + 'large_sub_rect_keep_aspect_border_zero.png', + ); + }); + + it('large sub keep aspect with rotation.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: Math.PI * -15 / 180, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: true, + borderMode: 'replicate' + }, + 'large_sub_rect_keep_aspect_with_rotation.png', + ); + }); + + it('large sub keep aspect with rotation border zero.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.5, + height: 1.1, + rotation: Math.PI * -15 / 180, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 128, height: 128}, + keepAspectRatio: true, + borderMode: 'zero' + }, + 'large_sub_rect_keep_aspect_with_rotation_border_zero.png'); + }); + + it('no op except range.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.0, + height: 1.0, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 64, height: 128}, + keepAspectRatio: true, + borderMode: 'replicate' + }, + 'noop_except_range.png', + ); + }); + + it('no op except range border zero.', async () => { + await runTest( + { + xCenter: 0.5, + yCenter: 0.5, + width: 1.0, + height: 1.0, + rotation: 0, + }, + { + outputTensorFloatRange: [0, 1], + outputTensorSize: {width: 64, height: 128}, + keepAspectRatio: true, + borderMode: 'zero' + }, + 'noop_except_range.png', + ); + }); +}); diff --git a/pose-detection/src/shared/calculators/create_ssd_anchors.ts b/pose-detection/src/shared/calculators/create_ssd_anchors.ts new file mode 100644 index 0000000000..791837d746 --- /dev/null +++ b/pose-detection/src/shared/calculators/create_ssd_anchors.ts @@ -0,0 +1,127 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {AnchorConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +// ref: +// https://github.com/google/mediapipe/blob/350fbb2100ad531bc110b93aaea23d96af5a5064/mediapipe/calculators/tflite/ssd_anchors_calculator.cc +export function createSsdAnchors(config: AnchorConfig): Rect[] { + // Set defaults. + if (config.reduceBoxesInLowestLayer == null) { + config.reduceBoxesInLowestLayer = false; + } + if (config.interpolatedScaleAspectRatio == null) { + config.interpolatedScaleAspectRatio = 1.0; + } + if (config.fixedAnchorSize == null) { + config.fixedAnchorSize = false; + } + + const anchors: Rect[] = []; + let layerId = 0; + while (layerId < config.numLayers) { + const anchorHeight = []; + const anchorWidth = []; + const aspectRatios = []; + const scales = []; + + // For same strides, we merge the anchors in the same order. + let lastSameStrideLayer = layerId; + while (lastSameStrideLayer < config.strides.length && + config.strides[lastSameStrideLayer] === config.strides[layerId]) { + const scale = calculateScale( + config.minScale, config.maxScale, lastSameStrideLayer, + config.strides.length); + if (lastSameStrideLayer === 0 && config.reduceBoxesInLowestLayer) { + // For first layer, it can be specified to use predefined anchors. + aspectRatios.push(1); + aspectRatios.push(2); + aspectRatios.push(0.5); + scales.push(0.1); + scales.push(scale); + scales.push(scale); + } else { + for (let aspectRatioId = 0; aspectRatioId < config.aspectRatios.length; + ++aspectRatioId) { + aspectRatios.push(config.aspectRatios[aspectRatioId]); + scales.push(scale); + } + if (config.interpolatedScaleAspectRatio > 0.0) { + const scaleNext = lastSameStrideLayer === config.strides.length - 1 ? + 1.0 : + calculateScale( + config.minScale, config.maxScale, lastSameStrideLayer + 1, + config.strides.length); + scales.push(Math.sqrt(scale * scaleNext)); + aspectRatios.push(config.interpolatedScaleAspectRatio); + } + } + lastSameStrideLayer++; + } + + for (let i = 0; i < aspectRatios.length; ++i) { + const ratioSqrts = Math.sqrt(aspectRatios[i]); + anchorHeight.push(scales[i] / ratioSqrts); + anchorWidth.push(scales[i] * ratioSqrts); + } + + let featureMapHeight = 0; + let featureMapWidth = 0; + if (config.featureMapHeight.length > 0) { + featureMapHeight = config.featureMapHeight[layerId]; + featureMapWidth = config.featureMapWidth[layerId]; + } else { + const stride = config.strides[layerId]; + featureMapHeight = Math.ceil(config.inputSizeHeight / stride); + featureMapWidth = Math.ceil(config.inputSizeWidth / stride); + } + + for (let y = 0; y < featureMapHeight; ++y) { + for (let x = 0; x < featureMapWidth; ++x) { + for (let anchorId = 0; anchorId < anchorHeight.length; ++anchorId) { + const xCenter = (x + config.anchorOffsetX) / featureMapWidth; + const yCenter = (y + config.anchorOffsetY) / featureMapHeight; + + const newAnchor: Rect = {xCenter, yCenter, width: 0, height: 0}; + + if (config.fixedAnchorSize) { + newAnchor.width = 1.0; + newAnchor.height = 1.0; + } else { + newAnchor.width = anchorWidth[anchorId]; + newAnchor.height = anchorHeight[anchorId]; + } + anchors.push(newAnchor); + } + } + } + layerId = lastSameStrideLayer; + } + + return anchors; +} + +function calculateScale( + minScale: number, maxScale: number, strideIndex: number, + numStrides: number) { + if (numStrides === 1) { + return (minScale + maxScale) * 0.5; + } else { + return minScale + (maxScale - minScale) * strideIndex / (numStrides - 1); + } +} diff --git a/pose-detection/src/shared/calculators/create_ssd_anchors_test.ts b/pose-detection/src/shared/calculators/create_ssd_anchors_test.ts new file mode 100644 index 0000000000..96a71c2cf1 --- /dev/null +++ b/pose-detection/src/shared/calculators/create_ssd_anchors_test.ts @@ -0,0 +1,100 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +// tslint:disable-next-line: no-imports-from-dist +import {expectNumbersClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {KARMA_SERVER} from '../test_util'; + +import {createSsdAnchors} from './create_ssd_anchors'; +import {AnchorConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +const EPS = 1e-5; + +function compareAnchors(actual: Rect[], expected: Rect[]) { + expect(actual.length).toBe(expected.length); + + for (let i = 0; i < actual.length; ++i) { + const actualAnchor = actual[i]; + const expectedAnchor = expected[i]; + + for (const key of ['xCenter', 'yCenter', 'width', 'height'] as const ) { + const actualValue = actualAnchor[key]; + const expectedValue = expectedAnchor[key]; + + expectNumbersClose(actualValue, expectedValue, EPS); + } + } +} + +function parseAnchors(anchorsValues: number[][]): Rect[] { + return anchorsValues.map(anchorValues => ({ + xCenter: anchorValues[0], + yCenter: anchorValues[1], + width: anchorValues[2], + height: anchorValues[3] + })); +} + +describe('createSsdAnchors', () => { + it('face detection config.', async () => { + const expectedAnchors = parseAnchors( + await fetch(`${KARMA_SERVER}/shared/anchor_golden_file_0.json`) + .then(response => response.json())); + const config: AnchorConfig = { + featureMapHeight: [] as number[], + featureMapWidth: [] as number[], + numLayers: 5, + minScale: 0.1171875, + maxScale: 0.75, + inputSizeHeight: 256, + inputSizeWidth: 256, + anchorOffsetX: 0.5, + anchorOffsetY: 0.5, + strides: [8, 16, 32, 32, 32], + aspectRatios: [1.0], + fixedAnchorSize: true + }; + const actualAnchors = createSsdAnchors(config); + + compareAnchors(actualAnchors, expectedAnchors); + }); + + it('3 inputs reverse.', async () => { + const expectedAnchors = parseAnchors( + await fetch(`${KARMA_SERVER}/shared/anchor_golden_file_1.json`) + .then(response => response.json())); + + const config: AnchorConfig = { + featureMapHeight: [] as number[], + featureMapWidth: [] as number[], + numLayers: 6, + minScale: 0.2, + maxScale: 0.95, + inputSizeHeight: 300, + inputSizeWidth: 300, + anchorOffsetX: 0.5, + anchorOffsetY: 0.5, + strides: [16, 32, 64, 128, 256, 512], + aspectRatios: [1.0, 2.0, 0.5, 3.0, 0.3333], + reduceBoxesInLowestLayer: true + }; + const actualAnchors = createSsdAnchors(config); + + compareAnchors(actualAnchors, expectedAnchors); + }); +}); diff --git a/pose-detection/src/shared/calculators/detection_projection.ts b/pose-detection/src/shared/calculators/detection_projection.ts new file mode 100644 index 0000000000..c433792d73 --- /dev/null +++ b/pose-detection/src/shared/calculators/detection_projection.ts @@ -0,0 +1,79 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Matrix4x4, matrix4x4ToArray} from './calculate_inverse_matrix'; +import {Detection} from './interfaces/shape_interfaces'; + +function project( + projectionMatrix: number[], [x, y]: [number, number]): [number, number] { + return [ + x * projectionMatrix[0] + y * projectionMatrix[1] + projectionMatrix[3], + x * projectionMatrix[4] + y * projectionMatrix[5] + projectionMatrix[7] + ]; +} +/** + * Projects detections to a different coordinate system using a provided + * projection matrix. + * + * @param detections A list of detections to project using the provided + * projection matrix. + * @param projectionMatrix Maps data from one coordinate system to another. + * @returns detections: A list of projected detections + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/detection_projection_calculator.cc +export function detectionProjection( + detections: Detection[] = [], projectionMatrix: Matrix4x4): Detection[] { + const flatProjectionMatrix = matrix4x4ToArray(projectionMatrix); + + detections.forEach(detection => { + const locationData = detection.locationData; + + // Project keypoints. + locationData.relativeKeypoints.forEach(keypoint => { + const [x, y] = project(flatProjectionMatrix, [keypoint.x, keypoint.y]); + keypoint.x = x; + keypoint.y = y; + }); + + // Project bounding box. + const box = locationData.relativeBoundingBox; + + let xMin = Number.MAX_VALUE, yMin = Number.MAX_VALUE, + xMax = Number.MIN_VALUE, yMax = Number.MIN_VALUE; + + [[box.xMin, box.yMin], [box.xMin + box.width, box.yMin], + [box.xMin + box.width, box.yMin + box.height], + [box.xMin, box.yMin + box.height]] + .forEach(coordinate => { + // a) Define and project box points. + const [x, y] = + project(flatProjectionMatrix, coordinate as [number, number]); + // b) Find new left top and right bottom points for a box which + // encompases + // non-projected (rotated) box. + xMin = Math.min(xMin, x); + xMax = Math.max(xMax, x); + yMin = Math.min(yMin, y); + yMax = Math.max(yMax, y); + }); + locationData.relativeBoundingBox = + {xMin, xMax, yMin, yMax, width: xMax - xMin, height: yMax - yMin}; + }); + + return detections; +} diff --git a/pose-detection/src/shared/calculators/detection_to_rect.ts b/pose-detection/src/shared/calculators/detection_to_rect.ts new file mode 100644 index 0000000000..2cd43e5587 --- /dev/null +++ b/pose-detection/src/shared/calculators/detection_to_rect.ts @@ -0,0 +1,128 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {normalizeRadians} from './image_utils'; +import {ImageSize} from './interfaces/common_interfaces'; +import {DetectionToRectConfig} from './interfaces/config_interfaces'; +import {BoundingBox, Detection, LocationData, Rect} from './interfaces/shape_interfaces'; + +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/detections_to_rects_calculator.cc +export function computeRotation( + detection: Detection, imageSize: ImageSize, config: DetectionToRectConfig) { + const locationData = detection.locationData; + const startKeypoint = config.rotationVectorStartKeypointIndex; + const endKeypoint = config.rotationVectorEndKeypointIndex; + + let targetAngle; + + if (config.rotationVectorTargetAngle) { + targetAngle = config.rotationVectorTargetAngle; + } else { + targetAngle = Math.PI * config.rotationVectorTargetAngleDegree / 180; + } + + const x0 = locationData.relativeKeypoints[startKeypoint].x * imageSize.width; + const y0 = locationData.relativeKeypoints[startKeypoint].y * imageSize.height; + const x1 = locationData.relativeKeypoints[endKeypoint].x * imageSize.width; + const y1 = locationData.relativeKeypoints[endKeypoint].y * imageSize.height; + + const rotation = + normalizeRadians(targetAngle - Math.atan2(-(y1 - y0), x1 - x0)); + + return rotation; +} + +function rectFromBox(box: BoundingBox) { + return { + xCenter: box.xMin + box.width / 2, + yCenter: box.yMin + box.height / 2, + width: box.width, + height: box.height, + }; +} + +function normRectFromKeypoints(locationData: LocationData) { + const keypoints = locationData.relativeKeypoints; + if (keypoints.length <= 1) { + throw new Error('2 or more keypoints required to calculate a rect.'); + } + let xMin = Number.MAX_VALUE, yMin = Number.MAX_VALUE, xMax = Number.MIN_VALUE, + yMax = Number.MIN_VALUE; + + keypoints.forEach(keypoint => { + xMin = Math.min(xMin, keypoint.x); + xMax = Math.max(xMax, keypoint.x); + yMin = Math.min(yMin, keypoint.y); + yMax = Math.max(yMax, keypoint.y); + }); + + return { + xCenter: (xMin + xMax) / 2, + yCenter: (yMin + yMax) / 2, + width: xMax - xMin, + height: yMax - yMin + }; +} + +function detectionToNormalizedRect( + detection: Detection, conversionMode: 'boundingbox'|'keypoints') { + const locationData = detection.locationData; + return conversionMode === 'boundingbox' ? + rectFromBox(locationData.relativeBoundingBox) : + normRectFromKeypoints(locationData); +} + +function detectionToRect( + detection: Detection, + conversionMode: 'boundingbox'|'keypoints', + imageSize?: ImageSize, + ): Rect { + const locationData = detection.locationData; + + let rect: Rect; + if (conversionMode === 'boundingbox') { + rect = rectFromBox(locationData.boundingBox); + } else { + rect = normRectFromKeypoints(locationData); + const {width, height} = imageSize; + + rect.xCenter = Math.round(rect.xCenter * width); + rect.yCenter = Math.round(rect.yCenter * height); + rect.width = Math.round(rect.width * width); + rect.height = Math.round(rect.height * height); + } + + return rect; +} + +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/detections_to_rects_calculator.cc +export function calculateDetectionsToRects( + detection: Detection, conversionMode: 'boundingbox'|'keypoints', + outputType: 'rect'|'normRect', imageSize?: ImageSize, + rotationConfig?: DetectionToRectConfig): Rect { + const rect: Rect = outputType === 'rect' ? + detectionToRect(detection, conversionMode, imageSize) : + detectionToNormalizedRect(detection, conversionMode); + + if (rotationConfig) { + rect.rotation = computeRotation(detection, imageSize, rotationConfig); + } + + return rect; +} diff --git a/pose-detection/src/shared/calculators/detection_to_rect_test.ts b/pose-detection/src/shared/calculators/detection_to_rect_test.ts new file mode 100644 index 0000000000..4603bf7097 --- /dev/null +++ b/pose-detection/src/shared/calculators/detection_to_rect_test.ts @@ -0,0 +1,133 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {calculateDetectionsToRects} from './detection_to_rect'; +import {Detection, Rect} from './interfaces/shape_interfaces'; + +function detectionWithLocationData( + xMin: number, yMin: number, width: number, height: number) { + const detection: Detection = { + locationData: { + boundingBox: + {xMin, yMin, width, height, xMax: xMin + width, yMax: yMin + height} + } + }; + return detection; +} + +function detectionWithKeyPoints(keypoints: Array<[number, number]>) { + const detection: Detection = { + locationData: { + relativeKeypoints: + keypoints.map(keypoint => ({x: keypoint[0], y: keypoint[1]})) + } + }; + return detection; +} + +function detectionWithRelativeLocationData( + xMin: number, yMin: number, width: number, height: number) { + const detection: Detection = { + locationData: { + relativeBoundingBox: + {xMin, yMin, width, height, xMax: xMin + width, yMax: yMin + height} + } + }; + return detection; +} + +function expectRectEq( + rect: Rect, xCenter: number, yCenter: number, width: number, + height: number) { + expect(rect.xCenter).toBe(xCenter); + expect(rect.yCenter).toBe(yCenter); + expect(rect.width).toBe(width); + expect(rect.height).toBe(height); +} + +describe('DetectionsToRects', () => { + it('detection to rect.', async () => { + const rects = calculateDetectionsToRects( + detectionWithLocationData(100, 200, 300, 400), 'boundingbox', 'rect'); + + expectRectEq(rects, 250, 400, 300, 400); + }); + + it('detection keypoints to rect.', async () => { + let rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0, 0], [1, 1]]), 'keypoints', 'rect', + {width: 640, height: 480}); + + expectRectEq(rects, 320, 240, 640, 480); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0.25, 0.25], [0.75, 0.75]]), 'keypoints', + 'rect', {width: 640, height: 480}); + + expectRectEq(rects, 320, 240, 320, 240); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0, 0], [0.5, 0.5]]), 'keypoints', 'rect', + {width: 640, height: 480}); + + expectRectEq(rects, 160, 120, 320, 240); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0.5, 0.5], [1, 1]]), 'keypoints', 'rect', + {width: 640, height: 480}); + + expectRectEq(rects, 480, 360, 320, 240); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0.25, 0.25], [0.75, 0.75]]), 'keypoints', + 'rect', {width: 0, height: 0}); + + expectRectEq(rects, 0, 0, 0, 0); + }); + + it('detection to normalized rect.', async () => { + const rects = calculateDetectionsToRects( + detectionWithRelativeLocationData(0.1, 0.2, 0.3, 0.4), 'boundingbox', + 'normRect'); + + expectRectEq(rects, 0.25, 0.4, 0.3, 0.4); + }); + + it('detection keypoints to normalized rect.', async () => { + let rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0, 0], [0.5, 0.5], [1, 1]]), 'keypoints', + 'normRect'); + + expectRectEq(rects, 0.5, 0.5, 1, 1); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0.25, 0.25], [0.75, 0.25], [0.75, 0.75]]), + 'keypoints', 'normRect'); + + expectRectEq(rects, 0.5, 0.5, 0.5, 0.5); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0, 0], [0.5, 0.5]]), 'keypoints', 'normRect'); + + expectRectEq(rects, 0.25, 0.25, 0.5, 0.5); + + rects = calculateDetectionsToRects( + detectionWithKeyPoints([[0.5, 0.5], [1, 1]]), 'keypoints', 'normRect'); + + expectRectEq(rects, 0.75, 0.75, 0.5, 0.5); + }); +}); diff --git a/pose-detection/src/shared/calculators/detector_result.ts b/pose-detection/src/shared/calculators/detector_result.ts new file mode 100644 index 0000000000..4c79002eee --- /dev/null +++ b/pose-detection/src/shared/calculators/detector_result.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {splitDetectionResult} from './split_detection_result'; + +export type detectorResult = { + boxes: tf.Tensor2D, + logits: tf.Tensor1D +}; + +export function detectorResult(detectionResult: tf.Tensor3D): detectorResult { + return tf.tidy(() => { + const [logits, rawBoxes] = splitDetectionResult(detectionResult); + // Shape [896, 12] + const rawBoxes2d = tf.squeeze(rawBoxes); + // Shape [896] + const logits1d = tf.squeeze(logits); + + return {boxes: rawBoxes2d as tf.Tensor2D, logits: logits1d as tf.Tensor1D}; + }); +} diff --git a/pose-detection/src/shared/calculators/get_object_scale.ts b/pose-detection/src/shared/calculators/get_object_scale.ts new file mode 100644 index 0000000000..40ca6d5f36 --- /dev/null +++ b/pose-detection/src/shared/calculators/get_object_scale.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {ImageSize} from './interfaces/common_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +/** + * Estimate object scale to allow filter work similarly on nearer or futher + * objects. + * @param roi Normalized rectangle. + * @param imageSize An object that contains width and height. + * @returns A number representing the object scale. + */ +export function getObjectScale(roi: Rect, imageSize: ImageSize): number { + const objectWidth = roi.width * imageSize.width; + const objectHeight = roi.height * imageSize.height; + + return (objectWidth + objectHeight) / 2; +} diff --git a/pose-detection/src/shared/calculators/get_rotated_sub_rect_to_rect_transformation_matrix.ts b/pose-detection/src/shared/calculators/get_rotated_sub_rect_to_rect_transformation_matrix.ts new file mode 100644 index 0000000000..26c930ce9d --- /dev/null +++ b/pose-detection/src/shared/calculators/get_rotated_sub_rect_to_rect_transformation_matrix.ts @@ -0,0 +1,123 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {arrayToMatrix4x4, Matrix4x4} from './calculate_inverse_matrix'; +import {Rect} from './interfaces/shape_interfaces'; + +/** + * Generates a 4x4 projective transform matrix M, so that for any point in the + * subRect image p(x, y), we can use the matrix to calculate the projected point + * in the original image p' (x', y'): p' = p * M; + * + * @param subRect Rotated sub rect in absolute coordinates. + * @param rectWidth + * @param rectHeight + * @param flipHorizontaly Whether to flip the image horizontally. + */ +// Ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/tensor/image_to_tensor_utils.h +export function getRotatedSubRectToRectTransformMatrix( + subRect: Rect, rectWidth: number, rectHeight: number, + flipHorizontally: boolean): Matrix4x4 { + // The resulting matrix is multiplication of below commented out matrices: + // postScaleMatrix + // * translateMatrix + // * rotateMatrix + // * flipMatrix + // * scaleMatrix + // * initialTranslateMatrix + + // For any point in the transformed image p, we can use the above matrix to + // calculate the projected point in the original image p'. So that: + // p' = p * M; + // Note: The transform matrix below assumes image coordinates is normalized + // to [0, 1] range. + + // Matrix to convert X,Y to [-0.5, 0.5] range "initialTranslateMatrix" + // [ 1.0, 0.0, 0.0, -0.5] + // [ 0.0, 1.0, 0.0, -0.5] + // [ 0.0, 0.0, 1.0, 0.0] + // [ 0.0, 0.0, 0.0, 1.0] + + const a = subRect.width; + const b = subRect.height; + // Matrix to scale X,Y,Z to sub rect "scaleMatrix" + // Z has the same scale as X. + // [ a, 0.0, 0.0, 0.0] + // [0.0, b, 0.0, 0.0] + // [0.0, 0.0, a, 0.0] + // [0.0, 0.0, 0.0, 1.0] + + const flip = flipHorizontally ? -1 : 1; + // Matrix for optional horizontal flip around middle of output image. + // [ fl , 0.0, 0.0, 0.0] + // [ 0.0, 1.0, 0.0, 0.0] + // [ 0.0, 0.0, 1.0, 0.0] + // [ 0.0, 0.0, 0.0, 1.0] + + const c = Math.cos(subRect.rotation); + const d = Math.sin(subRect.rotation); + // Matrix to do rotation around Z axis "rotateMatrix" + // [ c, -d, 0.0, 0.0] + // [ d, c, 0.0, 0.0] + // [ 0.0, 0.0, 1.0, 0.0] + // [ 0.0, 0.0, 0.0, 1.0] + + const e = subRect.xCenter; + const f = subRect.yCenter; + // Matrix to do X,Y translation of sub rect within parent rect + // "translateMatrix" + // [1.0, 0.0, 0.0, e ] + // [0.0, 1.0, 0.0, f ] + // [0.0, 0.0, 1.0, 0.0] + // [0.0, 0.0, 0.0, 1.0] + + const g = 1.0 / rectWidth; + const h = 1.0 / rectHeight; + // Matrix to scale X,Y,Z to [0.0, 1.0] range "postScaleMatrix" + // [g, 0.0, 0.0, 0.0] + // [0.0, h, 0.0, 0.0] + // [0.0, 0.0, g, 0.0] + // [0.0, 0.0, 0.0, 1.0] + + const matrix: number[] = new Array(16); + // row 1 + matrix[0] = a * c * flip * g; + matrix[1] = -b * d * g; + matrix[2] = 0.0; + matrix[3] = (-0.5 * a * c * flip + 0.5 * b * d + e) * g; + + // row 2 + matrix[4] = a * d * flip * h; + matrix[5] = b * c * h; + matrix[6] = 0.0; + matrix[7] = (-0.5 * b * c - 0.5 * a * d * flip + f) * h; + + // row 3 + matrix[8] = 0.0; + matrix[9] = 0.0; + matrix[10] = a * g; + matrix[11] = 0.0; + + // row 4 + matrix[12] = 0.0; + matrix[13] = 0.0; + matrix[14] = 0.0; + matrix[15] = 1.0; + + return arrayToMatrix4x4(matrix); +} diff --git a/pose-detection/src/shared/calculators/image_util_test.ts b/pose-detection/src/shared/calculators/image_util_test.ts new file mode 100644 index 0000000000..42c39658cb --- /dev/null +++ b/pose-detection/src/shared/calculators/image_util_test.ts @@ -0,0 +1,94 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +// tslint:disable-next-line: no-imports-from-dist +import {expectNumbersClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {getRoi, padRoi} from './image_utils'; +import {Padding} from './interfaces/common_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +function expectRotatedRectEq( + rect: Rect, width: number, height: number, xCenter: number, yCenter: number, + rotation: number) { + expect(rect.xCenter).toBe(xCenter); + expect(rect.yCenter).toBe(yCenter); + expect(rect.width).toBe(width); + expect(rect.height).toBe(height); + expect(rect.rotation).toBe(rotation); +} + +describe('GetRoi', () => { + it('no norm rect.', async () => { + expectRotatedRectEq(getRoi({width: 4, height: 4}), 4, 4, 2, 2, 0); + expectRotatedRectEq(getRoi({width: 25, height: 15}), 25, 15, 12.5, 7.5, 0); + }); + + it('whole image norm rect.', async () => { + const normRect = + {width: 1, height: 1, xCenter: 0.5, yCenter: 0.5, rotation: 0}; + expectRotatedRectEq(getRoi({width: 4, height: 4}, normRect), 4, 4, 2, 2, 0); + expectRotatedRectEq( + getRoi({width: 25, height: 15}, normRect), 25, 15, 12.5, 7.5, 0); + }); + + it('expanded norm rect.', async () => { + const normRect = + {width: 4, height: 2, xCenter: 0.5, yCenter: 1, rotation: 3}; + expectRotatedRectEq( + getRoi({width: 4, height: 4}, normRect), 16, 8, 2, 4, 3); + expectRotatedRectEq( + getRoi({width: 25, height: 15}, normRect), 100, 30, 12.5, 15, 3); + }); +}); + +const EPS = 1e-6; + +function expectPaddingClose( + padding: Padding, top: number, bottom: number, left: number, + right: number) { + expectNumbersClose(padding.top, top, EPS); + expectNumbersClose(padding.bottom, bottom, EPS); + expectNumbersClose(padding.left, left, EPS); + expectNumbersClose(padding.right, right, EPS); +} + +describe('PadRoi', () => { + it('no padding.', async () => { + const roi = + {xCenter: 20, yCenter: 10, width: 100, height: 200, rotation: 5}; + expectPaddingClose(padRoi(roi, {width: 10, height: 10}, false), 0, 0, 0, 0); + expectRotatedRectEq(roi, 100, 200, 20, 10, 5); + }); + + it('horizontal padding.', async () => { + const roi = + {xCenter: 20, yCenter: 10, width: 100, height: 200, rotation: 5}; + expectPaddingClose( + padRoi(roi, {width: 10, height: 10}, true), 0, 0, 0.25, 0.25); + expectRotatedRectEq(roi, 200, 200, 20, 10, 5); + }); + + it('vertical padding.', async () => { + const roi = {xCenter: 1, yCenter: 2, width: 21, height: 19, rotation: 3}; + const expectedHorizontalPadding = (21 - 19) / 2.0 / 21; + expectPaddingClose( + padRoi(roi, {width: 10, height: 10}, true), expectedHorizontalPadding, + expectedHorizontalPadding, 0, 0); + expectRotatedRectEq(roi, 21, 21, 1, 2, 3); + }); +}); diff --git a/pose-detection/src/shared/calculators/image_utils.ts b/pose-detection/src/shared/calculators/image_utils.ts new file mode 100644 index 0000000000..b7fdcc3c4b --- /dev/null +++ b/pose-detection/src/shared/calculators/image_utils.ts @@ -0,0 +1,193 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {Matrix4x4} from './calculate_inverse_matrix'; + +import {ImageSize, InputResolution, Padding, PixelInput, ValueTransform} from './interfaces/common_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +export function getImageSize(input: PixelInput): ImageSize { + if (input instanceof tf.Tensor) { + return {height: input.shape[0], width: input.shape[1]}; + } else { + return {height: input.height, width: input.width}; + } +} + +/** + * Normalizes the provided angle to the range -pi to pi. + * @param angle The angle in radians to be normalized. + */ +export function normalizeRadians(angle: number): number { + return angle - 2 * Math.PI * Math.floor((angle + Math.PI) / (2 * Math.PI)); +} + +/** + * Transform value ranges. + * @param fromMin Min of original value range. + * @param fromMax Max of original value range. + * @param toMin New min of transformed value range. + * @param toMax New max of transformed value range. + */ +export function transformValueRange( + fromMin: number, fromMax: number, toMin: number, + toMax: number): ValueTransform { + const fromRange = fromMax - fromMin; + const toRange = toMax - toMin; + + if (fromRange === 0) { + throw new Error( + `Original min and max are both ${fromMin}, range cannot be 0.`); + } + + const scale = toRange / fromRange; + const offset = toMin - fromMin * scale; + return {scale, offset}; +} + +/** + * Convert an image to an image tensor representation. + * + * The image tensor has a shape [1, height, width, colorChannel]. + * + * @param input An image, video frame, or image tensor. + */ +export function toImageTensor(input: PixelInput) { + return input instanceof tf.Tensor ? input : tf.browser.fromPixels(input); +} + +/** + * Padding ratio of left, top, right, bottom, based on the output dimensions. + * + * The padding values are non-zero only when the "keep_aspect_ratio" is true. + * + * For instance, when the input image is 10x10 (width x height) and the + * output dimensions is 20x40 and "keep_aspect_ratio" is true, we should scale + * the input image to 20x20 and places it in the middle of the output image with + * an equal padding of 10 pixels at the top and the bottom. The result is + * therefore {left: 0, top: 0.25, right: 0, bottom: 0.25} (10/40 = 0.25f). + * @param roi The original rectangle to pad. + * @param targetSize The target width and height of the result rectangle. + * @param keepAspectRatio Whether keep aspect ratio. Default to false. + */ +export function padRoi( + roi: Rect, targetSize: InputResolution, keepAspectRatio = false): Padding { + if (!keepAspectRatio) { + return {top: 0, left: 0, right: 0, bottom: 0}; + } + + const targetH = targetSize.height; + const targetW = targetSize.width; + + validateSize(targetSize, 'targetSize'); + validateSize(roi, 'roi'); + + const tensorAspectRatio = targetH / targetW; + const roiAspectRatio = roi.height / roi.width; + let newWidth; + let newHeight; + let horizontalPadding = 0; + let verticalPadding = 0; + if (tensorAspectRatio > roiAspectRatio) { + // pad height; + newWidth = roi.width; + newHeight = roi.width * tensorAspectRatio; + verticalPadding = (1 - roiAspectRatio / tensorAspectRatio) / 2; + } else { + // pad width. + newWidth = roi.height / tensorAspectRatio; + newHeight = roi.height; + horizontalPadding = (1 - tensorAspectRatio / roiAspectRatio) / 2; + } + + roi.width = newWidth; + roi.height = newHeight; + + return { + top: verticalPadding, + left: horizontalPadding, + right: horizontalPadding, + bottom: verticalPadding + }; +} + +/** + * Get the rectangle information of an image, including xCenter, yCenter, width, + * height and rotation. + * + * @param imageSize imageSize is used to calculate the rectangle. + * @param normRect Optional. If normRect is not null, it will be used to get + * a subarea rectangle information in the image. `imageSize` is used to + * calculate the actual non-normalized coordinates. + */ +export function getRoi(imageSize: ImageSize, normRect?: Rect): Rect { + if (normRect) { + return { + xCenter: normRect.xCenter * imageSize.width, + yCenter: normRect.yCenter * imageSize.height, + width: normRect.width * imageSize.width, + height: normRect.height * imageSize.height, + rotation: normRect.rotation + }; + } else { + return { + xCenter: 0.5 * imageSize.width, + yCenter: 0.5 * imageSize.height, + width: imageSize.width, + height: imageSize.height, + rotation: 0 + }; + } +} + +/** + * Generate the projective transformation matrix to be used for `tf.transform`. + * + * See more documentation in `tf.transform`. + * + * @param matrix The transformation matrix mapping subRect to rect, can be + * computed using `getRotatedSubRectToRectTransformMatrix` calculator. + * @param imageSize The original image height and width. + * @param inputResolution The target height and width. + */ +export function getProjectiveTransformMatrix( + matrix: Matrix4x4, imageSize: ImageSize, inputResolution: InputResolution): + [number, number, number, number, number, number, number, number] { + validateSize(inputResolution, 'inputResolution'); + + // To use M with regular x, y coordinates, we need to normalize them first. + // Because x' = a0 * x + a1 * y + a2, y' = b0 * x + b1 * y + b2, + // we need to use factor (1/inputResolution.width) to normalize x for a0 and + // b0, similarly we need to use factor (1/inputResolution.height) to normalize + // y for a1 and b1. + // Also at the end, we need to de-normalize x' and y' to regular coordinates. + // So we need to use factor imageSize.width for a0, a1 and a2, similarly + // we need to use factor imageSize.height for b0, b1 and b2. + const a0 = (1 / inputResolution.width) * matrix[0][0] * imageSize.width; + const a1 = (1 / inputResolution.height) * matrix[0][1] * imageSize.width; + const a2 = matrix[0][3] * imageSize.width; + const b0 = (1 / inputResolution.width) * matrix[1][0] * imageSize.height; + const b1 = (1 / inputResolution.height) * matrix[1][1] * imageSize.height; + const b2 = matrix[1][3] * imageSize.height; + + return [a0, a1, a2, b0, b1, b2, 0, 0]; +} + +function validateSize(size: {width: number, height: number}, name: string) { + tf.util.assert(size.width !== 0, () => `${name} width cannot be 0.`); + tf.util.assert(size.height !== 0, () => `${name} height cannot be 0.`); +} diff --git a/pose-detection/src/shared/calculators/interfaces/common_interfaces.ts b/pose-detection/src/shared/calculators/interfaces/common_interfaces.ts new file mode 100644 index 0000000000..f265b8d88a --- /dev/null +++ b/pose-detection/src/shared/calculators/interfaces/common_interfaces.ts @@ -0,0 +1,113 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Tensor3D} from '@tensorflow/tfjs-core'; + +export type PixelInput = Tensor3D|ImageData|HTMLVideoElement|HTMLImageElement| + HTMLCanvasElement|ImageBitmap; + +export interface InputResolution { + width: number; + height: number; +} + +/** + * A keypoint that contains coordinate information. + */ +export interface Keypoint { + x: number; + y: number; + z?: number; + score?: number; // The probability of a keypoint's visibility. + name?: string; +} + +export interface ImageSize { + height: number; + width: number; +} + +export interface Padding { + top: number; + bottom: number; + left: number; + right: number; +} + +export type ValueTransform = { + scale: number, + offset: number +}; + +export interface WindowElement { + distance: number; + duration: number; +} + +export interface KeypointsFilter { + apply(landmarks: Keypoint[], microSeconds: number, objectScale: number): + Keypoint[]; + reset(): void; +} + +export interface Mask { + toCanvasImageSource(): + Promise; /* RGBA image of same size as input, where + mask semantics are green and blue are always set to + 0. Different red values denote different body + parts(see maskValueToBodyPart explanation below). + Different alpha values denote the probability of + pixel being a foreground pixel (0 being lowest + probability and 255 being highest).*/ + + toImageData(): + Promise; /* 1 dimensional array of size image width * height * + 4, where each pixel is represented by RGBA in that order. + For each pixel, the semantics are green and blue are always + set to 0, and different red values denote different body + parts (see maskValueToBodyPart explanation below). Different + alpha values denote the probability of the pixel being a + foreground pixel (0 being lowest probability and 255 being + highest). */ + + toTensor(): + Promise; /* RGBA image of same size as input, where mask + semantics are green and blue are always set to 0. Different + red values denote different body parts (see + maskValueToBodyPart explanation below). Different alpha + values denote the probability of pixel being a foreground + pixel (0 being lowest probability and 255 being highest).*/ + + getUnderlyingType(): 'canvasimagesource'|'imagedata'| + 'tensor'; /* determines which type the mask currently stores in its + implementation so that conversion can be avoided */ +} + +export interface Segmentation { + maskValueToLabel: (maskValue: number) => + string; /* Maps a foreground pixel’s red value to the segmented part name + of that pixel. Should throw error for unsupported input + values.*/ + mask: Mask; +} + +export type Color = { + r: number, + g: number, + b: number, + a: number, +}; diff --git a/pose-detection/src/shared/calculators/interfaces/config_interfaces.ts b/pose-detection/src/shared/calculators/interfaces/config_interfaces.ts new file mode 100644 index 0000000000..c82ced63ff --- /dev/null +++ b/pose-detection/src/shared/calculators/interfaces/config_interfaces.ts @@ -0,0 +1,206 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {InputResolution} from './common_interfaces'; + +export interface ImageToTensorConfig { + outputTensorSize: InputResolution; + keepAspectRatio?: boolean; + outputTensorFloatRange?: [number, number]; + borderMode: 'zero'|'replicate'; +} +export interface VelocityFilterConfig { + windowSize?: number; // Number of value changes to keep over time. Higher + // value adds to lag and to stability. + velocityScale?: + number; // Scale to apply to the velocity calculated over the given + // window. With higher velocity `low pass filter` weights new new + // values higher. Lower value adds to lag and to stability. + minAllowedObjectScale?: + number; // If calculated object scale is less than given value smoothing + // will be disabled and landmarks will be returned as is. + disableValueScaling?: + boolean; // Disable value scaling based on object size and use `1.0` + // instead. Value scale is calculated as inverse value of object + // size. Object size is calculated as maximum side of + // rectangular bounding box of the object in XY plane. +} +export interface OneEuroFilterConfig { + frequency?: number; // Frequency of incoming frames defined in seconds. Used + // only if can't be calculated from provided events (e.g. + // on the very fist frame). + minCutOff?: + number; // Minimum cutoff frequency. Start by tuning this parameter while + // keeping `beta=0` to reduce jittering to the desired level. 1Hz + // (the default value) is a a good starting point. + beta?: number; // Cutoff slope. After `minCutOff` is configured, start + // increasing `beta` value to reduce the lag introduced by the + // `minCutoff`. Find the desired balance between jittering and + // lag. + derivateCutOff?: + number; // Cutoff frequency for derivate. It is set to 1Hz in the + // original algorithm, but can be turned to further smooth the + // speed (i.e. derivate) on the object. + thresholdCutOff?: + number; // The outlier threshold offset, will lead to a generally more + // reactive filter that will be less likely to discount outliers. + thresholdBeta?: number; // The outlier threshold slope, will lead to a filter + // that will more aggressively react whenever the + // keypoint speed increases by being less likely to + // consider that an observation is an outlier. + minAllowedObjectScale?: + number; // If calculated object scale is less than given value smoothing + // will be disabled and keypoints will be returned as is. This + // value helps filter adjust to the distance to camera. + disableValueScaling?: + boolean; // Disable value scaling based on object size and use `1.0` + // instead. Value scale is calculated as inverse value of object + // size. Object size is calculated as maximum side of + // rectangular bounding box of the object in XY plane. +} +export interface KeypointsSmoothingConfig { + velocityFilter?: VelocityFilterConfig; + oneEuroFilter?: OneEuroFilterConfig; +} +export interface AnchorConfig { + numLayers: + number; // Number of output feature maps to generate the anchors on. + minScale: number; // Min scales for generating anchor boxes on feature maps. + maxScale: number; // Max scales for generating anchor boxes on feature maps. + inputSizeHeight: number; // Size of input images. + inputSizeWidth: number; + anchorOffsetX: number; // The offset for the center of anchors. The values is + // in the scale of stride. E.g. 0.5 meaning 0.5 * + // currentStride in pixels. + anchorOffsetY: number; + featureMapWidth: + number[]; // Sizes of output feature maps to create anchors. Either + // featureMap size or stride should be provided. + featureMapHeight: number[]; + strides: number[]; // Strides of each output feature maps. + aspectRatios: + number[]; // List of different aspect ratio to generate anchors. + fixedAnchorSize?: + boolean; // Whether use fixed width and height (e.g. both 1.0) for each + // anchor. This option can be used when the predicted anchor + // width and height are in pixels. + reduceBoxesInLowestLayer?: + boolean; // A boolean to indicate whether the fixed 3 boxes per location + // is used in the lowest layer. + interpolatedScaleAspectRatio?: + number; // An additional anchor is added with this aspect ratio and a + // scale interpolated between the scale for a layer and the scale + // for the next layer (1.0 for the last layer). This anchor is + // not included if this value is 0. +} + +export interface TensorsToDetectionsConfig { + numClasses: + number; // The number of output classes predicted by the detection model. + numBoxes: + number; // The number of output boxes predicted by the detection model. + numCoords: number; // The number of output values per boxes predicted by the + // detection model. The values contain bounding boxes, + // keypoints, etc. + keypointCoordOffset?: number; // The offset of keypoint coordinates in the + // location tensor. + numKeypoints?: number; // The number of predicted keypoints. + numValuesPerKeypoint?: number; // The dimension of each keypoint, e.g. number + // of values predicted for each keypoint. + boxCoordOffset?: number; // The offset of box coordinates in the location + // tensor. + xScale?: number; // Parameters for decoding SSD detection model. + yScale?: number; // Parameters for decoding SSD detection model. + wScale?: number; // Parameters for decoding SSD detection model. + hScale?: number; // Parameters for decoding SSD detection model. + applyExponentialOnBoxSize?: boolean; + reverseOutputOrder?: boolean; // Whether to reverse the order of predicted x, + // y from output. If false, the order is + // [y_center, x_center, h, w], if true the + // order is [x_center, y_center, w, h]. + ignoreClasses?: number[]; // The ids of classes that should be ignored during + // decoding the score for each predicted box. + sigmoidScore?: boolean; + scoreClippingThresh?: number; + flipVertically?: boolean; // Whether the detection coordinates from the input + // tensors should be flipped vertically (along the + // y-direction). + minScoreThresh?: + number; // Score threshold for perserving decoded detections. +} +export interface DetectionToRectConfig { + rotationVectorStartKeypointIndex: number; + rotationVectorEndKeypointIndex: number; + rotationVectorTargetAngleDegree?: number; // In degrees. + rotationVectorTargetAngle?: number; // In radians. +} +export interface RectTransformationConfig { + scaleX?: number; // Scaling factor along the side of a rotated rect that + // was aligned with the X and Y axis before rotation + // respectively. + scaleY?: number; + rotation?: number; // Additional rotation (counter-clockwise) around the + // rect center either in radians or in degrees. + rotationDegree?: number; + shiftX?: number; // Shift along the side of a rotated rect that was + // aligned with the X and Y axis before rotation + // respectively. The shift is relative to the length of + // corresponding side. For example, for a rect with size + // (0.4, 0.6), with shiftX = 0.5 and shiftY = -0.5 the + // rect is shifted along the two sides by 0.2 and -0.3 + // respectively. + shiftY?: number; + squareLong?: boolean; // Change the final transformed rect into a square + // that shares the same center and rotation with + // the rect, and with the side of the square equal + // to either the long or short side of the rect + // respectively. + squareShort?: boolean; +} +export interface TensorsToLandmarksConfig { + numLandmarks: number; + inputImageWidth: number; + inputImageHeight: number; + visibilityActivation: 'none'|'sigmoid'; + flipHorizontally?: boolean; + flipVertically?: boolean; + normalizeZ?: number; +} +export interface TensorsToSegmentationConfig { + activation: 'none'|'sigmoid'|'softmax'; +} +export interface SegmentationSmoothingConfig { + combineWithPreviousRatio: number; +} +export interface RefineLandmarksFromHeatmapConfig { + kernelSize?: number; + minConfidenceToRefine: number; +} +export interface VisibilitySmoothingConfig { + alpha: number; // Coefficient applied to a new value, and `1 - alpha` is + // applied to a stored value. Should be in [0, 1] range. The + // smaller the value, the smoother result and the bigger lag. +} +type AssignAverage = number[]; +export interface LandmarksRefinementConfig { + indexesMapping: + number[]; // Maps indexes of the given set of landmarks to indexes of the + // resulting set of landmarks. Should be non empty and contain + // the same amount of indexes as landmarks in the corresponding + // input. + zRefinement: 'none'|'copy'|AssignAverage; // Z refinement instructions. +} diff --git a/pose-detection/src/shared/calculators/interfaces/shape_interfaces.ts b/pose-detection/src/shared/calculators/interfaces/shape_interfaces.ts new file mode 100644 index 0000000000..ce1ee4ebac --- /dev/null +++ b/pose-detection/src/shared/calculators/interfaces/shape_interfaces.ts @@ -0,0 +1,64 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +import {Keypoint} from './common_interfaces'; + +/** + * A rectangle that contains center point, height, width and rotation info. + * Can be normalized or non-normalized. + */ +export interface Rect { + xCenter: number; + yCenter: number; + height: number; + width: number; + rotation?: number; +} + +export interface BoundingBox { + xMin: number; + yMin: number; + xMax: number; + yMax: number; + width: number; + height: number; +} + +export interface AnchorTensor { + x: tf.Tensor1D; + y: tf.Tensor1D; + w: tf.Tensor1D; + h: tf.Tensor1D; +} + +export interface LocationData { + boundingBox?: BoundingBox; + relativeBoundingBox?: BoundingBox; + relativeKeypoints?: Keypoint[]; +} + +export interface Detection { + score?: number[]; + locationData: LocationData; // Location data corresponding to all + // detected labels above. + label?: string[]; // i-th label or labelid has a score encoded by the i-th + // element in score. Either string or integer labels must + // be used but not both. + labelId?: number[]; + ind?: number; +} diff --git a/pose-detection/src/shared/calculators/is_video.ts b/pose-detection/src/shared/calculators/is_video.ts new file mode 100644 index 0000000000..76e12a69e9 --- /dev/null +++ b/pose-detection/src/shared/calculators/is_video.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {PixelInput} from './interfaces/common_interfaces'; + +export function isVideo(image: PixelInput): image is HTMLVideoElement { + return (image != null) && (image as HTMLVideoElement).currentTime != null; +} diff --git a/pose-detection/src/shared/calculators/keypoints_to_normalized_keypoints.ts b/pose-detection/src/shared/calculators/keypoints_to_normalized_keypoints.ts new file mode 100644 index 0000000000..5f2ab6b461 --- /dev/null +++ b/pose-detection/src/shared/calculators/keypoints_to_normalized_keypoints.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {ImageSize, Keypoint} from './interfaces/common_interfaces'; + +export function keypointsToNormalizedKeypoints( + keypoints: Keypoint[], imageSize: ImageSize): Keypoint[] { + return keypoints.map(keypoint => { + const normalizedKeypoint = { + ...keypoint, + x: keypoint.x / imageSize.width, + y: keypoint.y / imageSize.height + }; + + if (keypoint.z != null) { + // Scale z the same way as x (using image width). + keypoint.z = keypoint.z / imageSize.width; + } + + return normalizedKeypoint; + }); +} diff --git a/pose-detection/src/shared/calculators/landmarks_refinement.ts b/pose-detection/src/shared/calculators/landmarks_refinement.ts new file mode 100644 index 0000000000..0422ee8716 --- /dev/null +++ b/pose-detection/src/shared/calculators/landmarks_refinement.ts @@ -0,0 +1,144 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './interfaces/common_interfaces'; +import {LandmarksRefinementConfig} from './interfaces/config_interfaces'; + +function getNumberOfRefinedLandmarks(refinements: LandmarksRefinementConfig[]) { + // Gather all used indexes. + const indices: number[] = [].concat.apply( + [], refinements.map(refinement => refinement.indexesMapping)); + + if (indices.length === 0) { + throw new Error('There should be at least one landmark in indexes mapping'); + } + + let minIndex = indices[0], maxIndex = indices[0]; + + const uniqueIndices = new Set(indices); + + uniqueIndices.forEach(index => { + minIndex = Math.min(minIndex, index); + maxIndex = Math.max(maxIndex, index); + }); + + // Check that indxes start with 0 and there is no gaps between min and max + // indexes. + const numIndices = uniqueIndices.size; + + if (minIndex !== 0) { + throw new Error( + `Indexes are expected to start with 0 instead of ${minIndex}`); + } + + if (maxIndex + 1 !== numIndices) { + throw new Error(`Indexes should have no gaps but ${ + maxIndex - numIndices + 1} indexes are missing`); + } + + return numIndices; +} + +function refineXY( + indexesMapping: number[], landmarks: Keypoint[], + refinedLandmarks: Keypoint[]) { + for (let i = 0; i < landmarks.length; ++i) { + const landmark = landmarks[i]; + const refinedLandmark = refinedLandmarks[indexesMapping[i]]; + refinedLandmark.x = landmark.x; + refinedLandmark.y = landmark.y; + } +} + +function getZAverage(landmarks: Keypoint[], indexes: number[]) { + let zSum = 0; + for (let i = 0; i < indexes.length; ++i) { + zSum += landmarks[indexes[i]].z; + } + return zSum / indexes.length; +} + +function refineZ( + indexesMapping: number[], + zRefinement: LandmarksRefinementConfig['zRefinement'], + landmarks: Keypoint[], refinedLandmarks: Keypoint[]) { + if (typeof zRefinement === 'string') { + switch (zRefinement) { + case 'copy': { + for (let i = 0; i < landmarks.length; ++i) { + refinedLandmarks[indexesMapping[i]].z = landmarks[i].z; + } + + break; + } + case 'none': + default: { + // Do nothing and keep Z that is already in refined landmarks. + break; + } + } + } else { + const zAverage = getZAverage(refinedLandmarks, zRefinement); + for (let i = 0; i < indexesMapping.length; ++i) { + refinedLandmarks[indexesMapping[i]].z = zAverage; + } + } +} + +/** + * Refine one set of landmarks with another. + * + * @param allLandmarks List of landmarks to use for refinement. They will be + * applied to the output in the provided order. Each list should be non + * empty and contain the same amount of landmarks as indexes in mapping. + * @param refinements Refinement instructions for input landmarks. + * + * @returns A list of refined landmarks. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmarks_refinement_calculator.cc +export function landmarksRefinement( + allLandmarks: Keypoint[][], + refinements: LandmarksRefinementConfig[]): Keypoint[] { + // Initialize refined landmarks list. + const numRefinedLandmarks = getNumberOfRefinedLandmarks(refinements); + const refinedLandmarks: Keypoint[] = + new Array(numRefinedLandmarks).fill(null).map(Object); + // Apply input landmarks to output refined landmarks in provided order. + for (let i = 0; i < allLandmarks.length; ++i) { + const landmarks = allLandmarks[i]; + const refinement = refinements[i]; + + if (landmarks.length !== refinement.indexesMapping.length) { + // Check number of landmarks in mapping and stream are the same. + throw new Error(`There are ${ + landmarks.length} refinement landmarks while mapping has ${ + refinement.indexesMapping.length}`); + } + + // Refine X and Y. + refineXY(refinement.indexesMapping, landmarks, refinedLandmarks); + + // Refine Z. + refineZ( + refinement.indexesMapping, refinement.zRefinement, landmarks, + refinedLandmarks); + // Visibility and presence are not currently refined and are left as `0`. + } + + return refinedLandmarks; +} diff --git a/pose-detection/src/shared/calculators/landmarks_to_detection.ts b/pose-detection/src/shared/calculators/landmarks_to_detection.ts new file mode 100644 index 0000000000..d2478cd2eb --- /dev/null +++ b/pose-detection/src/shared/calculators/landmarks_to_detection.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './interfaces/common_interfaces'; +import {Detection} from './interfaces/shape_interfaces'; + +/** + * Converts normalized Landmark to `Detection`. A relative bounding box will + * be created containing all landmarks exactly. + * @param landmarks List of normalized landmarks. + * + * @returns A `Detection`. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmarks_to_detection_calculator.cc +export function landmarksToDetection(landmarks: Keypoint[]): Detection { + const detection: + Detection = {locationData: {relativeKeypoints: [] as Keypoint[]}}; + + let xMin = Number.MAX_SAFE_INTEGER; + let xMax = Number.MIN_SAFE_INTEGER; + let yMin = Number.MAX_SAFE_INTEGER; + let yMax = Number.MIN_SAFE_INTEGER; + + for (let i = 0; i < landmarks.length; ++i) { + const landmark = landmarks[i]; + xMin = Math.min(xMin, landmark.x); + xMax = Math.max(xMax, landmark.x); + yMin = Math.min(yMin, landmark.y); + yMax = Math.max(yMax, landmark.y); + + detection.locationData.relativeKeypoints.push( + {x: landmark.x, y: landmark.y} as Keypoint); + } + + detection.locationData.relativeBoundingBox = + {xMin, yMin, xMax, yMax, width: (xMax - xMin), height: (yMax - yMin)}; + + return detection; +} diff --git a/pose-detection/src/shared/calculators/mask_util.ts b/pose-detection/src/shared/calculators/mask_util.ts new file mode 100644 index 0000000000..88a6aa6294 --- /dev/null +++ b/pose-detection/src/shared/calculators/mask_util.ts @@ -0,0 +1,106 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +function toNumber(value: number|SVGAnimatedLength) { + return value instanceof SVGAnimatedLength ? value.baseVal.value : value; +} + +/** + * Converts input image to an HTMLCanvasElement. Note that converting + * back from the output of this function to imageData or a Tensor will be lossy + * due to premultiplied alpha color values. For more details please reference: + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData#data_loss_due_to_browser_optimization + * @param image Input image. + * + * @returns Converted HTMLCanvasElement. + */ +export async function toHTMLCanvasElementLossy( + image: ImageData|tf.Tensor2D|tf.Tensor3D|SVGImageElement| + OffscreenCanvas): Promise { + const canvas = document.createElement('canvas'); + + if (image instanceof tf.Tensor) { + await tf.browser.toPixels(image, canvas); + } else { + canvas.width = toNumber(image.width); + canvas.height = toNumber(image.height); + + const ctx = canvas.getContext('2d'); + if (image instanceof ImageData) { + ctx.putImageData(image, 0, 0); + } else { + ctx.drawImage(image, 0, 0); + } + } + + return canvas; +} + +/** + * Converts input image to ImageData. Note that converting + * from a CanvasImageSource will be lossy due to premultiplied alpha color + * values. For more details please reference: + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData#data_loss_due_to_browser_optimization + * @param image Input image. + * + * @returns Converted ImageData. + */ +export async function toImageDataLossy(image: CanvasImageSource| + tf.Tensor3D): Promise { + if (image instanceof tf.Tensor) { + const [height, width] = image.shape.slice(0, 2); + return new ImageData(await tf.browser.toPixels(image), width, height); + } else { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + canvas.width = toNumber(image.width); + canvas.height = toNumber(image.height); + + ctx.drawImage(image, 0, 0); + return ctx.getImageData(0, 0, canvas.width, canvas.height); + } +} + +/** + * Converts input image to Tensor. Note that converting + * from a CanvasImageSource will be lossy due to premultiplied alpha color + * values. For more details please reference: + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData#data_loss_due_to_browser_optimization + * @param image Input image. + * + * @returns Converted Tensor. + */ +export async function toTensorLossy(image: CanvasImageSource| + ImageData): Promise { + const pixelsInput = + (image instanceof SVGImageElement || image instanceof OffscreenCanvas) ? + await toHTMLCanvasElementLossy(image) : + image; + return tf.browser.fromPixels(pixelsInput, 4); +} + +export function assertMaskValue(maskValue: number) { + if (maskValue < 0 || maskValue >= 256) { + throw new Error( + `Mask value must be in range [0, 255] but got ${maskValue}`); + } + if (!Number.isInteger(maskValue)) { + throw new Error(`Mask value must be an integer but got ${maskValue}`); + } +} diff --git a/pose-detection/src/shared/calculators/mask_util_test.ts b/pose-detection/src/shared/calculators/mask_util_test.ts new file mode 100644 index 0000000000..b38de8dd4d --- /dev/null +++ b/pose-detection/src/shared/calculators/mask_util_test.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {tensor3d} from '@tensorflow/tfjs-core'; +// tslint:disable-next-line: no-imports-from-dist +import {expectArraysEqual} from '@tensorflow/tfjs-core/dist/test_util'; + +import * as maskUtil from './mask_util'; + +describe('maskUtil', () => { + const rgbaValues: number[] = Array.from(new Array(24).keys()); + const expectedExact = rgbaValues; + const expectedLossy = [ + 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 11, + 17, 17, 17, 15, 13, 13, 13, 19, 22, 22, 22, 23 + ]; + it('ImageData to HTMLCanvasElement.', async () => { + const imageData = new ImageData(new Uint8ClampedArray(rgbaValues), 2, 3); + const canvas = await maskUtil.toHTMLCanvasElementLossy(imageData); + + expect(canvas.width).toBe(imageData.width); + expect(canvas.height).toBe(imageData.height); + + const actual = + Array.from(canvas.getContext('2d') + .getImageData(0, 0, canvas.width, canvas.height) + .data); + expectArraysEqual(actual, expectedLossy); + }); + + it('Tensor to HTMLCanvasElement.', async () => { + const tensor = tensor3d(rgbaValues, [2, 3, 4], 'int32'); + const [height, width] = tensor.shape.slice(0, 2); + const canvas = await maskUtil.toHTMLCanvasElementLossy(tensor); + + expect(canvas.width).toBe(width); + expect(canvas.height).toBe(height); + + const actual = + Array.from(canvas.getContext('2d') + .getImageData(0, 0, canvas.width, canvas.height) + .data); + expectArraysEqual(actual, expectedLossy); + }); + + it('HTMLCanvasElement to ImageData.', async () => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + canvas.width = 2; + canvas.height = 3; + ctx.putImageData( + new ImageData(new Uint8ClampedArray(rgbaValues), 2, 3), 0, 0); + + const imageData = await maskUtil.toImageDataLossy(canvas); + + expect(imageData.width).toBe(canvas.width); + expect(imageData.height).toBe(canvas.height); + + const actual = Array.from(imageData.data); + expectArraysEqual(actual, expectedLossy); + }); + + it('Tensor to ImageData.', async () => { + const tensor = tensor3d(rgbaValues, [2, 3, 4], 'int32'); + const [height, width] = tensor.shape.slice(0, 2); + const imageData = await maskUtil.toImageDataLossy(tensor); + + expect(imageData.width).toBe(width); + expect(imageData.height).toBe(height); + + const actual = Array.from(imageData.data); + expectArraysEqual(actual, expectedExact); + }); + + it('HTMLCanvasElement to Tensor.', async () => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + canvas.width = 2; + canvas.height = 3; + ctx.putImageData( + new ImageData(new Uint8ClampedArray(rgbaValues), 2, 3), 0, 0); + + const tensor = await maskUtil.toTensorLossy(canvas); + const [height, width] = tensor.shape.slice(0, 2); + + expect(width).toBe(canvas.width); + expect(height).toBe(canvas.height); + + expectArraysEqual(tensor.dataSync(), expectedLossy); + }); + + it('ImageData to Tensor.', async () => { + const imageData = new ImageData(new Uint8ClampedArray(rgbaValues), 2, 3); + + const tensor = await maskUtil.toTensorLossy(imageData); + const [height, width] = tensor.shape.slice(0, 2); + + expect(width).toBe(imageData.width); + expect(height).toBe(imageData.height); + + expectArraysEqual(tensor.dataSync(), expectedExact); + }); + + it('assertMaskValue.', async () => { + expect(() => maskUtil.assertMaskValue(-1)) + .toThrowError(/Mask value must be in range \[0, 255\]/); + expect(() => maskUtil.assertMaskValue(256)) + .toThrowError(/Mask value must be in range \[0, 255\]/); + expect(() => maskUtil.assertMaskValue(1.1)) + .toThrowError(/must be an integer/); + + const uint8Values = Array.from(Array(256).keys()); + uint8Values.forEach( + value => expect(() => maskUtil.assertMaskValue(value)).not.toThrow()); + }); +}); diff --git a/pose-detection/src/shared/calculators/non_max_suppression.ts b/pose-detection/src/shared/calculators/non_max_suppression.ts new file mode 100644 index 0000000000..ea7bd92598 --- /dev/null +++ b/pose-detection/src/shared/calculators/non_max_suppression.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {Detection} from './interfaces/shape_interfaces'; + +export async function nonMaxSuppression( + detections: Detection[], maxDetections: number, iouThreshold: number, + // Currently only IOU overap is supported. + overlapType: 'intersection-over-union'): Promise { + // Sort to match NonMaxSuppresion calculator's decreasing detection score + // traversal. + // NonMaxSuppresionCalculator: RetainMaxScoringLabelOnly + detections.sort( + (detectionA, detectionB) => + Math.max(...detectionB.score) - Math.max(...detectionA.score)); + + const detectionsTensor = tf.tensor2d(detections.map( + d => + [d.locationData.relativeBoundingBox.yMin, + d.locationData.relativeBoundingBox.xMin, + d.locationData.relativeBoundingBox.yMax, + d.locationData.relativeBoundingBox.xMax])); + const scoresTensor = tf.tensor1d(detections.map(d => d.score[0])); + + const selectedIdsTensor = await tf.image.nonMaxSuppressionAsync( + detectionsTensor, scoresTensor, maxDetections, iouThreshold); + const selectedIds = await selectedIdsTensor.array(); + + const selectedDetections = + detections.filter((_, i) => (selectedIds.indexOf(i) > -1)); + + tf.dispose([detectionsTensor, scoresTensor, selectedIdsTensor]); + + return selectedDetections; +} diff --git a/pose-detection/src/shared/calculators/non_max_suppression_test.ts b/pose-detection/src/shared/calculators/non_max_suppression_test.ts new file mode 100644 index 0000000000..f496126ba4 --- /dev/null +++ b/pose-detection/src/shared/calculators/non_max_suppression_test.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Detection} from './interfaces/shape_interfaces'; +import {nonMaxSuppression} from './non_max_suppression'; + +function createDetection( + label: string, score: number, xMin: number, yMin: number, + size: number): Detection { + return { + locationData: { + relativeBoundingBox: { + xMin, + yMin, + width: size, + height: size, + xMax: xMin + size, + yMax: yMin + size + }, + }, + label: [label], + score: [score] + }; +} + +describe('NonMaxSuppression', () => { + it('IntersectionOverUnion.', async () => { + const inputDetections = [ + createDetection('obj1', 1, 0, 0, 10), + createDetection('obj2', 1.2, 2, 2, 10), + createDetection('obj3', 1, 15, 15, 10), + createDetection('obj4', 2, 40, 40, 10) + ]; + const outputDetections = await nonMaxSuppression( + inputDetections.slice(), 2, 0.2, 'intersection-over-union'); + + expect(outputDetections.length).toBe(2); + expect(outputDetections[0]).toBe(inputDetections[3]); + expect(outputDetections[1]).toBe(inputDetections[1]); + }); +}); diff --git a/pose-detection/src/shared/calculators/normalized_keypoints_to_keypoints.ts b/pose-detection/src/shared/calculators/normalized_keypoints_to_keypoints.ts new file mode 100644 index 0000000000..c12464bacd --- /dev/null +++ b/pose-detection/src/shared/calculators/normalized_keypoints_to_keypoints.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {ImageSize, Keypoint} from './interfaces/common_interfaces'; + +export function normalizedKeypointsToKeypoints( + normalizedKeypoints: Keypoint[], imageSize: ImageSize): Keypoint[] { + return normalizedKeypoints.map(normalizedKeypoint => { + const keypoint = { + ...normalizedKeypoint, + x: normalizedKeypoint.x * imageSize.width, + y: normalizedKeypoint.y * imageSize.height + }; + + if (normalizedKeypoint.z != null) { + // Scale z the same way as x (using image width). + keypoint.z = normalizedKeypoint.z * imageSize.width; + } + + return keypoint; + }); +} diff --git a/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap.ts b/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap.ts new file mode 100644 index 0000000000..64d55c1557 --- /dev/null +++ b/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap.ts @@ -0,0 +1,104 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +import {Keypoint} from './interfaces/common_interfaces'; + +import {RefineLandmarksFromHeatmapConfig} from './interfaces/config_interfaces'; + +/** + * A calculator that refines landmarks using corresponding heatmap area. + * + * High level algorithm + * For each landmark, we replace original value with a value calculated from the + * area in heatmap close to original landmark position (the area is defined by + * config.kernelSize). To calculate new coordinate from heatmap we calculate an + * weighted average inside the kernel. We update the landmark if heatmap is + * confident in it's prediction i.e. max(heatmap) in kernel is at least bigger + * than config.minConfidenceToRefine. + * @param landmarks List of lardmarks to refine. + * @param heatmapTensor The heatmap for the landmarks with shape + * [height, width, channel]. The channel dimension has to be the same as + * the number of landmarks. + * @param config The config for refineLandmarksFromHeap, + * see `RefineLandmarksFromHeatmapConfig` for detail. + * + * @returns Normalized landmarks. + */ +export async function refineLandmarksFromHeatmap( + landmarks: Keypoint[], heatmapTensor: tf.Tensor4D, + config: RefineLandmarksFromHeatmapConfig): Promise { + // tslint:disable-next-line: no-unnecessary-type-assertion + const $heatmapTensor = tf.squeeze(heatmapTensor, [0]) as tf.Tensor3D; + const [hmHeight, hmWidth, hmChannels] = $heatmapTensor.shape; + if (landmarks.length !== hmChannels) { + throw new Error( + 'Expected heatmap to have same number of channels ' + + 'as the number of landmarks. But got landmarks length: ' + + `${landmarks.length}, heatmap length: ${hmChannels}`); + } + + const outLandmarks = []; + const heatmapBuf = await $heatmapTensor.buffer(); + + for (let i = 0; i < landmarks.length; i++) { + const landmark = landmarks[i]; + const outLandmark = {...landmark}; + outLandmarks.push(outLandmark); + + const centerCol = Math.trunc(outLandmark.x * hmWidth); + const centerRow = Math.trunc(outLandmark.y * hmHeight); + // Point is outside of the image let's keep it intact. + if (centerCol < 0 || centerCol >= hmWidth || centerRow < 0 || + centerCol >= hmHeight) { + continue; + } + + const offset = Math.trunc((config.kernelSize - 1) / 2); + // Calculate area to iterate over. Note that we decrease the kernel on the + // edges of the heatmap. Equivalent to zero border. + const beginCol = Math.max(0, centerCol - offset); + const endCol = Math.min(hmWidth, centerCol + offset + 1); + const beginRow = Math.max(0, centerRow - offset); + const endRow = Math.min(hmHeight, centerRow + offset + 1); + + let sum = 0; + let weightedCol = 0; + let weightedRow = 0; + let maxValue = 0; + + // Main loop. Go over kernel and calculate weighted sum of coordinates, + // sum of weights and max weights. + for (let row = beginRow; row < endRow; ++row) { + for (let col = beginCol; col < endCol; ++col) { + const confidence = heatmapBuf.get(row, col, i); + sum += confidence; + maxValue = Math.max(maxValue, confidence); + weightedCol += col * confidence; + weightedRow += row * confidence; + } + } + if (maxValue >= config.minConfidenceToRefine && sum > 0) { + outLandmark.x = weightedCol / hmWidth / sum; + outLandmark.y = weightedRow / hmHeight / sum; + } + } + + $heatmapTensor.dispose(); + + return outLandmarks; +} diff --git a/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap_test.ts b/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap_test.ts new file mode 100644 index 0000000000..3a2cd438d4 --- /dev/null +++ b/pose-detection/src/shared/calculators/refine_landmarks_from_heatmap_test.ts @@ -0,0 +1,104 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +import {refineLandmarksFromHeatmap} from './refine_landmarks_from_heatmap'; + +function chwToHWC( + chw: number[], height: number, width: number, depth: number): number[] { + let idx = 0; + const hwc = []; + for (let c = 0; c < depth; ++c) { + for (let row = 0; row < height; ++row) { + for (let col = 0; col < width; ++col) { + const destIdx = width * depth * row + depth * col + c; + hwc[destIdx] = chw[idx]; + idx++; + } + } + } + return hwc; +} + +describe('refineLandmarksFromHeatmap ', () => { + const z = -10000000000000000; + + it('smoke.', async () => { + const landmarks = [{x: 0.5, y: 0.5}]; + const heatmapTensor = + tf.sigmoid(tf.tensor4d([z, z, z, 1, z, z, z, z, z], [1, 3, 3, 1])); + + const result = await refineLandmarksFromHeatmap( + landmarks, heatmapTensor, {kernelSize: 3, minConfidenceToRefine: 0.1}); + expect(result[0].x).toBe(0); + expect(result[0].y).toBe(1 / 3); + }); + + it('multi-layer.', async () => { + const landmarks = [{x: 0.5, y: 0.5}, {x: 0.5, y: 0.5}, {x: 0.5, y: 0.5}]; + const heatmapTensor = tf.sigmoid(tf.tensor4d( + chwToHWC( + [ + z, z, z, 1, z, z, z, z, z, z, z, z, 1, z, + z, z, z, z, z, z, z, 1, z, z, z, z, z + ], + 3, 3, 3), + [1, 3, 3, 3])); + const result = await refineLandmarksFromHeatmap( + landmarks, heatmapTensor, {kernelSize: 3, minConfidenceToRefine: 0.1}); + + for (let i = 0; i < 3; i++) { + expect(result[i].x).toBe(0); + expect(result[i].y).toBe(1 / 3); + } + }); + + it('keep if not sure.', async () => { + const landmarks = [{x: 0.5, y: 0.5}, {x: 0.5, y: 0.5}, {x: 0.5, y: 0.5}]; + const heatmapTensor = tf.sigmoid(tf.tensor4d( + chwToHWC( + [ + z, z, z, 0, z, z, z, z, z, z, z, z, 0, z, + z, z, z, z, z, z, z, 0, z, z, z, z, z + ], + 3, 3, 3), + [1, 3, 3, 3])); + const result = await refineLandmarksFromHeatmap( + landmarks, heatmapTensor, {kernelSize: 3, minConfidenceToRefine: 0.6}); + + for (let i = 0; i < 3; i++) { + expect(result[i].x).toBe(0.5); + expect(result[i].y).toBe(0.5); + } + }); + + it('border.', async () => { + const landmarks = [{x: 0, y: 0}, {x: 0.9, y: 0.9}]; + const heatmapTensor = tf.sigmoid(tf.tensor4d( + chwToHWC( + [z, z, z, 0, z, 0, z, z, z, z, z, z, 0, z, 0, z, z, 0], 3, 3, 2), + [1, 3, 3, 2])); + + const result = await refineLandmarksFromHeatmap( + landmarks, heatmapTensor, {kernelSize: 3, minConfidenceToRefine: 0.1}); + + expect(result[0].x).toBe(0); + expect(result[0].y).toBe(1 / 3); + expect(result[1].x).toBe(2 / 3); + expect(result[1].y).toBe(1 / 6 + 2 / 6); + }); +}); diff --git a/pose-detection/src/shared/calculators/remove_detection_letterbox.ts b/pose-detection/src/shared/calculators/remove_detection_letterbox.ts new file mode 100644 index 0000000000..69430ed9ac --- /dev/null +++ b/pose-detection/src/shared/calculators/remove_detection_letterbox.ts @@ -0,0 +1,69 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Padding} from './interfaces/common_interfaces'; +import {Detection} from './interfaces/shape_interfaces'; + +/** + * Adjusts detection locations on the letterboxed image to the corresponding + * locations on the same image with the letterbox removed (the input image to + * the graph before image transformation). + * + * @param detections A list of detection boxes on an letterboxed image. + * @param letterboxPadding A `padding` object representing the letterbox padding + * from the 4 sides: left, top, right, bottom, of the letterboxed image, + * normalized by the letterboxed image dimensions. + * @returns detections: A list of detection boxes representing detections with + * their locations adjusted to the letterbox-removed (non-padded) image. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/detection_letterbox_removal_calculator.cc +export function removeDetectionLetterbox( + detections: Detection[] = [], letterboxPadding: Padding): Detection[] { + const left = letterboxPadding.left; + const top = letterboxPadding.top; + const leftAndRight = letterboxPadding.left + letterboxPadding.right; + const topAndBottom = letterboxPadding.top + letterboxPadding.bottom; + + for (let i = 0; i < detections.length; i++) { + const detection = detections[i]; + const relativeBoundingBox = detection.locationData.relativeBoundingBox; + const xMin = (relativeBoundingBox.xMin - left) / (1 - leftAndRight); + const yMin = (relativeBoundingBox.yMin - top) / (1 - topAndBottom); + const width = relativeBoundingBox.width / (1 - leftAndRight); + const height = relativeBoundingBox.height / (1 - topAndBottom); + relativeBoundingBox.xMin = xMin; + relativeBoundingBox.yMin = yMin; + relativeBoundingBox.width = width; + relativeBoundingBox.height = height; + relativeBoundingBox.xMax = xMin + width; + relativeBoundingBox.yMax = yMin + height; + + const relativeKeypoints = detection.locationData.relativeKeypoints; + + if (relativeKeypoints) { + relativeKeypoints.forEach(keypoint => { + const newX = (keypoint.x - left) / (1 - leftAndRight); + const newY = (keypoint.y - top) / (1 - topAndBottom); + keypoint.x = newX; + keypoint.y = newY; + }); + } + } + + return detections; +} diff --git a/pose-detection/src/shared/calculators/remove_detection_letterbox_test.ts b/pose-detection/src/shared/calculators/remove_detection_letterbox_test.ts new file mode 100644 index 0000000000..23b37ba7fe --- /dev/null +++ b/pose-detection/src/shared/calculators/remove_detection_letterbox_test.ts @@ -0,0 +1,116 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +// tslint:disable-next-line: no-imports-from-dist +import {expectNumbersClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {Detection, LocationData} from './interfaces/shape_interfaces'; +import {removeDetectionLetterbox} from './remove_detection_letterbox'; + +const EPS = 1e-5; + +function createRelativeLocationData( + xMin: number, yMin: number, width: number, height: number) { + return { + relativeBoundingBox: + {xMin, yMin, width, height, xMax: xMin + width, yMax: yMin + height} + }; +} + +function createDetection( + labels: string[], labelIds: number[], scores: number[], + locationData: LocationData) { + const detection: Detection = + {label: labels, score: scores, labelId: labelIds, locationData}; + return detection; +} + +describe('DetectionLetterboxRemovalCalculator', () => { + const locationData = createRelativeLocationData(0.25, 0.25, 0.25, 0.25); + const label = 'detected_object'; + const detections = [createDetection([label], [], [0.3], locationData)]; + const keys = ['xMin', 'yMin', 'width', 'height', 'xMax', 'yMax'] as const ; + + it('padding left right.', async () => { + const locationData = createRelativeLocationData(0.25, 0.25, 0.25, 0.25); + const label = 'detected_object'; + const detections = [createDetection([label], [], [0.3], locationData)]; + + const padding = {left: 0.2, top: 0, right: 0.3, bottom: 0}; + + const outputDetections = removeDetectionLetterbox(detections, padding); + + expect(outputDetections.length).toBe(1); + const outputDetection = outputDetections[0]; + + expect(outputDetection.label.length).toBe(1); + expect(outputDetection.label[0]).toBe(label); + expect(outputDetection.labelId.length).toBe(0); + expect(outputDetection.score.length).toBe(1); + expect(outputDetection.score[0]).toBe(0.3); + + const expectedLocationData: LocationData = { + relativeBoundingBox: { + xMin: 0.1, + yMin: 0.25, + width: 0.5, + height: 0.25, + xMax: 0.6, + yMax: 0.5 + } + }; + + for (const key of ['xMin', 'yMin', 'width', 'height', 'xMax', 'yMax'] as + const ) { + expectNumbersClose( + outputDetection.locationData.relativeBoundingBox[key], + expectedLocationData.relativeBoundingBox[key], EPS); + } + }); + + it('padding top bottom.', async () => { + const padding = {left: 0, top: 0.2, right: 0, bottom: 0.3}; + + const outputDetections = removeDetectionLetterbox(detections, padding); + + expect(outputDetections.length).toBe(1); + const outputDetection = outputDetections[0]; + + expect(outputDetection.label.length).toBe(1); + expect(outputDetection.label[0]).toBe(label); + expect(outputDetection.labelId.length).toBe(0); + expect(outputDetection.score.length).toBe(1); + expect(outputDetection.score[0]).toBe(0.3); + + const expectedLocationData: LocationData = { + relativeBoundingBox: { + xMin: 0.25, + yMin: 0.1, + width: 0.25, + height: 0.5, + xMax: 0.5, + yMax: 0.6 + } + }; + + for (const key of keys) { + expectNumbersClose( + outputDetection.locationData.relativeBoundingBox[key], + expectedLocationData.relativeBoundingBox[key], EPS); + } + }); +}); diff --git a/pose-detection/src/shared/calculators/remove_landmark_letterbox.ts b/pose-detection/src/shared/calculators/remove_landmark_letterbox.ts new file mode 100644 index 0000000000..e15675d11d --- /dev/null +++ b/pose-detection/src/shared/calculators/remove_landmark_letterbox.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint, Padding} from './interfaces/common_interfaces'; + +/** + * Adjusts landmark locations on a letterboxed image to the corresponding + * locations on the same image with the letterbox removed. + * @param rawLandmark A NormalizedLandmarkList representing landmarks on an + * letterboxed image. + * @param padding A `padding` representing the letterbox padding from the 4 + * sides, left, top, right, bottom, of the letterboxed image, normalized by + * the letterboxed image dimensions. + * @returns Normalized landmarks. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmark_letterbox_removal_calculator.cc +export function removeLandmarkLetterbox( + rawLandmark: Keypoint[], padding: Padding) { + const left = padding.left; + const top = padding.top; + const leftAndRight = padding.left + padding.right; + const topAndBottom = padding.top + padding.bottom; + + const outLandmarks = rawLandmark.map(landmark => { + return { + ...landmark, + x: (landmark.x - left) / (1 - leftAndRight), + y: (landmark.y - top) / (1 - topAndBottom), + z: landmark.z / (1 - leftAndRight) // Scale Z coordinate as X. + }; + }); + + return outLandmarks; +} diff --git a/pose-detection/src/shared/calculators/remove_landmark_letterbox_test.ts b/pose-detection/src/shared/calculators/remove_landmark_letterbox_test.ts new file mode 100644 index 0000000000..bbed81bfff --- /dev/null +++ b/pose-detection/src/shared/calculators/remove_landmark_letterbox_test.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +// tslint:disable-next-line: no-imports-from-dist +import {expectArraysClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {removeLandmarkLetterbox} from './remove_landmark_letterbox'; + +const EPS = 1e-5; + +describe('LandmarkLetterboxRemovalCalculator', () => { + const keypoints = [{x: 0.5, y: 0.5}, {x: 0.2, y: 0.2}, {x: 0.7, y: 0.7}]; + + it('padding left right.', async () => { + const padding = {left: 0.2, top: 0, right: 0.3, bottom: 0}; + + const outputKeypoints = removeLandmarkLetterbox(keypoints, padding); + expect(outputKeypoints.length).toBe(3); + + expectArraysClose( + outputKeypoints.map(keypoint => [keypoint.x, keypoint.y]), + [[0.6, 0.5], [0.0, 0.2], [1, 0.7]], EPS); + }); + + it('padding top bottom.', async () => { + const padding = {left: 0, top: 0.2, right: 0, bottom: 0.3}; + + const outputKeypoints = removeLandmarkLetterbox(keypoints, padding); + expect(outputKeypoints.length).toBe(3); + + expectArraysClose( + outputKeypoints.map(keypoint => [keypoint.x, keypoint.y]), + [[0.5, 0.6], [0.2, 0], [0.7, 1]], EPS); + }); +}); diff --git a/pose-detection/src/shared/calculators/render_util.ts b/pose-detection/src/shared/calculators/render_util.ts new file mode 100644 index 0000000000..9c72384c27 --- /dev/null +++ b/pose-detection/src/shared/calculators/render_util.ts @@ -0,0 +1,739 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import * as tf from '@tensorflow/tfjs-core'; +import {Color, PixelInput, Segmentation} from './interfaces/common_interfaces'; + +/** + * This render_util implementation is based on the body-pix output_rending_util + * code found here: + * https://github.com/tensorflow/tfjs-models/blob/master/body-pix/src/output_rendering_util.ts + * It is adapted to account for the generic segmentation interface. + */ + +type ImageType = CanvasImageSource|OffscreenCanvas|PixelInput; +type HasDimensions = { + width: number, + height: number +}; + +type Canvas = HTMLCanvasElement|OffscreenCanvas; + +const CANVAS_NAMES = { + blurred: 'blurred', + blurredMask: 'blurred-mask', + mask: 'mask', + lowresPartMask: 'lowres-part-mask', + drawImage: 'draw-image', +}; + +const offScreenCanvases: {[name: string]: Canvas} = {}; + +function isSafari() { + return (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)); +} + +function assertSameDimensions( + {width: widthA, height: heightA}: HasDimensions, + {width: widthB, height: heightB}: HasDimensions, nameA: string, + nameB: string) { + if (widthA !== widthB || heightA !== heightB) { + throw new Error(`error: dimensions must match. ${nameA} has dimensions ${ + widthA}x${heightA}, ${nameB} has dimensions ${widthB}x${heightB}`); + } +} + +function getSizeFromImageLikeElement(input: HTMLImageElement|HTMLCanvasElement| + OffscreenCanvas): [number, number] { + if ('offsetHeight' in input && input.offsetHeight !== 0 && + 'offsetWidth' in input && input.offsetWidth !== 0) { + return [input.offsetHeight, input.offsetWidth]; + } else if (input.height != null && input.width != null) { + return [input.height, input.width]; + } else { + throw new Error( + `HTMLImageElement must have height and width attributes set.`); + } +} + +function getSizeFromVideoElement(input: HTMLVideoElement): [number, number] { + if (input.hasAttribute('height') && input.hasAttribute('width')) { + // Prioritizes user specified height and width. + // We can't test the .height and .width properties directly, + // because they evaluate to 0 if unset. + return [input.height, input.width]; + } else { + return [input.videoHeight, input.videoWidth]; + } +} + +function getInputSize(input: ImageType): [number, number] { + if ((typeof (HTMLCanvasElement) !== 'undefined' && + input instanceof HTMLCanvasElement) || + (typeof (OffscreenCanvas) !== 'undefined' && + input instanceof OffscreenCanvas) || + (typeof (HTMLImageElement) !== 'undefined' && + input instanceof HTMLImageElement)) { + return getSizeFromImageLikeElement(input); + } else if (typeof (ImageData) !== 'undefined' && input instanceof ImageData) { + return [input.height, input.width]; + } else if ( + typeof (HTMLVideoElement) !== 'undefined' && + input instanceof HTMLVideoElement) { + return getSizeFromVideoElement(input); + } else if (input instanceof tf.Tensor) { + return [input.shape[0], input.shape[1]]; + } else { + throw new Error(`error: Unknown input type: ${input}.`); + } +} + +function createOffScreenCanvas(): Canvas { + if (typeof document !== 'undefined') { + return document.createElement('canvas'); + } else if (typeof OffscreenCanvas !== 'undefined') { + return new OffscreenCanvas(0, 0); + } else { + throw new Error('Cannot create a canvas in this context'); + } +} + +function ensureOffscreenCanvasCreated(id: string): Canvas { + if (!offScreenCanvases[id]) { + offScreenCanvases[id] = createOffScreenCanvas(); + } + return offScreenCanvases[id]; +} + +/** + * Draw image data on a canvas. + */ +function renderImageDataToCanvas(image: ImageData, canvas: Canvas) { + canvas.width = image.width; + canvas.height = image.height; + const ctx = canvas.getContext('2d'); + + ctx.putImageData(image, 0, 0); +} + +function renderImageDataToOffScreenCanvas( + image: ImageData, canvasName: string): Canvas { + const canvas = ensureOffscreenCanvasCreated(canvasName); + renderImageDataToCanvas(image, canvas); + + return canvas; +} + +/** + * Draw image on a 2D rendering context. + */ +async function drawImage( + ctx: CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, + image: ImageType, dx: number, dy: number, dw?: number, dh?: number) { + if (image instanceof tf.Tensor) { + const pixels = await tf.browser.toPixels(image); + const [height, width] = getInputSize(image); + image = new ImageData(pixels, width, height); + } + if (image instanceof ImageData) { + image = renderImageDataToOffScreenCanvas(image, CANVAS_NAMES.drawImage); + } + if (dw == null || dh == null) { + ctx.drawImage(image, dx, dy); + } else { + ctx.drawImage(image, dx, dy, dw, dh); + } +} + +/** + * Draw image on a canvas. + */ +async function renderImageToCanvas(image: ImageType, canvas: Canvas) { + const [height, width] = getInputSize(image); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + + await drawImage(ctx, image, 0, 0, width, height); +} + +function flipCanvasHorizontal(canvas: Canvas) { + const ctx = canvas.getContext('2d'); + ctx.scale(-1, 1); + ctx.translate(-canvas.width, 0); +} + +async function drawWithCompositing( + ctx: CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, + image: ImageType, compositeOperation: string) { + // TODO: Assert type 'compositeOperation as GlobalCompositeOperation' after + // typescript update to 4.6.0 or later + // tslint:disable-next-line: no-any + ctx.globalCompositeOperation = compositeOperation as any; + await drawImage(ctx, image, 0, 0); +} + +// method copied from bGlur in https://codepen.io/zhaojun/pen/zZmRQe +async function cpuBlur(canvas: Canvas, image: ImageType, blur: number) { + const ctx = canvas.getContext('2d'); + + let sum = 0; + const delta = 5; + const alphaLeft = 1 / (2 * Math.PI * delta * delta); + const step = blur < 3 ? 1 : 2; + for (let y = -blur; y <= blur; y += step) { + for (let x = -blur; x <= blur; x += step) { + const weight = + alphaLeft * Math.exp(-(x * x + y * y) / (2 * delta * delta)); + sum += weight; + } + } + for (let y = -blur; y <= blur; y += step) { + for (let x = -blur; x <= blur; x += step) { + ctx.globalAlpha = alphaLeft * + Math.exp(-(x * x + y * y) / (2 * delta * delta)) / sum * blur; + await drawImage(ctx, image, x, y); + } + } + ctx.globalAlpha = 1; +} + +async function drawAndBlurImageOnCanvas( + image: ImageType, blurAmount: number, canvas: Canvas) { + const [height, width] = getInputSize(image); + const ctx = canvas.getContext('2d'); + canvas.width = width; + canvas.height = height; + ctx.clearRect(0, 0, width, height); + ctx.save(); + if (isSafari()) { + await cpuBlur(canvas, image, blurAmount); + } else { + // tslint:disable:no-any + (ctx as any).filter = `blur(${blurAmount}px)`; + await drawImage(ctx, image, 0, 0, width, height); + } + ctx.restore(); +} + +async function drawAndBlurImageOnOffScreenCanvas( + image: ImageType, blurAmount: number, + offscreenCanvasName: string): Promise { + const canvas = ensureOffscreenCanvasCreated(offscreenCanvasName); + if (blurAmount === 0) { + await renderImageToCanvas(image, canvas); + } else { + await drawAndBlurImageOnCanvas(image, blurAmount, canvas); + } + return canvas; +} + +function drawStroke( + bytes: Uint8ClampedArray, row: number, column: number, width: number, + radius: number, color: Color = { + r: 0, + g: 255, + b: 255, + a: 255 + }) { + for (let i = -radius; i <= radius; i++) { + for (let j = -radius; j <= radius; j++) { + if (i !== 0 && j !== 0) { + const n = (row + i) * width + (column + j); + bytes[4 * n + 0] = color.r; + bytes[4 * n + 1] = color.g; + bytes[4 * n + 2] = color.b; + bytes[4 * n + 3] = color.a; + } + } + } +} + +function isSegmentationBoundary( + data: Uint8ClampedArray, + row: number, + column: number, + width: number, + isForegroundId: boolean[], + alphaCutoff: number, + radius = 1, + ): boolean { + let numberBackgroundPixels = 0; + for (let i = -radius; i <= radius; i++) { + for (let j = -radius; j <= radius; j++) { + if (i !== 0 && j !== 0) { + const n = (row + i) * width + (column + j); + if (!isForegroundId[data[4 * n]] || data[4 * n + 3] < alphaCutoff) { + numberBackgroundPixels += 1; + } + } + } + } + return numberBackgroundPixels > 0; +} + +/** + * Given a segmentation or array of segmentations, generates an + * image with foreground and background color at each pixel determined by the + * corresponding binary segmentation value at the pixel from the output. In + * other words, pixels where there is a person will be colored with foreground + * color and where there is not a person will be colored with background color. + * + * @param segmentation Single segmentation or array of segmentations. + * + * @param foreground Default to {r:0, g:0, b:0, a: 0}. The foreground color + * (r,g,b,a) for visualizing pixels that belong to people. + * + * @param background Default to {r:0, g:0, b:0, a: 255}. The background color + * (r,g,b,a) for visualizing pixels that don't belong to people. + * + * @param drawContour Default to false. Whether to draw the contour around each + * person's segmentation mask or body part mask. + * + * @param foregroundThreshold Default to 0.5. The minimum probability + * to color a pixel as foreground rather than background. The alpha channel + * integer values will be taken as the probabilities (for more information refer + * to `Segmentation` type's documentation). + * + * @param foregroundMaskValues Default to all mask values. The red channel + * integer values that represent foreground (for more information refer to + * `Segmentation` type's documentation). + * + * @returns An ImageData with the same width and height of + * the input segmentations, with opacity and + * transparency at each pixel determined by the corresponding binary + * segmentation value at the pixel from the output. + */ +export async function toBinaryMask( + segmentation: Segmentation|Segmentation[], foreground: Color = { + r: 0, + g: 0, + b: 0, + a: 0 + }, + background: Color = { + r: 0, + g: 0, + b: 0, + a: 255 + }, + drawContour = false, foregroundThreshold = 0.5, + foregroundMaskValues = Array.from(Array(256).keys())) { + const segmentations = + !Array.isArray(segmentation) ? [segmentation] : segmentation; + + if (segmentations.length === 0) { + return null; + } + + const masks = await Promise.all( + segmentations.map(segmentation => segmentation.mask.toImageData())); + + const {width, height} = masks[0]; + const bytes = new Uint8ClampedArray(width * height * 4); + const alphaCutoff = Math.round(255 * foregroundThreshold); + const isForegroundId: boolean[] = new Array(256).fill(false); + foregroundMaskValues.forEach(id => isForegroundId[id] = true); + + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + const n = i * width + j; + bytes[4 * n + 0] = background.r; + bytes[4 * n + 1] = background.g; + bytes[4 * n + 2] = background.b; + bytes[4 * n + 3] = background.a; + for (const mask of masks) { + if (isForegroundId[mask.data[4 * n]] && + mask.data[4 * n + 3] >= alphaCutoff) { + bytes[4 * n] = foreground.r; + bytes[4 * n + 1] = foreground.g; + bytes[4 * n + 2] = foreground.b; + bytes[4 * n + 3] = foreground.a; + if (drawContour && i - 1 >= 0 && i + 1 < height && j - 1 >= 0 && + j + 1 < width && + isSegmentationBoundary( + mask.data, i, j, width, isForegroundId, alphaCutoff)) { + drawStroke(bytes, i, j, width, 1); + } + } + } + } + } + + return new ImageData(bytes, width, height); +} + +/** + * Given a segmentation or array of segmentations, and a function mapping + * the red pixel values (representing body part labels) to colours, + * generates an image with the corresponding color for each part at each pixel, + * and background color used where there is no part. + * + * @param segmentation Single segmentation or array of segmentations. + * + * @param maskValueToColor A function mapping red channel mask values to + * colors to use in output image. + * + * @param background Default to {r:0, g:0, b:0, a: 255}. The background color + * (r,g,b,a) for visualizing pixels that don't belong to people. + * + * @param foregroundThreshold Default to 0.5. The minimum probability + * to color a pixel as foreground rather than background. The alpha channel + * integer values will be taken as the probabilities (for more information refer + * to `Segmentation` type's documentation). + * + * @returns An ImageData with the same width and height of input segmentations, + * with the corresponding color for each part at each pixel, and background + * pixels where there is no part. + */ +export async function toColoredMask( + segmentation: Segmentation|Segmentation[], + maskValueToColor: (maskValue: number) => Color, background: Color = { + r: 0, + g: 0, + b: 0, + a: 255 + }, + foregroundThreshold = 0.5) { + const segmentations = + !Array.isArray(segmentation) ? [segmentation] : segmentation; + + if (segmentations.length === 0) { + return null; + } + + const masks = await Promise.all( + segmentations.map(segmentation => segmentation.mask.toImageData())); + + const {width, height} = masks[0]; + const bytes = new Uint8ClampedArray(width * height * 4); + const alphaCutoff = Math.round(255 * foregroundThreshold); + + for (let i = 0; i < height * width; ++i) { + const j = i * 4; + bytes[j + 0] = background.r; + bytes[j + 1] = background.g; + bytes[j + 2] = background.b; + bytes[j + 3] = background.a; + for (const mask of masks) { + if (mask.data[j + 3] >= alphaCutoff) { + const maskValue = mask.data[j]; + const color = maskValueToColor(maskValue); + + bytes[j + 0] = color.r; + bytes[j + 1] = color.g; + bytes[j + 2] = color.b; + bytes[j + 3] = color.a; + } + } + } + + return new ImageData(bytes, width, height); +} + +/** + * Given an image and a maskImage of type ImageData, draws the image with the + * mask on top of it onto a canvas. + * + * @param canvas The canvas to be drawn onto. + * + * @param image The original image to apply the mask to. + * + * @param maskImage An ImageData containing the mask. Ideally this should be + * generated by toBinaryMask or toColoredMask. + * + * @param maskOpacity The opacity of the mask when drawing it on top of the + * image. Defaults to 0.7. Should be a float between 0 and 1. + * + * @param maskBlurAmount How many pixels to blur the mask by. Defaults to 0. + * Should be an integer between 0 and 20. + * + * @param flipHorizontal If the result should be flipped horizontally. Defaults + * to false. + */ +export async function drawMask( + canvas: Canvas, image: ImageType, maskImage: ImageData|null, + maskOpacity = 0.7, maskBlurAmount = 0, flipHorizontal = false) { + const [height, width] = getInputSize(image); + canvas.width = width; + canvas.height = height; + + const ctx = canvas.getContext('2d'); + ctx.save(); + if (flipHorizontal) { + flipCanvasHorizontal(canvas); + } + + await drawImage(ctx, image, 0, 0); + + ctx.globalAlpha = maskOpacity; + if (maskImage) { + assertSameDimensions({width, height}, maskImage, 'image', 'mask'); + + const mask = renderImageDataToOffScreenCanvas(maskImage, CANVAS_NAMES.mask); + + const blurredMask = await drawAndBlurImageOnOffScreenCanvas( + mask, maskBlurAmount, CANVAS_NAMES.blurredMask); + ctx.drawImage(blurredMask, 0, 0, width, height); + } + ctx.restore(); +} + +/** + * Given an image and a maskImage of type ImageData, draws the image with the + * pixelated mask on top of it onto a canvas. + * + * @param canvas The canvas to be drawn onto. + * + * @param image The original image to apply the mask to. + * + * @param maskImage An ImageData containing the mask. Ideally this should be + * generated by toColoredmask. + * + * @param maskOpacity The opacity of the mask when drawing it on top of the + * image. Defaults to 0.7. Should be a float between 0 and 1. + * + * @param maskBlurAmount How many pixels to blur the mask by. Defaults to 0. + * Should be an integer between 0 and 20. + * + * @param flipHorizontal If the result should be flipped horizontally. Defaults + * to false. + * + * @param pixelCellWidth The width of each pixel cell. Default to 10 px. + */ +export async function drawPixelatedMask( + canvas: Canvas, image: ImageType, maskImage: ImageData, maskOpacity = 0.7, + maskBlurAmount = 0, flipHorizontal = false, pixelCellWidth = 10.0) { + const [height, width] = getInputSize(image); + assertSameDimensions({width, height}, maskImage, 'image', 'mask'); + + const mask = renderImageDataToOffScreenCanvas(maskImage, CANVAS_NAMES.mask); + const blurredMask = await drawAndBlurImageOnOffScreenCanvas( + mask, maskBlurAmount, CANVAS_NAMES.blurredMask); + + canvas.width = blurredMask.width; + canvas.height = blurredMask.height; + + const ctx = canvas.getContext('2d'); + ctx.save(); + if (flipHorizontal) { + flipCanvasHorizontal(canvas); + } + + const offscreenCanvas = + ensureOffscreenCanvasCreated(CANVAS_NAMES.lowresPartMask); + const offscreenCanvasCtx = offscreenCanvas.getContext('2d'); + offscreenCanvas.width = blurredMask.width * (1.0 / pixelCellWidth); + offscreenCanvas.height = blurredMask.height * (1.0 / pixelCellWidth); + offscreenCanvasCtx.drawImage( + blurredMask, 0, 0, blurredMask.width, blurredMask.height, 0, 0, + offscreenCanvas.width, offscreenCanvas.height); + ctx.imageSmoothingEnabled = false; + ctx.drawImage( + offscreenCanvas, 0, 0, offscreenCanvas.width, offscreenCanvas.height, 0, + 0, canvas.width, canvas.height); + + // Draws vertical grid lines that are `pixelCellWidth` apart from each other. + for (let i = 0; i < offscreenCanvas.width; i++) { + ctx.beginPath(); + ctx.strokeStyle = '#ffffff'; + ctx.moveTo(pixelCellWidth * i, 0); + ctx.lineTo(pixelCellWidth * i, canvas.height); + ctx.stroke(); + } + + // Draws horizontal grid lines that are `pixelCellWidth` apart from each + // other. + for (let i = 0; i < offscreenCanvas.height; i++) { + ctx.beginPath(); + ctx.strokeStyle = '#ffffff'; + ctx.moveTo(0, pixelCellWidth * i); + ctx.lineTo(canvas.width, pixelCellWidth * i); + ctx.stroke(); + } + + ctx.globalAlpha = 1.0 - maskOpacity; + await drawImage(ctx, image, 0, 0, blurredMask.width, blurredMask.height); + ctx.restore(); +} + +async function createPersonMask( + segmentation: Segmentation|Segmentation[], foregroundThreshold: number, + edgeBlurAmount: number): Promise { + const backgroundMaskImage = await toBinaryMask( + segmentation, {r: 0, g: 0, b: 0, a: 255}, {r: 0, g: 0, b: 0, a: 0}, false, + foregroundThreshold); + + const backgroundMask = + renderImageDataToOffScreenCanvas(backgroundMaskImage, CANVAS_NAMES.mask); + if (edgeBlurAmount === 0) { + return backgroundMask; + } else { + return drawAndBlurImageOnOffScreenCanvas( + backgroundMask, edgeBlurAmount, CANVAS_NAMES.blurredMask); + } +} + +/** + * Given a segmentation or array of segmentations, and an image, draws the image + * with its background blurred onto the canvas. + * + * @param canvas The canvas to draw the background-blurred image onto. + * + * @param image The image to blur the background of and draw. + * + * @param segmentation Single segmentation or array of segmentations. + * + * @param foregroundThreshold Default to 0.5. The minimum probability + * to color a pixel as foreground rather than background. The alpha channel + * integer values will be taken as the probabilities (for more information refer + * to `Segmentation` type's documentation). + * + * @param backgroundBlurAmount How many pixels in the background blend into each + * other. Defaults to 3. Should be an integer between 1 and 20. + * + * @param edgeBlurAmount How many pixels to blur on the edge between the person + * and the background by. Defaults to 3. Should be an integer between 0 and 20. + * + * @param flipHorizontal If the output should be flipped horizontally. Defaults + * to false. + */ +export async function drawBokehEffect( + canvas: Canvas, image: ImageType, segmentation: Segmentation|Segmentation[], + foregroundThreshold = 0.5, backgroundBlurAmount = 3, edgeBlurAmount = 3, + flipHorizontal = false) { + const blurredImage = await drawAndBlurImageOnOffScreenCanvas( + image, backgroundBlurAmount, CANVAS_NAMES.blurred); + canvas.width = blurredImage.width; + canvas.height = blurredImage.height; + + const ctx = canvas.getContext('2d'); + + if (Array.isArray(segmentation) && segmentation.length === 0) { + ctx.drawImage(blurredImage, 0, 0); + return; + } + + const personMask = + await createPersonMask(segmentation, foregroundThreshold, edgeBlurAmount); + + ctx.save(); + if (flipHorizontal) { + flipCanvasHorizontal(canvas); + } + // draw the original image on the final canvas + const [height, width] = getInputSize(image); + await drawImage(ctx, image, 0, 0, width, height); + + // "destination-in" - "The existing canvas content is kept where both the + // new shape and existing canvas content overlap. Everything else is made + // transparent." + // crop what's not the person using the mask from the original image + await drawWithCompositing(ctx, personMask, 'destination-in'); + // "destination-over" - "The existing canvas content is kept where both the + // new shape and existing canvas content overlap. Everything else is made + // transparent." + // draw the blurred background on top of the original image where it doesn't + // overlap. + await drawWithCompositing(ctx, blurredImage, 'destination-over'); + ctx.restore(); +} + +async function createBodyPartMask( + segmentation: Segmentation|Segmentation[], maskValuesToBlur: number[], + foregroundThreshold: number, edgeBlurAmount: number): Promise { + const backgroundMaskImage = await toBinaryMask( + segmentation, {r: 0, g: 0, b: 0, a: 0}, {r: 0, g: 0, b: 0, a: 255}, true, + foregroundThreshold, maskValuesToBlur); + + const backgroundMask = + renderImageDataToOffScreenCanvas(backgroundMaskImage, CANVAS_NAMES.mask); + if (edgeBlurAmount === 0) { + return backgroundMask; + } else { + return drawAndBlurImageOnOffScreenCanvas( + backgroundMask, edgeBlurAmount, CANVAS_NAMES.blurredMask); + } +} + +/** + * Given a personSegmentation and an image, draws the image with its background + * blurred onto the canvas. + * + * @param canvas The canvas to draw the background-blurred image onto. + * + * @param image The image to blur the background of and draw. + * + * @param segmentation Single segmentation or array of segmentations. + * + * @param maskValuesToBlur An array of red channel mask values to blur + * (representing different body parts, refer to `Segmentation` interface + * docs for more details). + * + * @param foregroundThreshold Default to 0.5. The minimum probability + * to color a pixel as foreground rather than background. The alpha channel + * integer values will be taken as the probabilities (for more information refer + * to `Segmentation` type's documentation). + * + * @param backgroundBlurAmount How many pixels in the background blend into each + * other. Defaults to 3. Should be an integer between 1 and 20. + * + * @param edgeBlurAmount How many pixels to blur on the edge between the person + * and the background by. Defaults to 3. Should be an integer between 0 and 20. + * + * @param flipHorizontal If the output should be flipped horizontally. Defaults + * to false. + */ +export async function blurBodyPart( + canvas: Canvas, image: ImageType, segmentation: Segmentation|Segmentation[], + maskValuesToBlur: number[], foregroundThreshold = 0.5, + backgroundBlurAmount = 3, edgeBlurAmount = 3, flipHorizontal = false) { + const blurredImage = await drawAndBlurImageOnOffScreenCanvas( + image, backgroundBlurAmount, CANVAS_NAMES.blurred); + canvas.width = blurredImage.width; + canvas.height = blurredImage.height; + + const ctx = canvas.getContext('2d'); + + if (Array.isArray(segmentation) && segmentation.length === 0) { + ctx.drawImage(blurredImage, 0, 0); + return; + } + const bodyPartMask = await createBodyPartMask( + segmentation, maskValuesToBlur, foregroundThreshold, edgeBlurAmount); + + ctx.save(); + if (flipHorizontal) { + flipCanvasHorizontal(canvas); + } + // draw the original image on the final canvas + const [height, width] = getInputSize(image); + await drawImage(ctx, image, 0, 0, width, height); + + // "destination-in" - "The existing canvas content is kept where both the + // new shape and existing canvas content overlap. Everything else is made + // transparent." + // crop what's not the person using the mask from the original image + await drawWithCompositing(ctx, bodyPartMask, 'destination-in'); + // "destination-over" - "The existing canvas content is kept where both the + // new shape and existing canvas content overlap. Everything else is made + // transparent." + // draw the blurred background on top of the original image where it doesn't + // overlap. + await drawWithCompositing(ctx, blurredImage, 'destination-over'); + ctx.restore(); +} diff --git a/pose-detection/src/shared/calculators/render_util_test.ts b/pose-detection/src/shared/calculators/render_util_test.ts new file mode 100644 index 0000000000..bc650e0cfa --- /dev/null +++ b/pose-detection/src/shared/calculators/render_util_test.ts @@ -0,0 +1,182 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +// tslint:disable-next-line: no-imports-from-dist +import {BROWSER_ENVS, describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; +// tslint:disable-next-line: no-imports-from-dist +import {expectArraysClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {loadImage} from '../test_util'; +import {Color, Mask, Segmentation} from './interfaces/common_interfaces'; + +import {toHTMLCanvasElementLossy, toImageDataLossy, toTensorLossy} from './mask_util'; +import * as renderUtil from './render_util'; + +class ImageDataMask implements Mask { + constructor(private mask: ImageData) {} + + async toCanvasImageSource() { + return toHTMLCanvasElementLossy(this.mask); + } + + async toImageData() { + return this.mask; + } + + async toTensor() { + return toTensorLossy(this.mask); + } + + getUnderlyingType() { + return 'imagedata' as const ; + } +} + +function getSegmentation(imageData: ImageData): Segmentation { + return { + maskValueToLabel: (maskValue: number) => `${maskValue}`, + mask: new ImageDataMask(imageData), + }; +} + +const WIDTH = 1049; +const HEIGHT = 861; + +async function getBinarySegmentation() { + const image = await loadImage( + 'shared/three_people_binary_segmentation.png', WIDTH, HEIGHT); + const imageData = await toImageDataLossy(image); + + return [getSegmentation(imageData)]; +} + +async function getBinaryMask() { + const segmentation = await getBinarySegmentation(); + const binaryMask = await renderUtil.toBinaryMask( + segmentation, {r: 255, g: 255, b: 255, a: 255}, + {r: 0, g: 0, b: 0, a: 255}); + return binaryMask; +} + +async function getColoredSegmentation() { + const image = await loadImage( + 'shared/three_people_colored_segmentation.png', WIDTH, HEIGHT); + const imageData = await toImageDataLossy(image); + + return [getSegmentation(imageData)]; +} + +const RAINBOW_PART_COLORS: Array<[number, number, number]> = [ + [110, 64, 170], [143, 61, 178], [178, 60, 178], [210, 62, 167], + [238, 67, 149], [255, 78, 125], [255, 94, 99], [255, 115, 75], + [255, 140, 56], [239, 167, 47], [217, 194, 49], [194, 219, 64], + [175, 240, 91], [135, 245, 87], [96, 247, 96], [64, 243, 115], + [40, 234, 141], [28, 219, 169], [26, 199, 194], [33, 176, 213], + [47, 150, 224], [65, 125, 224], [84, 101, 214], [99, 81, 195] +]; + +function maskValueToRainbowColor(maskValue: number): Color { + const [r, g, b] = RAINBOW_PART_COLORS[maskValue]; + return {r, g, b, a: 255}; +} + +async function getColoredMask() { + const segmentation = await getColoredSegmentation(); + + const coloredMask = await renderUtil.toColoredMask( + segmentation, maskValueToRainbowColor, {r: 255, g: 255, b: 255, a: 255}); + + return coloredMask; +} + +async function expectImage(actual: ImageData, imageName: string) { + loadImage(imageName, WIDTH, HEIGHT) + .then(image => toImageDataLossy(image)) + .then( + async expectedImage => + expectArraysClose(actual.data, expectedImage.data)); +} + +describeWithFlags('renderUtil', BROWSER_ENVS, () => { + let image: HTMLImageElement; + let timeout: number; + + beforeAll(async () => { + timeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000; // 2mins + + image = await loadImage('shared/three_people.jpg', WIDTH, HEIGHT); + }); + + afterAll(() => { + jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout; + }); + + it('toBinaryMask.', async () => { + const binaryMask = await getBinaryMask(); + await expectImage(binaryMask, 'shared/three_people_binary_mask.png'); + }); + + it('toColoredMask.', async () => { + const coloredMask = await getColoredMask(); + await expectImage(coloredMask, 'shared/three_people_colored_mask.png'); + }); + + it('drawMask.', async () => { + const binaryMask = await getBinaryMask(); + + const canvas = document.createElement('canvas'); + await renderUtil.drawMask(canvas, image, binaryMask, 0.7, 3); + + const imageMask = await toImageDataLossy(canvas); + + await expectImage(imageMask, 'shared/three_people_draw_mask.png'); + }); + + it('drawPixelatedMask.', async () => { + const coloredMask = await getColoredMask(); + + const canvas = document.createElement('canvas'); + await renderUtil.drawPixelatedMask(canvas, image, coloredMask); + + const imageMask = await toImageDataLossy(canvas); + + await expectImage(imageMask, 'shared/three_people_pixelated_mask.png'); + }); + + it('drawBokehEffect.', async () => { + const binarySegmentation = await getBinarySegmentation(); + + const canvas = document.createElement('canvas'); + await renderUtil.drawBokehEffect(canvas, image, binarySegmentation); + + const imageMask = await toImageDataLossy(canvas); + + await expectImage(imageMask, 'shared/three_people_bokeh_effect.png'); + }); + + it('blurBodyPart.', async () => { + const coloredSegmentation = await getColoredSegmentation(); + + const canvas = document.createElement('canvas'); + await renderUtil.blurBodyPart(canvas, image, coloredSegmentation, [0, 1]); + + const imageMask = await toImageDataLossy(canvas); + + await expectImage(imageMask, 'shared/three_people_blur_body_parts.png'); + }); +}); diff --git a/pose-detection/src/shared/calculators/segmentation_smoothing.ts b/pose-detection/src/shared/calculators/segmentation_smoothing.ts new file mode 100644 index 0000000000..fc4520624e --- /dev/null +++ b/pose-detection/src/shared/calculators/segmentation_smoothing.ts @@ -0,0 +1,146 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {GPGPUProgram, MathBackendWebGL} from '@tensorflow/tfjs-backend-webgl'; +import * as tf from '@tensorflow/tfjs-core'; + +import {SegmentationSmoothingConfig} from './interfaces/config_interfaces'; + +/** + * A calculator for mixing two segmentation masks together, based on an + * uncertantity probability estimate. + * @param prevMaks Segmentation mask from previous image. + * @param newMask Segmentation mask of current image. + * @param config Contains ratio of amount of previous mask to blend with + * current. + * + * @returns Image mask. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/image/segmentation_smoothing_calculator.cc +export function smoothSegmentation( + prevMask: tf.Tensor2D, newMask: tf.Tensor2D, + config: SegmentationSmoothingConfig): tf.Tensor2D { + if (tf.getBackend() === 'webgl') { + // Same as implementation in the else case but reduces number of shader + // calls to 1 instead of 17. + return smoothSegmentationWebGL(prevMask, newMask, config); + } + return tf.tidy(() => { + /* + * Assume p := newMaskValue + * H(p) := 1 + (p * log(p) + (1-p) * log(1-p)) / log(2) + * uncertainty alpha(p) = + * Clamp(1 - (1 - H(p)) * (1 - H(p)), 0, 1) [squaring the + * uncertainty] + * + * The following polynomial approximates uncertainty alpha as a + * function of (p + 0.5): + */ + const c1 = 5.68842; + const c2 = -0.748699; + const c3 = -57.8051; + const c4 = 291.309; + const c5 = -624.717; + const t = tf.sub(newMask, 0.5); + const x = tf.square(t); + + // Per element calculation is: 1.0 - Math.min(1.0, x * (c1 + x * (c2 + x + // * (c3 + x * (c4 + x * c5))))). + + const uncertainty = tf.sub( + 1, + tf.minimum( + 1, + tf.mul( + x, + tf.add( + c1, + tf.mul( + x, + tf.add( + c2, + tf.mul( + x, + tf.add( + c3, + tf.mul( + x, tf.add(c4, tf.mul(x, c5))))))))))); + + // Per element calculation is: newMaskValue + (prevMaskValue - + // newMaskValue) * (uncertainty * combineWithPreviousRatio). + return tf.add( + newMask, + tf.mul( + tf.sub(prevMask, newMask), + tf.mul(uncertainty, config.combineWithPreviousRatio))); + }); +} + +function smoothSegmentationWebGL( + prevMask: tf.Tensor2D, newMask: tf.Tensor2D, + config: SegmentationSmoothingConfig): tf.Tensor2D { + const ratio = config.combineWithPreviousRatio.toFixed(2); + const program: GPGPUProgram = { + variableNames: ['prevMask', 'newMask'], + outputShape: prevMask.shape, + userCode: ` + void main() { + ivec2 coords = getOutputCoords(); + int height = coords[0]; + int width = coords[1]; + + float prevMaskValue = getPrevMask(height, width); + float newMaskValue = getNewMask(height, width); + + /* + * Assume p := newMaskValue + * H(p) := 1 + (p * log(p) + (1-p) * log(1-p)) / log(2) + * uncertainty alpha(p) = + * Clamp(1 - (1 - H(p)) * (1 - H(p)), 0, 1) [squaring the + * uncertainty] + * + * The following polynomial approximates uncertainty alpha as a + * function of (p + 0.5): + */ + const float c1 = 5.68842; + const float c2 = -0.748699; + const float c3 = -57.8051; + const float c4 = 291.309; + const float c5 = -624.717; + float t = newMaskValue - 0.5; + float x = t * t; + + float uncertainty = + 1.0 - min(1.0, x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * c5))))); + + float outputValue = newMaskValue + (prevMaskValue - newMaskValue) * + (uncertainty * ${ratio}); + + setOutput(outputValue); + } +` + }; + const webglBackend = tf.backend() as MathBackendWebGL; + + return tf.tidy(() => { + const outputTensorInfo = + webglBackend.compileAndRun(program, [prevMask, newMask]); + return tf.engine().makeTensorFromDataId( + outputTensorInfo.dataId, outputTensorInfo.shape, + outputTensorInfo.dtype) as tf.Tensor2D; + }); +} diff --git a/pose-detection/src/shared/calculators/segmentation_smoothing_test.ts b/pose-detection/src/shared/calculators/segmentation_smoothing_test.ts new file mode 100644 index 0000000000..396022e3a3 --- /dev/null +++ b/pose-detection/src/shared/calculators/segmentation_smoothing_test.ts @@ -0,0 +1,88 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +// tslint:disable-next-line: no-imports-from-dist +import {BROWSER_ENVS, describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; +// tslint:disable-next-line: no-imports-from-dist +import {expectNumbersClose} from '@tensorflow/tfjs-core/dist/test_util'; + +import {arrayToMatrix4x4} from './calculate_inverse_matrix'; +import {smoothSegmentation} from './segmentation_smoothing'; + +function runTest(useWebGL: boolean, mixRatio: number) { + const prevMask = arrayToMatrix4x4(new Array(16).fill(111 / 255)); + const curMask = arrayToMatrix4x4([ + 0.00, 0.00, 0.00, 0.00, // + 0.00, 0.98, 0.98, 0.00, // + 0.00, 0.98, 0.98, 0.00, // + 0.00, 0.00, 0.00, 0.00 + ]); + + tf.setBackend(useWebGL ? 'webgl' : 'cpu'); + + const resultMask = smoothSegmentation( + tf.tensor2d(prevMask), tf.tensor2d(curMask), + {combineWithPreviousRatio: mixRatio}); + + expect(resultMask.shape[0]).toBe(curMask.length); + expect(resultMask.shape[1]).toBe(curMask[0].length); + + const result = resultMask.arraySync(); + + if (mixRatio === 1.0) { + for (let i = 0; i < 4; ++i) { + for (let j = 0; j < 4; ++j) { + const input = curMask[i][j]; + const output = result[i][j]; + // Since the input has high value (250), it has low uncertainty. + // So the output should have changed lower (towards prev), + // but not too much. + if (input > 0) { + expect(input).not.toBeCloseTo(output); + } + expectNumbersClose(input, output, 3.0 / 255.0); + } + } + } else if (mixRatio === 0.0) { + for (let i = 0; i < 4; ++i) { + for (let j = 0; j < 4; ++j) { + const input = curMask[i][j]; + const output = result[i][j]; + expectNumbersClose( + input, output, 1e-7); // Output should match current. + } + } + } else { + throw new Error(`Invalid mixRatio: ${mixRatio}`); + } + + return result; +} +describeWithFlags('smoothSegmentation ', BROWSER_ENVS, () => { + it('test smoothing.', async () => { + runTest(false, 0.0); + const cpuResult = runTest(false, 1.0); + const glResult = runTest(true, 1.0); + + // CPU & webGL should match. + for (let i = 0; i < 4; ++i) { + for (let j = 0; j < 4; ++j) { + expectNumbersClose(cpuResult[i][j], glResult[i][j], 1e-7); + } + } + }); +}); diff --git a/pose-detection/src/shared/calculators/shift_image_value.ts b/pose-detection/src/shared/calculators/shift_image_value.ts new file mode 100644 index 0000000000..4c4aaed470 --- /dev/null +++ b/pose-detection/src/shared/calculators/shift_image_value.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {transformValueRange} from './image_utils'; + +export function shiftImageValue( + image: tf.Tensor4D, outputFloatRange: [number, number]): tf.Tensor4D { + // Calculate the scale and offset to shift from [0, 255] to [-1, 1]. + const valueRange = transformValueRange( + 0, 255, outputFloatRange[0] /* min */, outputFloatRange[1] /* max */); + + // Shift value range. + return tf.tidy( + () => tf.add(tf.mul(image, valueRange.scale), valueRange.offset)); +} diff --git a/pose-detection/src/shared/calculators/sigmoid.ts b/pose-detection/src/shared/calculators/sigmoid.ts new file mode 100644 index 0000000000..7a069d4e06 --- /dev/null +++ b/pose-detection/src/shared/calculators/sigmoid.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +export function sigmoid(value: number) { + return 1 / (1 + Math.exp(-value)); +} diff --git a/pose-detection/src/shared/calculators/split_detection_result.ts b/pose-detection/src/shared/calculators/split_detection_result.ts new file mode 100644 index 0000000000..4a456bfac6 --- /dev/null +++ b/pose-detection/src/shared/calculators/split_detection_result.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +export function splitDetectionResult(detectionResult: tf.Tensor3D): + [tf.Tensor3D, tf.Tensor3D] { + return tf.tidy(() => { + // logit is stored in the first element in each anchor data. + const logits = tf.slice(detectionResult, [0, 0, 0], [1, -1, 1]); + // Bounding box coords are stored in the next four elements for each anchor + // point. + const rawBoxes = tf.slice(detectionResult, [0, 0, 1], [1, -1, -1]); + + return [logits, rawBoxes]; + }); +} diff --git a/pose-detection/src/shared/calculators/tensors_to_detections.ts b/pose-detection/src/shared/calculators/tensors_to_detections.ts new file mode 100644 index 0000000000..fbc46cc458 --- /dev/null +++ b/pose-detection/src/shared/calculators/tensors_to_detections.ts @@ -0,0 +1,229 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {TensorsToDetectionsConfig} from './interfaces/config_interfaces'; +import {AnchorTensor, Detection} from './interfaces/shape_interfaces'; + +/** + * Convert result Tensors from object detection models into Detection boxes. + * + * @param detectionTensors List of Tensors of type Float32. The list of tensors + * can have 2 or 3 tensors. First tensor is the predicted raw + * boxes/keypoints. The size of the values must be + * (num_boxes * num_predicted_values). Second tensor is the score tensor. + * The size of the valuse must be (num_boxes * num_classes). It's optional + * to pass in a third tensor for anchors (e.g. for SSD models) depend on the + * outputs of the detection model. The size of anchor tensor must be + * (num_boxes * 4). + * @param anchor A tensor for anchors. The size of anchor tensor must be + * (num_boxes * 4). + * @param config + */ +export async function tensorsToDetections( + detectionTensors: [tf.Tensor1D, tf.Tensor2D], anchor: AnchorTensor, + config: TensorsToDetectionsConfig): Promise { + const rawScoreTensor = detectionTensors[0]; + const rawBoxTensor = detectionTensors[1]; + + // Shape [numOfBoxes, 4] or [numOfBoxes, 12]. + const boxes = decodeBoxes(rawBoxTensor, anchor, config); + + // Filter classes by scores. + const normalizedScore = tf.tidy(() => { + let normalizedScore = rawScoreTensor; + if (config.sigmoidScore) { + if (config.scoreClippingThresh != null) { + normalizedScore = tf.clipByValue( + rawScoreTensor, -config.scoreClippingThresh, + config.scoreClippingThresh); + } + normalizedScore = tf.sigmoid(normalizedScore); + return normalizedScore; + } + + return normalizedScore; + }); + + const outputDetections = + await convertToDetections(boxes, normalizedScore, config); + + tf.dispose([boxes, normalizedScore]); + + return outputDetections; +} + +export async function convertToDetections( + detectionBoxes: tf.Tensor2D, detectionScore: tf.Tensor1D, + config: TensorsToDetectionsConfig): Promise { + const outputDetections: Detection[] = []; + const detectionBoxesData = await detectionBoxes.data() as Float32Array; + const detectionScoresData = await detectionScore.data() as Float32Array; + + for (let i = 0; i < config.numBoxes; ++i) { + if (config.minScoreThresh != null && + detectionScoresData[i] < config.minScoreThresh) { + continue; + } + const boxOffset = i * config.numCoords; + const detection = convertToDetection( + detectionBoxesData[boxOffset + 0] /* boxYMin */, + detectionBoxesData[boxOffset + 1] /* boxXMin */, + detectionBoxesData[boxOffset + 2] /* boxYMax */, + detectionBoxesData[boxOffset + 3] /* boxXMax */, detectionScoresData[i], + config.flipVertically, i); + const bbox = detection.locationData.relativeBoundingBox; + + if (bbox.width < 0 || bbox.height < 0) { + // Decoded detection boxes could have negative values for width/height + // due to model prediction. Filter out those boxes since some + // downstream calculators may assume non-negative values. + continue; + } + // Add keypoints. + if (config.numKeypoints > 0) { + const locationData = detection.locationData; + locationData.relativeKeypoints = []; + const totalIdx = config.numKeypoints * config.numValuesPerKeypoint; + for (let kpId = 0; kpId < totalIdx; kpId += config.numValuesPerKeypoint) { + const keypointIndex = boxOffset + config.keypointCoordOffset + kpId; + const keypoint = { + x: detectionBoxesData[keypointIndex + 0], + y: config.flipVertically ? 1 - detectionBoxesData[keypointIndex + 1] : + detectionBoxesData[keypointIndex + 1] + }; + locationData.relativeKeypoints.push(keypoint); + } + } + outputDetections.push(detection); + } + + return outputDetections; +} + +function convertToDetection( + boxYMin: number, boxXMin: number, boxYMax: number, boxXMax: number, + score: number, flipVertically: boolean, i: number): Detection { + return { + score: [score], + ind: i, + locationData: { + relativeBoundingBox: { + xMin: boxXMin, + yMin: flipVertically ? 1 - boxYMax : boxYMin, + xMax: boxXMax, + yMax: flipVertically ? 1 - boxYMin : boxYMax, + width: boxXMax - boxXMin, + height: boxYMax - boxYMin + } + } + }; +} + +//[xCenter, yCenter, w, h, kp1, kp2, kp3, kp4] +//[yMin, xMin, yMax, xMax, kpX, kpY, kpX, kpY] +function decodeBoxes( + rawBoxes: tf.Tensor2D, anchor: AnchorTensor, + config: TensorsToDetectionsConfig): tf.Tensor2D { + return tf.tidy(() => { + let yCenter; + let xCenter; + let h; + let w; + + if (config.reverseOutputOrder) { + // Shape [numOfBoxes, 1]. + xCenter = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 0], [-1, 1])); + yCenter = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 1], [-1, 1])); + w = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 2], [-1, 1])); + h = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 3], [-1, 1])); + } else { + yCenter = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 0], [-1, 1])); + xCenter = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 1], [-1, 1])); + h = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 2], [-1, 1])); + w = tf.squeeze( + tf.slice(rawBoxes, [0, config.boxCoordOffset + 3], [-1, 1])); + } + + xCenter = + tf.add(tf.mul(tf.div(xCenter, config.xScale), anchor.w), anchor.x); + yCenter = + tf.add(tf.mul(tf.div(yCenter, config.yScale), anchor.h), anchor.y); + + if (config.applyExponentialOnBoxSize) { + h = tf.mul(tf.exp(tf.div(h, config.hScale)), anchor.h); + w = tf.mul(tf.exp(tf.div(w, config.wScale)), anchor.w); + } else { + h = tf.mul(tf.div(h, config.hScale), anchor.h); + w = tf.mul(tf.div(w, config.wScale), anchor.h); + } + + const yMin = tf.sub(yCenter, tf.div(h, 2)); + const xMin = tf.sub(xCenter, tf.div(w, 2)); + const yMax = tf.add(yCenter, tf.div(h, 2)); + const xMax = tf.add(xCenter, tf.div(w, 2)); + + // Shape [numOfBoxes, 4]. + let boxes = tf.concat( + [ + tf.reshape(yMin, [config.numBoxes, 1]), + tf.reshape(xMin, [config.numBoxes, 1]), + tf.reshape(yMax, [config.numBoxes, 1]), + tf.reshape(xMax, [config.numBoxes, 1]) + ], + 1); + + if (config.numKeypoints) { + for (let k = 0; k < config.numKeypoints; ++k) { + const keypointOffset = + config.keypointCoordOffset + k * config.numValuesPerKeypoint; + let keypointX; + let keypointY; + if (config.reverseOutputOrder) { + keypointX = + tf.squeeze(tf.slice(rawBoxes, [0, keypointOffset], [-1, 1])); + keypointY = + tf.squeeze(tf.slice(rawBoxes, [0, keypointOffset + 1], [-1, 1])); + } else { + keypointY = + tf.squeeze(tf.slice(rawBoxes, [0, keypointOffset], [-1, 1])); + keypointX = + tf.squeeze(tf.slice(rawBoxes, [0, keypointOffset + 1], [-1, 1])); + } + const keypointXNormalized = tf.add( + tf.mul(tf.div(keypointX, config.xScale), anchor.w), anchor.x); + const keypointYNormalized = tf.add( + tf.mul(tf.div(keypointY, config.yScale), anchor.h), anchor.y); + boxes = tf.concat( + [ + boxes, tf.reshape(keypointXNormalized, [config.numBoxes, 1]), + tf.reshape(keypointYNormalized, [config.numBoxes, 1]) + ], + 1); + } + } + + // Shape [numOfBoxes, 4] || [numOfBoxes, 12]. + return boxes as tf.Tensor2D; + }); +} diff --git a/pose-detection/src/shared/calculators/tensors_to_landmarks.ts b/pose-detection/src/shared/calculators/tensors_to_landmarks.ts new file mode 100644 index 0000000000..d1f0958aed --- /dev/null +++ b/pose-detection/src/shared/calculators/tensors_to_landmarks.ts @@ -0,0 +1,93 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; + +import {Keypoint} from './interfaces/common_interfaces'; + +import {TensorsToLandmarksConfig} from './interfaces/config_interfaces'; +import {sigmoid} from './sigmoid'; + +function applyActivation(activation: 'none'|'sigmoid', value: number) { + return activation === 'none' ? value : sigmoid(value); +} + +/** + * A calculator for converting Tensors from regression models into landmarks. + * Note that if the landmarks in the tensor has more than 5 dimensions, only the + * first 5 dimensions will be converted to [x,y,z, visibility, presence]. The + * latter two fields may also stay unset if such attributes are not supported in + * the model. + * @param landmarkTensor List of Tensors of type float32. Only the first tensor + * will be used. The size of the values must be (num_dimension x num_landmarks). + * @param flipHorizontally Optional. Whether to flip landmarks horizontally or + * not. Overrides corresponding field in config. + * @param flipVertically Optional. Whether to flip landmarks vertically or not. + * Overrides corresponding field in config. + * + * @param config + * + * @returns Normalized landmarks. + */ +export async function tensorsToLandmarks( + landmarkTensor: tf.Tensor, config: TensorsToLandmarksConfig, + flipHorizontally?: boolean, flipVertically?: boolean) { + flipHorizontally = flipHorizontally || config.flipHorizontally || false; + flipVertically = flipVertically || config.flipVertically || false; + + const numValues = landmarkTensor.size; + const numDimensions = numValues / config.numLandmarks; + const rawLandmarks = await landmarkTensor.data() as Float32Array; + + const outputLandmarks: Keypoint[] = []; + for (let ld = 0; ld < config.numLandmarks; ++ld) { + const offset = ld * numDimensions; + const landmark: Keypoint = {x: 0, y: 0}; + + if (flipHorizontally) { + landmark.x = config.inputImageWidth - rawLandmarks[offset]; + } else { + landmark.x = rawLandmarks[offset]; + } + if (numDimensions > 1) { + if (flipVertically) { + landmark.y = config.inputImageHeight - rawLandmarks[offset + 1]; + } else { + landmark.y = rawLandmarks[offset + 1]; + } + } + if (numDimensions > 2) { + landmark.z = rawLandmarks[offset + 2]; + } + if (numDimensions > 3) { + landmark.score = applyActivation( + config.visibilityActivation, rawLandmarks[offset + 3]); + } + // presence is in rawLandmarks[offset + 4], we don't expose it. + + outputLandmarks.push(landmark); + } + + for (let i = 0; i < outputLandmarks.length; ++i) { + const landmark = outputLandmarks[i]; + landmark.x = landmark.x / config.inputImageWidth; + landmark.y = landmark.y / config.inputImageHeight; + // Scale Z coordinate as X + allow additional uniform normalization. + landmark.z = landmark.z / config.inputImageWidth / (config.normalizeZ || 1); + } + + return outputLandmarks; +} diff --git a/pose-detection/src/shared/calculators/tensors_to_segmentation.ts b/pose-detection/src/shared/calculators/tensors_to_segmentation.ts new file mode 100644 index 0000000000..d0b4402098 --- /dev/null +++ b/pose-detection/src/shared/calculators/tensors_to_segmentation.ts @@ -0,0 +1,69 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import * as tf from '@tensorflow/tfjs-core'; +import {ImageSize} from './interfaces/common_interfaces'; + +import {TensorsToSegmentationConfig} from './interfaces/config_interfaces'; + +/** + * Converts a tensor from a segmentation model to an image mask. + * @param segmentationTensor Output from segmentation model of shape (1, height, + * width, channels). + * @param config Contains activation to apply. + * @param outputSize Desired dimensions of output image mask. + * + * @returns Image mask. + */ +export function tensorsToSegmentation( + segmentationTensor: tf.Tensor4D, config: TensorsToSegmentationConfig, + outputSize?: ImageSize): tf.Tensor2D { + return tf.tidy(() => { + // Remove batch dimension. + const $segmentationTensor = + // tslint:disable-next-line: no-unnecessary-type-assertion + tf.squeeze(segmentationTensor, [0]) as tf.Tensor3D; + + const tensorChannels = $segmentationTensor.shape[2]; + // Process mask tensor and apply activation function. + if (tensorChannels === 1) { + // Create initial working mask. + let smallMaskMat = $segmentationTensor; + switch (config.activation) { + case 'none': + break; + case 'sigmoid': + smallMaskMat = tf.sigmoid(smallMaskMat); + break; + case 'softmax': + throw new Error('Softmax activation requires two channels.'); + default: + throw new Error(`Activation not supported (${config.activation})`); + } + + const outputMat = outputSize ? + tf.image.resizeBilinear( + smallMaskMat, [outputSize.height, outputSize.width]) : + smallMaskMat; + + // Remove channel dimension. + return tf.squeeze(outputMat, [2]); + } else { + throw new Error( + `Unsupported number of tensor channels ${tensorChannels}`); + } + }); +} diff --git a/pose-detection/src/shared/calculators/transform_rect.ts b/pose-detection/src/shared/calculators/transform_rect.ts new file mode 100644 index 0000000000..0760724851 --- /dev/null +++ b/pose-detection/src/shared/calculators/transform_rect.ts @@ -0,0 +1,83 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {normalizeRadians} from './image_utils'; +import {ImageSize} from './interfaces/common_interfaces'; +import {RectTransformationConfig} from './interfaces/config_interfaces'; +import {Rect} from './interfaces/shape_interfaces'; + +/** + * Performs geometric transformation to the input normalized rectangle, + * correpsonding to input normalized rectangle respectively. + * @param rect The normalized rectangle. + * @param imageSize The original imageSize. + * @param config See documentation in `RectTransformationConfig`. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/rect_transformation_calculator.cc +export function transformNormalizedRect( + rect: Rect, imageSize: ImageSize, config: RectTransformationConfig): Rect { + let width = rect.width; + let height = rect.height; + let rotation = rect.rotation; + + if (config.rotation != null || config.rotationDegree != null) { + rotation = computeNewRotation(rotation, config); + } + + if (rotation === 0) { + rect.xCenter = rect.xCenter + width * config.shiftX; + rect.yCenter = rect.yCenter + height * config.shiftY; + } else { + const xShift = + (imageSize.width * width * config.shiftX * Math.cos(rotation) - + imageSize.height * height * config.shiftY * Math.sin(rotation)) / + imageSize.width; + const yShift = + (imageSize.width * width * config.shiftX * Math.sin(rotation) + + imageSize.height * height * config.shiftY * Math.cos(rotation)) / + imageSize.height; + rect.xCenter = rect.xCenter + xShift; + rect.yCenter = rect.yCenter + yShift; + } + + if (config.squareLong) { + const longSide = + Math.max(width * imageSize.width, height * imageSize.height); + width = longSide / imageSize.width; + height = longSide / imageSize.height; + } else if (config.squareShort) { + const shortSide = + Math.min(width * imageSize.width, height * imageSize.height); + width = shortSide / imageSize.width; + height = shortSide / imageSize.height; + } + rect.width = width * config.scaleX; + rect.height = height * config.scaleY; + + return rect; +} + +export function computeNewRotation( + rotation: number, config: RectTransformationConfig): number { + if (config.rotation != null) { + rotation += config.rotation; + } else if (config.rotationDegree != null) { + rotation += Math.PI * config.rotationDegree / 180; + } + return normalizeRadians(rotation); +} diff --git a/pose-detection/src/shared/filters/keypoints_one_euro_filter.ts b/pose-detection/src/shared/filters/keypoints_one_euro_filter.ts new file mode 100644 index 0000000000..89cc3c6b1e --- /dev/null +++ b/pose-detection/src/shared/filters/keypoints_one_euro_filter.ts @@ -0,0 +1,91 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint, KeypointsFilter} from '../calculators/interfaces/common_interfaces'; +import {OneEuroFilterConfig} from '../calculators/interfaces/config_interfaces'; +import {OneEuroFilter} from './one_euro_filter'; + +/** + * A stateful filter that smoothes keypoints values overtime. + * + * More specifically, it uses `OneEuroFilter` to smooth every x, y, z + * coordinates over time, which as result gives us velocity of how these values + * change over time. With higher velocity it weights new values higher. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmarks_smoothing_calculator.cc +export class KeypointsOneEuroFilter implements KeypointsFilter { + private xFilters: OneEuroFilter[]; + private yFilters: OneEuroFilter[]; + private zFilters: OneEuroFilter[]; + + constructor(private readonly config: OneEuroFilterConfig) {} + + apply(keypoints: Keypoint[], microSeconds: number, objectScale: number): + Keypoint[] { + if (keypoints == null) { + this.reset(); + return null; + } + + // Initialize filters once. + this.initializeFiltersIfEmpty(keypoints); + + // Get value scale as inverse value of the object scale. + // If value is too small smoothing will be disabled and keypoints will be + // returned as is. + let valueScale = 1; + if (!this.config.disableValueScaling) { + if (objectScale < this.config.minAllowedObjectScale) { + return [...keypoints]; + } + valueScale = 1.0 / objectScale; + } + + // Filter keypoints. Every axis of every keypoint is filtered separately. + return keypoints.map((keypoint, i) => { + const outKeypoint = { + ...keypoint, + x: this.xFilters[i].apply(keypoint.x, microSeconds, valueScale), + y: this.yFilters[i].apply(keypoint.y, microSeconds, valueScale), + }; + + if (keypoint.z != null) { + outKeypoint.z = + this.zFilters[i].apply(keypoint.z, microSeconds, valueScale); + } + + return outKeypoint; + }); + } + + reset() { + this.xFilters = null; + this.yFilters = null; + this.zFilters = null; + } + + // Initializes filters for the first time or after reset. If initialized the + // check the size. + private initializeFiltersIfEmpty(keypoints: Keypoint[]) { + if (this.xFilters == null || this.xFilters.length !== keypoints.length) { + this.xFilters = keypoints.map(_ => new OneEuroFilter(this.config)); + this.yFilters = keypoints.map(_ => new OneEuroFilter(this.config)); + this.zFilters = keypoints.map(_ => new OneEuroFilter(this.config)); + } + } +} diff --git a/pose-detection/src/shared/filters/keypoints_smoothing.ts b/pose-detection/src/shared/filters/keypoints_smoothing.ts new file mode 100644 index 0000000000..179b7c1291 --- /dev/null +++ b/pose-detection/src/shared/filters/keypoints_smoothing.ts @@ -0,0 +1,81 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {getObjectScale} from '../calculators/get_object_scale'; +import {ImageSize, Keypoint, KeypointsFilter} from '../calculators/interfaces/common_interfaces'; +import {KeypointsSmoothingConfig} from '../calculators/interfaces/config_interfaces'; +import {Rect} from '../calculators/interfaces/shape_interfaces'; +import {keypointsToNormalizedKeypoints} from '../calculators/keypoints_to_normalized_keypoints'; +import {normalizedKeypointsToKeypoints} from '../calculators/normalized_keypoints_to_keypoints'; + +import {KeypointsOneEuroFilter} from './keypoints_one_euro_filter'; +import {KeypointsVelocityFilter} from './keypoints_velocity_filter'; + +/** + * A Calculator to smooth keypoints over time. + */ +export class KeypointsSmoothingFilter { + private readonly keypointsFilter: KeypointsFilter; + + constructor(config: KeypointsSmoothingConfig) { + if (config.velocityFilter != null) { + this.keypointsFilter = new KeypointsVelocityFilter(config.velocityFilter); + } else if (config.oneEuroFilter != null) { + this.keypointsFilter = new KeypointsOneEuroFilter(config.oneEuroFilter); + } else { + throw new Error( + 'Either configure velocityFilter or oneEuroFilter, but got ' + + `${config}.`); + } + } + + /** + * Apply one of the stateful `KeypointsFilter` to keypoints. + * + * Currently supports `OneEuroFilter` and `VelocityFilter`. + * @param keypoints A list of 2D or 3D keypoints, can be normalized or + * non-normalized. + * @param timestamp The timestamp of the video frame. + * @param imageSize Optional. The imageSize is useful when keypoints are + * normalized. + * @param normalized Optional. Whether the keypoints are normalized. Default + * to false. + * @param objectScaleROI Optional. The auxiliary ROI to calculate object + * scale. If not set, objectScale defaults to 1. + */ + apply( + keypoints: Keypoint[], timestamp: number, imageSize?: ImageSize, + normalized = false, objectScaleROI?: Rect): Keypoint[] { + if (keypoints == null) { + this.keypointsFilter.reset(); + return null; + } + + const objectScale = + objectScaleROI != null ? getObjectScale(objectScaleROI, imageSize) : 1; + + const scaledKeypoints = normalized ? + normalizedKeypointsToKeypoints(keypoints, imageSize) : + keypoints; + const scaledKeypointsFiltered = + this.keypointsFilter.apply(scaledKeypoints, timestamp, objectScale); + + return normalized ? + keypointsToNormalizedKeypoints(scaledKeypointsFiltered, imageSize) : + scaledKeypointsFiltered; + } +} diff --git a/pose-detection/src/shared/filters/keypoints_velocity_filter.ts b/pose-detection/src/shared/filters/keypoints_velocity_filter.ts new file mode 100644 index 0000000000..6f7e60b082 --- /dev/null +++ b/pose-detection/src/shared/filters/keypoints_velocity_filter.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {Keypoint, KeypointsFilter} from '../calculators/interfaces/common_interfaces'; +import {VelocityFilterConfig} from '../calculators/interfaces/config_interfaces'; +import {RelativeVelocityFilter} from './relative_velocity_filter'; + +/** + * A stateful filter that smoothes landmark values overtime. + * + * More specifically, it uses `RelativeVelocityFilter` to smooth every x, y, z + * coordinates over time, which as result gives us velocity of how these values + * change over time. With higher velocity it weights new values higher. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/calculators/util/landmarks_smoothing_calculator.cc +export class KeypointsVelocityFilter implements KeypointsFilter { + private xFilters: RelativeVelocityFilter[]; + private yFilters: RelativeVelocityFilter[]; + private zFilters: RelativeVelocityFilter[]; + + constructor(private readonly config: VelocityFilterConfig) {} + + apply(keypoints: Keypoint[], microSeconds: number, objectScale: number): + Keypoint[] { + if (keypoints == null) { + this.reset(); + return null; + } + // Get value scale as inverse value of the object scale. + // If value is too small smoothing will be disabled and keypoints will be + // returned as is. + let valueScale = 1; + if (!this.config.disableValueScaling) { + if (objectScale < this.config.minAllowedObjectScale) { + return [...keypoints]; + } + valueScale = 1 / objectScale; + } + + // Initialize filters once. + this.initializeFiltersIfEmpty(keypoints); + + // Filter keypoints. Every axis of every keypoint is filtered separately. + return keypoints.map((keypoint, i) => { + const outKeypoint = { + ...keypoint, + x: this.xFilters[i].apply(keypoint.x, microSeconds, valueScale), + y: this.yFilters[i].apply(keypoint.y, microSeconds, valueScale), + }; + + if (keypoint.z != null) { + outKeypoint.z = + this.zFilters[i].apply(keypoint.z, microSeconds, valueScale); + } + + return outKeypoint; + }); + } + + reset() { + this.xFilters = null; + this.yFilters = null; + this.zFilters = null; + } + + // Initializes filters for the first time or after reset. If initialized the + // check the size. + private initializeFiltersIfEmpty(keypoints: Keypoint[]) { + if (this.xFilters == null || this.xFilters.length !== keypoints.length) { + this.xFilters = + keypoints.map(_ => new RelativeVelocityFilter(this.config)); + this.yFilters = + keypoints.map(_ => new RelativeVelocityFilter(this.config)); + this.zFilters = + keypoints.map(_ => new RelativeVelocityFilter(this.config)); + } + } +} diff --git a/pose-detection/src/shared/filters/low_pass_filter.ts b/pose-detection/src/shared/filters/low_pass_filter.ts new file mode 100644 index 0000000000..f6a4a86966 --- /dev/null +++ b/pose-detection/src/shared/filters/low_pass_filter.ts @@ -0,0 +1,80 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +/** + * A stateful filter that smoothes values overtime. + * + * More specifically, it stores the previous value, and when there's a new + * value, a coefficient 'alpha' is applied to the new value, and `1 - alpha` is + * applied to the previous value. The smaller the alpha is, the smoother result + * and the bigger lag. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/util/filtering/low_pass_filter.cc +export class LowPassFilter { + private initialized = false; + private rawValue: number; + private storedValue: number; + + constructor(private alpha: number) {} + + apply(value: number, threshold?: number): number { + let result; + if (this.initialized) { + if (threshold == null) { + // Regular lowpass filter. + // result = this.alpha * value + (1 - this.alpha) * this.storedValue; + result = this.storedValue + this.alpha * (value - this.storedValue); + } else { + // We need to reformat the formula to be able to conveniently apply + // another optional non-linear function to the + // (value - this.storedValue) part. + // Add additional non-linearity to cap extreme value. + // More specifically, assume x = (value - this.storedValue), when x is + // close zero, the derived x is close to x, when x is several magnitudes + // larger, the drived x grows much slower then x. It behaves like + // sign(x)log(abs(x)). + result = this.storedValue + + this.alpha * threshold * + Math.asinh((value - this.storedValue) / threshold); + } + } else { + result = value; + this.initialized = true; + } + this.rawValue = value; + this.storedValue = result; + + return result; + } + + applyWithAlpha(value: number, alpha: number, threshold?: number): number { + this.alpha = alpha; + return this.apply(value, threshold); + } + + hasLastRawValue(): boolean { + return this.initialized; + } + + lastRawValue(): number { + return this.rawValue; + } + + reset() { + this.initialized = false; + } +} diff --git a/pose-detection/src/shared/filters/one_euro_filter.ts b/pose-detection/src/shared/filters/one_euro_filter.ts new file mode 100644 index 0000000000..0fd302979f --- /dev/null +++ b/pose-detection/src/shared/filters/one_euro_filter.ts @@ -0,0 +1,102 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {MICRO_SECONDS_TO_SECOND} from '../calculators/constants'; +import {OneEuroFilterConfig} from '../calculators/interfaces/config_interfaces'; +import {LowPassFilter} from './low_pass_filter'; +/** + * OneEuroFilter. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/util/filtering/one_euro_filter.cc +// Also ref original paper: +// https://cristal.univ-lille.fr/~casiez/1euro/ +export class OneEuroFilter { + private readonly minCutOff: number; + private readonly beta: number; + private readonly derivateCutOff: number; + private readonly x: LowPassFilter; + private readonly dx: LowPassFilter; + private readonly thresholdCutOff: number; + private readonly thresholdBeta: number; + + private frequency: number; + private lastTimestamp: number; + + /** + * Constructor of `OneEuroFilter` class. + * @param config See documentation of `OneEuroFilterConfig`. + */ + constructor(config: OneEuroFilterConfig) { + this.frequency = config.frequency; + this.minCutOff = config.minCutOff; + this.beta = config.beta; + this.thresholdCutOff = config.thresholdCutOff; + this.thresholdBeta = config.thresholdBeta; + this.derivateCutOff = config.derivateCutOff; + this.x = new LowPassFilter(this.getAlpha(this.minCutOff)); + this.dx = new LowPassFilter(this.getAlpha(this.derivateCutOff)); + this.lastTimestamp = 0; + } + + /** + * Applies filter to the value. + * @param value valueToFilter. + * @param microSeconds timestamp associated with the value (for instance, + * timestamp of the frame where you got value from). + */ + apply(value: number, microSeconds: number, valueScale: number): number { + if (value == null) { + return value; + } + + const $microSeconds = Math.trunc(microSeconds); + if (this.lastTimestamp >= $microSeconds) { + // Results are unpreditable in this case, so nothing to do but return + // same value. + return value; + } + + // Update the sampling frequency based on timestamps. + if (this.lastTimestamp !== 0 && $microSeconds !== 0) { + this.frequency = + 1 / (($microSeconds - this.lastTimestamp) * MICRO_SECONDS_TO_SECOND); + } + this.lastTimestamp = $microSeconds; + + // Estimate the current variation per second. + const dValue = this.x.hasLastRawValue() ? + (value - this.x.lastRawValue()) * valueScale * this.frequency : + 0; + const edValue = + this.dx.applyWithAlpha(dValue, this.getAlpha(this.derivateCutOff)); + const cutOff = this.minCutOff + this.beta * Math.abs(edValue); + + const threshold = this.thresholdCutOff != null ? + this.thresholdCutOff + this.thresholdBeta * Math.abs(edValue) : + null; + + // filter the given value. + return this.x.applyWithAlpha(value, this.getAlpha(cutOff), threshold); + } + + private getAlpha(cutoff: number): number { + // te = 1.0 / this.frequency + // tau = 1.0 / (2 * Math.PI * cutoff) + // result = 1 / (1.0 + (tau / te)) + return 1.0 / (1.0 + (this.frequency / (2 * Math.PI * cutoff))); + } +} diff --git a/pose-detection/src/shared/filters/one_euro_filter_test.ts b/pose-detection/src/shared/filters/one_euro_filter_test.ts new file mode 100644 index 0000000000..544951ab4e --- /dev/null +++ b/pose-detection/src/shared/filters/one_euro_filter_test.ts @@ -0,0 +1,61 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +// tslint:disable-next-line: no-imports-from-dist +import {ALL_ENVS, describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; + +import {OneEuroFilter} from './one_euro_filter'; + +describeWithFlags('OneEuroFilter ', ALL_ENVS, () => { + let oneEuroFilter: OneEuroFilter; + + beforeEach(async () => { + oneEuroFilter = new OneEuroFilter({ + frequency: 30.0, + minCutOff: 15.5, + beta: 20.2, + derivateCutOff: 21.5, + thresholdCutOff: 0.02, + thresholdBeta: 0.5 + }); + }); + + it('outputs are in convex hull of inputs', async () => { + const value0 = -1.0; + const timestamp0 = 1; + + const value1 = 2.0; + const timestamp1 = 15; + + const value2 = -3.0; + const timestamp2 = 33; + + const output0: number = + oneEuroFilter.apply(value0, timestamp0, 1 /* valueScale */); + expect(output0).toEqual(value0); + + const output1: number = + oneEuroFilter.apply(value1, timestamp1, 1 /* valueScale */); + expect(output1).toBeLessThan(Math.max(value0, value1)); + expect(output1).toBeGreaterThan(Math.min(value0, value1)); + + const output2: number = + oneEuroFilter.apply(value2, timestamp2, 1 /* valueScale */); + expect(output2).toBeLessThan(Math.max(value0, value1, value2)); + expect(output2).toBeGreaterThan(Math.min(value0, value1, value2)); + }); +}); diff --git a/pose-detection/src/shared/filters/relative_velocity_filter.ts b/pose-detection/src/shared/filters/relative_velocity_filter.ts new file mode 100644 index 0000000000..658cc05d61 --- /dev/null +++ b/pose-detection/src/shared/filters/relative_velocity_filter.ts @@ -0,0 +1,115 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {MICRO_SECONDS_TO_SECOND, SECOND_TO_MICRO_SECONDS} from '../calculators/constants'; +import {WindowElement} from '../calculators/interfaces/common_interfaces'; +import {VelocityFilterConfig} from '../calculators/interfaces/config_interfaces'; +import {LowPassFilter} from './low_pass_filter'; + +/** + * This filter keeps track (on a window of specified size) of value changes + * over time, which as result gives us velocity of how value changes over time. + * With higher velocity it weights new values higher. + * + * Use `windowSize` and `velocityScale` to tweak this filter for your use case. + */ +// ref: +// https://github.com/google/mediapipe/blob/master/mediapipe/util/filtering/relative_velocity_filter.cc +export class RelativeVelocityFilter { + private readonly window: WindowElement[] = []; + private readonly lowPassFilter: LowPassFilter = new LowPassFilter(1.0); + + private lastValue = 0; + private lastValueScale = 1; + private lastTimestamp = -1; + + /** + * Constructor of `RelativeVelocityFilter` class. + * @param config + * `windowSize`: Higher windowSize adds to lag and to stability. + * `velocityScale`: Lower velocityScale adds to lag and to stability. + */ + constructor(private readonly config: VelocityFilterConfig) {} + + /** + * Applies filter to the value. + * @param value valueToFilter. + * @param microSeconds timestamp associated with the value (for instance, + * timestamp of the frame where you got value from). + * @param valueScale value scale (for instance, if your value is a distance + * detected on a frame, it can look same on different devices but have + * quite different absolute values due to different resolution, you + * should come up with an appropriate parameter for your particular use + * case). + */ + apply(value: number, microSeconds: number, valueScale: number): number { + if (value == null) { + return value; + } + + const $microSeconds = Math.trunc(microSeconds); + if (this.lastTimestamp >= $microSeconds) { + // Results are unpreditable in this case, so nothing to do but return + // same value. + return value; + } + + let alpha; + if (this.lastTimestamp === -1) { + alpha = 1; + } else { + // Implement the DistanceEstimationMode.kLegacyTransition. + // TODO(lina128): Change to kForceCurrentScale or at least add an option + // that can be tweaked with parameter. + const distance = + value * valueScale - this.lastValue * this.lastValueScale; + const duration = $microSeconds - this.lastTimestamp; + + let cumulativeDistance = distance; + let cumulativeDuration = duration; + + // Define max cumulative duration assuming 30 frames per second is a good + // frame rate, so assuming 30 values per second or 1 / 30 of a second is + // a good duration per window element. + const assumedMaxDuration = SECOND_TO_MICRO_SECONDS / 30; + const maxCumulativeDuration = + (1 + this.window.length) * assumedMaxDuration; + for (const el of this.window) { + if (cumulativeDuration + el.duration > maxCumulativeDuration) { + // This helps in cases when durations are large and outdated + // window elements have bad impact on filtering results. + break; + } + cumulativeDistance += el.distance; + cumulativeDuration += el.duration; + } + + const velocity = + cumulativeDistance / (cumulativeDuration * MICRO_SECONDS_TO_SECOND); + alpha = 1 - 1 / (1 + this.config.velocityScale * Math.abs(velocity)); + this.window.unshift({distance, duration}); + if (this.window.length > this.config.windowSize) { + this.window.pop(); + } + } + + this.lastValue = value; + this.lastValueScale = valueScale; + this.lastTimestamp = $microSeconds; + + return this.lowPassFilter.applyWithAlpha(value, alpha); + } +} diff --git a/pose-detection/src/shared/filters/relative_velocity_filter_test.ts b/pose-detection/src/shared/filters/relative_velocity_filter_test.ts new file mode 100644 index 0000000000..f55c02c51d --- /dev/null +++ b/pose-detection/src/shared/filters/relative_velocity_filter_test.ts @@ -0,0 +1,205 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ +import {RelativeVelocityFilter} from './relative_velocity_filter'; + +describe('Relative velocity filter ', () => { + it('Smoke.', () => { + const filter = + new RelativeVelocityFilter({windowSize: 1, velocityScale: 1}); + + const timestamp1 = 1; + + expect(filter.apply(95.5, timestamp1, 0.5)).toBe(95.5); + expect(filter.apply(200.5, timestamp1, 0.5)).toBe(200.5); + expect(filter.apply(1000.5, timestamp1, 0.5)).toBe(1000.5); + expect(filter.apply(2000, timestamp1, 0.5)).toBe(2000); + }); + + it('Same value scale different velocity scales legacy.', () => { + // More sensitive filter. + const filter1 = + new RelativeVelocityFilter({windowSize: 5, velocityScale: 45}); + + // Less sensitive filter. + const filter2 = + new RelativeVelocityFilter({windowSize: 5, velocityScale: 0.1}); + + let result1; + let result2; + let value; + const valueScale = 1; + + value = 1; + result1 = filter1.apply(value, 1000 /* 1ms */, valueScale); + result2 = filter2.apply(value, 1000 /* 1ms */, valueScale); + expect(result1).toEqual(result2); + + value = 10; + result1 = filter1.apply(value, 2000 /* 2ms */, valueScale); + result2 = filter2.apply(value, 2000 /* 2ms */, valueScale); + expect(result1).toBeGreaterThan(result2); + + value = 2; + result1 = filter1.apply(value, 3000 /* 3ms */, valueScale); + result2 = filter2.apply(value, 3000 /* 3ms */, valueScale); + expect(result1).toBeLessThan(result2); + + value = 20; + result1 = filter1.apply(value, 4000 /* 4ms */, valueScale); + result2 = filter2.apply(value, 4000 /* 4ms */, valueScale); + expect(result1).toBeGreaterThan(result2); + + value = 10; + result1 = filter1.apply(value, 5000 /* 5ms */, valueScale); + result2 = filter2.apply(value, 5000 /* 5ms */, valueScale); + expect(result1).toBeLessThan(result2); + + value = 50; + result1 = filter1.apply(value, 6000 /* 6ms */, valueScale); + result2 = filter2.apply(value, 6000 /* 6ms */, valueScale); + expect(result1).toBeGreaterThan(result2); + + value = 30; + result1 = filter1.apply(value, 7000 /* 7ms */, valueScale); + result2 = filter2.apply(value, 7000 /* 7ms */, valueScale); + expect(result1).toBeLessThan(result2); + }); + + it('Different constant value scales same velocity scale legacy.', () => { + const sameVelocityScale = 1; + const filter1 = new RelativeVelocityFilter( + {windowSize: 3, velocityScale: sameVelocityScale}); + const filter2 = new RelativeVelocityFilter( + {windowSize: 3, velocityScale: sameVelocityScale}); + + let result1; + let result2; + let value; + // smaller value scale will decrease cumulative speed and alpha so with + // smaller scale and same other params filter will believe new values + // a little bit less. + const valueScale1 = 0.5; + const valueScale2 = 1; + + value = 1; + result1 = filter1.apply(value, 1000 /* 1ms */, valueScale1); + result2 = filter2.apply(value, 1000 /* 1ms */, valueScale2); + expect(result1).toEqual(result2); + + value = 10; + result1 = filter1.apply(value, 2000 /* 2ms */, valueScale1); + result2 = filter2.apply(value, 2000 /* 2ms */, valueScale2); + expect(result1).toBeLessThan(result2); + + value = 2; + result1 = filter1.apply(value, 3000 /* 3ms */, valueScale1); + result2 = filter2.apply(value, 3000 /* 3ms */, valueScale2); + expect(result1).toBeGreaterThan(result2); + + value = 20; + result1 = filter1.apply(value, 4000 /* 4ms */, valueScale1); + result2 = filter2.apply(value, 4000 /* 4ms */, valueScale2); + expect(result1).toBeLessThan(result2); + }); + + it('Translation invariance.', () => { + const originalDataPoints = [ + {value: 1, scale: 0.5}, {value: 10, scale: 5}, {value: 20, scale: 10}, + {value: 30, scale: 15}, {value: 40, scale: 0.5}, {value: 50, scale: 0.5}, + {value: 60, scale: 5}, {value: 70, scale: 10}, {value: 80, scale: 15}, + {value: 90, scale: 5}, {value: 70, scale: 10}, {value: 50, scale: 15}, + {value: 80, scale: 15} + ]; + + // The amount by which the input values are uniformly translated. + const valueOffset = 100; + + // The uniform time delta. + const timeDelta = 1000; /* 1ms */ + + // The filter parameters are the same between the two filters. + const windowSize = 5; + const velocityScale = 0.1; + + // Perform the translation. + const translatedDataPoints = []; + for (const dp of originalDataPoints) { + translatedDataPoints.push( + {value: dp.value + valueOffset, scale: dp.scale}); + } + + const originalPointsFilter = + new RelativeVelocityFilter({windowSize, velocityScale}); + const translatedPointsFilter = + new RelativeVelocityFilter({windowSize, velocityScale}); + + // The minimal difference which is considered a divergence. + const divergenceGap = 0.001; + + // The amount of the times this gap is achieved with legacy transition. + // Note that on the first iteration the filters should output the unfiltered + // input values, so no divergence should occur. + // This amount obviously depends on the values in `originalDataPoints`, so + // should be changed accordingly when they are updated. + const divergenceTimes = 5; + + // The minimal difference which is considered a large divergence. + const largeDivergenceGap = 10; + + // The amount of times it is achieved. + // This amount obviously depends on the values in `originalDataPoints`, so + // should be changed accordingly when they are updated. + const largeDivergenceTimes = 1; + + let timesDiverged = 0; + let timesLargelyDiverged = 0; + let timestamp = 0; + for (let iteration = 0; iteration < originalDataPoints.length; + ++iteration, timestamp += timeDelta) { + const originalDataPoint = originalDataPoints[iteration]; + const filteredOriginalValue = originalPointsFilter.apply( + originalDataPoint.value, timestamp, originalDataPoint.scale); + const translatedDataPoint = translatedDataPoints[iteration]; + const actualFilteredTranslatedValue = translatedPointsFilter.apply( + translatedDataPoint.value, timestamp, translatedDataPoint.scale); + + const expectedFilteredTranslatedValue = + filteredOriginalValue + valueOffset; + + const difference = Math.abs( + actualFilteredTranslatedValue - expectedFilteredTranslatedValue); + + if (iteration === 0) { + // On the first iteration, the unfiltered values are returned. + expect(filteredOriginalValue).toEqual(originalDataPoint.value); + expect(actualFilteredTranslatedValue) + .toEqual(translatedDataPoint.value); + expect(difference).toEqual(0); + } else { + if (difference >= divergenceGap) { + ++timesDiverged; + } + if (difference >= largeDivergenceGap) { + ++timesLargelyDiverged; + } + } + } + + expect(timesDiverged).toBeGreaterThanOrEqual(divergenceTimes); + expect(timesLargelyDiverged).toBeGreaterThanOrEqual(largeDivergenceTimes); + }); +}); diff --git a/pose-detection/src/shared/filters/visibility_smoothing.ts b/pose-detection/src/shared/filters/visibility_smoothing.ts new file mode 100644 index 0000000000..e6e60262c5 --- /dev/null +++ b/pose-detection/src/shared/filters/visibility_smoothing.ts @@ -0,0 +1,61 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from '../calculators/interfaces/common_interfaces'; + +import {VisibilitySmoothingConfig} from '../calculators/interfaces/config_interfaces'; +import {LowPassFilter} from './low_pass_filter'; + +/** + * Smoothing visibility using a `LowPassFilter` for each landmark. + */ +export class LowPassVisibilityFilter { + private readonly alpha: number; + private visibilityFilters: LowPassFilter[]; + + constructor(config: VisibilitySmoothingConfig) { + this.alpha = config.alpha; + } + + apply(landmarks?: Keypoint[]): Keypoint[] { + if (landmarks == null) { + // Reset filters. + this.visibilityFilters = null; + return null; + } + if (this.visibilityFilters == null || + (this.visibilityFilters.length !== landmarks.length)) { + // Initialize new filters. + this.visibilityFilters = + landmarks.map(_ => new LowPassFilter(this.alpha)); + } + + const outLandmarks = []; + + // Filter visibilities. + for (let i = 0; i < landmarks.length; ++i) { + const landmark = landmarks[i]; + + const outLandmark = {...landmark}; + outLandmark.score = this.visibilityFilters[i].apply(landmark.score); + + outLandmarks.push(outLandmark); + } + + return outLandmarks; + } +} diff --git a/pose-detection/src/shared/test_data/anchor_golden_file_0.json b/pose-detection/src/shared/test_data/anchor_golden_file_0.json new file mode 100644 index 0000000000..e5cc332330 --- /dev/null +++ b/pose-detection/src/shared/test_data/anchor_golden_file_0.json @@ -0,0 +1 @@ +[[0.015625, 0.015625, 1.0, 1.0], [0.015625, 0.015625, 1.0, 1.0], [0.046875, 0.015625, 1.0, 1.0], [0.046875, 0.015625, 1.0, 1.0], [0.078125, 0.015625, 1.0, 1.0], [0.078125, 0.015625, 1.0, 1.0], [0.109375, 0.015625, 1.0, 1.0], [0.109375, 0.015625, 1.0, 1.0], [0.140625, 0.015625, 1.0, 1.0], [0.140625, 0.015625, 1.0, 1.0], [0.171875, 0.015625, 1.0, 1.0], [0.171875, 0.015625, 1.0, 1.0], [0.203125, 0.015625, 1.0, 1.0], [0.203125, 0.015625, 1.0, 1.0], [0.234375, 0.015625, 1.0, 1.0], [0.234375, 0.015625, 1.0, 1.0], [0.265625, 0.015625, 1.0, 1.0], [0.265625, 0.015625, 1.0, 1.0], [0.296875, 0.015625, 1.0, 1.0], [0.296875, 0.015625, 1.0, 1.0], [0.328125, 0.015625, 1.0, 1.0], [0.328125, 0.015625, 1.0, 1.0], [0.359375, 0.015625, 1.0, 1.0], [0.359375, 0.015625, 1.0, 1.0], [0.390625, 0.015625, 1.0, 1.0], [0.390625, 0.015625, 1.0, 1.0], [0.421875, 0.015625, 1.0, 1.0], [0.421875, 0.015625, 1.0, 1.0], [0.453125, 0.015625, 1.0, 1.0], [0.453125, 0.015625, 1.0, 1.0], [0.484375, 0.015625, 1.0, 1.0], [0.484375, 0.015625, 1.0, 1.0], [0.515625, 0.015625, 1.0, 1.0], [0.515625, 0.015625, 1.0, 1.0], [0.546875, 0.015625, 1.0, 1.0], [0.546875, 0.015625, 1.0, 1.0], [0.578125, 0.015625, 1.0, 1.0], [0.578125, 0.015625, 1.0, 1.0], [0.609375, 0.015625, 1.0, 1.0], [0.609375, 0.015625, 1.0, 1.0], [0.640625, 0.015625, 1.0, 1.0], [0.640625, 0.015625, 1.0, 1.0], [0.671875, 0.015625, 1.0, 1.0], [0.671875, 0.015625, 1.0, 1.0], [0.703125, 0.015625, 1.0, 1.0], [0.703125, 0.015625, 1.0, 1.0], [0.734375, 0.015625, 1.0, 1.0], [0.734375, 0.015625, 1.0, 1.0], [0.765625, 0.015625, 1.0, 1.0], [0.765625, 0.015625, 1.0, 1.0], [0.796875, 0.015625, 1.0, 1.0], [0.796875, 0.015625, 1.0, 1.0], [0.828125, 0.015625, 1.0, 1.0], [0.828125, 0.015625, 1.0, 1.0], [0.859375, 0.015625, 1.0, 1.0], [0.859375, 0.015625, 1.0, 1.0], [0.890625, 0.015625, 1.0, 1.0], [0.890625, 0.015625, 1.0, 1.0], [0.921875, 0.015625, 1.0, 1.0], [0.921875, 0.015625, 1.0, 1.0], [0.953125, 0.015625, 1.0, 1.0], [0.953125, 0.015625, 1.0, 1.0], [0.984375, 0.015625, 1.0, 1.0], [0.984375, 0.015625, 1.0, 1.0], [0.015625, 0.046875, 1.0, 1.0], [0.015625, 0.046875, 1.0, 1.0], [0.046875, 0.046875, 1.0, 1.0], [0.046875, 0.046875, 1.0, 1.0], [0.078125, 0.046875, 1.0, 1.0], [0.078125, 0.046875, 1.0, 1.0], [0.109375, 0.046875, 1.0, 1.0], [0.109375, 0.046875, 1.0, 1.0], [0.140625, 0.046875, 1.0, 1.0], [0.140625, 0.046875, 1.0, 1.0], [0.171875, 0.046875, 1.0, 1.0], [0.171875, 0.046875, 1.0, 1.0], [0.203125, 0.046875, 1.0, 1.0], [0.203125, 0.046875, 1.0, 1.0], [0.234375, 0.046875, 1.0, 1.0], [0.234375, 0.046875, 1.0, 1.0], [0.265625, 0.046875, 1.0, 1.0], [0.265625, 0.046875, 1.0, 1.0], [0.296875, 0.046875, 1.0, 1.0], [0.296875, 0.046875, 1.0, 1.0], [0.328125, 0.046875, 1.0, 1.0], [0.328125, 0.046875, 1.0, 1.0], [0.359375, 0.046875, 1.0, 1.0], [0.359375, 0.046875, 1.0, 1.0], [0.390625, 0.046875, 1.0, 1.0], [0.390625, 0.046875, 1.0, 1.0], [0.421875, 0.046875, 1.0, 1.0], [0.421875, 0.046875, 1.0, 1.0], [0.453125, 0.046875, 1.0, 1.0], [0.453125, 0.046875, 1.0, 1.0], [0.484375, 0.046875, 1.0, 1.0], [0.484375, 0.046875, 1.0, 1.0], [0.515625, 0.046875, 1.0, 1.0], [0.515625, 0.046875, 1.0, 1.0], [0.546875, 0.046875, 1.0, 1.0], [0.546875, 0.046875, 1.0, 1.0], [0.578125, 0.046875, 1.0, 1.0], [0.578125, 0.046875, 1.0, 1.0], [0.609375, 0.046875, 1.0, 1.0], [0.609375, 0.046875, 1.0, 1.0], [0.640625, 0.046875, 1.0, 1.0], [0.640625, 0.046875, 1.0, 1.0], [0.671875, 0.046875, 1.0, 1.0], [0.671875, 0.046875, 1.0, 1.0], [0.703125, 0.046875, 1.0, 1.0], [0.703125, 0.046875, 1.0, 1.0], [0.734375, 0.046875, 1.0, 1.0], [0.734375, 0.046875, 1.0, 1.0], [0.765625, 0.046875, 1.0, 1.0], [0.765625, 0.046875, 1.0, 1.0], [0.796875, 0.046875, 1.0, 1.0], [0.796875, 0.046875, 1.0, 1.0], [0.828125, 0.046875, 1.0, 1.0], [0.828125, 0.046875, 1.0, 1.0], [0.859375, 0.046875, 1.0, 1.0], [0.859375, 0.046875, 1.0, 1.0], [0.890625, 0.046875, 1.0, 1.0], [0.890625, 0.046875, 1.0, 1.0], [0.921875, 0.046875, 1.0, 1.0], [0.921875, 0.046875, 1.0, 1.0], [0.953125, 0.046875, 1.0, 1.0], [0.953125, 0.046875, 1.0, 1.0], [0.984375, 0.046875, 1.0, 1.0], [0.984375, 0.046875, 1.0, 1.0], [0.015625, 0.078125, 1.0, 1.0], [0.015625, 0.078125, 1.0, 1.0], [0.046875, 0.078125, 1.0, 1.0], [0.046875, 0.078125, 1.0, 1.0], [0.078125, 0.078125, 1.0, 1.0], [0.078125, 0.078125, 1.0, 1.0], [0.109375, 0.078125, 1.0, 1.0], [0.109375, 0.078125, 1.0, 1.0], [0.140625, 0.078125, 1.0, 1.0], [0.140625, 0.078125, 1.0, 1.0], [0.171875, 0.078125, 1.0, 1.0], [0.171875, 0.078125, 1.0, 1.0], [0.203125, 0.078125, 1.0, 1.0], [0.203125, 0.078125, 1.0, 1.0], [0.234375, 0.078125, 1.0, 1.0], [0.234375, 0.078125, 1.0, 1.0], [0.265625, 0.078125, 1.0, 1.0], [0.265625, 0.078125, 1.0, 1.0], [0.296875, 0.078125, 1.0, 1.0], [0.296875, 0.078125, 1.0, 1.0], [0.328125, 0.078125, 1.0, 1.0], [0.328125, 0.078125, 1.0, 1.0], [0.359375, 0.078125, 1.0, 1.0], [0.359375, 0.078125, 1.0, 1.0], [0.390625, 0.078125, 1.0, 1.0], [0.390625, 0.078125, 1.0, 1.0], [0.421875, 0.078125, 1.0, 1.0], [0.421875, 0.078125, 1.0, 1.0], [0.453125, 0.078125, 1.0, 1.0], [0.453125, 0.078125, 1.0, 1.0], [0.484375, 0.078125, 1.0, 1.0], [0.484375, 0.078125, 1.0, 1.0], [0.515625, 0.078125, 1.0, 1.0], [0.515625, 0.078125, 1.0, 1.0], [0.546875, 0.078125, 1.0, 1.0], [0.546875, 0.078125, 1.0, 1.0], [0.578125, 0.078125, 1.0, 1.0], [0.578125, 0.078125, 1.0, 1.0], [0.609375, 0.078125, 1.0, 1.0], [0.609375, 0.078125, 1.0, 1.0], [0.640625, 0.078125, 1.0, 1.0], [0.640625, 0.078125, 1.0, 1.0], [0.671875, 0.078125, 1.0, 1.0], [0.671875, 0.078125, 1.0, 1.0], [0.703125, 0.078125, 1.0, 1.0], [0.703125, 0.078125, 1.0, 1.0], [0.734375, 0.078125, 1.0, 1.0], [0.734375, 0.078125, 1.0, 1.0], [0.765625, 0.078125, 1.0, 1.0], [0.765625, 0.078125, 1.0, 1.0], [0.796875, 0.078125, 1.0, 1.0], [0.796875, 0.078125, 1.0, 1.0], [0.828125, 0.078125, 1.0, 1.0], [0.828125, 0.078125, 1.0, 1.0], [0.859375, 0.078125, 1.0, 1.0], [0.859375, 0.078125, 1.0, 1.0], [0.890625, 0.078125, 1.0, 1.0], [0.890625, 0.078125, 1.0, 1.0], [0.921875, 0.078125, 1.0, 1.0], [0.921875, 0.078125, 1.0, 1.0], [0.953125, 0.078125, 1.0, 1.0], [0.953125, 0.078125, 1.0, 1.0], [0.984375, 0.078125, 1.0, 1.0], [0.984375, 0.078125, 1.0, 1.0], [0.015625, 0.109375, 1.0, 1.0], [0.015625, 0.109375, 1.0, 1.0], [0.046875, 0.109375, 1.0, 1.0], [0.046875, 0.109375, 1.0, 1.0], [0.078125, 0.109375, 1.0, 1.0], [0.078125, 0.109375, 1.0, 1.0], [0.109375, 0.109375, 1.0, 1.0], [0.109375, 0.109375, 1.0, 1.0], [0.140625, 0.109375, 1.0, 1.0], [0.140625, 0.109375, 1.0, 1.0], [0.171875, 0.109375, 1.0, 1.0], [0.171875, 0.109375, 1.0, 1.0], [0.203125, 0.109375, 1.0, 1.0], [0.203125, 0.109375, 1.0, 1.0], [0.234375, 0.109375, 1.0, 1.0], [0.234375, 0.109375, 1.0, 1.0], [0.265625, 0.109375, 1.0, 1.0], [0.265625, 0.109375, 1.0, 1.0], [0.296875, 0.109375, 1.0, 1.0], [0.296875, 0.109375, 1.0, 1.0], [0.328125, 0.109375, 1.0, 1.0], [0.328125, 0.109375, 1.0, 1.0], [0.359375, 0.109375, 1.0, 1.0], [0.359375, 0.109375, 1.0, 1.0], [0.390625, 0.109375, 1.0, 1.0], [0.390625, 0.109375, 1.0, 1.0], [0.421875, 0.109375, 1.0, 1.0], [0.421875, 0.109375, 1.0, 1.0], [0.453125, 0.109375, 1.0, 1.0], [0.453125, 0.109375, 1.0, 1.0], [0.484375, 0.109375, 1.0, 1.0], [0.484375, 0.109375, 1.0, 1.0], [0.515625, 0.109375, 1.0, 1.0], [0.515625, 0.109375, 1.0, 1.0], [0.546875, 0.109375, 1.0, 1.0], [0.546875, 0.109375, 1.0, 1.0], [0.578125, 0.109375, 1.0, 1.0], [0.578125, 0.109375, 1.0, 1.0], [0.609375, 0.109375, 1.0, 1.0], [0.609375, 0.109375, 1.0, 1.0], [0.640625, 0.109375, 1.0, 1.0], [0.640625, 0.109375, 1.0, 1.0], [0.671875, 0.109375, 1.0, 1.0], [0.671875, 0.109375, 1.0, 1.0], [0.703125, 0.109375, 1.0, 1.0], [0.703125, 0.109375, 1.0, 1.0], [0.734375, 0.109375, 1.0, 1.0], [0.734375, 0.109375, 1.0, 1.0], [0.765625, 0.109375, 1.0, 1.0], [0.765625, 0.109375, 1.0, 1.0], [0.796875, 0.109375, 1.0, 1.0], [0.796875, 0.109375, 1.0, 1.0], [0.828125, 0.109375, 1.0, 1.0], [0.828125, 0.109375, 1.0, 1.0], [0.859375, 0.109375, 1.0, 1.0], [0.859375, 0.109375, 1.0, 1.0], [0.890625, 0.109375, 1.0, 1.0], [0.890625, 0.109375, 1.0, 1.0], [0.921875, 0.109375, 1.0, 1.0], [0.921875, 0.109375, 1.0, 1.0], [0.953125, 0.109375, 1.0, 1.0], [0.953125, 0.109375, 1.0, 1.0], [0.984375, 0.109375, 1.0, 1.0], [0.984375, 0.109375, 1.0, 1.0], [0.015625, 0.140625, 1.0, 1.0], [0.015625, 0.140625, 1.0, 1.0], [0.046875, 0.140625, 1.0, 1.0], [0.046875, 0.140625, 1.0, 1.0], [0.078125, 0.140625, 1.0, 1.0], [0.078125, 0.140625, 1.0, 1.0], [0.109375, 0.140625, 1.0, 1.0], [0.109375, 0.140625, 1.0, 1.0], [0.140625, 0.140625, 1.0, 1.0], [0.140625, 0.140625, 1.0, 1.0], [0.171875, 0.140625, 1.0, 1.0], [0.171875, 0.140625, 1.0, 1.0], [0.203125, 0.140625, 1.0, 1.0], [0.203125, 0.140625, 1.0, 1.0], [0.234375, 0.140625, 1.0, 1.0], [0.234375, 0.140625, 1.0, 1.0], [0.265625, 0.140625, 1.0, 1.0], [0.265625, 0.140625, 1.0, 1.0], [0.296875, 0.140625, 1.0, 1.0], [0.296875, 0.140625, 1.0, 1.0], [0.328125, 0.140625, 1.0, 1.0], [0.328125, 0.140625, 1.0, 1.0], [0.359375, 0.140625, 1.0, 1.0], [0.359375, 0.140625, 1.0, 1.0], [0.390625, 0.140625, 1.0, 1.0], [0.390625, 0.140625, 1.0, 1.0], [0.421875, 0.140625, 1.0, 1.0], [0.421875, 0.140625, 1.0, 1.0], [0.453125, 0.140625, 1.0, 1.0], [0.453125, 0.140625, 1.0, 1.0], [0.484375, 0.140625, 1.0, 1.0], [0.484375, 0.140625, 1.0, 1.0], [0.515625, 0.140625, 1.0, 1.0], [0.515625, 0.140625, 1.0, 1.0], [0.546875, 0.140625, 1.0, 1.0], [0.546875, 0.140625, 1.0, 1.0], [0.578125, 0.140625, 1.0, 1.0], [0.578125, 0.140625, 1.0, 1.0], [0.609375, 0.140625, 1.0, 1.0], [0.609375, 0.140625, 1.0, 1.0], [0.640625, 0.140625, 1.0, 1.0], [0.640625, 0.140625, 1.0, 1.0], [0.671875, 0.140625, 1.0, 1.0], [0.671875, 0.140625, 1.0, 1.0], [0.703125, 0.140625, 1.0, 1.0], [0.703125, 0.140625, 1.0, 1.0], [0.734375, 0.140625, 1.0, 1.0], [0.734375, 0.140625, 1.0, 1.0], [0.765625, 0.140625, 1.0, 1.0], [0.765625, 0.140625, 1.0, 1.0], [0.796875, 0.140625, 1.0, 1.0], [0.796875, 0.140625, 1.0, 1.0], [0.828125, 0.140625, 1.0, 1.0], [0.828125, 0.140625, 1.0, 1.0], [0.859375, 0.140625, 1.0, 1.0], [0.859375, 0.140625, 1.0, 1.0], [0.890625, 0.140625, 1.0, 1.0], [0.890625, 0.140625, 1.0, 1.0], [0.921875, 0.140625, 1.0, 1.0], [0.921875, 0.140625, 1.0, 1.0], [0.953125, 0.140625, 1.0, 1.0], [0.953125, 0.140625, 1.0, 1.0], [0.984375, 0.140625, 1.0, 1.0], [0.984375, 0.140625, 1.0, 1.0], [0.015625, 0.171875, 1.0, 1.0], [0.015625, 0.171875, 1.0, 1.0], [0.046875, 0.171875, 1.0, 1.0], [0.046875, 0.171875, 1.0, 1.0], [0.078125, 0.171875, 1.0, 1.0], [0.078125, 0.171875, 1.0, 1.0], [0.109375, 0.171875, 1.0, 1.0], [0.109375, 0.171875, 1.0, 1.0], [0.140625, 0.171875, 1.0, 1.0], [0.140625, 0.171875, 1.0, 1.0], [0.171875, 0.171875, 1.0, 1.0], [0.171875, 0.171875, 1.0, 1.0], [0.203125, 0.171875, 1.0, 1.0], [0.203125, 0.171875, 1.0, 1.0], [0.234375, 0.171875, 1.0, 1.0], [0.234375, 0.171875, 1.0, 1.0], [0.265625, 0.171875, 1.0, 1.0], [0.265625, 0.171875, 1.0, 1.0], [0.296875, 0.171875, 1.0, 1.0], [0.296875, 0.171875, 1.0, 1.0], [0.328125, 0.171875, 1.0, 1.0], [0.328125, 0.171875, 1.0, 1.0], [0.359375, 0.171875, 1.0, 1.0], [0.359375, 0.171875, 1.0, 1.0], [0.390625, 0.171875, 1.0, 1.0], [0.390625, 0.171875, 1.0, 1.0], [0.421875, 0.171875, 1.0, 1.0], [0.421875, 0.171875, 1.0, 1.0], [0.453125, 0.171875, 1.0, 1.0], [0.453125, 0.171875, 1.0, 1.0], [0.484375, 0.171875, 1.0, 1.0], [0.484375, 0.171875, 1.0, 1.0], [0.515625, 0.171875, 1.0, 1.0], [0.515625, 0.171875, 1.0, 1.0], [0.546875, 0.171875, 1.0, 1.0], [0.546875, 0.171875, 1.0, 1.0], [0.578125, 0.171875, 1.0, 1.0], [0.578125, 0.171875, 1.0, 1.0], [0.609375, 0.171875, 1.0, 1.0], [0.609375, 0.171875, 1.0, 1.0], [0.640625, 0.171875, 1.0, 1.0], [0.640625, 0.171875, 1.0, 1.0], [0.671875, 0.171875, 1.0, 1.0], [0.671875, 0.171875, 1.0, 1.0], [0.703125, 0.171875, 1.0, 1.0], [0.703125, 0.171875, 1.0, 1.0], [0.734375, 0.171875, 1.0, 1.0], [0.734375, 0.171875, 1.0, 1.0], [0.765625, 0.171875, 1.0, 1.0], [0.765625, 0.171875, 1.0, 1.0], [0.796875, 0.171875, 1.0, 1.0], [0.796875, 0.171875, 1.0, 1.0], [0.828125, 0.171875, 1.0, 1.0], [0.828125, 0.171875, 1.0, 1.0], [0.859375, 0.171875, 1.0, 1.0], [0.859375, 0.171875, 1.0, 1.0], [0.890625, 0.171875, 1.0, 1.0], [0.890625, 0.171875, 1.0, 1.0], [0.921875, 0.171875, 1.0, 1.0], [0.921875, 0.171875, 1.0, 1.0], [0.953125, 0.171875, 1.0, 1.0], [0.953125, 0.171875, 1.0, 1.0], [0.984375, 0.171875, 1.0, 1.0], [0.984375, 0.171875, 1.0, 1.0], [0.015625, 0.203125, 1.0, 1.0], [0.015625, 0.203125, 1.0, 1.0], [0.046875, 0.203125, 1.0, 1.0], [0.046875, 0.203125, 1.0, 1.0], [0.078125, 0.203125, 1.0, 1.0], [0.078125, 0.203125, 1.0, 1.0], [0.109375, 0.203125, 1.0, 1.0], [0.109375, 0.203125, 1.0, 1.0], [0.140625, 0.203125, 1.0, 1.0], [0.140625, 0.203125, 1.0, 1.0], [0.171875, 0.203125, 1.0, 1.0], [0.171875, 0.203125, 1.0, 1.0], [0.203125, 0.203125, 1.0, 1.0], [0.203125, 0.203125, 1.0, 1.0], [0.234375, 0.203125, 1.0, 1.0], [0.234375, 0.203125, 1.0, 1.0], [0.265625, 0.203125, 1.0, 1.0], [0.265625, 0.203125, 1.0, 1.0], [0.296875, 0.203125, 1.0, 1.0], [0.296875, 0.203125, 1.0, 1.0], [0.328125, 0.203125, 1.0, 1.0], [0.328125, 0.203125, 1.0, 1.0], [0.359375, 0.203125, 1.0, 1.0], [0.359375, 0.203125, 1.0, 1.0], [0.390625, 0.203125, 1.0, 1.0], [0.390625, 0.203125, 1.0, 1.0], [0.421875, 0.203125, 1.0, 1.0], [0.421875, 0.203125, 1.0, 1.0], [0.453125, 0.203125, 1.0, 1.0], [0.453125, 0.203125, 1.0, 1.0], [0.484375, 0.203125, 1.0, 1.0], [0.484375, 0.203125, 1.0, 1.0], [0.515625, 0.203125, 1.0, 1.0], [0.515625, 0.203125, 1.0, 1.0], [0.546875, 0.203125, 1.0, 1.0], [0.546875, 0.203125, 1.0, 1.0], [0.578125, 0.203125, 1.0, 1.0], [0.578125, 0.203125, 1.0, 1.0], [0.609375, 0.203125, 1.0, 1.0], [0.609375, 0.203125, 1.0, 1.0], [0.640625, 0.203125, 1.0, 1.0], [0.640625, 0.203125, 1.0, 1.0], [0.671875, 0.203125, 1.0, 1.0], [0.671875, 0.203125, 1.0, 1.0], [0.703125, 0.203125, 1.0, 1.0], [0.703125, 0.203125, 1.0, 1.0], [0.734375, 0.203125, 1.0, 1.0], [0.734375, 0.203125, 1.0, 1.0], [0.765625, 0.203125, 1.0, 1.0], [0.765625, 0.203125, 1.0, 1.0], [0.796875, 0.203125, 1.0, 1.0], [0.796875, 0.203125, 1.0, 1.0], [0.828125, 0.203125, 1.0, 1.0], [0.828125, 0.203125, 1.0, 1.0], [0.859375, 0.203125, 1.0, 1.0], [0.859375, 0.203125, 1.0, 1.0], [0.890625, 0.203125, 1.0, 1.0], [0.890625, 0.203125, 1.0, 1.0], [0.921875, 0.203125, 1.0, 1.0], [0.921875, 0.203125, 1.0, 1.0], [0.953125, 0.203125, 1.0, 1.0], [0.953125, 0.203125, 1.0, 1.0], [0.984375, 0.203125, 1.0, 1.0], [0.984375, 0.203125, 1.0, 1.0], [0.015625, 0.234375, 1.0, 1.0], [0.015625, 0.234375, 1.0, 1.0], [0.046875, 0.234375, 1.0, 1.0], [0.046875, 0.234375, 1.0, 1.0], [0.078125, 0.234375, 1.0, 1.0], [0.078125, 0.234375, 1.0, 1.0], [0.109375, 0.234375, 1.0, 1.0], [0.109375, 0.234375, 1.0, 1.0], [0.140625, 0.234375, 1.0, 1.0], [0.140625, 0.234375, 1.0, 1.0], [0.171875, 0.234375, 1.0, 1.0], [0.171875, 0.234375, 1.0, 1.0], [0.203125, 0.234375, 1.0, 1.0], [0.203125, 0.234375, 1.0, 1.0], [0.234375, 0.234375, 1.0, 1.0], [0.234375, 0.234375, 1.0, 1.0], [0.265625, 0.234375, 1.0, 1.0], [0.265625, 0.234375, 1.0, 1.0], [0.296875, 0.234375, 1.0, 1.0], [0.296875, 0.234375, 1.0, 1.0], [0.328125, 0.234375, 1.0, 1.0], [0.328125, 0.234375, 1.0, 1.0], [0.359375, 0.234375, 1.0, 1.0], [0.359375, 0.234375, 1.0, 1.0], [0.390625, 0.234375, 1.0, 1.0], [0.390625, 0.234375, 1.0, 1.0], [0.421875, 0.234375, 1.0, 1.0], [0.421875, 0.234375, 1.0, 1.0], [0.453125, 0.234375, 1.0, 1.0], [0.453125, 0.234375, 1.0, 1.0], [0.484375, 0.234375, 1.0, 1.0], [0.484375, 0.234375, 1.0, 1.0], [0.515625, 0.234375, 1.0, 1.0], [0.515625, 0.234375, 1.0, 1.0], [0.546875, 0.234375, 1.0, 1.0], [0.546875, 0.234375, 1.0, 1.0], [0.578125, 0.234375, 1.0, 1.0], [0.578125, 0.234375, 1.0, 1.0], [0.609375, 0.234375, 1.0, 1.0], [0.609375, 0.234375, 1.0, 1.0], [0.640625, 0.234375, 1.0, 1.0], [0.640625, 0.234375, 1.0, 1.0], [0.671875, 0.234375, 1.0, 1.0], [0.671875, 0.234375, 1.0, 1.0], [0.703125, 0.234375, 1.0, 1.0], [0.703125, 0.234375, 1.0, 1.0], [0.734375, 0.234375, 1.0, 1.0], [0.734375, 0.234375, 1.0, 1.0], [0.765625, 0.234375, 1.0, 1.0], [0.765625, 0.234375, 1.0, 1.0], [0.796875, 0.234375, 1.0, 1.0], [0.796875, 0.234375, 1.0, 1.0], [0.828125, 0.234375, 1.0, 1.0], [0.828125, 0.234375, 1.0, 1.0], [0.859375, 0.234375, 1.0, 1.0], [0.859375, 0.234375, 1.0, 1.0], [0.890625, 0.234375, 1.0, 1.0], [0.890625, 0.234375, 1.0, 1.0], [0.921875, 0.234375, 1.0, 1.0], [0.921875, 0.234375, 1.0, 1.0], [0.953125, 0.234375, 1.0, 1.0], [0.953125, 0.234375, 1.0, 1.0], [0.984375, 0.234375, 1.0, 1.0], [0.984375, 0.234375, 1.0, 1.0], [0.015625, 0.265625, 1.0, 1.0], [0.015625, 0.265625, 1.0, 1.0], [0.046875, 0.265625, 1.0, 1.0], [0.046875, 0.265625, 1.0, 1.0], [0.078125, 0.265625, 1.0, 1.0], [0.078125, 0.265625, 1.0, 1.0], [0.109375, 0.265625, 1.0, 1.0], [0.109375, 0.265625, 1.0, 1.0], [0.140625, 0.265625, 1.0, 1.0], [0.140625, 0.265625, 1.0, 1.0], [0.171875, 0.265625, 1.0, 1.0], [0.171875, 0.265625, 1.0, 1.0], [0.203125, 0.265625, 1.0, 1.0], [0.203125, 0.265625, 1.0, 1.0], [0.234375, 0.265625, 1.0, 1.0], [0.234375, 0.265625, 1.0, 1.0], [0.265625, 0.265625, 1.0, 1.0], [0.265625, 0.265625, 1.0, 1.0], [0.296875, 0.265625, 1.0, 1.0], [0.296875, 0.265625, 1.0, 1.0], [0.328125, 0.265625, 1.0, 1.0], [0.328125, 0.265625, 1.0, 1.0], [0.359375, 0.265625, 1.0, 1.0], [0.359375, 0.265625, 1.0, 1.0], [0.390625, 0.265625, 1.0, 1.0], [0.390625, 0.265625, 1.0, 1.0], [0.421875, 0.265625, 1.0, 1.0], [0.421875, 0.265625, 1.0, 1.0], [0.453125, 0.265625, 1.0, 1.0], [0.453125, 0.265625, 1.0, 1.0], [0.484375, 0.265625, 1.0, 1.0], [0.484375, 0.265625, 1.0, 1.0], [0.515625, 0.265625, 1.0, 1.0], [0.515625, 0.265625, 1.0, 1.0], [0.546875, 0.265625, 1.0, 1.0], [0.546875, 0.265625, 1.0, 1.0], [0.578125, 0.265625, 1.0, 1.0], [0.578125, 0.265625, 1.0, 1.0], [0.609375, 0.265625, 1.0, 1.0], [0.609375, 0.265625, 1.0, 1.0], [0.640625, 0.265625, 1.0, 1.0], [0.640625, 0.265625, 1.0, 1.0], [0.671875, 0.265625, 1.0, 1.0], [0.671875, 0.265625, 1.0, 1.0], [0.703125, 0.265625, 1.0, 1.0], [0.703125, 0.265625, 1.0, 1.0], [0.734375, 0.265625, 1.0, 1.0], [0.734375, 0.265625, 1.0, 1.0], [0.765625, 0.265625, 1.0, 1.0], [0.765625, 0.265625, 1.0, 1.0], [0.796875, 0.265625, 1.0, 1.0], [0.796875, 0.265625, 1.0, 1.0], [0.828125, 0.265625, 1.0, 1.0], [0.828125, 0.265625, 1.0, 1.0], [0.859375, 0.265625, 1.0, 1.0], [0.859375, 0.265625, 1.0, 1.0], [0.890625, 0.265625, 1.0, 1.0], [0.890625, 0.265625, 1.0, 1.0], [0.921875, 0.265625, 1.0, 1.0], [0.921875, 0.265625, 1.0, 1.0], [0.953125, 0.265625, 1.0, 1.0], [0.953125, 0.265625, 1.0, 1.0], [0.984375, 0.265625, 1.0, 1.0], [0.984375, 0.265625, 1.0, 1.0], [0.015625, 0.296875, 1.0, 1.0], [0.015625, 0.296875, 1.0, 1.0], [0.046875, 0.296875, 1.0, 1.0], [0.046875, 0.296875, 1.0, 1.0], [0.078125, 0.296875, 1.0, 1.0], [0.078125, 0.296875, 1.0, 1.0], [0.109375, 0.296875, 1.0, 1.0], [0.109375, 0.296875, 1.0, 1.0], [0.140625, 0.296875, 1.0, 1.0], [0.140625, 0.296875, 1.0, 1.0], [0.171875, 0.296875, 1.0, 1.0], [0.171875, 0.296875, 1.0, 1.0], [0.203125, 0.296875, 1.0, 1.0], [0.203125, 0.296875, 1.0, 1.0], [0.234375, 0.296875, 1.0, 1.0], [0.234375, 0.296875, 1.0, 1.0], [0.265625, 0.296875, 1.0, 1.0], [0.265625, 0.296875, 1.0, 1.0], [0.296875, 0.296875, 1.0, 1.0], [0.296875, 0.296875, 1.0, 1.0], [0.328125, 0.296875, 1.0, 1.0], [0.328125, 0.296875, 1.0, 1.0], [0.359375, 0.296875, 1.0, 1.0], [0.359375, 0.296875, 1.0, 1.0], [0.390625, 0.296875, 1.0, 1.0], [0.390625, 0.296875, 1.0, 1.0], [0.421875, 0.296875, 1.0, 1.0], [0.421875, 0.296875, 1.0, 1.0], [0.453125, 0.296875, 1.0, 1.0], [0.453125, 0.296875, 1.0, 1.0], [0.484375, 0.296875, 1.0, 1.0], [0.484375, 0.296875, 1.0, 1.0], [0.515625, 0.296875, 1.0, 1.0], [0.515625, 0.296875, 1.0, 1.0], [0.546875, 0.296875, 1.0, 1.0], [0.546875, 0.296875, 1.0, 1.0], [0.578125, 0.296875, 1.0, 1.0], [0.578125, 0.296875, 1.0, 1.0], [0.609375, 0.296875, 1.0, 1.0], [0.609375, 0.296875, 1.0, 1.0], [0.640625, 0.296875, 1.0, 1.0], [0.640625, 0.296875, 1.0, 1.0], [0.671875, 0.296875, 1.0, 1.0], [0.671875, 0.296875, 1.0, 1.0], [0.703125, 0.296875, 1.0, 1.0], [0.703125, 0.296875, 1.0, 1.0], [0.734375, 0.296875, 1.0, 1.0], [0.734375, 0.296875, 1.0, 1.0], [0.765625, 0.296875, 1.0, 1.0], [0.765625, 0.296875, 1.0, 1.0], [0.796875, 0.296875, 1.0, 1.0], [0.796875, 0.296875, 1.0, 1.0], [0.828125, 0.296875, 1.0, 1.0], [0.828125, 0.296875, 1.0, 1.0], [0.859375, 0.296875, 1.0, 1.0], [0.859375, 0.296875, 1.0, 1.0], [0.890625, 0.296875, 1.0, 1.0], [0.890625, 0.296875, 1.0, 1.0], [0.921875, 0.296875, 1.0, 1.0], [0.921875, 0.296875, 1.0, 1.0], [0.953125, 0.296875, 1.0, 1.0], [0.953125, 0.296875, 1.0, 1.0], [0.984375, 0.296875, 1.0, 1.0], [0.984375, 0.296875, 1.0, 1.0], [0.015625, 0.328125, 1.0, 1.0], [0.015625, 0.328125, 1.0, 1.0], [0.046875, 0.328125, 1.0, 1.0], [0.046875, 0.328125, 1.0, 1.0], [0.078125, 0.328125, 1.0, 1.0], [0.078125, 0.328125, 1.0, 1.0], [0.109375, 0.328125, 1.0, 1.0], [0.109375, 0.328125, 1.0, 1.0], [0.140625, 0.328125, 1.0, 1.0], [0.140625, 0.328125, 1.0, 1.0], [0.171875, 0.328125, 1.0, 1.0], [0.171875, 0.328125, 1.0, 1.0], [0.203125, 0.328125, 1.0, 1.0], [0.203125, 0.328125, 1.0, 1.0], [0.234375, 0.328125, 1.0, 1.0], [0.234375, 0.328125, 1.0, 1.0], [0.265625, 0.328125, 1.0, 1.0], [0.265625, 0.328125, 1.0, 1.0], [0.296875, 0.328125, 1.0, 1.0], [0.296875, 0.328125, 1.0, 1.0], [0.328125, 0.328125, 1.0, 1.0], [0.328125, 0.328125, 1.0, 1.0], [0.359375, 0.328125, 1.0, 1.0], [0.359375, 0.328125, 1.0, 1.0], [0.390625, 0.328125, 1.0, 1.0], [0.390625, 0.328125, 1.0, 1.0], [0.421875, 0.328125, 1.0, 1.0], [0.421875, 0.328125, 1.0, 1.0], [0.453125, 0.328125, 1.0, 1.0], [0.453125, 0.328125, 1.0, 1.0], [0.484375, 0.328125, 1.0, 1.0], [0.484375, 0.328125, 1.0, 1.0], [0.515625, 0.328125, 1.0, 1.0], [0.515625, 0.328125, 1.0, 1.0], [0.546875, 0.328125, 1.0, 1.0], [0.546875, 0.328125, 1.0, 1.0], [0.578125, 0.328125, 1.0, 1.0], [0.578125, 0.328125, 1.0, 1.0], [0.609375, 0.328125, 1.0, 1.0], [0.609375, 0.328125, 1.0, 1.0], [0.640625, 0.328125, 1.0, 1.0], [0.640625, 0.328125, 1.0, 1.0], [0.671875, 0.328125, 1.0, 1.0], [0.671875, 0.328125, 1.0, 1.0], [0.703125, 0.328125, 1.0, 1.0], [0.703125, 0.328125, 1.0, 1.0], [0.734375, 0.328125, 1.0, 1.0], [0.734375, 0.328125, 1.0, 1.0], [0.765625, 0.328125, 1.0, 1.0], [0.765625, 0.328125, 1.0, 1.0], [0.796875, 0.328125, 1.0, 1.0], [0.796875, 0.328125, 1.0, 1.0], [0.828125, 0.328125, 1.0, 1.0], [0.828125, 0.328125, 1.0, 1.0], [0.859375, 0.328125, 1.0, 1.0], [0.859375, 0.328125, 1.0, 1.0], [0.890625, 0.328125, 1.0, 1.0], [0.890625, 0.328125, 1.0, 1.0], [0.921875, 0.328125, 1.0, 1.0], [0.921875, 0.328125, 1.0, 1.0], [0.953125, 0.328125, 1.0, 1.0], [0.953125, 0.328125, 1.0, 1.0], [0.984375, 0.328125, 1.0, 1.0], [0.984375, 0.328125, 1.0, 1.0], [0.015625, 0.359375, 1.0, 1.0], [0.015625, 0.359375, 1.0, 1.0], [0.046875, 0.359375, 1.0, 1.0], [0.046875, 0.359375, 1.0, 1.0], [0.078125, 0.359375, 1.0, 1.0], [0.078125, 0.359375, 1.0, 1.0], [0.109375, 0.359375, 1.0, 1.0], [0.109375, 0.359375, 1.0, 1.0], [0.140625, 0.359375, 1.0, 1.0], [0.140625, 0.359375, 1.0, 1.0], [0.171875, 0.359375, 1.0, 1.0], [0.171875, 0.359375, 1.0, 1.0], [0.203125, 0.359375, 1.0, 1.0], [0.203125, 0.359375, 1.0, 1.0], [0.234375, 0.359375, 1.0, 1.0], [0.234375, 0.359375, 1.0, 1.0], [0.265625, 0.359375, 1.0, 1.0], [0.265625, 0.359375, 1.0, 1.0], [0.296875, 0.359375, 1.0, 1.0], [0.296875, 0.359375, 1.0, 1.0], [0.328125, 0.359375, 1.0, 1.0], [0.328125, 0.359375, 1.0, 1.0], [0.359375, 0.359375, 1.0, 1.0], [0.359375, 0.359375, 1.0, 1.0], [0.390625, 0.359375, 1.0, 1.0], [0.390625, 0.359375, 1.0, 1.0], [0.421875, 0.359375, 1.0, 1.0], [0.421875, 0.359375, 1.0, 1.0], [0.453125, 0.359375, 1.0, 1.0], [0.453125, 0.359375, 1.0, 1.0], [0.484375, 0.359375, 1.0, 1.0], [0.484375, 0.359375, 1.0, 1.0], [0.515625, 0.359375, 1.0, 1.0], [0.515625, 0.359375, 1.0, 1.0], [0.546875, 0.359375, 1.0, 1.0], [0.546875, 0.359375, 1.0, 1.0], [0.578125, 0.359375, 1.0, 1.0], [0.578125, 0.359375, 1.0, 1.0], [0.609375, 0.359375, 1.0, 1.0], [0.609375, 0.359375, 1.0, 1.0], [0.640625, 0.359375, 1.0, 1.0], [0.640625, 0.359375, 1.0, 1.0], [0.671875, 0.359375, 1.0, 1.0], [0.671875, 0.359375, 1.0, 1.0], [0.703125, 0.359375, 1.0, 1.0], [0.703125, 0.359375, 1.0, 1.0], [0.734375, 0.359375, 1.0, 1.0], [0.734375, 0.359375, 1.0, 1.0], [0.765625, 0.359375, 1.0, 1.0], [0.765625, 0.359375, 1.0, 1.0], [0.796875, 0.359375, 1.0, 1.0], [0.796875, 0.359375, 1.0, 1.0], [0.828125, 0.359375, 1.0, 1.0], [0.828125, 0.359375, 1.0, 1.0], [0.859375, 0.359375, 1.0, 1.0], [0.859375, 0.359375, 1.0, 1.0], [0.890625, 0.359375, 1.0, 1.0], [0.890625, 0.359375, 1.0, 1.0], [0.921875, 0.359375, 1.0, 1.0], [0.921875, 0.359375, 1.0, 1.0], [0.953125, 0.359375, 1.0, 1.0], [0.953125, 0.359375, 1.0, 1.0], [0.984375, 0.359375, 1.0, 1.0], [0.984375, 0.359375, 1.0, 1.0], [0.015625, 0.390625, 1.0, 1.0], [0.015625, 0.390625, 1.0, 1.0], [0.046875, 0.390625, 1.0, 1.0], [0.046875, 0.390625, 1.0, 1.0], [0.078125, 0.390625, 1.0, 1.0], [0.078125, 0.390625, 1.0, 1.0], [0.109375, 0.390625, 1.0, 1.0], [0.109375, 0.390625, 1.0, 1.0], [0.140625, 0.390625, 1.0, 1.0], [0.140625, 0.390625, 1.0, 1.0], [0.171875, 0.390625, 1.0, 1.0], [0.171875, 0.390625, 1.0, 1.0], [0.203125, 0.390625, 1.0, 1.0], [0.203125, 0.390625, 1.0, 1.0], [0.234375, 0.390625, 1.0, 1.0], [0.234375, 0.390625, 1.0, 1.0], [0.265625, 0.390625, 1.0, 1.0], [0.265625, 0.390625, 1.0, 1.0], [0.296875, 0.390625, 1.0, 1.0], [0.296875, 0.390625, 1.0, 1.0], [0.328125, 0.390625, 1.0, 1.0], [0.328125, 0.390625, 1.0, 1.0], [0.359375, 0.390625, 1.0, 1.0], [0.359375, 0.390625, 1.0, 1.0], [0.390625, 0.390625, 1.0, 1.0], [0.390625, 0.390625, 1.0, 1.0], [0.421875, 0.390625, 1.0, 1.0], [0.421875, 0.390625, 1.0, 1.0], [0.453125, 0.390625, 1.0, 1.0], [0.453125, 0.390625, 1.0, 1.0], [0.484375, 0.390625, 1.0, 1.0], [0.484375, 0.390625, 1.0, 1.0], [0.515625, 0.390625, 1.0, 1.0], [0.515625, 0.390625, 1.0, 1.0], [0.546875, 0.390625, 1.0, 1.0], [0.546875, 0.390625, 1.0, 1.0], [0.578125, 0.390625, 1.0, 1.0], [0.578125, 0.390625, 1.0, 1.0], [0.609375, 0.390625, 1.0, 1.0], [0.609375, 0.390625, 1.0, 1.0], [0.640625, 0.390625, 1.0, 1.0], [0.640625, 0.390625, 1.0, 1.0], [0.671875, 0.390625, 1.0, 1.0], [0.671875, 0.390625, 1.0, 1.0], [0.703125, 0.390625, 1.0, 1.0], [0.703125, 0.390625, 1.0, 1.0], [0.734375, 0.390625, 1.0, 1.0], [0.734375, 0.390625, 1.0, 1.0], [0.765625, 0.390625, 1.0, 1.0], [0.765625, 0.390625, 1.0, 1.0], [0.796875, 0.390625, 1.0, 1.0], [0.796875, 0.390625, 1.0, 1.0], [0.828125, 0.390625, 1.0, 1.0], [0.828125, 0.390625, 1.0, 1.0], [0.859375, 0.390625, 1.0, 1.0], [0.859375, 0.390625, 1.0, 1.0], [0.890625, 0.390625, 1.0, 1.0], [0.890625, 0.390625, 1.0, 1.0], [0.921875, 0.390625, 1.0, 1.0], [0.921875, 0.390625, 1.0, 1.0], [0.953125, 0.390625, 1.0, 1.0], [0.953125, 0.390625, 1.0, 1.0], [0.984375, 0.390625, 1.0, 1.0], [0.984375, 0.390625, 1.0, 1.0], [0.015625, 0.421875, 1.0, 1.0], [0.015625, 0.421875, 1.0, 1.0], [0.046875, 0.421875, 1.0, 1.0], [0.046875, 0.421875, 1.0, 1.0], [0.078125, 0.421875, 1.0, 1.0], [0.078125, 0.421875, 1.0, 1.0], [0.109375, 0.421875, 1.0, 1.0], [0.109375, 0.421875, 1.0, 1.0], [0.140625, 0.421875, 1.0, 1.0], [0.140625, 0.421875, 1.0, 1.0], [0.171875, 0.421875, 1.0, 1.0], [0.171875, 0.421875, 1.0, 1.0], [0.203125, 0.421875, 1.0, 1.0], [0.203125, 0.421875, 1.0, 1.0], [0.234375, 0.421875, 1.0, 1.0], [0.234375, 0.421875, 1.0, 1.0], [0.265625, 0.421875, 1.0, 1.0], [0.265625, 0.421875, 1.0, 1.0], [0.296875, 0.421875, 1.0, 1.0], [0.296875, 0.421875, 1.0, 1.0], [0.328125, 0.421875, 1.0, 1.0], [0.328125, 0.421875, 1.0, 1.0], [0.359375, 0.421875, 1.0, 1.0], [0.359375, 0.421875, 1.0, 1.0], [0.390625, 0.421875, 1.0, 1.0], [0.390625, 0.421875, 1.0, 1.0], [0.421875, 0.421875, 1.0, 1.0], [0.421875, 0.421875, 1.0, 1.0], [0.453125, 0.421875, 1.0, 1.0], [0.453125, 0.421875, 1.0, 1.0], [0.484375, 0.421875, 1.0, 1.0], [0.484375, 0.421875, 1.0, 1.0], [0.515625, 0.421875, 1.0, 1.0], [0.515625, 0.421875, 1.0, 1.0], [0.546875, 0.421875, 1.0, 1.0], [0.546875, 0.421875, 1.0, 1.0], [0.578125, 0.421875, 1.0, 1.0], [0.578125, 0.421875, 1.0, 1.0], [0.609375, 0.421875, 1.0, 1.0], [0.609375, 0.421875, 1.0, 1.0], [0.640625, 0.421875, 1.0, 1.0], [0.640625, 0.421875, 1.0, 1.0], [0.671875, 0.421875, 1.0, 1.0], [0.671875, 0.421875, 1.0, 1.0], [0.703125, 0.421875, 1.0, 1.0], [0.703125, 0.421875, 1.0, 1.0], [0.734375, 0.421875, 1.0, 1.0], [0.734375, 0.421875, 1.0, 1.0], [0.765625, 0.421875, 1.0, 1.0], [0.765625, 0.421875, 1.0, 1.0], [0.796875, 0.421875, 1.0, 1.0], [0.796875, 0.421875, 1.0, 1.0], [0.828125, 0.421875, 1.0, 1.0], [0.828125, 0.421875, 1.0, 1.0], [0.859375, 0.421875, 1.0, 1.0], [0.859375, 0.421875, 1.0, 1.0], [0.890625, 0.421875, 1.0, 1.0], [0.890625, 0.421875, 1.0, 1.0], [0.921875, 0.421875, 1.0, 1.0], [0.921875, 0.421875, 1.0, 1.0], [0.953125, 0.421875, 1.0, 1.0], [0.953125, 0.421875, 1.0, 1.0], [0.984375, 0.421875, 1.0, 1.0], [0.984375, 0.421875, 1.0, 1.0], [0.015625, 0.453125, 1.0, 1.0], [0.015625, 0.453125, 1.0, 1.0], [0.046875, 0.453125, 1.0, 1.0], [0.046875, 0.453125, 1.0, 1.0], [0.078125, 0.453125, 1.0, 1.0], [0.078125, 0.453125, 1.0, 1.0], [0.109375, 0.453125, 1.0, 1.0], [0.109375, 0.453125, 1.0, 1.0], [0.140625, 0.453125, 1.0, 1.0], [0.140625, 0.453125, 1.0, 1.0], [0.171875, 0.453125, 1.0, 1.0], [0.171875, 0.453125, 1.0, 1.0], [0.203125, 0.453125, 1.0, 1.0], [0.203125, 0.453125, 1.0, 1.0], [0.234375, 0.453125, 1.0, 1.0], [0.234375, 0.453125, 1.0, 1.0], [0.265625, 0.453125, 1.0, 1.0], [0.265625, 0.453125, 1.0, 1.0], [0.296875, 0.453125, 1.0, 1.0], [0.296875, 0.453125, 1.0, 1.0], [0.328125, 0.453125, 1.0, 1.0], [0.328125, 0.453125, 1.0, 1.0], [0.359375, 0.453125, 1.0, 1.0], [0.359375, 0.453125, 1.0, 1.0], [0.390625, 0.453125, 1.0, 1.0], [0.390625, 0.453125, 1.0, 1.0], [0.421875, 0.453125, 1.0, 1.0], [0.421875, 0.453125, 1.0, 1.0], [0.453125, 0.453125, 1.0, 1.0], [0.453125, 0.453125, 1.0, 1.0], [0.484375, 0.453125, 1.0, 1.0], [0.484375, 0.453125, 1.0, 1.0], [0.515625, 0.453125, 1.0, 1.0], [0.515625, 0.453125, 1.0, 1.0], [0.546875, 0.453125, 1.0, 1.0], [0.546875, 0.453125, 1.0, 1.0], [0.578125, 0.453125, 1.0, 1.0], [0.578125, 0.453125, 1.0, 1.0], [0.609375, 0.453125, 1.0, 1.0], [0.609375, 0.453125, 1.0, 1.0], [0.640625, 0.453125, 1.0, 1.0], [0.640625, 0.453125, 1.0, 1.0], [0.671875, 0.453125, 1.0, 1.0], [0.671875, 0.453125, 1.0, 1.0], [0.703125, 0.453125, 1.0, 1.0], [0.703125, 0.453125, 1.0, 1.0], [0.734375, 0.453125, 1.0, 1.0], [0.734375, 0.453125, 1.0, 1.0], [0.765625, 0.453125, 1.0, 1.0], [0.765625, 0.453125, 1.0, 1.0], [0.796875, 0.453125, 1.0, 1.0], [0.796875, 0.453125, 1.0, 1.0], [0.828125, 0.453125, 1.0, 1.0], [0.828125, 0.453125, 1.0, 1.0], [0.859375, 0.453125, 1.0, 1.0], [0.859375, 0.453125, 1.0, 1.0], [0.890625, 0.453125, 1.0, 1.0], [0.890625, 0.453125, 1.0, 1.0], [0.921875, 0.453125, 1.0, 1.0], [0.921875, 0.453125, 1.0, 1.0], [0.953125, 0.453125, 1.0, 1.0], [0.953125, 0.453125, 1.0, 1.0], [0.984375, 0.453125, 1.0, 1.0], [0.984375, 0.453125, 1.0, 1.0], [0.015625, 0.484375, 1.0, 1.0], [0.015625, 0.484375, 1.0, 1.0], [0.046875, 0.484375, 1.0, 1.0], [0.046875, 0.484375, 1.0, 1.0], [0.078125, 0.484375, 1.0, 1.0], [0.078125, 0.484375, 1.0, 1.0], [0.109375, 0.484375, 1.0, 1.0], [0.109375, 0.484375, 1.0, 1.0], [0.140625, 0.484375, 1.0, 1.0], [0.140625, 0.484375, 1.0, 1.0], [0.171875, 0.484375, 1.0, 1.0], [0.171875, 0.484375, 1.0, 1.0], [0.203125, 0.484375, 1.0, 1.0], [0.203125, 0.484375, 1.0, 1.0], [0.234375, 0.484375, 1.0, 1.0], [0.234375, 0.484375, 1.0, 1.0], [0.265625, 0.484375, 1.0, 1.0], [0.265625, 0.484375, 1.0, 1.0], [0.296875, 0.484375, 1.0, 1.0], [0.296875, 0.484375, 1.0, 1.0], [0.328125, 0.484375, 1.0, 1.0], [0.328125, 0.484375, 1.0, 1.0], [0.359375, 0.484375, 1.0, 1.0], [0.359375, 0.484375, 1.0, 1.0], [0.390625, 0.484375, 1.0, 1.0], [0.390625, 0.484375, 1.0, 1.0], [0.421875, 0.484375, 1.0, 1.0], [0.421875, 0.484375, 1.0, 1.0], [0.453125, 0.484375, 1.0, 1.0], [0.453125, 0.484375, 1.0, 1.0], [0.484375, 0.484375, 1.0, 1.0], [0.484375, 0.484375, 1.0, 1.0], [0.515625, 0.484375, 1.0, 1.0], [0.515625, 0.484375, 1.0, 1.0], [0.546875, 0.484375, 1.0, 1.0], [0.546875, 0.484375, 1.0, 1.0], [0.578125, 0.484375, 1.0, 1.0], [0.578125, 0.484375, 1.0, 1.0], [0.609375, 0.484375, 1.0, 1.0], [0.609375, 0.484375, 1.0, 1.0], [0.640625, 0.484375, 1.0, 1.0], [0.640625, 0.484375, 1.0, 1.0], [0.671875, 0.484375, 1.0, 1.0], [0.671875, 0.484375, 1.0, 1.0], [0.703125, 0.484375, 1.0, 1.0], [0.703125, 0.484375, 1.0, 1.0], [0.734375, 0.484375, 1.0, 1.0], [0.734375, 0.484375, 1.0, 1.0], [0.765625, 0.484375, 1.0, 1.0], [0.765625, 0.484375, 1.0, 1.0], [0.796875, 0.484375, 1.0, 1.0], [0.796875, 0.484375, 1.0, 1.0], [0.828125, 0.484375, 1.0, 1.0], [0.828125, 0.484375, 1.0, 1.0], [0.859375, 0.484375, 1.0, 1.0], [0.859375, 0.484375, 1.0, 1.0], [0.890625, 0.484375, 1.0, 1.0], [0.890625, 0.484375, 1.0, 1.0], [0.921875, 0.484375, 1.0, 1.0], [0.921875, 0.484375, 1.0, 1.0], [0.953125, 0.484375, 1.0, 1.0], [0.953125, 0.484375, 1.0, 1.0], [0.984375, 0.484375, 1.0, 1.0], [0.984375, 0.484375, 1.0, 1.0], [0.015625, 0.515625, 1.0, 1.0], [0.015625, 0.515625, 1.0, 1.0], [0.046875, 0.515625, 1.0, 1.0], [0.046875, 0.515625, 1.0, 1.0], [0.078125, 0.515625, 1.0, 1.0], [0.078125, 0.515625, 1.0, 1.0], [0.109375, 0.515625, 1.0, 1.0], [0.109375, 0.515625, 1.0, 1.0], [0.140625, 0.515625, 1.0, 1.0], [0.140625, 0.515625, 1.0, 1.0], [0.171875, 0.515625, 1.0, 1.0], [0.171875, 0.515625, 1.0, 1.0], [0.203125, 0.515625, 1.0, 1.0], [0.203125, 0.515625, 1.0, 1.0], [0.234375, 0.515625, 1.0, 1.0], [0.234375, 0.515625, 1.0, 1.0], [0.265625, 0.515625, 1.0, 1.0], [0.265625, 0.515625, 1.0, 1.0], [0.296875, 0.515625, 1.0, 1.0], [0.296875, 0.515625, 1.0, 1.0], [0.328125, 0.515625, 1.0, 1.0], [0.328125, 0.515625, 1.0, 1.0], [0.359375, 0.515625, 1.0, 1.0], [0.359375, 0.515625, 1.0, 1.0], [0.390625, 0.515625, 1.0, 1.0], [0.390625, 0.515625, 1.0, 1.0], [0.421875, 0.515625, 1.0, 1.0], [0.421875, 0.515625, 1.0, 1.0], [0.453125, 0.515625, 1.0, 1.0], [0.453125, 0.515625, 1.0, 1.0], [0.484375, 0.515625, 1.0, 1.0], [0.484375, 0.515625, 1.0, 1.0], [0.515625, 0.515625, 1.0, 1.0], [0.515625, 0.515625, 1.0, 1.0], [0.546875, 0.515625, 1.0, 1.0], [0.546875, 0.515625, 1.0, 1.0], [0.578125, 0.515625, 1.0, 1.0], [0.578125, 0.515625, 1.0, 1.0], [0.609375, 0.515625, 1.0, 1.0], [0.609375, 0.515625, 1.0, 1.0], [0.640625, 0.515625, 1.0, 1.0], [0.640625, 0.515625, 1.0, 1.0], [0.671875, 0.515625, 1.0, 1.0], [0.671875, 0.515625, 1.0, 1.0], [0.703125, 0.515625, 1.0, 1.0], [0.703125, 0.515625, 1.0, 1.0], [0.734375, 0.515625, 1.0, 1.0], [0.734375, 0.515625, 1.0, 1.0], [0.765625, 0.515625, 1.0, 1.0], [0.765625, 0.515625, 1.0, 1.0], [0.796875, 0.515625, 1.0, 1.0], [0.796875, 0.515625, 1.0, 1.0], [0.828125, 0.515625, 1.0, 1.0], [0.828125, 0.515625, 1.0, 1.0], [0.859375, 0.515625, 1.0, 1.0], [0.859375, 0.515625, 1.0, 1.0], [0.890625, 0.515625, 1.0, 1.0], [0.890625, 0.515625, 1.0, 1.0], [0.921875, 0.515625, 1.0, 1.0], [0.921875, 0.515625, 1.0, 1.0], [0.953125, 0.515625, 1.0, 1.0], [0.953125, 0.515625, 1.0, 1.0], [0.984375, 0.515625, 1.0, 1.0], [0.984375, 0.515625, 1.0, 1.0], [0.015625, 0.546875, 1.0, 1.0], [0.015625, 0.546875, 1.0, 1.0], [0.046875, 0.546875, 1.0, 1.0], [0.046875, 0.546875, 1.0, 1.0], [0.078125, 0.546875, 1.0, 1.0], [0.078125, 0.546875, 1.0, 1.0], [0.109375, 0.546875, 1.0, 1.0], [0.109375, 0.546875, 1.0, 1.0], [0.140625, 0.546875, 1.0, 1.0], [0.140625, 0.546875, 1.0, 1.0], [0.171875, 0.546875, 1.0, 1.0], [0.171875, 0.546875, 1.0, 1.0], [0.203125, 0.546875, 1.0, 1.0], [0.203125, 0.546875, 1.0, 1.0], [0.234375, 0.546875, 1.0, 1.0], [0.234375, 0.546875, 1.0, 1.0], [0.265625, 0.546875, 1.0, 1.0], [0.265625, 0.546875, 1.0, 1.0], [0.296875, 0.546875, 1.0, 1.0], [0.296875, 0.546875, 1.0, 1.0], [0.328125, 0.546875, 1.0, 1.0], [0.328125, 0.546875, 1.0, 1.0], [0.359375, 0.546875, 1.0, 1.0], [0.359375, 0.546875, 1.0, 1.0], [0.390625, 0.546875, 1.0, 1.0], [0.390625, 0.546875, 1.0, 1.0], [0.421875, 0.546875, 1.0, 1.0], [0.421875, 0.546875, 1.0, 1.0], [0.453125, 0.546875, 1.0, 1.0], [0.453125, 0.546875, 1.0, 1.0], [0.484375, 0.546875, 1.0, 1.0], [0.484375, 0.546875, 1.0, 1.0], [0.515625, 0.546875, 1.0, 1.0], [0.515625, 0.546875, 1.0, 1.0], [0.546875, 0.546875, 1.0, 1.0], [0.546875, 0.546875, 1.0, 1.0], [0.578125, 0.546875, 1.0, 1.0], [0.578125, 0.546875, 1.0, 1.0], [0.609375, 0.546875, 1.0, 1.0], [0.609375, 0.546875, 1.0, 1.0], [0.640625, 0.546875, 1.0, 1.0], [0.640625, 0.546875, 1.0, 1.0], [0.671875, 0.546875, 1.0, 1.0], [0.671875, 0.546875, 1.0, 1.0], [0.703125, 0.546875, 1.0, 1.0], [0.703125, 0.546875, 1.0, 1.0], [0.734375, 0.546875, 1.0, 1.0], [0.734375, 0.546875, 1.0, 1.0], [0.765625, 0.546875, 1.0, 1.0], [0.765625, 0.546875, 1.0, 1.0], [0.796875, 0.546875, 1.0, 1.0], [0.796875, 0.546875, 1.0, 1.0], [0.828125, 0.546875, 1.0, 1.0], [0.828125, 0.546875, 1.0, 1.0], [0.859375, 0.546875, 1.0, 1.0], [0.859375, 0.546875, 1.0, 1.0], [0.890625, 0.546875, 1.0, 1.0], [0.890625, 0.546875, 1.0, 1.0], [0.921875, 0.546875, 1.0, 1.0], [0.921875, 0.546875, 1.0, 1.0], [0.953125, 0.546875, 1.0, 1.0], [0.953125, 0.546875, 1.0, 1.0], [0.984375, 0.546875, 1.0, 1.0], [0.984375, 0.546875, 1.0, 1.0], [0.015625, 0.578125, 1.0, 1.0], [0.015625, 0.578125, 1.0, 1.0], [0.046875, 0.578125, 1.0, 1.0], [0.046875, 0.578125, 1.0, 1.0], [0.078125, 0.578125, 1.0, 1.0], [0.078125, 0.578125, 1.0, 1.0], [0.109375, 0.578125, 1.0, 1.0], [0.109375, 0.578125, 1.0, 1.0], [0.140625, 0.578125, 1.0, 1.0], [0.140625, 0.578125, 1.0, 1.0], [0.171875, 0.578125, 1.0, 1.0], [0.171875, 0.578125, 1.0, 1.0], [0.203125, 0.578125, 1.0, 1.0], [0.203125, 0.578125, 1.0, 1.0], [0.234375, 0.578125, 1.0, 1.0], [0.234375, 0.578125, 1.0, 1.0], [0.265625, 0.578125, 1.0, 1.0], [0.265625, 0.578125, 1.0, 1.0], [0.296875, 0.578125, 1.0, 1.0], [0.296875, 0.578125, 1.0, 1.0], [0.328125, 0.578125, 1.0, 1.0], [0.328125, 0.578125, 1.0, 1.0], [0.359375, 0.578125, 1.0, 1.0], [0.359375, 0.578125, 1.0, 1.0], [0.390625, 0.578125, 1.0, 1.0], [0.390625, 0.578125, 1.0, 1.0], [0.421875, 0.578125, 1.0, 1.0], [0.421875, 0.578125, 1.0, 1.0], [0.453125, 0.578125, 1.0, 1.0], [0.453125, 0.578125, 1.0, 1.0], [0.484375, 0.578125, 1.0, 1.0], [0.484375, 0.578125, 1.0, 1.0], [0.515625, 0.578125, 1.0, 1.0], [0.515625, 0.578125, 1.0, 1.0], [0.546875, 0.578125, 1.0, 1.0], [0.546875, 0.578125, 1.0, 1.0], [0.578125, 0.578125, 1.0, 1.0], [0.578125, 0.578125, 1.0, 1.0], [0.609375, 0.578125, 1.0, 1.0], [0.609375, 0.578125, 1.0, 1.0], [0.640625, 0.578125, 1.0, 1.0], [0.640625, 0.578125, 1.0, 1.0], [0.671875, 0.578125, 1.0, 1.0], [0.671875, 0.578125, 1.0, 1.0], [0.703125, 0.578125, 1.0, 1.0], [0.703125, 0.578125, 1.0, 1.0], [0.734375, 0.578125, 1.0, 1.0], [0.734375, 0.578125, 1.0, 1.0], [0.765625, 0.578125, 1.0, 1.0], [0.765625, 0.578125, 1.0, 1.0], [0.796875, 0.578125, 1.0, 1.0], [0.796875, 0.578125, 1.0, 1.0], [0.828125, 0.578125, 1.0, 1.0], [0.828125, 0.578125, 1.0, 1.0], [0.859375, 0.578125, 1.0, 1.0], [0.859375, 0.578125, 1.0, 1.0], [0.890625, 0.578125, 1.0, 1.0], [0.890625, 0.578125, 1.0, 1.0], [0.921875, 0.578125, 1.0, 1.0], [0.921875, 0.578125, 1.0, 1.0], [0.953125, 0.578125, 1.0, 1.0], [0.953125, 0.578125, 1.0, 1.0], [0.984375, 0.578125, 1.0, 1.0], [0.984375, 0.578125, 1.0, 1.0], [0.015625, 0.609375, 1.0, 1.0], [0.015625, 0.609375, 1.0, 1.0], [0.046875, 0.609375, 1.0, 1.0], [0.046875, 0.609375, 1.0, 1.0], [0.078125, 0.609375, 1.0, 1.0], [0.078125, 0.609375, 1.0, 1.0], [0.109375, 0.609375, 1.0, 1.0], [0.109375, 0.609375, 1.0, 1.0], [0.140625, 0.609375, 1.0, 1.0], [0.140625, 0.609375, 1.0, 1.0], [0.171875, 0.609375, 1.0, 1.0], [0.171875, 0.609375, 1.0, 1.0], [0.203125, 0.609375, 1.0, 1.0], [0.203125, 0.609375, 1.0, 1.0], [0.234375, 0.609375, 1.0, 1.0], [0.234375, 0.609375, 1.0, 1.0], [0.265625, 0.609375, 1.0, 1.0], [0.265625, 0.609375, 1.0, 1.0], [0.296875, 0.609375, 1.0, 1.0], [0.296875, 0.609375, 1.0, 1.0], [0.328125, 0.609375, 1.0, 1.0], [0.328125, 0.609375, 1.0, 1.0], [0.359375, 0.609375, 1.0, 1.0], [0.359375, 0.609375, 1.0, 1.0], [0.390625, 0.609375, 1.0, 1.0], [0.390625, 0.609375, 1.0, 1.0], [0.421875, 0.609375, 1.0, 1.0], [0.421875, 0.609375, 1.0, 1.0], [0.453125, 0.609375, 1.0, 1.0], [0.453125, 0.609375, 1.0, 1.0], [0.484375, 0.609375, 1.0, 1.0], [0.484375, 0.609375, 1.0, 1.0], [0.515625, 0.609375, 1.0, 1.0], [0.515625, 0.609375, 1.0, 1.0], [0.546875, 0.609375, 1.0, 1.0], [0.546875, 0.609375, 1.0, 1.0], [0.578125, 0.609375, 1.0, 1.0], [0.578125, 0.609375, 1.0, 1.0], [0.609375, 0.609375, 1.0, 1.0], [0.609375, 0.609375, 1.0, 1.0], [0.640625, 0.609375, 1.0, 1.0], [0.640625, 0.609375, 1.0, 1.0], [0.671875, 0.609375, 1.0, 1.0], [0.671875, 0.609375, 1.0, 1.0], [0.703125, 0.609375, 1.0, 1.0], [0.703125, 0.609375, 1.0, 1.0], [0.734375, 0.609375, 1.0, 1.0], [0.734375, 0.609375, 1.0, 1.0], [0.765625, 0.609375, 1.0, 1.0], [0.765625, 0.609375, 1.0, 1.0], [0.796875, 0.609375, 1.0, 1.0], [0.796875, 0.609375, 1.0, 1.0], [0.828125, 0.609375, 1.0, 1.0], [0.828125, 0.609375, 1.0, 1.0], [0.859375, 0.609375, 1.0, 1.0], [0.859375, 0.609375, 1.0, 1.0], [0.890625, 0.609375, 1.0, 1.0], [0.890625, 0.609375, 1.0, 1.0], [0.921875, 0.609375, 1.0, 1.0], [0.921875, 0.609375, 1.0, 1.0], [0.953125, 0.609375, 1.0, 1.0], [0.953125, 0.609375, 1.0, 1.0], [0.984375, 0.609375, 1.0, 1.0], [0.984375, 0.609375, 1.0, 1.0], [0.015625, 0.640625, 1.0, 1.0], [0.015625, 0.640625, 1.0, 1.0], [0.046875, 0.640625, 1.0, 1.0], [0.046875, 0.640625, 1.0, 1.0], [0.078125, 0.640625, 1.0, 1.0], [0.078125, 0.640625, 1.0, 1.0], [0.109375, 0.640625, 1.0, 1.0], [0.109375, 0.640625, 1.0, 1.0], [0.140625, 0.640625, 1.0, 1.0], [0.140625, 0.640625, 1.0, 1.0], [0.171875, 0.640625, 1.0, 1.0], [0.171875, 0.640625, 1.0, 1.0], [0.203125, 0.640625, 1.0, 1.0], [0.203125, 0.640625, 1.0, 1.0], [0.234375, 0.640625, 1.0, 1.0], [0.234375, 0.640625, 1.0, 1.0], [0.265625, 0.640625, 1.0, 1.0], [0.265625, 0.640625, 1.0, 1.0], [0.296875, 0.640625, 1.0, 1.0], [0.296875, 0.640625, 1.0, 1.0], [0.328125, 0.640625, 1.0, 1.0], [0.328125, 0.640625, 1.0, 1.0], [0.359375, 0.640625, 1.0, 1.0], [0.359375, 0.640625, 1.0, 1.0], [0.390625, 0.640625, 1.0, 1.0], [0.390625, 0.640625, 1.0, 1.0], [0.421875, 0.640625, 1.0, 1.0], [0.421875, 0.640625, 1.0, 1.0], [0.453125, 0.640625, 1.0, 1.0], [0.453125, 0.640625, 1.0, 1.0], [0.484375, 0.640625, 1.0, 1.0], [0.484375, 0.640625, 1.0, 1.0], [0.515625, 0.640625, 1.0, 1.0], [0.515625, 0.640625, 1.0, 1.0], [0.546875, 0.640625, 1.0, 1.0], [0.546875, 0.640625, 1.0, 1.0], [0.578125, 0.640625, 1.0, 1.0], [0.578125, 0.640625, 1.0, 1.0], [0.609375, 0.640625, 1.0, 1.0], [0.609375, 0.640625, 1.0, 1.0], [0.640625, 0.640625, 1.0, 1.0], [0.640625, 0.640625, 1.0, 1.0], [0.671875, 0.640625, 1.0, 1.0], [0.671875, 0.640625, 1.0, 1.0], [0.703125, 0.640625, 1.0, 1.0], [0.703125, 0.640625, 1.0, 1.0], [0.734375, 0.640625, 1.0, 1.0], [0.734375, 0.640625, 1.0, 1.0], [0.765625, 0.640625, 1.0, 1.0], [0.765625, 0.640625, 1.0, 1.0], [0.796875, 0.640625, 1.0, 1.0], [0.796875, 0.640625, 1.0, 1.0], [0.828125, 0.640625, 1.0, 1.0], [0.828125, 0.640625, 1.0, 1.0], [0.859375, 0.640625, 1.0, 1.0], [0.859375, 0.640625, 1.0, 1.0], [0.890625, 0.640625, 1.0, 1.0], [0.890625, 0.640625, 1.0, 1.0], [0.921875, 0.640625, 1.0, 1.0], [0.921875, 0.640625, 1.0, 1.0], [0.953125, 0.640625, 1.0, 1.0], [0.953125, 0.640625, 1.0, 1.0], [0.984375, 0.640625, 1.0, 1.0], [0.984375, 0.640625, 1.0, 1.0], [0.015625, 0.671875, 1.0, 1.0], [0.015625, 0.671875, 1.0, 1.0], [0.046875, 0.671875, 1.0, 1.0], [0.046875, 0.671875, 1.0, 1.0], [0.078125, 0.671875, 1.0, 1.0], [0.078125, 0.671875, 1.0, 1.0], [0.109375, 0.671875, 1.0, 1.0], [0.109375, 0.671875, 1.0, 1.0], [0.140625, 0.671875, 1.0, 1.0], [0.140625, 0.671875, 1.0, 1.0], [0.171875, 0.671875, 1.0, 1.0], [0.171875, 0.671875, 1.0, 1.0], [0.203125, 0.671875, 1.0, 1.0], [0.203125, 0.671875, 1.0, 1.0], [0.234375, 0.671875, 1.0, 1.0], [0.234375, 0.671875, 1.0, 1.0], [0.265625, 0.671875, 1.0, 1.0], [0.265625, 0.671875, 1.0, 1.0], [0.296875, 0.671875, 1.0, 1.0], [0.296875, 0.671875, 1.0, 1.0], [0.328125, 0.671875, 1.0, 1.0], [0.328125, 0.671875, 1.0, 1.0], [0.359375, 0.671875, 1.0, 1.0], [0.359375, 0.671875, 1.0, 1.0], [0.390625, 0.671875, 1.0, 1.0], [0.390625, 0.671875, 1.0, 1.0], [0.421875, 0.671875, 1.0, 1.0], [0.421875, 0.671875, 1.0, 1.0], [0.453125, 0.671875, 1.0, 1.0], [0.453125, 0.671875, 1.0, 1.0], [0.484375, 0.671875, 1.0, 1.0], [0.484375, 0.671875, 1.0, 1.0], [0.515625, 0.671875, 1.0, 1.0], [0.515625, 0.671875, 1.0, 1.0], [0.546875, 0.671875, 1.0, 1.0], [0.546875, 0.671875, 1.0, 1.0], [0.578125, 0.671875, 1.0, 1.0], [0.578125, 0.671875, 1.0, 1.0], [0.609375, 0.671875, 1.0, 1.0], [0.609375, 0.671875, 1.0, 1.0], [0.640625, 0.671875, 1.0, 1.0], [0.640625, 0.671875, 1.0, 1.0], [0.671875, 0.671875, 1.0, 1.0], [0.671875, 0.671875, 1.0, 1.0], [0.703125, 0.671875, 1.0, 1.0], [0.703125, 0.671875, 1.0, 1.0], [0.734375, 0.671875, 1.0, 1.0], [0.734375, 0.671875, 1.0, 1.0], [0.765625, 0.671875, 1.0, 1.0], [0.765625, 0.671875, 1.0, 1.0], [0.796875, 0.671875, 1.0, 1.0], [0.796875, 0.671875, 1.0, 1.0], [0.828125, 0.671875, 1.0, 1.0], [0.828125, 0.671875, 1.0, 1.0], [0.859375, 0.671875, 1.0, 1.0], [0.859375, 0.671875, 1.0, 1.0], [0.890625, 0.671875, 1.0, 1.0], [0.890625, 0.671875, 1.0, 1.0], [0.921875, 0.671875, 1.0, 1.0], [0.921875, 0.671875, 1.0, 1.0], [0.953125, 0.671875, 1.0, 1.0], [0.953125, 0.671875, 1.0, 1.0], [0.984375, 0.671875, 1.0, 1.0], [0.984375, 0.671875, 1.0, 1.0], [0.015625, 0.703125, 1.0, 1.0], [0.015625, 0.703125, 1.0, 1.0], [0.046875, 0.703125, 1.0, 1.0], [0.046875, 0.703125, 1.0, 1.0], [0.078125, 0.703125, 1.0, 1.0], [0.078125, 0.703125, 1.0, 1.0], [0.109375, 0.703125, 1.0, 1.0], [0.109375, 0.703125, 1.0, 1.0], [0.140625, 0.703125, 1.0, 1.0], [0.140625, 0.703125, 1.0, 1.0], [0.171875, 0.703125, 1.0, 1.0], [0.171875, 0.703125, 1.0, 1.0], [0.203125, 0.703125, 1.0, 1.0], [0.203125, 0.703125, 1.0, 1.0], [0.234375, 0.703125, 1.0, 1.0], [0.234375, 0.703125, 1.0, 1.0], [0.265625, 0.703125, 1.0, 1.0], [0.265625, 0.703125, 1.0, 1.0], [0.296875, 0.703125, 1.0, 1.0], [0.296875, 0.703125, 1.0, 1.0], [0.328125, 0.703125, 1.0, 1.0], [0.328125, 0.703125, 1.0, 1.0], [0.359375, 0.703125, 1.0, 1.0], [0.359375, 0.703125, 1.0, 1.0], [0.390625, 0.703125, 1.0, 1.0], [0.390625, 0.703125, 1.0, 1.0], [0.421875, 0.703125, 1.0, 1.0], [0.421875, 0.703125, 1.0, 1.0], [0.453125, 0.703125, 1.0, 1.0], [0.453125, 0.703125, 1.0, 1.0], [0.484375, 0.703125, 1.0, 1.0], [0.484375, 0.703125, 1.0, 1.0], [0.515625, 0.703125, 1.0, 1.0], [0.515625, 0.703125, 1.0, 1.0], [0.546875, 0.703125, 1.0, 1.0], [0.546875, 0.703125, 1.0, 1.0], [0.578125, 0.703125, 1.0, 1.0], [0.578125, 0.703125, 1.0, 1.0], [0.609375, 0.703125, 1.0, 1.0], [0.609375, 0.703125, 1.0, 1.0], [0.640625, 0.703125, 1.0, 1.0], [0.640625, 0.703125, 1.0, 1.0], [0.671875, 0.703125, 1.0, 1.0], [0.671875, 0.703125, 1.0, 1.0], [0.703125, 0.703125, 1.0, 1.0], [0.703125, 0.703125, 1.0, 1.0], [0.734375, 0.703125, 1.0, 1.0], [0.734375, 0.703125, 1.0, 1.0], [0.765625, 0.703125, 1.0, 1.0], [0.765625, 0.703125, 1.0, 1.0], [0.796875, 0.703125, 1.0, 1.0], [0.796875, 0.703125, 1.0, 1.0], [0.828125, 0.703125, 1.0, 1.0], [0.828125, 0.703125, 1.0, 1.0], [0.859375, 0.703125, 1.0, 1.0], [0.859375, 0.703125, 1.0, 1.0], [0.890625, 0.703125, 1.0, 1.0], [0.890625, 0.703125, 1.0, 1.0], [0.921875, 0.703125, 1.0, 1.0], [0.921875, 0.703125, 1.0, 1.0], [0.953125, 0.703125, 1.0, 1.0], [0.953125, 0.703125, 1.0, 1.0], [0.984375, 0.703125, 1.0, 1.0], [0.984375, 0.703125, 1.0, 1.0], [0.015625, 0.734375, 1.0, 1.0], [0.015625, 0.734375, 1.0, 1.0], [0.046875, 0.734375, 1.0, 1.0], [0.046875, 0.734375, 1.0, 1.0], [0.078125, 0.734375, 1.0, 1.0], [0.078125, 0.734375, 1.0, 1.0], [0.109375, 0.734375, 1.0, 1.0], [0.109375, 0.734375, 1.0, 1.0], [0.140625, 0.734375, 1.0, 1.0], [0.140625, 0.734375, 1.0, 1.0], [0.171875, 0.734375, 1.0, 1.0], [0.171875, 0.734375, 1.0, 1.0], [0.203125, 0.734375, 1.0, 1.0], [0.203125, 0.734375, 1.0, 1.0], [0.234375, 0.734375, 1.0, 1.0], [0.234375, 0.734375, 1.0, 1.0], [0.265625, 0.734375, 1.0, 1.0], [0.265625, 0.734375, 1.0, 1.0], [0.296875, 0.734375, 1.0, 1.0], [0.296875, 0.734375, 1.0, 1.0], [0.328125, 0.734375, 1.0, 1.0], [0.328125, 0.734375, 1.0, 1.0], [0.359375, 0.734375, 1.0, 1.0], [0.359375, 0.734375, 1.0, 1.0], [0.390625, 0.734375, 1.0, 1.0], [0.390625, 0.734375, 1.0, 1.0], [0.421875, 0.734375, 1.0, 1.0], [0.421875, 0.734375, 1.0, 1.0], [0.453125, 0.734375, 1.0, 1.0], [0.453125, 0.734375, 1.0, 1.0], [0.484375, 0.734375, 1.0, 1.0], [0.484375, 0.734375, 1.0, 1.0], [0.515625, 0.734375, 1.0, 1.0], [0.515625, 0.734375, 1.0, 1.0], [0.546875, 0.734375, 1.0, 1.0], [0.546875, 0.734375, 1.0, 1.0], [0.578125, 0.734375, 1.0, 1.0], [0.578125, 0.734375, 1.0, 1.0], [0.609375, 0.734375, 1.0, 1.0], [0.609375, 0.734375, 1.0, 1.0], [0.640625, 0.734375, 1.0, 1.0], [0.640625, 0.734375, 1.0, 1.0], [0.671875, 0.734375, 1.0, 1.0], [0.671875, 0.734375, 1.0, 1.0], [0.703125, 0.734375, 1.0, 1.0], [0.703125, 0.734375, 1.0, 1.0], [0.734375, 0.734375, 1.0, 1.0], [0.734375, 0.734375, 1.0, 1.0], [0.765625, 0.734375, 1.0, 1.0], [0.765625, 0.734375, 1.0, 1.0], [0.796875, 0.734375, 1.0, 1.0], [0.796875, 0.734375, 1.0, 1.0], [0.828125, 0.734375, 1.0, 1.0], [0.828125, 0.734375, 1.0, 1.0], [0.859375, 0.734375, 1.0, 1.0], [0.859375, 0.734375, 1.0, 1.0], [0.890625, 0.734375, 1.0, 1.0], [0.890625, 0.734375, 1.0, 1.0], [0.921875, 0.734375, 1.0, 1.0], [0.921875, 0.734375, 1.0, 1.0], [0.953125, 0.734375, 1.0, 1.0], [0.953125, 0.734375, 1.0, 1.0], [0.984375, 0.734375, 1.0, 1.0], [0.984375, 0.734375, 1.0, 1.0], [0.015625, 0.765625, 1.0, 1.0], [0.015625, 0.765625, 1.0, 1.0], [0.046875, 0.765625, 1.0, 1.0], [0.046875, 0.765625, 1.0, 1.0], [0.078125, 0.765625, 1.0, 1.0], [0.078125, 0.765625, 1.0, 1.0], [0.109375, 0.765625, 1.0, 1.0], [0.109375, 0.765625, 1.0, 1.0], [0.140625, 0.765625, 1.0, 1.0], [0.140625, 0.765625, 1.0, 1.0], [0.171875, 0.765625, 1.0, 1.0], [0.171875, 0.765625, 1.0, 1.0], [0.203125, 0.765625, 1.0, 1.0], [0.203125, 0.765625, 1.0, 1.0], [0.234375, 0.765625, 1.0, 1.0], [0.234375, 0.765625, 1.0, 1.0], [0.265625, 0.765625, 1.0, 1.0], [0.265625, 0.765625, 1.0, 1.0], [0.296875, 0.765625, 1.0, 1.0], [0.296875, 0.765625, 1.0, 1.0], [0.328125, 0.765625, 1.0, 1.0], [0.328125, 0.765625, 1.0, 1.0], [0.359375, 0.765625, 1.0, 1.0], [0.359375, 0.765625, 1.0, 1.0], [0.390625, 0.765625, 1.0, 1.0], [0.390625, 0.765625, 1.0, 1.0], [0.421875, 0.765625, 1.0, 1.0], [0.421875, 0.765625, 1.0, 1.0], [0.453125, 0.765625, 1.0, 1.0], [0.453125, 0.765625, 1.0, 1.0], [0.484375, 0.765625, 1.0, 1.0], [0.484375, 0.765625, 1.0, 1.0], [0.515625, 0.765625, 1.0, 1.0], [0.515625, 0.765625, 1.0, 1.0], [0.546875, 0.765625, 1.0, 1.0], [0.546875, 0.765625, 1.0, 1.0], [0.578125, 0.765625, 1.0, 1.0], [0.578125, 0.765625, 1.0, 1.0], [0.609375, 0.765625, 1.0, 1.0], [0.609375, 0.765625, 1.0, 1.0], [0.640625, 0.765625, 1.0, 1.0], [0.640625, 0.765625, 1.0, 1.0], [0.671875, 0.765625, 1.0, 1.0], [0.671875, 0.765625, 1.0, 1.0], [0.703125, 0.765625, 1.0, 1.0], [0.703125, 0.765625, 1.0, 1.0], [0.734375, 0.765625, 1.0, 1.0], [0.734375, 0.765625, 1.0, 1.0], [0.765625, 0.765625, 1.0, 1.0], [0.765625, 0.765625, 1.0, 1.0], [0.796875, 0.765625, 1.0, 1.0], [0.796875, 0.765625, 1.0, 1.0], [0.828125, 0.765625, 1.0, 1.0], [0.828125, 0.765625, 1.0, 1.0], [0.859375, 0.765625, 1.0, 1.0], [0.859375, 0.765625, 1.0, 1.0], [0.890625, 0.765625, 1.0, 1.0], [0.890625, 0.765625, 1.0, 1.0], [0.921875, 0.765625, 1.0, 1.0], [0.921875, 0.765625, 1.0, 1.0], [0.953125, 0.765625, 1.0, 1.0], [0.953125, 0.765625, 1.0, 1.0], [0.984375, 0.765625, 1.0, 1.0], [0.984375, 0.765625, 1.0, 1.0], [0.015625, 0.796875, 1.0, 1.0], [0.015625, 0.796875, 1.0, 1.0], [0.046875, 0.796875, 1.0, 1.0], [0.046875, 0.796875, 1.0, 1.0], [0.078125, 0.796875, 1.0, 1.0], [0.078125, 0.796875, 1.0, 1.0], [0.109375, 0.796875, 1.0, 1.0], [0.109375, 0.796875, 1.0, 1.0], [0.140625, 0.796875, 1.0, 1.0], [0.140625, 0.796875, 1.0, 1.0], [0.171875, 0.796875, 1.0, 1.0], [0.171875, 0.796875, 1.0, 1.0], [0.203125, 0.796875, 1.0, 1.0], [0.203125, 0.796875, 1.0, 1.0], [0.234375, 0.796875, 1.0, 1.0], [0.234375, 0.796875, 1.0, 1.0], [0.265625, 0.796875, 1.0, 1.0], [0.265625, 0.796875, 1.0, 1.0], [0.296875, 0.796875, 1.0, 1.0], [0.296875, 0.796875, 1.0, 1.0], [0.328125, 0.796875, 1.0, 1.0], [0.328125, 0.796875, 1.0, 1.0], [0.359375, 0.796875, 1.0, 1.0], [0.359375, 0.796875, 1.0, 1.0], [0.390625, 0.796875, 1.0, 1.0], [0.390625, 0.796875, 1.0, 1.0], [0.421875, 0.796875, 1.0, 1.0], [0.421875, 0.796875, 1.0, 1.0], [0.453125, 0.796875, 1.0, 1.0], [0.453125, 0.796875, 1.0, 1.0], [0.484375, 0.796875, 1.0, 1.0], [0.484375, 0.796875, 1.0, 1.0], [0.515625, 0.796875, 1.0, 1.0], [0.515625, 0.796875, 1.0, 1.0], [0.546875, 0.796875, 1.0, 1.0], [0.546875, 0.796875, 1.0, 1.0], [0.578125, 0.796875, 1.0, 1.0], [0.578125, 0.796875, 1.0, 1.0], [0.609375, 0.796875, 1.0, 1.0], [0.609375, 0.796875, 1.0, 1.0], [0.640625, 0.796875, 1.0, 1.0], [0.640625, 0.796875, 1.0, 1.0], [0.671875, 0.796875, 1.0, 1.0], [0.671875, 0.796875, 1.0, 1.0], [0.703125, 0.796875, 1.0, 1.0], [0.703125, 0.796875, 1.0, 1.0], [0.734375, 0.796875, 1.0, 1.0], [0.734375, 0.796875, 1.0, 1.0], [0.765625, 0.796875, 1.0, 1.0], [0.765625, 0.796875, 1.0, 1.0], [0.796875, 0.796875, 1.0, 1.0], [0.796875, 0.796875, 1.0, 1.0], [0.828125, 0.796875, 1.0, 1.0], [0.828125, 0.796875, 1.0, 1.0], [0.859375, 0.796875, 1.0, 1.0], [0.859375, 0.796875, 1.0, 1.0], [0.890625, 0.796875, 1.0, 1.0], [0.890625, 0.796875, 1.0, 1.0], [0.921875, 0.796875, 1.0, 1.0], [0.921875, 0.796875, 1.0, 1.0], [0.953125, 0.796875, 1.0, 1.0], [0.953125, 0.796875, 1.0, 1.0], [0.984375, 0.796875, 1.0, 1.0], [0.984375, 0.796875, 1.0, 1.0], [0.015625, 0.828125, 1.0, 1.0], [0.015625, 0.828125, 1.0, 1.0], [0.046875, 0.828125, 1.0, 1.0], [0.046875, 0.828125, 1.0, 1.0], [0.078125, 0.828125, 1.0, 1.0], [0.078125, 0.828125, 1.0, 1.0], [0.109375, 0.828125, 1.0, 1.0], [0.109375, 0.828125, 1.0, 1.0], [0.140625, 0.828125, 1.0, 1.0], [0.140625, 0.828125, 1.0, 1.0], [0.171875, 0.828125, 1.0, 1.0], [0.171875, 0.828125, 1.0, 1.0], [0.203125, 0.828125, 1.0, 1.0], [0.203125, 0.828125, 1.0, 1.0], [0.234375, 0.828125, 1.0, 1.0], [0.234375, 0.828125, 1.0, 1.0], [0.265625, 0.828125, 1.0, 1.0], [0.265625, 0.828125, 1.0, 1.0], [0.296875, 0.828125, 1.0, 1.0], [0.296875, 0.828125, 1.0, 1.0], [0.328125, 0.828125, 1.0, 1.0], [0.328125, 0.828125, 1.0, 1.0], [0.359375, 0.828125, 1.0, 1.0], [0.359375, 0.828125, 1.0, 1.0], [0.390625, 0.828125, 1.0, 1.0], [0.390625, 0.828125, 1.0, 1.0], [0.421875, 0.828125, 1.0, 1.0], [0.421875, 0.828125, 1.0, 1.0], [0.453125, 0.828125, 1.0, 1.0], [0.453125, 0.828125, 1.0, 1.0], [0.484375, 0.828125, 1.0, 1.0], [0.484375, 0.828125, 1.0, 1.0], [0.515625, 0.828125, 1.0, 1.0], [0.515625, 0.828125, 1.0, 1.0], [0.546875, 0.828125, 1.0, 1.0], [0.546875, 0.828125, 1.0, 1.0], [0.578125, 0.828125, 1.0, 1.0], [0.578125, 0.828125, 1.0, 1.0], [0.609375, 0.828125, 1.0, 1.0], [0.609375, 0.828125, 1.0, 1.0], [0.640625, 0.828125, 1.0, 1.0], [0.640625, 0.828125, 1.0, 1.0], [0.671875, 0.828125, 1.0, 1.0], [0.671875, 0.828125, 1.0, 1.0], [0.703125, 0.828125, 1.0, 1.0], [0.703125, 0.828125, 1.0, 1.0], [0.734375, 0.828125, 1.0, 1.0], [0.734375, 0.828125, 1.0, 1.0], [0.765625, 0.828125, 1.0, 1.0], [0.765625, 0.828125, 1.0, 1.0], [0.796875, 0.828125, 1.0, 1.0], [0.796875, 0.828125, 1.0, 1.0], [0.828125, 0.828125, 1.0, 1.0], [0.828125, 0.828125, 1.0, 1.0], [0.859375, 0.828125, 1.0, 1.0], [0.859375, 0.828125, 1.0, 1.0], [0.890625, 0.828125, 1.0, 1.0], [0.890625, 0.828125, 1.0, 1.0], [0.921875, 0.828125, 1.0, 1.0], [0.921875, 0.828125, 1.0, 1.0], [0.953125, 0.828125, 1.0, 1.0], [0.953125, 0.828125, 1.0, 1.0], [0.984375, 0.828125, 1.0, 1.0], [0.984375, 0.828125, 1.0, 1.0], [0.015625, 0.859375, 1.0, 1.0], [0.015625, 0.859375, 1.0, 1.0], [0.046875, 0.859375, 1.0, 1.0], [0.046875, 0.859375, 1.0, 1.0], [0.078125, 0.859375, 1.0, 1.0], [0.078125, 0.859375, 1.0, 1.0], [0.109375, 0.859375, 1.0, 1.0], [0.109375, 0.859375, 1.0, 1.0], [0.140625, 0.859375, 1.0, 1.0], [0.140625, 0.859375, 1.0, 1.0], [0.171875, 0.859375, 1.0, 1.0], [0.171875, 0.859375, 1.0, 1.0], [0.203125, 0.859375, 1.0, 1.0], [0.203125, 0.859375, 1.0, 1.0], [0.234375, 0.859375, 1.0, 1.0], [0.234375, 0.859375, 1.0, 1.0], [0.265625, 0.859375, 1.0, 1.0], [0.265625, 0.859375, 1.0, 1.0], [0.296875, 0.859375, 1.0, 1.0], [0.296875, 0.859375, 1.0, 1.0], [0.328125, 0.859375, 1.0, 1.0], [0.328125, 0.859375, 1.0, 1.0], [0.359375, 0.859375, 1.0, 1.0], [0.359375, 0.859375, 1.0, 1.0], [0.390625, 0.859375, 1.0, 1.0], [0.390625, 0.859375, 1.0, 1.0], [0.421875, 0.859375, 1.0, 1.0], [0.421875, 0.859375, 1.0, 1.0], [0.453125, 0.859375, 1.0, 1.0], [0.453125, 0.859375, 1.0, 1.0], [0.484375, 0.859375, 1.0, 1.0], [0.484375, 0.859375, 1.0, 1.0], [0.515625, 0.859375, 1.0, 1.0], [0.515625, 0.859375, 1.0, 1.0], [0.546875, 0.859375, 1.0, 1.0], [0.546875, 0.859375, 1.0, 1.0], [0.578125, 0.859375, 1.0, 1.0], [0.578125, 0.859375, 1.0, 1.0], [0.609375, 0.859375, 1.0, 1.0], [0.609375, 0.859375, 1.0, 1.0], [0.640625, 0.859375, 1.0, 1.0], [0.640625, 0.859375, 1.0, 1.0], [0.671875, 0.859375, 1.0, 1.0], [0.671875, 0.859375, 1.0, 1.0], [0.703125, 0.859375, 1.0, 1.0], [0.703125, 0.859375, 1.0, 1.0], [0.734375, 0.859375, 1.0, 1.0], [0.734375, 0.859375, 1.0, 1.0], [0.765625, 0.859375, 1.0, 1.0], [0.765625, 0.859375, 1.0, 1.0], [0.796875, 0.859375, 1.0, 1.0], [0.796875, 0.859375, 1.0, 1.0], [0.828125, 0.859375, 1.0, 1.0], [0.828125, 0.859375, 1.0, 1.0], [0.859375, 0.859375, 1.0, 1.0], [0.859375, 0.859375, 1.0, 1.0], [0.890625, 0.859375, 1.0, 1.0], [0.890625, 0.859375, 1.0, 1.0], [0.921875, 0.859375, 1.0, 1.0], [0.921875, 0.859375, 1.0, 1.0], [0.953125, 0.859375, 1.0, 1.0], [0.953125, 0.859375, 1.0, 1.0], [0.984375, 0.859375, 1.0, 1.0], [0.984375, 0.859375, 1.0, 1.0], [0.015625, 0.890625, 1.0, 1.0], [0.015625, 0.890625, 1.0, 1.0], [0.046875, 0.890625, 1.0, 1.0], [0.046875, 0.890625, 1.0, 1.0], [0.078125, 0.890625, 1.0, 1.0], [0.078125, 0.890625, 1.0, 1.0], [0.109375, 0.890625, 1.0, 1.0], [0.109375, 0.890625, 1.0, 1.0], [0.140625, 0.890625, 1.0, 1.0], [0.140625, 0.890625, 1.0, 1.0], [0.171875, 0.890625, 1.0, 1.0], [0.171875, 0.890625, 1.0, 1.0], [0.203125, 0.890625, 1.0, 1.0], [0.203125, 0.890625, 1.0, 1.0], [0.234375, 0.890625, 1.0, 1.0], [0.234375, 0.890625, 1.0, 1.0], [0.265625, 0.890625, 1.0, 1.0], [0.265625, 0.890625, 1.0, 1.0], [0.296875, 0.890625, 1.0, 1.0], [0.296875, 0.890625, 1.0, 1.0], [0.328125, 0.890625, 1.0, 1.0], [0.328125, 0.890625, 1.0, 1.0], [0.359375, 0.890625, 1.0, 1.0], [0.359375, 0.890625, 1.0, 1.0], [0.390625, 0.890625, 1.0, 1.0], [0.390625, 0.890625, 1.0, 1.0], [0.421875, 0.890625, 1.0, 1.0], [0.421875, 0.890625, 1.0, 1.0], [0.453125, 0.890625, 1.0, 1.0], [0.453125, 0.890625, 1.0, 1.0], [0.484375, 0.890625, 1.0, 1.0], [0.484375, 0.890625, 1.0, 1.0], [0.515625, 0.890625, 1.0, 1.0], [0.515625, 0.890625, 1.0, 1.0], [0.546875, 0.890625, 1.0, 1.0], [0.546875, 0.890625, 1.0, 1.0], [0.578125, 0.890625, 1.0, 1.0], [0.578125, 0.890625, 1.0, 1.0], [0.609375, 0.890625, 1.0, 1.0], [0.609375, 0.890625, 1.0, 1.0], [0.640625, 0.890625, 1.0, 1.0], [0.640625, 0.890625, 1.0, 1.0], [0.671875, 0.890625, 1.0, 1.0], [0.671875, 0.890625, 1.0, 1.0], [0.703125, 0.890625, 1.0, 1.0], [0.703125, 0.890625, 1.0, 1.0], [0.734375, 0.890625, 1.0, 1.0], [0.734375, 0.890625, 1.0, 1.0], [0.765625, 0.890625, 1.0, 1.0], [0.765625, 0.890625, 1.0, 1.0], [0.796875, 0.890625, 1.0, 1.0], [0.796875, 0.890625, 1.0, 1.0], [0.828125, 0.890625, 1.0, 1.0], [0.828125, 0.890625, 1.0, 1.0], [0.859375, 0.890625, 1.0, 1.0], [0.859375, 0.890625, 1.0, 1.0], [0.890625, 0.890625, 1.0, 1.0], [0.890625, 0.890625, 1.0, 1.0], [0.921875, 0.890625, 1.0, 1.0], [0.921875, 0.890625, 1.0, 1.0], [0.953125, 0.890625, 1.0, 1.0], [0.953125, 0.890625, 1.0, 1.0], [0.984375, 0.890625, 1.0, 1.0], [0.984375, 0.890625, 1.0, 1.0], [0.015625, 0.921875, 1.0, 1.0], [0.015625, 0.921875, 1.0, 1.0], [0.046875, 0.921875, 1.0, 1.0], [0.046875, 0.921875, 1.0, 1.0], [0.078125, 0.921875, 1.0, 1.0], [0.078125, 0.921875, 1.0, 1.0], [0.109375, 0.921875, 1.0, 1.0], [0.109375, 0.921875, 1.0, 1.0], [0.140625, 0.921875, 1.0, 1.0], [0.140625, 0.921875, 1.0, 1.0], [0.171875, 0.921875, 1.0, 1.0], [0.171875, 0.921875, 1.0, 1.0], [0.203125, 0.921875, 1.0, 1.0], [0.203125, 0.921875, 1.0, 1.0], [0.234375, 0.921875, 1.0, 1.0], [0.234375, 0.921875, 1.0, 1.0], [0.265625, 0.921875, 1.0, 1.0], [0.265625, 0.921875, 1.0, 1.0], [0.296875, 0.921875, 1.0, 1.0], [0.296875, 0.921875, 1.0, 1.0], [0.328125, 0.921875, 1.0, 1.0], [0.328125, 0.921875, 1.0, 1.0], [0.359375, 0.921875, 1.0, 1.0], [0.359375, 0.921875, 1.0, 1.0], [0.390625, 0.921875, 1.0, 1.0], [0.390625, 0.921875, 1.0, 1.0], [0.421875, 0.921875, 1.0, 1.0], [0.421875, 0.921875, 1.0, 1.0], [0.453125, 0.921875, 1.0, 1.0], [0.453125, 0.921875, 1.0, 1.0], [0.484375, 0.921875, 1.0, 1.0], [0.484375, 0.921875, 1.0, 1.0], [0.515625, 0.921875, 1.0, 1.0], [0.515625, 0.921875, 1.0, 1.0], [0.546875, 0.921875, 1.0, 1.0], [0.546875, 0.921875, 1.0, 1.0], [0.578125, 0.921875, 1.0, 1.0], [0.578125, 0.921875, 1.0, 1.0], [0.609375, 0.921875, 1.0, 1.0], [0.609375, 0.921875, 1.0, 1.0], [0.640625, 0.921875, 1.0, 1.0], [0.640625, 0.921875, 1.0, 1.0], [0.671875, 0.921875, 1.0, 1.0], [0.671875, 0.921875, 1.0, 1.0], [0.703125, 0.921875, 1.0, 1.0], [0.703125, 0.921875, 1.0, 1.0], [0.734375, 0.921875, 1.0, 1.0], [0.734375, 0.921875, 1.0, 1.0], [0.765625, 0.921875, 1.0, 1.0], [0.765625, 0.921875, 1.0, 1.0], [0.796875, 0.921875, 1.0, 1.0], [0.796875, 0.921875, 1.0, 1.0], [0.828125, 0.921875, 1.0, 1.0], [0.828125, 0.921875, 1.0, 1.0], [0.859375, 0.921875, 1.0, 1.0], [0.859375, 0.921875, 1.0, 1.0], [0.890625, 0.921875, 1.0, 1.0], [0.890625, 0.921875, 1.0, 1.0], [0.921875, 0.921875, 1.0, 1.0], [0.921875, 0.921875, 1.0, 1.0], [0.953125, 0.921875, 1.0, 1.0], [0.953125, 0.921875, 1.0, 1.0], [0.984375, 0.921875, 1.0, 1.0], [0.984375, 0.921875, 1.0, 1.0], [0.015625, 0.953125, 1.0, 1.0], [0.015625, 0.953125, 1.0, 1.0], [0.046875, 0.953125, 1.0, 1.0], [0.046875, 0.953125, 1.0, 1.0], [0.078125, 0.953125, 1.0, 1.0], [0.078125, 0.953125, 1.0, 1.0], [0.109375, 0.953125, 1.0, 1.0], [0.109375, 0.953125, 1.0, 1.0], [0.140625, 0.953125, 1.0, 1.0], [0.140625, 0.953125, 1.0, 1.0], [0.171875, 0.953125, 1.0, 1.0], [0.171875, 0.953125, 1.0, 1.0], [0.203125, 0.953125, 1.0, 1.0], [0.203125, 0.953125, 1.0, 1.0], [0.234375, 0.953125, 1.0, 1.0], [0.234375, 0.953125, 1.0, 1.0], [0.265625, 0.953125, 1.0, 1.0], [0.265625, 0.953125, 1.0, 1.0], [0.296875, 0.953125, 1.0, 1.0], [0.296875, 0.953125, 1.0, 1.0], [0.328125, 0.953125, 1.0, 1.0], [0.328125, 0.953125, 1.0, 1.0], [0.359375, 0.953125, 1.0, 1.0], [0.359375, 0.953125, 1.0, 1.0], [0.390625, 0.953125, 1.0, 1.0], [0.390625, 0.953125, 1.0, 1.0], [0.421875, 0.953125, 1.0, 1.0], [0.421875, 0.953125, 1.0, 1.0], [0.453125, 0.953125, 1.0, 1.0], [0.453125, 0.953125, 1.0, 1.0], [0.484375, 0.953125, 1.0, 1.0], [0.484375, 0.953125, 1.0, 1.0], [0.515625, 0.953125, 1.0, 1.0], [0.515625, 0.953125, 1.0, 1.0], [0.546875, 0.953125, 1.0, 1.0], [0.546875, 0.953125, 1.0, 1.0], [0.578125, 0.953125, 1.0, 1.0], [0.578125, 0.953125, 1.0, 1.0], [0.609375, 0.953125, 1.0, 1.0], [0.609375, 0.953125, 1.0, 1.0], [0.640625, 0.953125, 1.0, 1.0], [0.640625, 0.953125, 1.0, 1.0], [0.671875, 0.953125, 1.0, 1.0], [0.671875, 0.953125, 1.0, 1.0], [0.703125, 0.953125, 1.0, 1.0], [0.703125, 0.953125, 1.0, 1.0], [0.734375, 0.953125, 1.0, 1.0], [0.734375, 0.953125, 1.0, 1.0], [0.765625, 0.953125, 1.0, 1.0], [0.765625, 0.953125, 1.0, 1.0], [0.796875, 0.953125, 1.0, 1.0], [0.796875, 0.953125, 1.0, 1.0], [0.828125, 0.953125, 1.0, 1.0], [0.828125, 0.953125, 1.0, 1.0], [0.859375, 0.953125, 1.0, 1.0], [0.859375, 0.953125, 1.0, 1.0], [0.890625, 0.953125, 1.0, 1.0], [0.890625, 0.953125, 1.0, 1.0], [0.921875, 0.953125, 1.0, 1.0], [0.921875, 0.953125, 1.0, 1.0], [0.953125, 0.953125, 1.0, 1.0], [0.953125, 0.953125, 1.0, 1.0], [0.984375, 0.953125, 1.0, 1.0], [0.984375, 0.953125, 1.0, 1.0], [0.015625, 0.984375, 1.0, 1.0], [0.015625, 0.984375, 1.0, 1.0], [0.046875, 0.984375, 1.0, 1.0], [0.046875, 0.984375, 1.0, 1.0], [0.078125, 0.984375, 1.0, 1.0], [0.078125, 0.984375, 1.0, 1.0], [0.109375, 0.984375, 1.0, 1.0], [0.109375, 0.984375, 1.0, 1.0], [0.140625, 0.984375, 1.0, 1.0], [0.140625, 0.984375, 1.0, 1.0], [0.171875, 0.984375, 1.0, 1.0], [0.171875, 0.984375, 1.0, 1.0], [0.203125, 0.984375, 1.0, 1.0], [0.203125, 0.984375, 1.0, 1.0], [0.234375, 0.984375, 1.0, 1.0], [0.234375, 0.984375, 1.0, 1.0], [0.265625, 0.984375, 1.0, 1.0], [0.265625, 0.984375, 1.0, 1.0], [0.296875, 0.984375, 1.0, 1.0], [0.296875, 0.984375, 1.0, 1.0], [0.328125, 0.984375, 1.0, 1.0], [0.328125, 0.984375, 1.0, 1.0], [0.359375, 0.984375, 1.0, 1.0], [0.359375, 0.984375, 1.0, 1.0], [0.390625, 0.984375, 1.0, 1.0], [0.390625, 0.984375, 1.0, 1.0], [0.421875, 0.984375, 1.0, 1.0], [0.421875, 0.984375, 1.0, 1.0], [0.453125, 0.984375, 1.0, 1.0], [0.453125, 0.984375, 1.0, 1.0], [0.484375, 0.984375, 1.0, 1.0], [0.484375, 0.984375, 1.0, 1.0], [0.515625, 0.984375, 1.0, 1.0], [0.515625, 0.984375, 1.0, 1.0], [0.546875, 0.984375, 1.0, 1.0], [0.546875, 0.984375, 1.0, 1.0], [0.578125, 0.984375, 1.0, 1.0], [0.578125, 0.984375, 1.0, 1.0], [0.609375, 0.984375, 1.0, 1.0], [0.609375, 0.984375, 1.0, 1.0], [0.640625, 0.984375, 1.0, 1.0], [0.640625, 0.984375, 1.0, 1.0], [0.671875, 0.984375, 1.0, 1.0], [0.671875, 0.984375, 1.0, 1.0], [0.703125, 0.984375, 1.0, 1.0], [0.703125, 0.984375, 1.0, 1.0], [0.734375, 0.984375, 1.0, 1.0], [0.734375, 0.984375, 1.0, 1.0], [0.765625, 0.984375, 1.0, 1.0], [0.765625, 0.984375, 1.0, 1.0], [0.796875, 0.984375, 1.0, 1.0], [0.796875, 0.984375, 1.0, 1.0], [0.828125, 0.984375, 1.0, 1.0], [0.828125, 0.984375, 1.0, 1.0], [0.859375, 0.984375, 1.0, 1.0], [0.859375, 0.984375, 1.0, 1.0], [0.890625, 0.984375, 1.0, 1.0], [0.890625, 0.984375, 1.0, 1.0], [0.921875, 0.984375, 1.0, 1.0], [0.921875, 0.984375, 1.0, 1.0], [0.953125, 0.984375, 1.0, 1.0], [0.953125, 0.984375, 1.0, 1.0], [0.984375, 0.984375, 1.0, 1.0], [0.984375, 0.984375, 1.0, 1.0], [0.03125, 0.03125, 1.0, 1.0], [0.03125, 0.03125, 1.0, 1.0], [0.09375, 0.03125, 1.0, 1.0], [0.09375, 0.03125, 1.0, 1.0], [0.15625, 0.03125, 1.0, 1.0], [0.15625, 0.03125, 1.0, 1.0], [0.21875, 0.03125, 1.0, 1.0], [0.21875, 0.03125, 1.0, 1.0], [0.28125, 0.03125, 1.0, 1.0], [0.28125, 0.03125, 1.0, 1.0], [0.34375, 0.03125, 1.0, 1.0], [0.34375, 0.03125, 1.0, 1.0], [0.40625, 0.03125, 1.0, 1.0], [0.40625, 0.03125, 1.0, 1.0], [0.46875, 0.03125, 1.0, 1.0], [0.46875, 0.03125, 1.0, 1.0], [0.53125, 0.03125, 1.0, 1.0], [0.53125, 0.03125, 1.0, 1.0], [0.59375, 0.03125, 1.0, 1.0], [0.59375, 0.03125, 1.0, 1.0], [0.65625, 0.03125, 1.0, 1.0], [0.65625, 0.03125, 1.0, 1.0], [0.71875, 0.03125, 1.0, 1.0], [0.71875, 0.03125, 1.0, 1.0], [0.78125, 0.03125, 1.0, 1.0], [0.78125, 0.03125, 1.0, 1.0], [0.84375, 0.03125, 1.0, 1.0], [0.84375, 0.03125, 1.0, 1.0], [0.90625, 0.03125, 1.0, 1.0], [0.90625, 0.03125, 1.0, 1.0], [0.96875, 0.03125, 1.0, 1.0], [0.96875, 0.03125, 1.0, 1.0], [0.03125, 0.09375, 1.0, 1.0], [0.03125, 0.09375, 1.0, 1.0], [0.09375, 0.09375, 1.0, 1.0], [0.09375, 0.09375, 1.0, 1.0], [0.15625, 0.09375, 1.0, 1.0], [0.15625, 0.09375, 1.0, 1.0], [0.21875, 0.09375, 1.0, 1.0], [0.21875, 0.09375, 1.0, 1.0], [0.28125, 0.09375, 1.0, 1.0], [0.28125, 0.09375, 1.0, 1.0], [0.34375, 0.09375, 1.0, 1.0], [0.34375, 0.09375, 1.0, 1.0], [0.40625, 0.09375, 1.0, 1.0], [0.40625, 0.09375, 1.0, 1.0], [0.46875, 0.09375, 1.0, 1.0], [0.46875, 0.09375, 1.0, 1.0], [0.53125, 0.09375, 1.0, 1.0], [0.53125, 0.09375, 1.0, 1.0], [0.59375, 0.09375, 1.0, 1.0], [0.59375, 0.09375, 1.0, 1.0], [0.65625, 0.09375, 1.0, 1.0], [0.65625, 0.09375, 1.0, 1.0], [0.71875, 0.09375, 1.0, 1.0], [0.71875, 0.09375, 1.0, 1.0], [0.78125, 0.09375, 1.0, 1.0], [0.78125, 0.09375, 1.0, 1.0], [0.84375, 0.09375, 1.0, 1.0], [0.84375, 0.09375, 1.0, 1.0], [0.90625, 0.09375, 1.0, 1.0], [0.90625, 0.09375, 1.0, 1.0], [0.96875, 0.09375, 1.0, 1.0], [0.96875, 0.09375, 1.0, 1.0], [0.03125, 0.15625, 1.0, 1.0], [0.03125, 0.15625, 1.0, 1.0], [0.09375, 0.15625, 1.0, 1.0], [0.09375, 0.15625, 1.0, 1.0], [0.15625, 0.15625, 1.0, 1.0], [0.15625, 0.15625, 1.0, 1.0], [0.21875, 0.15625, 1.0, 1.0], [0.21875, 0.15625, 1.0, 1.0], [0.28125, 0.15625, 1.0, 1.0], [0.28125, 0.15625, 1.0, 1.0], [0.34375, 0.15625, 1.0, 1.0], [0.34375, 0.15625, 1.0, 1.0], [0.40625, 0.15625, 1.0, 1.0], [0.40625, 0.15625, 1.0, 1.0], [0.46875, 0.15625, 1.0, 1.0], [0.46875, 0.15625, 1.0, 1.0], [0.53125, 0.15625, 1.0, 1.0], [0.53125, 0.15625, 1.0, 1.0], [0.59375, 0.15625, 1.0, 1.0], [0.59375, 0.15625, 1.0, 1.0], [0.65625, 0.15625, 1.0, 1.0], [0.65625, 0.15625, 1.0, 1.0], [0.71875, 0.15625, 1.0, 1.0], [0.71875, 0.15625, 1.0, 1.0], [0.78125, 0.15625, 1.0, 1.0], [0.78125, 0.15625, 1.0, 1.0], [0.84375, 0.15625, 1.0, 1.0], [0.84375, 0.15625, 1.0, 1.0], [0.90625, 0.15625, 1.0, 1.0], [0.90625, 0.15625, 1.0, 1.0], [0.96875, 0.15625, 1.0, 1.0], [0.96875, 0.15625, 1.0, 1.0], [0.03125, 0.21875, 1.0, 1.0], [0.03125, 0.21875, 1.0, 1.0], [0.09375, 0.21875, 1.0, 1.0], [0.09375, 0.21875, 1.0, 1.0], [0.15625, 0.21875, 1.0, 1.0], [0.15625, 0.21875, 1.0, 1.0], [0.21875, 0.21875, 1.0, 1.0], [0.21875, 0.21875, 1.0, 1.0], [0.28125, 0.21875, 1.0, 1.0], [0.28125, 0.21875, 1.0, 1.0], [0.34375, 0.21875, 1.0, 1.0], [0.34375, 0.21875, 1.0, 1.0], [0.40625, 0.21875, 1.0, 1.0], [0.40625, 0.21875, 1.0, 1.0], [0.46875, 0.21875, 1.0, 1.0], [0.46875, 0.21875, 1.0, 1.0], [0.53125, 0.21875, 1.0, 1.0], [0.53125, 0.21875, 1.0, 1.0], [0.59375, 0.21875, 1.0, 1.0], [0.59375, 0.21875, 1.0, 1.0], [0.65625, 0.21875, 1.0, 1.0], [0.65625, 0.21875, 1.0, 1.0], [0.71875, 0.21875, 1.0, 1.0], [0.71875, 0.21875, 1.0, 1.0], [0.78125, 0.21875, 1.0, 1.0], [0.78125, 0.21875, 1.0, 1.0], [0.84375, 0.21875, 1.0, 1.0], [0.84375, 0.21875, 1.0, 1.0], [0.90625, 0.21875, 1.0, 1.0], [0.90625, 0.21875, 1.0, 1.0], [0.96875, 0.21875, 1.0, 1.0], [0.96875, 0.21875, 1.0, 1.0], [0.03125, 0.28125, 1.0, 1.0], [0.03125, 0.28125, 1.0, 1.0], [0.09375, 0.28125, 1.0, 1.0], [0.09375, 0.28125, 1.0, 1.0], [0.15625, 0.28125, 1.0, 1.0], [0.15625, 0.28125, 1.0, 1.0], [0.21875, 0.28125, 1.0, 1.0], [0.21875, 0.28125, 1.0, 1.0], [0.28125, 0.28125, 1.0, 1.0], [0.28125, 0.28125, 1.0, 1.0], [0.34375, 0.28125, 1.0, 1.0], [0.34375, 0.28125, 1.0, 1.0], [0.40625, 0.28125, 1.0, 1.0], [0.40625, 0.28125, 1.0, 1.0], [0.46875, 0.28125, 1.0, 1.0], [0.46875, 0.28125, 1.0, 1.0], [0.53125, 0.28125, 1.0, 1.0], [0.53125, 0.28125, 1.0, 1.0], [0.59375, 0.28125, 1.0, 1.0], [0.59375, 0.28125, 1.0, 1.0], [0.65625, 0.28125, 1.0, 1.0], [0.65625, 0.28125, 1.0, 1.0], [0.71875, 0.28125, 1.0, 1.0], [0.71875, 0.28125, 1.0, 1.0], [0.78125, 0.28125, 1.0, 1.0], [0.78125, 0.28125, 1.0, 1.0], [0.84375, 0.28125, 1.0, 1.0], [0.84375, 0.28125, 1.0, 1.0], [0.90625, 0.28125, 1.0, 1.0], [0.90625, 0.28125, 1.0, 1.0], [0.96875, 0.28125, 1.0, 1.0], [0.96875, 0.28125, 1.0, 1.0], [0.03125, 0.34375, 1.0, 1.0], [0.03125, 0.34375, 1.0, 1.0], [0.09375, 0.34375, 1.0, 1.0], [0.09375, 0.34375, 1.0, 1.0], [0.15625, 0.34375, 1.0, 1.0], [0.15625, 0.34375, 1.0, 1.0], [0.21875, 0.34375, 1.0, 1.0], [0.21875, 0.34375, 1.0, 1.0], [0.28125, 0.34375, 1.0, 1.0], [0.28125, 0.34375, 1.0, 1.0], [0.34375, 0.34375, 1.0, 1.0], [0.34375, 0.34375, 1.0, 1.0], [0.40625, 0.34375, 1.0, 1.0], [0.40625, 0.34375, 1.0, 1.0], [0.46875, 0.34375, 1.0, 1.0], [0.46875, 0.34375, 1.0, 1.0], [0.53125, 0.34375, 1.0, 1.0], [0.53125, 0.34375, 1.0, 1.0], [0.59375, 0.34375, 1.0, 1.0], [0.59375, 0.34375, 1.0, 1.0], [0.65625, 0.34375, 1.0, 1.0], [0.65625, 0.34375, 1.0, 1.0], [0.71875, 0.34375, 1.0, 1.0], [0.71875, 0.34375, 1.0, 1.0], [0.78125, 0.34375, 1.0, 1.0], [0.78125, 0.34375, 1.0, 1.0], [0.84375, 0.34375, 1.0, 1.0], [0.84375, 0.34375, 1.0, 1.0], [0.90625, 0.34375, 1.0, 1.0], [0.90625, 0.34375, 1.0, 1.0], [0.96875, 0.34375, 1.0, 1.0], [0.96875, 0.34375, 1.0, 1.0], [0.03125, 0.40625, 1.0, 1.0], [0.03125, 0.40625, 1.0, 1.0], [0.09375, 0.40625, 1.0, 1.0], [0.09375, 0.40625, 1.0, 1.0], [0.15625, 0.40625, 1.0, 1.0], [0.15625, 0.40625, 1.0, 1.0], [0.21875, 0.40625, 1.0, 1.0], [0.21875, 0.40625, 1.0, 1.0], [0.28125, 0.40625, 1.0, 1.0], [0.28125, 0.40625, 1.0, 1.0], [0.34375, 0.40625, 1.0, 1.0], [0.34375, 0.40625, 1.0, 1.0], [0.40625, 0.40625, 1.0, 1.0], [0.40625, 0.40625, 1.0, 1.0], [0.46875, 0.40625, 1.0, 1.0], [0.46875, 0.40625, 1.0, 1.0], [0.53125, 0.40625, 1.0, 1.0], [0.53125, 0.40625, 1.0, 1.0], [0.59375, 0.40625, 1.0, 1.0], [0.59375, 0.40625, 1.0, 1.0], [0.65625, 0.40625, 1.0, 1.0], [0.65625, 0.40625, 1.0, 1.0], [0.71875, 0.40625, 1.0, 1.0], [0.71875, 0.40625, 1.0, 1.0], [0.78125, 0.40625, 1.0, 1.0], [0.78125, 0.40625, 1.0, 1.0], [0.84375, 0.40625, 1.0, 1.0], [0.84375, 0.40625, 1.0, 1.0], [0.90625, 0.40625, 1.0, 1.0], [0.90625, 0.40625, 1.0, 1.0], [0.96875, 0.40625, 1.0, 1.0], [0.96875, 0.40625, 1.0, 1.0], [0.03125, 0.46875, 1.0, 1.0], [0.03125, 0.46875, 1.0, 1.0], [0.09375, 0.46875, 1.0, 1.0], [0.09375, 0.46875, 1.0, 1.0], [0.15625, 0.46875, 1.0, 1.0], [0.15625, 0.46875, 1.0, 1.0], [0.21875, 0.46875, 1.0, 1.0], [0.21875, 0.46875, 1.0, 1.0], [0.28125, 0.46875, 1.0, 1.0], [0.28125, 0.46875, 1.0, 1.0], [0.34375, 0.46875, 1.0, 1.0], [0.34375, 0.46875, 1.0, 1.0], [0.40625, 0.46875, 1.0, 1.0], [0.40625, 0.46875, 1.0, 1.0], [0.46875, 0.46875, 1.0, 1.0], [0.46875, 0.46875, 1.0, 1.0], [0.53125, 0.46875, 1.0, 1.0], [0.53125, 0.46875, 1.0, 1.0], [0.59375, 0.46875, 1.0, 1.0], [0.59375, 0.46875, 1.0, 1.0], [0.65625, 0.46875, 1.0, 1.0], [0.65625, 0.46875, 1.0, 1.0], [0.71875, 0.46875, 1.0, 1.0], [0.71875, 0.46875, 1.0, 1.0], [0.78125, 0.46875, 1.0, 1.0], [0.78125, 0.46875, 1.0, 1.0], [0.84375, 0.46875, 1.0, 1.0], [0.84375, 0.46875, 1.0, 1.0], [0.90625, 0.46875, 1.0, 1.0], [0.90625, 0.46875, 1.0, 1.0], [0.96875, 0.46875, 1.0, 1.0], [0.96875, 0.46875, 1.0, 1.0], [0.03125, 0.53125, 1.0, 1.0], [0.03125, 0.53125, 1.0, 1.0], [0.09375, 0.53125, 1.0, 1.0], [0.09375, 0.53125, 1.0, 1.0], [0.15625, 0.53125, 1.0, 1.0], [0.15625, 0.53125, 1.0, 1.0], [0.21875, 0.53125, 1.0, 1.0], [0.21875, 0.53125, 1.0, 1.0], [0.28125, 0.53125, 1.0, 1.0], [0.28125, 0.53125, 1.0, 1.0], [0.34375, 0.53125, 1.0, 1.0], [0.34375, 0.53125, 1.0, 1.0], [0.40625, 0.53125, 1.0, 1.0], [0.40625, 0.53125, 1.0, 1.0], [0.46875, 0.53125, 1.0, 1.0], [0.46875, 0.53125, 1.0, 1.0], [0.53125, 0.53125, 1.0, 1.0], [0.53125, 0.53125, 1.0, 1.0], [0.59375, 0.53125, 1.0, 1.0], [0.59375, 0.53125, 1.0, 1.0], [0.65625, 0.53125, 1.0, 1.0], [0.65625, 0.53125, 1.0, 1.0], [0.71875, 0.53125, 1.0, 1.0], [0.71875, 0.53125, 1.0, 1.0], [0.78125, 0.53125, 1.0, 1.0], [0.78125, 0.53125, 1.0, 1.0], [0.84375, 0.53125, 1.0, 1.0], [0.84375, 0.53125, 1.0, 1.0], [0.90625, 0.53125, 1.0, 1.0], [0.90625, 0.53125, 1.0, 1.0], [0.96875, 0.53125, 1.0, 1.0], [0.96875, 0.53125, 1.0, 1.0], [0.03125, 0.59375, 1.0, 1.0], [0.03125, 0.59375, 1.0, 1.0], [0.09375, 0.59375, 1.0, 1.0], [0.09375, 0.59375, 1.0, 1.0], [0.15625, 0.59375, 1.0, 1.0], [0.15625, 0.59375, 1.0, 1.0], [0.21875, 0.59375, 1.0, 1.0], [0.21875, 0.59375, 1.0, 1.0], [0.28125, 0.59375, 1.0, 1.0], [0.28125, 0.59375, 1.0, 1.0], [0.34375, 0.59375, 1.0, 1.0], [0.34375, 0.59375, 1.0, 1.0], [0.40625, 0.59375, 1.0, 1.0], [0.40625, 0.59375, 1.0, 1.0], [0.46875, 0.59375, 1.0, 1.0], [0.46875, 0.59375, 1.0, 1.0], [0.53125, 0.59375, 1.0, 1.0], [0.53125, 0.59375, 1.0, 1.0], [0.59375, 0.59375, 1.0, 1.0], [0.59375, 0.59375, 1.0, 1.0], [0.65625, 0.59375, 1.0, 1.0], [0.65625, 0.59375, 1.0, 1.0], [0.71875, 0.59375, 1.0, 1.0], [0.71875, 0.59375, 1.0, 1.0], [0.78125, 0.59375, 1.0, 1.0], [0.78125, 0.59375, 1.0, 1.0], [0.84375, 0.59375, 1.0, 1.0], [0.84375, 0.59375, 1.0, 1.0], [0.90625, 0.59375, 1.0, 1.0], [0.90625, 0.59375, 1.0, 1.0], [0.96875, 0.59375, 1.0, 1.0], [0.96875, 0.59375, 1.0, 1.0], [0.03125, 0.65625, 1.0, 1.0], [0.03125, 0.65625, 1.0, 1.0], [0.09375, 0.65625, 1.0, 1.0], [0.09375, 0.65625, 1.0, 1.0], [0.15625, 0.65625, 1.0, 1.0], [0.15625, 0.65625, 1.0, 1.0], [0.21875, 0.65625, 1.0, 1.0], [0.21875, 0.65625, 1.0, 1.0], [0.28125, 0.65625, 1.0, 1.0], [0.28125, 0.65625, 1.0, 1.0], [0.34375, 0.65625, 1.0, 1.0], [0.34375, 0.65625, 1.0, 1.0], [0.40625, 0.65625, 1.0, 1.0], [0.40625, 0.65625, 1.0, 1.0], [0.46875, 0.65625, 1.0, 1.0], [0.46875, 0.65625, 1.0, 1.0], [0.53125, 0.65625, 1.0, 1.0], [0.53125, 0.65625, 1.0, 1.0], [0.59375, 0.65625, 1.0, 1.0], [0.59375, 0.65625, 1.0, 1.0], [0.65625, 0.65625, 1.0, 1.0], [0.65625, 0.65625, 1.0, 1.0], [0.71875, 0.65625, 1.0, 1.0], [0.71875, 0.65625, 1.0, 1.0], [0.78125, 0.65625, 1.0, 1.0], [0.78125, 0.65625, 1.0, 1.0], [0.84375, 0.65625, 1.0, 1.0], [0.84375, 0.65625, 1.0, 1.0], [0.90625, 0.65625, 1.0, 1.0], [0.90625, 0.65625, 1.0, 1.0], [0.96875, 0.65625, 1.0, 1.0], [0.96875, 0.65625, 1.0, 1.0], [0.03125, 0.71875, 1.0, 1.0], [0.03125, 0.71875, 1.0, 1.0], [0.09375, 0.71875, 1.0, 1.0], [0.09375, 0.71875, 1.0, 1.0], [0.15625, 0.71875, 1.0, 1.0], [0.15625, 0.71875, 1.0, 1.0], [0.21875, 0.71875, 1.0, 1.0], [0.21875, 0.71875, 1.0, 1.0], [0.28125, 0.71875, 1.0, 1.0], [0.28125, 0.71875, 1.0, 1.0], [0.34375, 0.71875, 1.0, 1.0], [0.34375, 0.71875, 1.0, 1.0], [0.40625, 0.71875, 1.0, 1.0], [0.40625, 0.71875, 1.0, 1.0], [0.46875, 0.71875, 1.0, 1.0], [0.46875, 0.71875, 1.0, 1.0], [0.53125, 0.71875, 1.0, 1.0], [0.53125, 0.71875, 1.0, 1.0], [0.59375, 0.71875, 1.0, 1.0], [0.59375, 0.71875, 1.0, 1.0], [0.65625, 0.71875, 1.0, 1.0], [0.65625, 0.71875, 1.0, 1.0], [0.71875, 0.71875, 1.0, 1.0], [0.71875, 0.71875, 1.0, 1.0], [0.78125, 0.71875, 1.0, 1.0], [0.78125, 0.71875, 1.0, 1.0], [0.84375, 0.71875, 1.0, 1.0], [0.84375, 0.71875, 1.0, 1.0], [0.90625, 0.71875, 1.0, 1.0], [0.90625, 0.71875, 1.0, 1.0], [0.96875, 0.71875, 1.0, 1.0], [0.96875, 0.71875, 1.0, 1.0], [0.03125, 0.78125, 1.0, 1.0], [0.03125, 0.78125, 1.0, 1.0], [0.09375, 0.78125, 1.0, 1.0], [0.09375, 0.78125, 1.0, 1.0], [0.15625, 0.78125, 1.0, 1.0], [0.15625, 0.78125, 1.0, 1.0], [0.21875, 0.78125, 1.0, 1.0], [0.21875, 0.78125, 1.0, 1.0], [0.28125, 0.78125, 1.0, 1.0], [0.28125, 0.78125, 1.0, 1.0], [0.34375, 0.78125, 1.0, 1.0], [0.34375, 0.78125, 1.0, 1.0], [0.40625, 0.78125, 1.0, 1.0], [0.40625, 0.78125, 1.0, 1.0], [0.46875, 0.78125, 1.0, 1.0], [0.46875, 0.78125, 1.0, 1.0], [0.53125, 0.78125, 1.0, 1.0], [0.53125, 0.78125, 1.0, 1.0], [0.59375, 0.78125, 1.0, 1.0], [0.59375, 0.78125, 1.0, 1.0], [0.65625, 0.78125, 1.0, 1.0], [0.65625, 0.78125, 1.0, 1.0], [0.71875, 0.78125, 1.0, 1.0], [0.71875, 0.78125, 1.0, 1.0], [0.78125, 0.78125, 1.0, 1.0], [0.78125, 0.78125, 1.0, 1.0], [0.84375, 0.78125, 1.0, 1.0], [0.84375, 0.78125, 1.0, 1.0], [0.90625, 0.78125, 1.0, 1.0], [0.90625, 0.78125, 1.0, 1.0], [0.96875, 0.78125, 1.0, 1.0], [0.96875, 0.78125, 1.0, 1.0], [0.03125, 0.84375, 1.0, 1.0], [0.03125, 0.84375, 1.0, 1.0], [0.09375, 0.84375, 1.0, 1.0], [0.09375, 0.84375, 1.0, 1.0], [0.15625, 0.84375, 1.0, 1.0], [0.15625, 0.84375, 1.0, 1.0], [0.21875, 0.84375, 1.0, 1.0], [0.21875, 0.84375, 1.0, 1.0], [0.28125, 0.84375, 1.0, 1.0], [0.28125, 0.84375, 1.0, 1.0], [0.34375, 0.84375, 1.0, 1.0], [0.34375, 0.84375, 1.0, 1.0], [0.40625, 0.84375, 1.0, 1.0], [0.40625, 0.84375, 1.0, 1.0], [0.46875, 0.84375, 1.0, 1.0], [0.46875, 0.84375, 1.0, 1.0], [0.53125, 0.84375, 1.0, 1.0], [0.53125, 0.84375, 1.0, 1.0], [0.59375, 0.84375, 1.0, 1.0], [0.59375, 0.84375, 1.0, 1.0], [0.65625, 0.84375, 1.0, 1.0], [0.65625, 0.84375, 1.0, 1.0], [0.71875, 0.84375, 1.0, 1.0], [0.71875, 0.84375, 1.0, 1.0], [0.78125, 0.84375, 1.0, 1.0], [0.78125, 0.84375, 1.0, 1.0], [0.84375, 0.84375, 1.0, 1.0], [0.84375, 0.84375, 1.0, 1.0], [0.90625, 0.84375, 1.0, 1.0], [0.90625, 0.84375, 1.0, 1.0], [0.96875, 0.84375, 1.0, 1.0], [0.96875, 0.84375, 1.0, 1.0], [0.03125, 0.90625, 1.0, 1.0], [0.03125, 0.90625, 1.0, 1.0], [0.09375, 0.90625, 1.0, 1.0], [0.09375, 0.90625, 1.0, 1.0], [0.15625, 0.90625, 1.0, 1.0], [0.15625, 0.90625, 1.0, 1.0], [0.21875, 0.90625, 1.0, 1.0], [0.21875, 0.90625, 1.0, 1.0], [0.28125, 0.90625, 1.0, 1.0], [0.28125, 0.90625, 1.0, 1.0], [0.34375, 0.90625, 1.0, 1.0], [0.34375, 0.90625, 1.0, 1.0], [0.40625, 0.90625, 1.0, 1.0], [0.40625, 0.90625, 1.0, 1.0], [0.46875, 0.90625, 1.0, 1.0], [0.46875, 0.90625, 1.0, 1.0], [0.53125, 0.90625, 1.0, 1.0], [0.53125, 0.90625, 1.0, 1.0], [0.59375, 0.90625, 1.0, 1.0], [0.59375, 0.90625, 1.0, 1.0], [0.65625, 0.90625, 1.0, 1.0], [0.65625, 0.90625, 1.0, 1.0], [0.71875, 0.90625, 1.0, 1.0], [0.71875, 0.90625, 1.0, 1.0], [0.78125, 0.90625, 1.0, 1.0], [0.78125, 0.90625, 1.0, 1.0], [0.84375, 0.90625, 1.0, 1.0], [0.84375, 0.90625, 1.0, 1.0], [0.90625, 0.90625, 1.0, 1.0], [0.90625, 0.90625, 1.0, 1.0], [0.96875, 0.90625, 1.0, 1.0], [0.96875, 0.90625, 1.0, 1.0], [0.03125, 0.96875, 1.0, 1.0], [0.03125, 0.96875, 1.0, 1.0], [0.09375, 0.96875, 1.0, 1.0], [0.09375, 0.96875, 1.0, 1.0], [0.15625, 0.96875, 1.0, 1.0], [0.15625, 0.96875, 1.0, 1.0], [0.21875, 0.96875, 1.0, 1.0], [0.21875, 0.96875, 1.0, 1.0], [0.28125, 0.96875, 1.0, 1.0], [0.28125, 0.96875, 1.0, 1.0], [0.34375, 0.96875, 1.0, 1.0], [0.34375, 0.96875, 1.0, 1.0], [0.40625, 0.96875, 1.0, 1.0], [0.40625, 0.96875, 1.0, 1.0], [0.46875, 0.96875, 1.0, 1.0], [0.46875, 0.96875, 1.0, 1.0], [0.53125, 0.96875, 1.0, 1.0], [0.53125, 0.96875, 1.0, 1.0], [0.59375, 0.96875, 1.0, 1.0], [0.59375, 0.96875, 1.0, 1.0], [0.65625, 0.96875, 1.0, 1.0], [0.65625, 0.96875, 1.0, 1.0], [0.71875, 0.96875, 1.0, 1.0], [0.71875, 0.96875, 1.0, 1.0], [0.78125, 0.96875, 1.0, 1.0], [0.78125, 0.96875, 1.0, 1.0], [0.84375, 0.96875, 1.0, 1.0], [0.84375, 0.96875, 1.0, 1.0], [0.90625, 0.96875, 1.0, 1.0], [0.90625, 0.96875, 1.0, 1.0], [0.96875, 0.96875, 1.0, 1.0], [0.96875, 0.96875, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0]] \ No newline at end of file diff --git a/pose-detection/src/shared/test_data/anchor_golden_file_1.json b/pose-detection/src/shared/test_data/anchor_golden_file_1.json new file mode 100644 index 0000000000..c69a9a0b33 --- /dev/null +++ b/pose-detection/src/shared/test_data/anchor_golden_file_1.json @@ -0,0 +1 @@ +[[0.0263158, 0.0263158, 0.1, 0.1], [0.0263158, 0.0263158, 0.282843, 0.141421], [0.0263158, 0.0263158, 0.141421, 0.282843], [0.0789474, 0.0263158, 0.1, 0.1], [0.0789474, 0.0263158, 0.282843, 0.141421], [0.0789474, 0.0263158, 0.141421, 0.282843], [0.131579, 0.0263158, 0.1, 0.1], [0.131579, 0.0263158, 0.282843, 0.141421], [0.131579, 0.0263158, 0.141421, 0.282843], [0.184211, 0.0263158, 0.1, 0.1], [0.184211, 0.0263158, 0.282843, 0.141421], [0.184211, 0.0263158, 0.141421, 0.282843], [0.236842, 0.0263158, 0.1, 0.1], [0.236842, 0.0263158, 0.282843, 0.141421], [0.236842, 0.0263158, 0.141421, 0.282843], [0.289474, 0.0263158, 0.1, 0.1], [0.289474, 0.0263158, 0.282843, 0.141421], [0.289474, 0.0263158, 0.141421, 0.282843], [0.342105, 0.0263158, 0.1, 0.1], [0.342105, 0.0263158, 0.282843, 0.141421], [0.342105, 0.0263158, 0.141421, 0.282843], [0.394737, 0.0263158, 0.1, 0.1], [0.394737, 0.0263158, 0.282843, 0.141421], [0.394737, 0.0263158, 0.141421, 0.282843], [0.447368, 0.0263158, 0.1, 0.1], [0.447368, 0.0263158, 0.282843, 0.141421], [0.447368, 0.0263158, 0.141421, 0.282843], [0.5, 0.0263158, 0.1, 0.1], [0.5, 0.0263158, 0.282843, 0.141421], [0.5, 0.0263158, 0.141421, 0.282843], [0.552632, 0.0263158, 0.1, 0.1], [0.552632, 0.0263158, 0.282843, 0.141421], [0.552632, 0.0263158, 0.141421, 0.282843], [0.605263, 0.0263158, 0.1, 0.1], [0.605263, 0.0263158, 0.282843, 0.141421], [0.605263, 0.0263158, 0.141421, 0.282843], [0.657895, 0.0263158, 0.1, 0.1], [0.657895, 0.0263158, 0.282843, 0.141421], [0.657895, 0.0263158, 0.141421, 0.282843], [0.710526, 0.0263158, 0.1, 0.1], [0.710526, 0.0263158, 0.282843, 0.141421], [0.710526, 0.0263158, 0.141421, 0.282843], [0.763158, 0.0263158, 0.1, 0.1], [0.763158, 0.0263158, 0.282843, 0.141421], [0.763158, 0.0263158, 0.141421, 0.282843], [0.81579, 0.0263158, 0.1, 0.1], [0.81579, 0.0263158, 0.282843, 0.141421], [0.81579, 0.0263158, 0.141421, 0.282843], [0.868421, 0.0263158, 0.1, 0.1], [0.868421, 0.0263158, 0.282843, 0.141421], [0.868421, 0.0263158, 0.141421, 0.282843], [0.921053, 0.0263158, 0.1, 0.1], [0.921053, 0.0263158, 0.282843, 0.141421], [0.921053, 0.0263158, 0.141421, 0.282843], [0.973684, 0.0263158, 0.1, 0.1], [0.973684, 0.0263158, 0.282843, 0.141421], [0.973684, 0.0263158, 0.141421, 0.282843], [0.0263158, 0.0789474, 0.1, 0.1], [0.0263158, 0.0789474, 0.282843, 0.141421], [0.0263158, 0.0789474, 0.141421, 0.282843], [0.0789474, 0.0789474, 0.1, 0.1], [0.0789474, 0.0789474, 0.282843, 0.141421], [0.0789474, 0.0789474, 0.141421, 0.282843], [0.131579, 0.0789474, 0.1, 0.1], [0.131579, 0.0789474, 0.282843, 0.141421], [0.131579, 0.0789474, 0.141421, 0.282843], [0.184211, 0.0789474, 0.1, 0.1], [0.184211, 0.0789474, 0.282843, 0.141421], [0.184211, 0.0789474, 0.141421, 0.282843], [0.236842, 0.0789474, 0.1, 0.1], [0.236842, 0.0789474, 0.282843, 0.141421], [0.236842, 0.0789474, 0.141421, 0.282843], [0.289474, 0.0789474, 0.1, 0.1], [0.289474, 0.0789474, 0.282843, 0.141421], [0.289474, 0.0789474, 0.141421, 0.282843], [0.342105, 0.0789474, 0.1, 0.1], [0.342105, 0.0789474, 0.282843, 0.141421], [0.342105, 0.0789474, 0.141421, 0.282843], [0.394737, 0.0789474, 0.1, 0.1], [0.394737, 0.0789474, 0.282843, 0.141421], [0.394737, 0.0789474, 0.141421, 0.282843], [0.447368, 0.0789474, 0.1, 0.1], [0.447368, 0.0789474, 0.282843, 0.141421], [0.447368, 0.0789474, 0.141421, 0.282843], [0.5, 0.0789474, 0.1, 0.1], [0.5, 0.0789474, 0.282843, 0.141421], [0.5, 0.0789474, 0.141421, 0.282843], [0.552632, 0.0789474, 0.1, 0.1], [0.552632, 0.0789474, 0.282843, 0.141421], [0.552632, 0.0789474, 0.141421, 0.282843], [0.605263, 0.0789474, 0.1, 0.1], [0.605263, 0.0789474, 0.282843, 0.141421], [0.605263, 0.0789474, 0.141421, 0.282843], [0.657895, 0.0789474, 0.1, 0.1], [0.657895, 0.0789474, 0.282843, 0.141421], [0.657895, 0.0789474, 0.141421, 0.282843], [0.710526, 0.0789474, 0.1, 0.1], [0.710526, 0.0789474, 0.282843, 0.141421], [0.710526, 0.0789474, 0.141421, 0.282843], [0.763158, 0.0789474, 0.1, 0.1], [0.763158, 0.0789474, 0.282843, 0.141421], [0.763158, 0.0789474, 0.141421, 0.282843], [0.81579, 0.0789474, 0.1, 0.1], [0.81579, 0.0789474, 0.282843, 0.141421], [0.81579, 0.0789474, 0.141421, 0.282843], [0.868421, 0.0789474, 0.1, 0.1], [0.868421, 0.0789474, 0.282843, 0.141421], [0.868421, 0.0789474, 0.141421, 0.282843], [0.921053, 0.0789474, 0.1, 0.1], [0.921053, 0.0789474, 0.282843, 0.141421], [0.921053, 0.0789474, 0.141421, 0.282843], [0.973684, 0.0789474, 0.1, 0.1], [0.973684, 0.0789474, 0.282843, 0.141421], [0.973684, 0.0789474, 0.141421, 0.282843], [0.0263158, 0.131579, 0.1, 0.1], [0.0263158, 0.131579, 0.282843, 0.141421], [0.0263158, 0.131579, 0.141421, 0.282843], [0.0789474, 0.131579, 0.1, 0.1], [0.0789474, 0.131579, 0.282843, 0.141421], [0.0789474, 0.131579, 0.141421, 0.282843], [0.131579, 0.131579, 0.1, 0.1], [0.131579, 0.131579, 0.282843, 0.141421], [0.131579, 0.131579, 0.141421, 0.282843], [0.184211, 0.131579, 0.1, 0.1], [0.184211, 0.131579, 0.282843, 0.141421], [0.184211, 0.131579, 0.141421, 0.282843], [0.236842, 0.131579, 0.1, 0.1], [0.236842, 0.131579, 0.282843, 0.141421], [0.236842, 0.131579, 0.141421, 0.282843], [0.289474, 0.131579, 0.1, 0.1], [0.289474, 0.131579, 0.282843, 0.141421], [0.289474, 0.131579, 0.141421, 0.282843], [0.342105, 0.131579, 0.1, 0.1], [0.342105, 0.131579, 0.282843, 0.141421], [0.342105, 0.131579, 0.141421, 0.282843], [0.394737, 0.131579, 0.1, 0.1], [0.394737, 0.131579, 0.282843, 0.141421], [0.394737, 0.131579, 0.141421, 0.282843], [0.447368, 0.131579, 0.1, 0.1], [0.447368, 0.131579, 0.282843, 0.141421], [0.447368, 0.131579, 0.141421, 0.282843], [0.5, 0.131579, 0.1, 0.1], [0.5, 0.131579, 0.282843, 0.141421], [0.5, 0.131579, 0.141421, 0.282843], [0.552632, 0.131579, 0.1, 0.1], [0.552632, 0.131579, 0.282843, 0.141421], [0.552632, 0.131579, 0.141421, 0.282843], [0.605263, 0.131579, 0.1, 0.1], [0.605263, 0.131579, 0.282843, 0.141421], [0.605263, 0.131579, 0.141421, 0.282843], [0.657895, 0.131579, 0.1, 0.1], [0.657895, 0.131579, 0.282843, 0.141421], [0.657895, 0.131579, 0.141421, 0.282843], [0.710526, 0.131579, 0.1, 0.1], [0.710526, 0.131579, 0.282843, 0.141421], [0.710526, 0.131579, 0.141421, 0.282843], [0.763158, 0.131579, 0.1, 0.1], [0.763158, 0.131579, 0.282843, 0.141421], [0.763158, 0.131579, 0.141421, 0.282843], [0.81579, 0.131579, 0.1, 0.1], [0.81579, 0.131579, 0.282843, 0.141421], [0.81579, 0.131579, 0.141421, 0.282843], [0.868421, 0.131579, 0.1, 0.1], [0.868421, 0.131579, 0.282843, 0.141421], [0.868421, 0.131579, 0.141421, 0.282843], [0.921053, 0.131579, 0.1, 0.1], [0.921053, 0.131579, 0.282843, 0.141421], [0.921053, 0.131579, 0.141421, 0.282843], [0.973684, 0.131579, 0.1, 0.1], [0.973684, 0.131579, 0.282843, 0.141421], [0.973684, 0.131579, 0.141421, 0.282843], [0.0263158, 0.184211, 0.1, 0.1], [0.0263158, 0.184211, 0.282843, 0.141421], [0.0263158, 0.184211, 0.141421, 0.282843], [0.0789474, 0.184211, 0.1, 0.1], [0.0789474, 0.184211, 0.282843, 0.141421], [0.0789474, 0.184211, 0.141421, 0.282843], [0.131579, 0.184211, 0.1, 0.1], [0.131579, 0.184211, 0.282843, 0.141421], [0.131579, 0.184211, 0.141421, 0.282843], [0.184211, 0.184211, 0.1, 0.1], [0.184211, 0.184211, 0.282843, 0.141421], [0.184211, 0.184211, 0.141421, 0.282843], [0.236842, 0.184211, 0.1, 0.1], [0.236842, 0.184211, 0.282843, 0.141421], [0.236842, 0.184211, 0.141421, 0.282843], [0.289474, 0.184211, 0.1, 0.1], [0.289474, 0.184211, 0.282843, 0.141421], [0.289474, 0.184211, 0.141421, 0.282843], [0.342105, 0.184211, 0.1, 0.1], [0.342105, 0.184211, 0.282843, 0.141421], [0.342105, 0.184211, 0.141421, 0.282843], [0.394737, 0.184211, 0.1, 0.1], [0.394737, 0.184211, 0.282843, 0.141421], [0.394737, 0.184211, 0.141421, 0.282843], [0.447368, 0.184211, 0.1, 0.1], [0.447368, 0.184211, 0.282843, 0.141421], [0.447368, 0.184211, 0.141421, 0.282843], [0.5, 0.184211, 0.1, 0.1], [0.5, 0.184211, 0.282843, 0.141421], [0.5, 0.184211, 0.141421, 0.282843], [0.552632, 0.184211, 0.1, 0.1], [0.552632, 0.184211, 0.282843, 0.141421], [0.552632, 0.184211, 0.141421, 0.282843], [0.605263, 0.184211, 0.1, 0.1], [0.605263, 0.184211, 0.282843, 0.141421], [0.605263, 0.184211, 0.141421, 0.282843], [0.657895, 0.184211, 0.1, 0.1], [0.657895, 0.184211, 0.282843, 0.141421], [0.657895, 0.184211, 0.141421, 0.282843], [0.710526, 0.184211, 0.1, 0.1], [0.710526, 0.184211, 0.282843, 0.141421], [0.710526, 0.184211, 0.141421, 0.282843], [0.763158, 0.184211, 0.1, 0.1], [0.763158, 0.184211, 0.282843, 0.141421], [0.763158, 0.184211, 0.141421, 0.282843], [0.81579, 0.184211, 0.1, 0.1], [0.81579, 0.184211, 0.282843, 0.141421], [0.81579, 0.184211, 0.141421, 0.282843], [0.868421, 0.184211, 0.1, 0.1], [0.868421, 0.184211, 0.282843, 0.141421], [0.868421, 0.184211, 0.141421, 0.282843], [0.921053, 0.184211, 0.1, 0.1], [0.921053, 0.184211, 0.282843, 0.141421], [0.921053, 0.184211, 0.141421, 0.282843], [0.973684, 0.184211, 0.1, 0.1], [0.973684, 0.184211, 0.282843, 0.141421], [0.973684, 0.184211, 0.141421, 0.282843], [0.0263158, 0.236842, 0.1, 0.1], [0.0263158, 0.236842, 0.282843, 0.141421], [0.0263158, 0.236842, 0.141421, 0.282843], [0.0789474, 0.236842, 0.1, 0.1], [0.0789474, 0.236842, 0.282843, 0.141421], [0.0789474, 0.236842, 0.141421, 0.282843], [0.131579, 0.236842, 0.1, 0.1], [0.131579, 0.236842, 0.282843, 0.141421], [0.131579, 0.236842, 0.141421, 0.282843], [0.184211, 0.236842, 0.1, 0.1], [0.184211, 0.236842, 0.282843, 0.141421], [0.184211, 0.236842, 0.141421, 0.282843], [0.236842, 0.236842, 0.1, 0.1], [0.236842, 0.236842, 0.282843, 0.141421], [0.236842, 0.236842, 0.141421, 0.282843], [0.289474, 0.236842, 0.1, 0.1], [0.289474, 0.236842, 0.282843, 0.141421], [0.289474, 0.236842, 0.141421, 0.282843], [0.342105, 0.236842, 0.1, 0.1], [0.342105, 0.236842, 0.282843, 0.141421], [0.342105, 0.236842, 0.141421, 0.282843], [0.394737, 0.236842, 0.1, 0.1], [0.394737, 0.236842, 0.282843, 0.141421], [0.394737, 0.236842, 0.141421, 0.282843], [0.447368, 0.236842, 0.1, 0.1], [0.447368, 0.236842, 0.282843, 0.141421], [0.447368, 0.236842, 0.141421, 0.282843], [0.5, 0.236842, 0.1, 0.1], [0.5, 0.236842, 0.282843, 0.141421], [0.5, 0.236842, 0.141421, 0.282843], [0.552632, 0.236842, 0.1, 0.1], [0.552632, 0.236842, 0.282843, 0.141421], [0.552632, 0.236842, 0.141421, 0.282843], [0.605263, 0.236842, 0.1, 0.1], [0.605263, 0.236842, 0.282843, 0.141421], [0.605263, 0.236842, 0.141421, 0.282843], [0.657895, 0.236842, 0.1, 0.1], [0.657895, 0.236842, 0.282843, 0.141421], [0.657895, 0.236842, 0.141421, 0.282843], [0.710526, 0.236842, 0.1, 0.1], [0.710526, 0.236842, 0.282843, 0.141421], [0.710526, 0.236842, 0.141421, 0.282843], [0.763158, 0.236842, 0.1, 0.1], [0.763158, 0.236842, 0.282843, 0.141421], [0.763158, 0.236842, 0.141421, 0.282843], [0.81579, 0.236842, 0.1, 0.1], [0.81579, 0.236842, 0.282843, 0.141421], [0.81579, 0.236842, 0.141421, 0.282843], [0.868421, 0.236842, 0.1, 0.1], [0.868421, 0.236842, 0.282843, 0.141421], [0.868421, 0.236842, 0.141421, 0.282843], [0.921053, 0.236842, 0.1, 0.1], [0.921053, 0.236842, 0.282843, 0.141421], [0.921053, 0.236842, 0.141421, 0.282843], [0.973684, 0.236842, 0.1, 0.1], [0.973684, 0.236842, 0.282843, 0.141421], [0.973684, 0.236842, 0.141421, 0.282843], [0.0263158, 0.289474, 0.1, 0.1], [0.0263158, 0.289474, 0.282843, 0.141421], [0.0263158, 0.289474, 0.141421, 0.282843], [0.0789474, 0.289474, 0.1, 0.1], [0.0789474, 0.289474, 0.282843, 0.141421], [0.0789474, 0.289474, 0.141421, 0.282843], [0.131579, 0.289474, 0.1, 0.1], [0.131579, 0.289474, 0.282843, 0.141421], [0.131579, 0.289474, 0.141421, 0.282843], [0.184211, 0.289474, 0.1, 0.1], [0.184211, 0.289474, 0.282843, 0.141421], [0.184211, 0.289474, 0.141421, 0.282843], [0.236842, 0.289474, 0.1, 0.1], [0.236842, 0.289474, 0.282843, 0.141421], [0.236842, 0.289474, 0.141421, 0.282843], [0.289474, 0.289474, 0.1, 0.1], [0.289474, 0.289474, 0.282843, 0.141421], [0.289474, 0.289474, 0.141421, 0.282843], [0.342105, 0.289474, 0.1, 0.1], [0.342105, 0.289474, 0.282843, 0.141421], [0.342105, 0.289474, 0.141421, 0.282843], [0.394737, 0.289474, 0.1, 0.1], [0.394737, 0.289474, 0.282843, 0.141421], [0.394737, 0.289474, 0.141421, 0.282843], [0.447368, 0.289474, 0.1, 0.1], [0.447368, 0.289474, 0.282843, 0.141421], [0.447368, 0.289474, 0.141421, 0.282843], [0.5, 0.289474, 0.1, 0.1], [0.5, 0.289474, 0.282843, 0.141421], [0.5, 0.289474, 0.141421, 0.282843], [0.552632, 0.289474, 0.1, 0.1], [0.552632, 0.289474, 0.282843, 0.141421], [0.552632, 0.289474, 0.141421, 0.282843], [0.605263, 0.289474, 0.1, 0.1], [0.605263, 0.289474, 0.282843, 0.141421], [0.605263, 0.289474, 0.141421, 0.282843], [0.657895, 0.289474, 0.1, 0.1], [0.657895, 0.289474, 0.282843, 0.141421], [0.657895, 0.289474, 0.141421, 0.282843], [0.710526, 0.289474, 0.1, 0.1], [0.710526, 0.289474, 0.282843, 0.141421], [0.710526, 0.289474, 0.141421, 0.282843], [0.763158, 0.289474, 0.1, 0.1], [0.763158, 0.289474, 0.282843, 0.141421], [0.763158, 0.289474, 0.141421, 0.282843], [0.81579, 0.289474, 0.1, 0.1], [0.81579, 0.289474, 0.282843, 0.141421], [0.81579, 0.289474, 0.141421, 0.282843], [0.868421, 0.289474, 0.1, 0.1], [0.868421, 0.289474, 0.282843, 0.141421], [0.868421, 0.289474, 0.141421, 0.282843], [0.921053, 0.289474, 0.1, 0.1], [0.921053, 0.289474, 0.282843, 0.141421], [0.921053, 0.289474, 0.141421, 0.282843], [0.973684, 0.289474, 0.1, 0.1], [0.973684, 0.289474, 0.282843, 0.141421], [0.973684, 0.289474, 0.141421, 0.282843], [0.0263158, 0.342105, 0.1, 0.1], [0.0263158, 0.342105, 0.282843, 0.141421], [0.0263158, 0.342105, 0.141421, 0.282843], [0.0789474, 0.342105, 0.1, 0.1], [0.0789474, 0.342105, 0.282843, 0.141421], [0.0789474, 0.342105, 0.141421, 0.282843], [0.131579, 0.342105, 0.1, 0.1], [0.131579, 0.342105, 0.282843, 0.141421], [0.131579, 0.342105, 0.141421, 0.282843], [0.184211, 0.342105, 0.1, 0.1], [0.184211, 0.342105, 0.282843, 0.141421], [0.184211, 0.342105, 0.141421, 0.282843], [0.236842, 0.342105, 0.1, 0.1], [0.236842, 0.342105, 0.282843, 0.141421], [0.236842, 0.342105, 0.141421, 0.282843], [0.289474, 0.342105, 0.1, 0.1], [0.289474, 0.342105, 0.282843, 0.141421], [0.289474, 0.342105, 0.141421, 0.282843], [0.342105, 0.342105, 0.1, 0.1], [0.342105, 0.342105, 0.282843, 0.141421], [0.342105, 0.342105, 0.141421, 0.282843], [0.394737, 0.342105, 0.1, 0.1], [0.394737, 0.342105, 0.282843, 0.141421], [0.394737, 0.342105, 0.141421, 0.282843], [0.447368, 0.342105, 0.1, 0.1], [0.447368, 0.342105, 0.282843, 0.141421], [0.447368, 0.342105, 0.141421, 0.282843], [0.5, 0.342105, 0.1, 0.1], [0.5, 0.342105, 0.282843, 0.141421], [0.5, 0.342105, 0.141421, 0.282843], [0.552632, 0.342105, 0.1, 0.1], [0.552632, 0.342105, 0.282843, 0.141421], [0.552632, 0.342105, 0.141421, 0.282843], [0.605263, 0.342105, 0.1, 0.1], [0.605263, 0.342105, 0.282843, 0.141421], [0.605263, 0.342105, 0.141421, 0.282843], [0.657895, 0.342105, 0.1, 0.1], [0.657895, 0.342105, 0.282843, 0.141421], [0.657895, 0.342105, 0.141421, 0.282843], [0.710526, 0.342105, 0.1, 0.1], [0.710526, 0.342105, 0.282843, 0.141421], [0.710526, 0.342105, 0.141421, 0.282843], [0.763158, 0.342105, 0.1, 0.1], [0.763158, 0.342105, 0.282843, 0.141421], [0.763158, 0.342105, 0.141421, 0.282843], [0.81579, 0.342105, 0.1, 0.1], [0.81579, 0.342105, 0.282843, 0.141421], [0.81579, 0.342105, 0.141421, 0.282843], [0.868421, 0.342105, 0.1, 0.1], [0.868421, 0.342105, 0.282843, 0.141421], [0.868421, 0.342105, 0.141421, 0.282843], [0.921053, 0.342105, 0.1, 0.1], [0.921053, 0.342105, 0.282843, 0.141421], [0.921053, 0.342105, 0.141421, 0.282843], [0.973684, 0.342105, 0.1, 0.1], [0.973684, 0.342105, 0.282843, 0.141421], [0.973684, 0.342105, 0.141421, 0.282843], [0.0263158, 0.394737, 0.1, 0.1], [0.0263158, 0.394737, 0.282843, 0.141421], [0.0263158, 0.394737, 0.141421, 0.282843], [0.0789474, 0.394737, 0.1, 0.1], [0.0789474, 0.394737, 0.282843, 0.141421], [0.0789474, 0.394737, 0.141421, 0.282843], [0.131579, 0.394737, 0.1, 0.1], [0.131579, 0.394737, 0.282843, 0.141421], [0.131579, 0.394737, 0.141421, 0.282843], [0.184211, 0.394737, 0.1, 0.1], [0.184211, 0.394737, 0.282843, 0.141421], [0.184211, 0.394737, 0.141421, 0.282843], [0.236842, 0.394737, 0.1, 0.1], [0.236842, 0.394737, 0.282843, 0.141421], [0.236842, 0.394737, 0.141421, 0.282843], [0.289474, 0.394737, 0.1, 0.1], [0.289474, 0.394737, 0.282843, 0.141421], [0.289474, 0.394737, 0.141421, 0.282843], [0.342105, 0.394737, 0.1, 0.1], [0.342105, 0.394737, 0.282843, 0.141421], [0.342105, 0.394737, 0.141421, 0.282843], [0.394737, 0.394737, 0.1, 0.1], [0.394737, 0.394737, 0.282843, 0.141421], [0.394737, 0.394737, 0.141421, 0.282843], [0.447368, 0.394737, 0.1, 0.1], [0.447368, 0.394737, 0.282843, 0.141421], [0.447368, 0.394737, 0.141421, 0.282843], [0.5, 0.394737, 0.1, 0.1], [0.5, 0.394737, 0.282843, 0.141421], [0.5, 0.394737, 0.141421, 0.282843], [0.552632, 0.394737, 0.1, 0.1], [0.552632, 0.394737, 0.282843, 0.141421], [0.552632, 0.394737, 0.141421, 0.282843], [0.605263, 0.394737, 0.1, 0.1], [0.605263, 0.394737, 0.282843, 0.141421], [0.605263, 0.394737, 0.141421, 0.282843], [0.657895, 0.394737, 0.1, 0.1], [0.657895, 0.394737, 0.282843, 0.141421], [0.657895, 0.394737, 0.141421, 0.282843], [0.710526, 0.394737, 0.1, 0.1], [0.710526, 0.394737, 0.282843, 0.141421], [0.710526, 0.394737, 0.141421, 0.282843], [0.763158, 0.394737, 0.1, 0.1], [0.763158, 0.394737, 0.282843, 0.141421], [0.763158, 0.394737, 0.141421, 0.282843], [0.81579, 0.394737, 0.1, 0.1], [0.81579, 0.394737, 0.282843, 0.141421], [0.81579, 0.394737, 0.141421, 0.282843], [0.868421, 0.394737, 0.1, 0.1], [0.868421, 0.394737, 0.282843, 0.141421], [0.868421, 0.394737, 0.141421, 0.282843], [0.921053, 0.394737, 0.1, 0.1], [0.921053, 0.394737, 0.282843, 0.141421], [0.921053, 0.394737, 0.141421, 0.282843], [0.973684, 0.394737, 0.1, 0.1], [0.973684, 0.394737, 0.282843, 0.141421], [0.973684, 0.394737, 0.141421, 0.282843], [0.0263158, 0.447368, 0.1, 0.1], [0.0263158, 0.447368, 0.282843, 0.141421], [0.0263158, 0.447368, 0.141421, 0.282843], [0.0789474, 0.447368, 0.1, 0.1], [0.0789474, 0.447368, 0.282843, 0.141421], [0.0789474, 0.447368, 0.141421, 0.282843], [0.131579, 0.447368, 0.1, 0.1], [0.131579, 0.447368, 0.282843, 0.141421], [0.131579, 0.447368, 0.141421, 0.282843], [0.184211, 0.447368, 0.1, 0.1], [0.184211, 0.447368, 0.282843, 0.141421], [0.184211, 0.447368, 0.141421, 0.282843], [0.236842, 0.447368, 0.1, 0.1], [0.236842, 0.447368, 0.282843, 0.141421], [0.236842, 0.447368, 0.141421, 0.282843], [0.289474, 0.447368, 0.1, 0.1], [0.289474, 0.447368, 0.282843, 0.141421], [0.289474, 0.447368, 0.141421, 0.282843], [0.342105, 0.447368, 0.1, 0.1], [0.342105, 0.447368, 0.282843, 0.141421], [0.342105, 0.447368, 0.141421, 0.282843], [0.394737, 0.447368, 0.1, 0.1], [0.394737, 0.447368, 0.282843, 0.141421], [0.394737, 0.447368, 0.141421, 0.282843], [0.447368, 0.447368, 0.1, 0.1], [0.447368, 0.447368, 0.282843, 0.141421], [0.447368, 0.447368, 0.141421, 0.282843], [0.5, 0.447368, 0.1, 0.1], [0.5, 0.447368, 0.282843, 0.141421], [0.5, 0.447368, 0.141421, 0.282843], [0.552632, 0.447368, 0.1, 0.1], [0.552632, 0.447368, 0.282843, 0.141421], [0.552632, 0.447368, 0.141421, 0.282843], [0.605263, 0.447368, 0.1, 0.1], [0.605263, 0.447368, 0.282843, 0.141421], [0.605263, 0.447368, 0.141421, 0.282843], [0.657895, 0.447368, 0.1, 0.1], [0.657895, 0.447368, 0.282843, 0.141421], [0.657895, 0.447368, 0.141421, 0.282843], [0.710526, 0.447368, 0.1, 0.1], [0.710526, 0.447368, 0.282843, 0.141421], [0.710526, 0.447368, 0.141421, 0.282843], [0.763158, 0.447368, 0.1, 0.1], [0.763158, 0.447368, 0.282843, 0.141421], [0.763158, 0.447368, 0.141421, 0.282843], [0.81579, 0.447368, 0.1, 0.1], [0.81579, 0.447368, 0.282843, 0.141421], [0.81579, 0.447368, 0.141421, 0.282843], [0.868421, 0.447368, 0.1, 0.1], [0.868421, 0.447368, 0.282843, 0.141421], [0.868421, 0.447368, 0.141421, 0.282843], [0.921053, 0.447368, 0.1, 0.1], [0.921053, 0.447368, 0.282843, 0.141421], [0.921053, 0.447368, 0.141421, 0.282843], [0.973684, 0.447368, 0.1, 0.1], [0.973684, 0.447368, 0.282843, 0.141421], [0.973684, 0.447368, 0.141421, 0.282843], [0.0263158, 0.5, 0.1, 0.1], [0.0263158, 0.5, 0.282843, 0.141421], [0.0263158, 0.5, 0.141421, 0.282843], [0.0789474, 0.5, 0.1, 0.1], [0.0789474, 0.5, 0.282843, 0.141421], [0.0789474, 0.5, 0.141421, 0.282843], [0.131579, 0.5, 0.1, 0.1], [0.131579, 0.5, 0.282843, 0.141421], [0.131579, 0.5, 0.141421, 0.282843], [0.184211, 0.5, 0.1, 0.1], [0.184211, 0.5, 0.282843, 0.141421], [0.184211, 0.5, 0.141421, 0.282843], [0.236842, 0.5, 0.1, 0.1], [0.236842, 0.5, 0.282843, 0.141421], [0.236842, 0.5, 0.141421, 0.282843], [0.289474, 0.5, 0.1, 0.1], [0.289474, 0.5, 0.282843, 0.141421], [0.289474, 0.5, 0.141421, 0.282843], [0.342105, 0.5, 0.1, 0.1], [0.342105, 0.5, 0.282843, 0.141421], [0.342105, 0.5, 0.141421, 0.282843], [0.394737, 0.5, 0.1, 0.1], [0.394737, 0.5, 0.282843, 0.141421], [0.394737, 0.5, 0.141421, 0.282843], [0.447368, 0.5, 0.1, 0.1], [0.447368, 0.5, 0.282843, 0.141421], [0.447368, 0.5, 0.141421, 0.282843], [0.5, 0.5, 0.1, 0.1], [0.5, 0.5, 0.282843, 0.141421], [0.5, 0.5, 0.141421, 0.282843], [0.552632, 0.5, 0.1, 0.1], [0.552632, 0.5, 0.282843, 0.141421], [0.552632, 0.5, 0.141421, 0.282843], [0.605263, 0.5, 0.1, 0.1], [0.605263, 0.5, 0.282843, 0.141421], [0.605263, 0.5, 0.141421, 0.282843], [0.657895, 0.5, 0.1, 0.1], [0.657895, 0.5, 0.282843, 0.141421], [0.657895, 0.5, 0.141421, 0.282843], [0.710526, 0.5, 0.1, 0.1], [0.710526, 0.5, 0.282843, 0.141421], [0.710526, 0.5, 0.141421, 0.282843], [0.763158, 0.5, 0.1, 0.1], [0.763158, 0.5, 0.282843, 0.141421], [0.763158, 0.5, 0.141421, 0.282843], [0.81579, 0.5, 0.1, 0.1], [0.81579, 0.5, 0.282843, 0.141421], [0.81579, 0.5, 0.141421, 0.282843], [0.868421, 0.5, 0.1, 0.1], [0.868421, 0.5, 0.282843, 0.141421], [0.868421, 0.5, 0.141421, 0.282843], [0.921053, 0.5, 0.1, 0.1], [0.921053, 0.5, 0.282843, 0.141421], [0.921053, 0.5, 0.141421, 0.282843], [0.973684, 0.5, 0.1, 0.1], [0.973684, 0.5, 0.282843, 0.141421], [0.973684, 0.5, 0.141421, 0.282843], [0.0263158, 0.552632, 0.1, 0.1], [0.0263158, 0.552632, 0.282843, 0.141421], [0.0263158, 0.552632, 0.141421, 0.282843], [0.0789474, 0.552632, 0.1, 0.1], [0.0789474, 0.552632, 0.282843, 0.141421], [0.0789474, 0.552632, 0.141421, 0.282843], [0.131579, 0.552632, 0.1, 0.1], [0.131579, 0.552632, 0.282843, 0.141421], [0.131579, 0.552632, 0.141421, 0.282843], [0.184211, 0.552632, 0.1, 0.1], [0.184211, 0.552632, 0.282843, 0.141421], [0.184211, 0.552632, 0.141421, 0.282843], [0.236842, 0.552632, 0.1, 0.1], [0.236842, 0.552632, 0.282843, 0.141421], [0.236842, 0.552632, 0.141421, 0.282843], [0.289474, 0.552632, 0.1, 0.1], [0.289474, 0.552632, 0.282843, 0.141421], [0.289474, 0.552632, 0.141421, 0.282843], [0.342105, 0.552632, 0.1, 0.1], [0.342105, 0.552632, 0.282843, 0.141421], [0.342105, 0.552632, 0.141421, 0.282843], [0.394737, 0.552632, 0.1, 0.1], [0.394737, 0.552632, 0.282843, 0.141421], [0.394737, 0.552632, 0.141421, 0.282843], [0.447368, 0.552632, 0.1, 0.1], [0.447368, 0.552632, 0.282843, 0.141421], [0.447368, 0.552632, 0.141421, 0.282843], [0.5, 0.552632, 0.1, 0.1], [0.5, 0.552632, 0.282843, 0.141421], [0.5, 0.552632, 0.141421, 0.282843], [0.552632, 0.552632, 0.1, 0.1], [0.552632, 0.552632, 0.282843, 0.141421], [0.552632, 0.552632, 0.141421, 0.282843], [0.605263, 0.552632, 0.1, 0.1], [0.605263, 0.552632, 0.282843, 0.141421], [0.605263, 0.552632, 0.141421, 0.282843], [0.657895, 0.552632, 0.1, 0.1], [0.657895, 0.552632, 0.282843, 0.141421], [0.657895, 0.552632, 0.141421, 0.282843], [0.710526, 0.552632, 0.1, 0.1], [0.710526, 0.552632, 0.282843, 0.141421], [0.710526, 0.552632, 0.141421, 0.282843], [0.763158, 0.552632, 0.1, 0.1], [0.763158, 0.552632, 0.282843, 0.141421], [0.763158, 0.552632, 0.141421, 0.282843], [0.81579, 0.552632, 0.1, 0.1], [0.81579, 0.552632, 0.282843, 0.141421], [0.81579, 0.552632, 0.141421, 0.282843], [0.868421, 0.552632, 0.1, 0.1], [0.868421, 0.552632, 0.282843, 0.141421], [0.868421, 0.552632, 0.141421, 0.282843], [0.921053, 0.552632, 0.1, 0.1], [0.921053, 0.552632, 0.282843, 0.141421], [0.921053, 0.552632, 0.141421, 0.282843], [0.973684, 0.552632, 0.1, 0.1], [0.973684, 0.552632, 0.282843, 0.141421], [0.973684, 0.552632, 0.141421, 0.282843], [0.0263158, 0.605263, 0.1, 0.1], [0.0263158, 0.605263, 0.282843, 0.141421], [0.0263158, 0.605263, 0.141421, 0.282843], [0.0789474, 0.605263, 0.1, 0.1], [0.0789474, 0.605263, 0.282843, 0.141421], [0.0789474, 0.605263, 0.141421, 0.282843], [0.131579, 0.605263, 0.1, 0.1], [0.131579, 0.605263, 0.282843, 0.141421], [0.131579, 0.605263, 0.141421, 0.282843], [0.184211, 0.605263, 0.1, 0.1], [0.184211, 0.605263, 0.282843, 0.141421], [0.184211, 0.605263, 0.141421, 0.282843], [0.236842, 0.605263, 0.1, 0.1], [0.236842, 0.605263, 0.282843, 0.141421], [0.236842, 0.605263, 0.141421, 0.282843], [0.289474, 0.605263, 0.1, 0.1], [0.289474, 0.605263, 0.282843, 0.141421], [0.289474, 0.605263, 0.141421, 0.282843], [0.342105, 0.605263, 0.1, 0.1], [0.342105, 0.605263, 0.282843, 0.141421], [0.342105, 0.605263, 0.141421, 0.282843], [0.394737, 0.605263, 0.1, 0.1], [0.394737, 0.605263, 0.282843, 0.141421], [0.394737, 0.605263, 0.141421, 0.282843], [0.447368, 0.605263, 0.1, 0.1], [0.447368, 0.605263, 0.282843, 0.141421], [0.447368, 0.605263, 0.141421, 0.282843], [0.5, 0.605263, 0.1, 0.1], [0.5, 0.605263, 0.282843, 0.141421], [0.5, 0.605263, 0.141421, 0.282843], [0.552632, 0.605263, 0.1, 0.1], [0.552632, 0.605263, 0.282843, 0.141421], [0.552632, 0.605263, 0.141421, 0.282843], [0.605263, 0.605263, 0.1, 0.1], [0.605263, 0.605263, 0.282843, 0.141421], [0.605263, 0.605263, 0.141421, 0.282843], [0.657895, 0.605263, 0.1, 0.1], [0.657895, 0.605263, 0.282843, 0.141421], [0.657895, 0.605263, 0.141421, 0.282843], [0.710526, 0.605263, 0.1, 0.1], [0.710526, 0.605263, 0.282843, 0.141421], [0.710526, 0.605263, 0.141421, 0.282843], [0.763158, 0.605263, 0.1, 0.1], [0.763158, 0.605263, 0.282843, 0.141421], [0.763158, 0.605263, 0.141421, 0.282843], [0.81579, 0.605263, 0.1, 0.1], [0.81579, 0.605263, 0.282843, 0.141421], [0.81579, 0.605263, 0.141421, 0.282843], [0.868421, 0.605263, 0.1, 0.1], [0.868421, 0.605263, 0.282843, 0.141421], [0.868421, 0.605263, 0.141421, 0.282843], [0.921053, 0.605263, 0.1, 0.1], [0.921053, 0.605263, 0.282843, 0.141421], [0.921053, 0.605263, 0.141421, 0.282843], [0.973684, 0.605263, 0.1, 0.1], [0.973684, 0.605263, 0.282843, 0.141421], [0.973684, 0.605263, 0.141421, 0.282843], [0.0263158, 0.657895, 0.1, 0.1], [0.0263158, 0.657895, 0.282843, 0.141421], [0.0263158, 0.657895, 0.141421, 0.282843], [0.0789474, 0.657895, 0.1, 0.1], [0.0789474, 0.657895, 0.282843, 0.141421], [0.0789474, 0.657895, 0.141421, 0.282843], [0.131579, 0.657895, 0.1, 0.1], [0.131579, 0.657895, 0.282843, 0.141421], [0.131579, 0.657895, 0.141421, 0.282843], [0.184211, 0.657895, 0.1, 0.1], [0.184211, 0.657895, 0.282843, 0.141421], [0.184211, 0.657895, 0.141421, 0.282843], [0.236842, 0.657895, 0.1, 0.1], [0.236842, 0.657895, 0.282843, 0.141421], [0.236842, 0.657895, 0.141421, 0.282843], [0.289474, 0.657895, 0.1, 0.1], [0.289474, 0.657895, 0.282843, 0.141421], [0.289474, 0.657895, 0.141421, 0.282843], [0.342105, 0.657895, 0.1, 0.1], [0.342105, 0.657895, 0.282843, 0.141421], [0.342105, 0.657895, 0.141421, 0.282843], [0.394737, 0.657895, 0.1, 0.1], [0.394737, 0.657895, 0.282843, 0.141421], [0.394737, 0.657895, 0.141421, 0.282843], [0.447368, 0.657895, 0.1, 0.1], [0.447368, 0.657895, 0.282843, 0.141421], [0.447368, 0.657895, 0.141421, 0.282843], [0.5, 0.657895, 0.1, 0.1], [0.5, 0.657895, 0.282843, 0.141421], [0.5, 0.657895, 0.141421, 0.282843], [0.552632, 0.657895, 0.1, 0.1], [0.552632, 0.657895, 0.282843, 0.141421], [0.552632, 0.657895, 0.141421, 0.282843], [0.605263, 0.657895, 0.1, 0.1], [0.605263, 0.657895, 0.282843, 0.141421], [0.605263, 0.657895, 0.141421, 0.282843], [0.657895, 0.657895, 0.1, 0.1], [0.657895, 0.657895, 0.282843, 0.141421], [0.657895, 0.657895, 0.141421, 0.282843], [0.710526, 0.657895, 0.1, 0.1], [0.710526, 0.657895, 0.282843, 0.141421], [0.710526, 0.657895, 0.141421, 0.282843], [0.763158, 0.657895, 0.1, 0.1], [0.763158, 0.657895, 0.282843, 0.141421], [0.763158, 0.657895, 0.141421, 0.282843], [0.81579, 0.657895, 0.1, 0.1], [0.81579, 0.657895, 0.282843, 0.141421], [0.81579, 0.657895, 0.141421, 0.282843], [0.868421, 0.657895, 0.1, 0.1], [0.868421, 0.657895, 0.282843, 0.141421], [0.868421, 0.657895, 0.141421, 0.282843], [0.921053, 0.657895, 0.1, 0.1], [0.921053, 0.657895, 0.282843, 0.141421], [0.921053, 0.657895, 0.141421, 0.282843], [0.973684, 0.657895, 0.1, 0.1], [0.973684, 0.657895, 0.282843, 0.141421], [0.973684, 0.657895, 0.141421, 0.282843], [0.0263158, 0.710526, 0.1, 0.1], [0.0263158, 0.710526, 0.282843, 0.141421], [0.0263158, 0.710526, 0.141421, 0.282843], [0.0789474, 0.710526, 0.1, 0.1], [0.0789474, 0.710526, 0.282843, 0.141421], [0.0789474, 0.710526, 0.141421, 0.282843], [0.131579, 0.710526, 0.1, 0.1], [0.131579, 0.710526, 0.282843, 0.141421], [0.131579, 0.710526, 0.141421, 0.282843], [0.184211, 0.710526, 0.1, 0.1], [0.184211, 0.710526, 0.282843, 0.141421], [0.184211, 0.710526, 0.141421, 0.282843], [0.236842, 0.710526, 0.1, 0.1], [0.236842, 0.710526, 0.282843, 0.141421], [0.236842, 0.710526, 0.141421, 0.282843], [0.289474, 0.710526, 0.1, 0.1], [0.289474, 0.710526, 0.282843, 0.141421], [0.289474, 0.710526, 0.141421, 0.282843], [0.342105, 0.710526, 0.1, 0.1], [0.342105, 0.710526, 0.282843, 0.141421], [0.342105, 0.710526, 0.141421, 0.282843], [0.394737, 0.710526, 0.1, 0.1], [0.394737, 0.710526, 0.282843, 0.141421], [0.394737, 0.710526, 0.141421, 0.282843], [0.447368, 0.710526, 0.1, 0.1], [0.447368, 0.710526, 0.282843, 0.141421], [0.447368, 0.710526, 0.141421, 0.282843], [0.5, 0.710526, 0.1, 0.1], [0.5, 0.710526, 0.282843, 0.141421], [0.5, 0.710526, 0.141421, 0.282843], [0.552632, 0.710526, 0.1, 0.1], [0.552632, 0.710526, 0.282843, 0.141421], [0.552632, 0.710526, 0.141421, 0.282843], [0.605263, 0.710526, 0.1, 0.1], [0.605263, 0.710526, 0.282843, 0.141421], [0.605263, 0.710526, 0.141421, 0.282843], [0.657895, 0.710526, 0.1, 0.1], [0.657895, 0.710526, 0.282843, 0.141421], [0.657895, 0.710526, 0.141421, 0.282843], [0.710526, 0.710526, 0.1, 0.1], [0.710526, 0.710526, 0.282843, 0.141421], [0.710526, 0.710526, 0.141421, 0.282843], [0.763158, 0.710526, 0.1, 0.1], [0.763158, 0.710526, 0.282843, 0.141421], [0.763158, 0.710526, 0.141421, 0.282843], [0.81579, 0.710526, 0.1, 0.1], [0.81579, 0.710526, 0.282843, 0.141421], [0.81579, 0.710526, 0.141421, 0.282843], [0.868421, 0.710526, 0.1, 0.1], [0.868421, 0.710526, 0.282843, 0.141421], [0.868421, 0.710526, 0.141421, 0.282843], [0.921053, 0.710526, 0.1, 0.1], [0.921053, 0.710526, 0.282843, 0.141421], [0.921053, 0.710526, 0.141421, 0.282843], [0.973684, 0.710526, 0.1, 0.1], [0.973684, 0.710526, 0.282843, 0.141421], [0.973684, 0.710526, 0.141421, 0.282843], [0.0263158, 0.763158, 0.1, 0.1], [0.0263158, 0.763158, 0.282843, 0.141421], [0.0263158, 0.763158, 0.141421, 0.282843], [0.0789474, 0.763158, 0.1, 0.1], [0.0789474, 0.763158, 0.282843, 0.141421], [0.0789474, 0.763158, 0.141421, 0.282843], [0.131579, 0.763158, 0.1, 0.1], [0.131579, 0.763158, 0.282843, 0.141421], [0.131579, 0.763158, 0.141421, 0.282843], [0.184211, 0.763158, 0.1, 0.1], [0.184211, 0.763158, 0.282843, 0.141421], [0.184211, 0.763158, 0.141421, 0.282843], [0.236842, 0.763158, 0.1, 0.1], [0.236842, 0.763158, 0.282843, 0.141421], [0.236842, 0.763158, 0.141421, 0.282843], [0.289474, 0.763158, 0.1, 0.1], [0.289474, 0.763158, 0.282843, 0.141421], [0.289474, 0.763158, 0.141421, 0.282843], [0.342105, 0.763158, 0.1, 0.1], [0.342105, 0.763158, 0.282843, 0.141421], [0.342105, 0.763158, 0.141421, 0.282843], [0.394737, 0.763158, 0.1, 0.1], [0.394737, 0.763158, 0.282843, 0.141421], [0.394737, 0.763158, 0.141421, 0.282843], [0.447368, 0.763158, 0.1, 0.1], [0.447368, 0.763158, 0.282843, 0.141421], [0.447368, 0.763158, 0.141421, 0.282843], [0.5, 0.763158, 0.1, 0.1], [0.5, 0.763158, 0.282843, 0.141421], [0.5, 0.763158, 0.141421, 0.282843], [0.552632, 0.763158, 0.1, 0.1], [0.552632, 0.763158, 0.282843, 0.141421], [0.552632, 0.763158, 0.141421, 0.282843], [0.605263, 0.763158, 0.1, 0.1], [0.605263, 0.763158, 0.282843, 0.141421], [0.605263, 0.763158, 0.141421, 0.282843], [0.657895, 0.763158, 0.1, 0.1], [0.657895, 0.763158, 0.282843, 0.141421], [0.657895, 0.763158, 0.141421, 0.282843], [0.710526, 0.763158, 0.1, 0.1], [0.710526, 0.763158, 0.282843, 0.141421], [0.710526, 0.763158, 0.141421, 0.282843], [0.763158, 0.763158, 0.1, 0.1], [0.763158, 0.763158, 0.282843, 0.141421], [0.763158, 0.763158, 0.141421, 0.282843], [0.81579, 0.763158, 0.1, 0.1], [0.81579, 0.763158, 0.282843, 0.141421], [0.81579, 0.763158, 0.141421, 0.282843], [0.868421, 0.763158, 0.1, 0.1], [0.868421, 0.763158, 0.282843, 0.141421], [0.868421, 0.763158, 0.141421, 0.282843], [0.921053, 0.763158, 0.1, 0.1], [0.921053, 0.763158, 0.282843, 0.141421], [0.921053, 0.763158, 0.141421, 0.282843], [0.973684, 0.763158, 0.1, 0.1], [0.973684, 0.763158, 0.282843, 0.141421], [0.973684, 0.763158, 0.141421, 0.282843], [0.0263158, 0.81579, 0.1, 0.1], [0.0263158, 0.81579, 0.282843, 0.141421], [0.0263158, 0.81579, 0.141421, 0.282843], [0.0789474, 0.81579, 0.1, 0.1], [0.0789474, 0.81579, 0.282843, 0.141421], [0.0789474, 0.81579, 0.141421, 0.282843], [0.131579, 0.81579, 0.1, 0.1], [0.131579, 0.81579, 0.282843, 0.141421], [0.131579, 0.81579, 0.141421, 0.282843], [0.184211, 0.81579, 0.1, 0.1], [0.184211, 0.81579, 0.282843, 0.141421], [0.184211, 0.81579, 0.141421, 0.282843], [0.236842, 0.81579, 0.1, 0.1], [0.236842, 0.81579, 0.282843, 0.141421], [0.236842, 0.81579, 0.141421, 0.282843], [0.289474, 0.81579, 0.1, 0.1], [0.289474, 0.81579, 0.282843, 0.141421], [0.289474, 0.81579, 0.141421, 0.282843], [0.342105, 0.81579, 0.1, 0.1], [0.342105, 0.81579, 0.282843, 0.141421], [0.342105, 0.81579, 0.141421, 0.282843], [0.394737, 0.81579, 0.1, 0.1], [0.394737, 0.81579, 0.282843, 0.141421], [0.394737, 0.81579, 0.141421, 0.282843], [0.447368, 0.81579, 0.1, 0.1], [0.447368, 0.81579, 0.282843, 0.141421], [0.447368, 0.81579, 0.141421, 0.282843], [0.5, 0.81579, 0.1, 0.1], [0.5, 0.81579, 0.282843, 0.141421], [0.5, 0.81579, 0.141421, 0.282843], [0.552632, 0.81579, 0.1, 0.1], [0.552632, 0.81579, 0.282843, 0.141421], [0.552632, 0.81579, 0.141421, 0.282843], [0.605263, 0.81579, 0.1, 0.1], [0.605263, 0.81579, 0.282843, 0.141421], [0.605263, 0.81579, 0.141421, 0.282843], [0.657895, 0.81579, 0.1, 0.1], [0.657895, 0.81579, 0.282843, 0.141421], [0.657895, 0.81579, 0.141421, 0.282843], [0.710526, 0.81579, 0.1, 0.1], [0.710526, 0.81579, 0.282843, 0.141421], [0.710526, 0.81579, 0.141421, 0.282843], [0.763158, 0.81579, 0.1, 0.1], [0.763158, 0.81579, 0.282843, 0.141421], [0.763158, 0.81579, 0.141421, 0.282843], [0.81579, 0.81579, 0.1, 0.1], [0.81579, 0.81579, 0.282843, 0.141421], [0.81579, 0.81579, 0.141421, 0.282843], [0.868421, 0.81579, 0.1, 0.1], [0.868421, 0.81579, 0.282843, 0.141421], [0.868421, 0.81579, 0.141421, 0.282843], [0.921053, 0.81579, 0.1, 0.1], [0.921053, 0.81579, 0.282843, 0.141421], [0.921053, 0.81579, 0.141421, 0.282843], [0.973684, 0.81579, 0.1, 0.1], [0.973684, 0.81579, 0.282843, 0.141421], [0.973684, 0.81579, 0.141421, 0.282843], [0.0263158, 0.868421, 0.1, 0.1], [0.0263158, 0.868421, 0.282843, 0.141421], [0.0263158, 0.868421, 0.141421, 0.282843], [0.0789474, 0.868421, 0.1, 0.1], [0.0789474, 0.868421, 0.282843, 0.141421], [0.0789474, 0.868421, 0.141421, 0.282843], [0.131579, 0.868421, 0.1, 0.1], [0.131579, 0.868421, 0.282843, 0.141421], [0.131579, 0.868421, 0.141421, 0.282843], [0.184211, 0.868421, 0.1, 0.1], [0.184211, 0.868421, 0.282843, 0.141421], [0.184211, 0.868421, 0.141421, 0.282843], [0.236842, 0.868421, 0.1, 0.1], [0.236842, 0.868421, 0.282843, 0.141421], [0.236842, 0.868421, 0.141421, 0.282843], [0.289474, 0.868421, 0.1, 0.1], [0.289474, 0.868421, 0.282843, 0.141421], [0.289474, 0.868421, 0.141421, 0.282843], [0.342105, 0.868421, 0.1, 0.1], [0.342105, 0.868421, 0.282843, 0.141421], [0.342105, 0.868421, 0.141421, 0.282843], [0.394737, 0.868421, 0.1, 0.1], [0.394737, 0.868421, 0.282843, 0.141421], [0.394737, 0.868421, 0.141421, 0.282843], [0.447368, 0.868421, 0.1, 0.1], [0.447368, 0.868421, 0.282843, 0.141421], [0.447368, 0.868421, 0.141421, 0.282843], [0.5, 0.868421, 0.1, 0.1], [0.5, 0.868421, 0.282843, 0.141421], [0.5, 0.868421, 0.141421, 0.282843], [0.552632, 0.868421, 0.1, 0.1], [0.552632, 0.868421, 0.282843, 0.141421], [0.552632, 0.868421, 0.141421, 0.282843], [0.605263, 0.868421, 0.1, 0.1], [0.605263, 0.868421, 0.282843, 0.141421], [0.605263, 0.868421, 0.141421, 0.282843], [0.657895, 0.868421, 0.1, 0.1], [0.657895, 0.868421, 0.282843, 0.141421], [0.657895, 0.868421, 0.141421, 0.282843], [0.710526, 0.868421, 0.1, 0.1], [0.710526, 0.868421, 0.282843, 0.141421], [0.710526, 0.868421, 0.141421, 0.282843], [0.763158, 0.868421, 0.1, 0.1], [0.763158, 0.868421, 0.282843, 0.141421], [0.763158, 0.868421, 0.141421, 0.282843], [0.81579, 0.868421, 0.1, 0.1], [0.81579, 0.868421, 0.282843, 0.141421], [0.81579, 0.868421, 0.141421, 0.282843], [0.868421, 0.868421, 0.1, 0.1], [0.868421, 0.868421, 0.282843, 0.141421], [0.868421, 0.868421, 0.141421, 0.282843], [0.921053, 0.868421, 0.1, 0.1], [0.921053, 0.868421, 0.282843, 0.141421], [0.921053, 0.868421, 0.141421, 0.282843], [0.973684, 0.868421, 0.1, 0.1], [0.973684, 0.868421, 0.282843, 0.141421], [0.973684, 0.868421, 0.141421, 0.282843], [0.0263158, 0.921053, 0.1, 0.1], [0.0263158, 0.921053, 0.282843, 0.141421], [0.0263158, 0.921053, 0.141421, 0.282843], [0.0789474, 0.921053, 0.1, 0.1], [0.0789474, 0.921053, 0.282843, 0.141421], [0.0789474, 0.921053, 0.141421, 0.282843], [0.131579, 0.921053, 0.1, 0.1], [0.131579, 0.921053, 0.282843, 0.141421], [0.131579, 0.921053, 0.141421, 0.282843], [0.184211, 0.921053, 0.1, 0.1], [0.184211, 0.921053, 0.282843, 0.141421], [0.184211, 0.921053, 0.141421, 0.282843], [0.236842, 0.921053, 0.1, 0.1], [0.236842, 0.921053, 0.282843, 0.141421], [0.236842, 0.921053, 0.141421, 0.282843], [0.289474, 0.921053, 0.1, 0.1], [0.289474, 0.921053, 0.282843, 0.141421], [0.289474, 0.921053, 0.141421, 0.282843], [0.342105, 0.921053, 0.1, 0.1], [0.342105, 0.921053, 0.282843, 0.141421], [0.342105, 0.921053, 0.141421, 0.282843], [0.394737, 0.921053, 0.1, 0.1], [0.394737, 0.921053, 0.282843, 0.141421], [0.394737, 0.921053, 0.141421, 0.282843], [0.447368, 0.921053, 0.1, 0.1], [0.447368, 0.921053, 0.282843, 0.141421], [0.447368, 0.921053, 0.141421, 0.282843], [0.5, 0.921053, 0.1, 0.1], [0.5, 0.921053, 0.282843, 0.141421], [0.5, 0.921053, 0.141421, 0.282843], [0.552632, 0.921053, 0.1, 0.1], [0.552632, 0.921053, 0.282843, 0.141421], [0.552632, 0.921053, 0.141421, 0.282843], [0.605263, 0.921053, 0.1, 0.1], [0.605263, 0.921053, 0.282843, 0.141421], [0.605263, 0.921053, 0.141421, 0.282843], [0.657895, 0.921053, 0.1, 0.1], [0.657895, 0.921053, 0.282843, 0.141421], [0.657895, 0.921053, 0.141421, 0.282843], [0.710526, 0.921053, 0.1, 0.1], [0.710526, 0.921053, 0.282843, 0.141421], [0.710526, 0.921053, 0.141421, 0.282843], [0.763158, 0.921053, 0.1, 0.1], [0.763158, 0.921053, 0.282843, 0.141421], [0.763158, 0.921053, 0.141421, 0.282843], [0.81579, 0.921053, 0.1, 0.1], [0.81579, 0.921053, 0.282843, 0.141421], [0.81579, 0.921053, 0.141421, 0.282843], [0.868421, 0.921053, 0.1, 0.1], [0.868421, 0.921053, 0.282843, 0.141421], [0.868421, 0.921053, 0.141421, 0.282843], [0.921053, 0.921053, 0.1, 0.1], [0.921053, 0.921053, 0.282843, 0.141421], [0.921053, 0.921053, 0.141421, 0.282843], [0.973684, 0.921053, 0.1, 0.1], [0.973684, 0.921053, 0.282843, 0.141421], [0.973684, 0.921053, 0.141421, 0.282843], [0.0263158, 0.973684, 0.1, 0.1], [0.0263158, 0.973684, 0.282843, 0.141421], [0.0263158, 0.973684, 0.141421, 0.282843], [0.0789474, 0.973684, 0.1, 0.1], [0.0789474, 0.973684, 0.282843, 0.141421], [0.0789474, 0.973684, 0.141421, 0.282843], [0.131579, 0.973684, 0.1, 0.1], [0.131579, 0.973684, 0.282843, 0.141421], [0.131579, 0.973684, 0.141421, 0.282843], [0.184211, 0.973684, 0.1, 0.1], [0.184211, 0.973684, 0.282843, 0.141421], [0.184211, 0.973684, 0.141421, 0.282843], [0.236842, 0.973684, 0.1, 0.1], [0.236842, 0.973684, 0.282843, 0.141421], [0.236842, 0.973684, 0.141421, 0.282843], [0.289474, 0.973684, 0.1, 0.1], [0.289474, 0.973684, 0.282843, 0.141421], [0.289474, 0.973684, 0.141421, 0.282843], [0.342105, 0.973684, 0.1, 0.1], [0.342105, 0.973684, 0.282843, 0.141421], [0.342105, 0.973684, 0.141421, 0.282843], [0.394737, 0.973684, 0.1, 0.1], [0.394737, 0.973684, 0.282843, 0.141421], [0.394737, 0.973684, 0.141421, 0.282843], [0.447368, 0.973684, 0.1, 0.1], [0.447368, 0.973684, 0.282843, 0.141421], [0.447368, 0.973684, 0.141421, 0.282843], [0.5, 0.973684, 0.1, 0.1], [0.5, 0.973684, 0.282843, 0.141421], [0.5, 0.973684, 0.141421, 0.282843], [0.552632, 0.973684, 0.1, 0.1], [0.552632, 0.973684, 0.282843, 0.141421], [0.552632, 0.973684, 0.141421, 0.282843], [0.605263, 0.973684, 0.1, 0.1], [0.605263, 0.973684, 0.282843, 0.141421], [0.605263, 0.973684, 0.141421, 0.282843], [0.657895, 0.973684, 0.1, 0.1], [0.657895, 0.973684, 0.282843, 0.141421], [0.657895, 0.973684, 0.141421, 0.282843], [0.710526, 0.973684, 0.1, 0.1], [0.710526, 0.973684, 0.282843, 0.141421], [0.710526, 0.973684, 0.141421, 0.282843], [0.763158, 0.973684, 0.1, 0.1], [0.763158, 0.973684, 0.282843, 0.141421], [0.763158, 0.973684, 0.141421, 0.282843], [0.81579, 0.973684, 0.1, 0.1], [0.81579, 0.973684, 0.282843, 0.141421], [0.81579, 0.973684, 0.141421, 0.282843], [0.868421, 0.973684, 0.1, 0.1], [0.868421, 0.973684, 0.282843, 0.141421], [0.868421, 0.973684, 0.141421, 0.282843], [0.921053, 0.973684, 0.1, 0.1], [0.921053, 0.973684, 0.282843, 0.141421], [0.921053, 0.973684, 0.141421, 0.282843], [0.973684, 0.973684, 0.1, 0.1], [0.973684, 0.973684, 0.282843, 0.141421], [0.973684, 0.973684, 0.141421, 0.282843], [0.05, 0.05, 0.35, 0.35], [0.05, 0.05, 0.494975, 0.247487], [0.05, 0.05, 0.247487, 0.494975], [0.05, 0.05, 0.606218, 0.202073], [0.05, 0.05, 0.202062, 0.606248], [0.05, 0.05, 0.41833, 0.41833], [0.15, 0.05, 0.35, 0.35], [0.15, 0.05, 0.494975, 0.247487], [0.15, 0.05, 0.247487, 0.494975], [0.15, 0.05, 0.606218, 0.202073], [0.15, 0.05, 0.202062, 0.606248], [0.15, 0.05, 0.41833, 0.41833], [0.25, 0.05, 0.35, 0.35], [0.25, 0.05, 0.494975, 0.247487], [0.25, 0.05, 0.247487, 0.494975], [0.25, 0.05, 0.606218, 0.202073], [0.25, 0.05, 0.202062, 0.606248], [0.25, 0.05, 0.41833, 0.41833], [0.35, 0.05, 0.35, 0.35], [0.35, 0.05, 0.494975, 0.247487], [0.35, 0.05, 0.247487, 0.494975], [0.35, 0.05, 0.606218, 0.202073], [0.35, 0.05, 0.202062, 0.606248], [0.35, 0.05, 0.41833, 0.41833], [0.45, 0.05, 0.35, 0.35], [0.45, 0.05, 0.494975, 0.247487], [0.45, 0.05, 0.247487, 0.494975], [0.45, 0.05, 0.606218, 0.202073], [0.45, 0.05, 0.202062, 0.606248], [0.45, 0.05, 0.41833, 0.41833], [0.55, 0.05, 0.35, 0.35], [0.55, 0.05, 0.494975, 0.247487], [0.55, 0.05, 0.247487, 0.494975], [0.55, 0.05, 0.606218, 0.202073], [0.55, 0.05, 0.202062, 0.606248], [0.55, 0.05, 0.41833, 0.41833], [0.65, 0.05, 0.35, 0.35], [0.65, 0.05, 0.494975, 0.247487], [0.65, 0.05, 0.247487, 0.494975], [0.65, 0.05, 0.606218, 0.202073], [0.65, 0.05, 0.202062, 0.606248], [0.65, 0.05, 0.41833, 0.41833], [0.75, 0.05, 0.35, 0.35], [0.75, 0.05, 0.494975, 0.247487], [0.75, 0.05, 0.247487, 0.494975], [0.75, 0.05, 0.606218, 0.202073], [0.75, 0.05, 0.202062, 0.606248], [0.75, 0.05, 0.41833, 0.41833], [0.85, 0.05, 0.35, 0.35], [0.85, 0.05, 0.494975, 0.247487], [0.85, 0.05, 0.247487, 0.494975], [0.85, 0.05, 0.606218, 0.202073], [0.85, 0.05, 0.202062, 0.606248], [0.85, 0.05, 0.41833, 0.41833], [0.95, 0.05, 0.35, 0.35], [0.95, 0.05, 0.494975, 0.247487], [0.95, 0.05, 0.247487, 0.494975], [0.95, 0.05, 0.606218, 0.202073], [0.95, 0.05, 0.202063, 0.606248], [0.95, 0.05, 0.41833, 0.41833], [0.05, 0.15, 0.35, 0.35], [0.05, 0.15, 0.494975, 0.247487], [0.05, 0.15, 0.247487, 0.494975], [0.05, 0.15, 0.606218, 0.202073], [0.05, 0.15, 0.202062, 0.606248], [0.05, 0.15, 0.41833, 0.41833], [0.15, 0.15, 0.35, 0.35], [0.15, 0.15, 0.494975, 0.247487], [0.15, 0.15, 0.247487, 0.494975], [0.15, 0.15, 0.606218, 0.202073], [0.15, 0.15, 0.202062, 0.606248], [0.15, 0.15, 0.41833, 0.41833], [0.25, 0.15, 0.35, 0.35], [0.25, 0.15, 0.494975, 0.247487], [0.25, 0.15, 0.247487, 0.494975], [0.25, 0.15, 0.606218, 0.202073], [0.25, 0.15, 0.202062, 0.606248], [0.25, 0.15, 0.41833, 0.41833], [0.35, 0.15, 0.35, 0.35], [0.35, 0.15, 0.494975, 0.247487], [0.35, 0.15, 0.247487, 0.494975], [0.35, 0.15, 0.606218, 0.202073], [0.35, 0.15, 0.202062, 0.606248], [0.35, 0.15, 0.41833, 0.41833], [0.45, 0.15, 0.35, 0.35], [0.45, 0.15, 0.494975, 0.247487], [0.45, 0.15, 0.247487, 0.494975], [0.45, 0.15, 0.606218, 0.202073], [0.45, 0.15, 0.202062, 0.606248], [0.45, 0.15, 0.41833, 0.41833], [0.55, 0.15, 0.35, 0.35], [0.55, 0.15, 0.494975, 0.247487], [0.55, 0.15, 0.247487, 0.494975], [0.55, 0.15, 0.606218, 0.202073], [0.55, 0.15, 0.202062, 0.606248], [0.55, 0.15, 0.41833, 0.41833], [0.65, 0.15, 0.35, 0.35], [0.65, 0.15, 0.494975, 0.247487], [0.65, 0.15, 0.247487, 0.494975], [0.65, 0.15, 0.606218, 0.202073], [0.65, 0.15, 0.202062, 0.606248], [0.65, 0.15, 0.41833, 0.41833], [0.75, 0.15, 0.35, 0.35], [0.75, 0.15, 0.494975, 0.247487], [0.75, 0.15, 0.247487, 0.494975], [0.75, 0.15, 0.606218, 0.202073], [0.75, 0.15, 0.202062, 0.606248], [0.75, 0.15, 0.41833, 0.41833], [0.85, 0.15, 0.35, 0.35], [0.85, 0.15, 0.494975, 0.247487], [0.85, 0.15, 0.247487, 0.494975], [0.85, 0.15, 0.606218, 0.202073], [0.85, 0.15, 0.202062, 0.606248], [0.85, 0.15, 0.41833, 0.41833], [0.95, 0.15, 0.35, 0.35], [0.95, 0.15, 0.494975, 0.247487], [0.95, 0.15, 0.247487, 0.494975], [0.95, 0.15, 0.606218, 0.202073], [0.95, 0.15, 0.202063, 0.606248], [0.95, 0.15, 0.41833, 0.41833], [0.05, 0.25, 0.35, 0.35], [0.05, 0.25, 0.494975, 0.247487], [0.05, 0.25, 0.247487, 0.494975], [0.05, 0.25, 0.606218, 0.202073], [0.05, 0.25, 0.202062, 0.606248], [0.05, 0.25, 0.41833, 0.41833], [0.15, 0.25, 0.35, 0.35], [0.15, 0.25, 0.494975, 0.247487], [0.15, 0.25, 0.247487, 0.494975], [0.15, 0.25, 0.606218, 0.202073], [0.15, 0.25, 0.202062, 0.606248], [0.15, 0.25, 0.41833, 0.41833], [0.25, 0.25, 0.35, 0.35], [0.25, 0.25, 0.494975, 0.247487], [0.25, 0.25, 0.247487, 0.494975], [0.25, 0.25, 0.606218, 0.202073], [0.25, 0.25, 0.202062, 0.606248], [0.25, 0.25, 0.41833, 0.41833], [0.35, 0.25, 0.35, 0.35], [0.35, 0.25, 0.494975, 0.247487], [0.35, 0.25, 0.247487, 0.494975], [0.35, 0.25, 0.606218, 0.202073], [0.35, 0.25, 0.202062, 0.606248], [0.35, 0.25, 0.41833, 0.41833], [0.45, 0.25, 0.35, 0.35], [0.45, 0.25, 0.494975, 0.247487], [0.45, 0.25, 0.247487, 0.494975], [0.45, 0.25, 0.606218, 0.202073], [0.45, 0.25, 0.202062, 0.606248], [0.45, 0.25, 0.41833, 0.41833], [0.55, 0.25, 0.35, 0.35], [0.55, 0.25, 0.494975, 0.247487], [0.55, 0.25, 0.247487, 0.494975], [0.55, 0.25, 0.606218, 0.202073], [0.55, 0.25, 0.202062, 0.606248], [0.55, 0.25, 0.41833, 0.41833], [0.65, 0.25, 0.35, 0.35], [0.65, 0.25, 0.494975, 0.247487], [0.65, 0.25, 0.247487, 0.494975], [0.65, 0.25, 0.606218, 0.202073], [0.65, 0.25, 0.202062, 0.606248], [0.65, 0.25, 0.41833, 0.41833], [0.75, 0.25, 0.35, 0.35], [0.75, 0.25, 0.494975, 0.247487], [0.75, 0.25, 0.247487, 0.494975], [0.75, 0.25, 0.606218, 0.202073], [0.75, 0.25, 0.202062, 0.606248], [0.75, 0.25, 0.41833, 0.41833], [0.85, 0.25, 0.35, 0.35], [0.85, 0.25, 0.494975, 0.247487], [0.85, 0.25, 0.247487, 0.494975], [0.85, 0.25, 0.606218, 0.202073], [0.85, 0.25, 0.202062, 0.606248], [0.85, 0.25, 0.41833, 0.41833], [0.95, 0.25, 0.35, 0.35], [0.95, 0.25, 0.494975, 0.247487], [0.95, 0.25, 0.247487, 0.494975], [0.95, 0.25, 0.606218, 0.202073], [0.95, 0.25, 0.202063, 0.606248], [0.95, 0.25, 0.41833, 0.41833], [0.05, 0.35, 0.35, 0.35], [0.05, 0.35, 0.494975, 0.247487], [0.05, 0.35, 0.247487, 0.494975], [0.05, 0.35, 0.606218, 0.202073], [0.05, 0.35, 0.202062, 0.606248], [0.05, 0.35, 0.41833, 0.41833], [0.15, 0.35, 0.35, 0.35], [0.15, 0.35, 0.494975, 0.247487], [0.15, 0.35, 0.247487, 0.494975], [0.15, 0.35, 0.606218, 0.202073], [0.15, 0.35, 0.202062, 0.606248], [0.15, 0.35, 0.41833, 0.41833], [0.25, 0.35, 0.35, 0.35], [0.25, 0.35, 0.494975, 0.247487], [0.25, 0.35, 0.247487, 0.494975], [0.25, 0.35, 0.606218, 0.202073], [0.25, 0.35, 0.202062, 0.606248], [0.25, 0.35, 0.41833, 0.41833], [0.35, 0.35, 0.35, 0.35], [0.35, 0.35, 0.494975, 0.247487], [0.35, 0.35, 0.247487, 0.494975], [0.35, 0.35, 0.606218, 0.202073], [0.35, 0.35, 0.202062, 0.606248], [0.35, 0.35, 0.41833, 0.41833], [0.45, 0.35, 0.35, 0.35], [0.45, 0.35, 0.494975, 0.247487], [0.45, 0.35, 0.247487, 0.494975], [0.45, 0.35, 0.606218, 0.202073], [0.45, 0.35, 0.202062, 0.606248], [0.45, 0.35, 0.41833, 0.41833], [0.55, 0.35, 0.35, 0.35], [0.55, 0.35, 0.494975, 0.247487], [0.55, 0.35, 0.247487, 0.494975], [0.55, 0.35, 0.606218, 0.202073], [0.55, 0.35, 0.202062, 0.606248], [0.55, 0.35, 0.41833, 0.41833], [0.65, 0.35, 0.35, 0.35], [0.65, 0.35, 0.494975, 0.247487], [0.65, 0.35, 0.247487, 0.494975], [0.65, 0.35, 0.606218, 0.202073], [0.65, 0.35, 0.202062, 0.606248], [0.65, 0.35, 0.41833, 0.41833], [0.75, 0.35, 0.35, 0.35], [0.75, 0.35, 0.494975, 0.247487], [0.75, 0.35, 0.247487, 0.494975], [0.75, 0.35, 0.606218, 0.202073], [0.75, 0.35, 0.202062, 0.606248], [0.75, 0.35, 0.41833, 0.41833], [0.85, 0.35, 0.35, 0.35], [0.85, 0.35, 0.494975, 0.247487], [0.85, 0.35, 0.247487, 0.494975], [0.85, 0.35, 0.606218, 0.202073], [0.85, 0.35, 0.202062, 0.606248], [0.85, 0.35, 0.41833, 0.41833], [0.95, 0.35, 0.35, 0.35], [0.95, 0.35, 0.494975, 0.247487], [0.95, 0.35, 0.247487, 0.494975], [0.95, 0.35, 0.606218, 0.202073], [0.95, 0.35, 0.202063, 0.606248], [0.95, 0.35, 0.41833, 0.41833], [0.05, 0.45, 0.35, 0.35], [0.05, 0.45, 0.494975, 0.247487], [0.05, 0.45, 0.247487, 0.494975], [0.05, 0.45, 0.606218, 0.202073], [0.05, 0.45, 0.202062, 0.606248], [0.05, 0.45, 0.41833, 0.41833], [0.15, 0.45, 0.35, 0.35], [0.15, 0.45, 0.494975, 0.247487], [0.15, 0.45, 0.247487, 0.494975], [0.15, 0.45, 0.606218, 0.202073], [0.15, 0.45, 0.202062, 0.606248], [0.15, 0.45, 0.41833, 0.41833], [0.25, 0.45, 0.35, 0.35], [0.25, 0.45, 0.494975, 0.247487], [0.25, 0.45, 0.247487, 0.494975], [0.25, 0.45, 0.606218, 0.202073], [0.25, 0.45, 0.202062, 0.606248], [0.25, 0.45, 0.41833, 0.41833], [0.35, 0.45, 0.35, 0.35], [0.35, 0.45, 0.494975, 0.247487], [0.35, 0.45, 0.247487, 0.494975], [0.35, 0.45, 0.606218, 0.202073], [0.35, 0.45, 0.202062, 0.606248], [0.35, 0.45, 0.41833, 0.41833], [0.45, 0.45, 0.35, 0.35], [0.45, 0.45, 0.494975, 0.247487], [0.45, 0.45, 0.247487, 0.494975], [0.45, 0.45, 0.606218, 0.202073], [0.45, 0.45, 0.202062, 0.606248], [0.45, 0.45, 0.41833, 0.41833], [0.55, 0.45, 0.35, 0.35], [0.55, 0.45, 0.494975, 0.247487], [0.55, 0.45, 0.247487, 0.494975], [0.55, 0.45, 0.606218, 0.202073], [0.55, 0.45, 0.202062, 0.606248], [0.55, 0.45, 0.41833, 0.41833], [0.65, 0.45, 0.35, 0.35], [0.65, 0.45, 0.494975, 0.247487], [0.65, 0.45, 0.247487, 0.494975], [0.65, 0.45, 0.606218, 0.202073], [0.65, 0.45, 0.202062, 0.606248], [0.65, 0.45, 0.41833, 0.41833], [0.75, 0.45, 0.35, 0.35], [0.75, 0.45, 0.494975, 0.247487], [0.75, 0.45, 0.247487, 0.494975], [0.75, 0.45, 0.606218, 0.202073], [0.75, 0.45, 0.202062, 0.606248], [0.75, 0.45, 0.41833, 0.41833], [0.85, 0.45, 0.35, 0.35], [0.85, 0.45, 0.494975, 0.247487], [0.85, 0.45, 0.247487, 0.494975], [0.85, 0.45, 0.606218, 0.202073], [0.85, 0.45, 0.202062, 0.606248], [0.85, 0.45, 0.41833, 0.41833], [0.95, 0.45, 0.35, 0.35], [0.95, 0.45, 0.494975, 0.247487], [0.95, 0.45, 0.247487, 0.494975], [0.95, 0.45, 0.606218, 0.202073], [0.95, 0.45, 0.202063, 0.606248], [0.95, 0.45, 0.41833, 0.41833], [0.05, 0.55, 0.35, 0.35], [0.05, 0.55, 0.494975, 0.247487], [0.05, 0.55, 0.247487, 0.494975], [0.05, 0.55, 0.606218, 0.202073], [0.05, 0.55, 0.202062, 0.606248], [0.05, 0.55, 0.41833, 0.41833], [0.15, 0.55, 0.35, 0.35], [0.15, 0.55, 0.494975, 0.247487], [0.15, 0.55, 0.247487, 0.494975], [0.15, 0.55, 0.606218, 0.202073], [0.15, 0.55, 0.202062, 0.606248], [0.15, 0.55, 0.41833, 0.41833], [0.25, 0.55, 0.35, 0.35], [0.25, 0.55, 0.494975, 0.247487], [0.25, 0.55, 0.247487, 0.494975], [0.25, 0.55, 0.606218, 0.202073], [0.25, 0.55, 0.202062, 0.606248], [0.25, 0.55, 0.41833, 0.41833], [0.35, 0.55, 0.35, 0.35], [0.35, 0.55, 0.494975, 0.247487], [0.35, 0.55, 0.247487, 0.494975], [0.35, 0.55, 0.606218, 0.202073], [0.35, 0.55, 0.202062, 0.606248], [0.35, 0.55, 0.41833, 0.41833], [0.45, 0.55, 0.35, 0.35], [0.45, 0.55, 0.494975, 0.247487], [0.45, 0.55, 0.247487, 0.494975], [0.45, 0.55, 0.606218, 0.202073], [0.45, 0.55, 0.202062, 0.606248], [0.45, 0.55, 0.41833, 0.41833], [0.55, 0.55, 0.35, 0.35], [0.55, 0.55, 0.494975, 0.247487], [0.55, 0.55, 0.247487, 0.494975], [0.55, 0.55, 0.606218, 0.202073], [0.55, 0.55, 0.202062, 0.606248], [0.55, 0.55, 0.41833, 0.41833], [0.65, 0.55, 0.35, 0.35], [0.65, 0.55, 0.494975, 0.247487], [0.65, 0.55, 0.247487, 0.494975], [0.65, 0.55, 0.606218, 0.202073], [0.65, 0.55, 0.202062, 0.606248], [0.65, 0.55, 0.41833, 0.41833], [0.75, 0.55, 0.35, 0.35], [0.75, 0.55, 0.494975, 0.247487], [0.75, 0.55, 0.247487, 0.494975], [0.75, 0.55, 0.606218, 0.202073], [0.75, 0.55, 0.202062, 0.606248], [0.75, 0.55, 0.41833, 0.41833], [0.85, 0.55, 0.35, 0.35], [0.85, 0.55, 0.494975, 0.247487], [0.85, 0.55, 0.247487, 0.494975], [0.85, 0.55, 0.606218, 0.202073], [0.85, 0.55, 0.202062, 0.606248], [0.85, 0.55, 0.41833, 0.41833], [0.95, 0.55, 0.35, 0.35], [0.95, 0.55, 0.494975, 0.247487], [0.95, 0.55, 0.247487, 0.494975], [0.95, 0.55, 0.606218, 0.202073], [0.95, 0.55, 0.202063, 0.606248], [0.95, 0.55, 0.41833, 0.41833], [0.05, 0.65, 0.35, 0.35], [0.05, 0.65, 0.494975, 0.247487], [0.05, 0.65, 0.247487, 0.494975], [0.05, 0.65, 0.606218, 0.202073], [0.05, 0.65, 0.202062, 0.606248], [0.05, 0.65, 0.41833, 0.41833], [0.15, 0.65, 0.35, 0.35], [0.15, 0.65, 0.494975, 0.247487], [0.15, 0.65, 0.247487, 0.494975], [0.15, 0.65, 0.606218, 0.202073], [0.15, 0.65, 0.202062, 0.606248], [0.15, 0.65, 0.41833, 0.41833], [0.25, 0.65, 0.35, 0.35], [0.25, 0.65, 0.494975, 0.247487], [0.25, 0.65, 0.247487, 0.494975], [0.25, 0.65, 0.606218, 0.202073], [0.25, 0.65, 0.202062, 0.606248], [0.25, 0.65, 0.41833, 0.41833], [0.35, 0.65, 0.35, 0.35], [0.35, 0.65, 0.494975, 0.247487], [0.35, 0.65, 0.247487, 0.494975], [0.35, 0.65, 0.606218, 0.202073], [0.35, 0.65, 0.202062, 0.606248], [0.35, 0.65, 0.41833, 0.41833], [0.45, 0.65, 0.35, 0.35], [0.45, 0.65, 0.494975, 0.247487], [0.45, 0.65, 0.247487, 0.494975], [0.45, 0.65, 0.606218, 0.202073], [0.45, 0.65, 0.202062, 0.606248], [0.45, 0.65, 0.41833, 0.41833], [0.55, 0.65, 0.35, 0.35], [0.55, 0.65, 0.494975, 0.247487], [0.55, 0.65, 0.247487, 0.494975], [0.55, 0.65, 0.606218, 0.202073], [0.55, 0.65, 0.202062, 0.606248], [0.55, 0.65, 0.41833, 0.41833], [0.65, 0.65, 0.35, 0.35], [0.65, 0.65, 0.494975, 0.247487], [0.65, 0.65, 0.247487, 0.494975], [0.65, 0.65, 0.606218, 0.202073], [0.65, 0.65, 0.202062, 0.606248], [0.65, 0.65, 0.41833, 0.41833], [0.75, 0.65, 0.35, 0.35], [0.75, 0.65, 0.494975, 0.247487], [0.75, 0.65, 0.247487, 0.494975], [0.75, 0.65, 0.606218, 0.202073], [0.75, 0.65, 0.202062, 0.606248], [0.75, 0.65, 0.41833, 0.41833], [0.85, 0.65, 0.35, 0.35], [0.85, 0.65, 0.494975, 0.247487], [0.85, 0.65, 0.247487, 0.494975], [0.85, 0.65, 0.606218, 0.202073], [0.85, 0.65, 0.202062, 0.606248], [0.85, 0.65, 0.41833, 0.41833], [0.95, 0.65, 0.35, 0.35], [0.95, 0.65, 0.494975, 0.247487], [0.95, 0.65, 0.247487, 0.494975], [0.95, 0.65, 0.606218, 0.202073], [0.95, 0.65, 0.202063, 0.606248], [0.95, 0.65, 0.41833, 0.41833], [0.05, 0.75, 0.35, 0.35], [0.05, 0.75, 0.494975, 0.247487], [0.05, 0.75, 0.247487, 0.494975], [0.05, 0.75, 0.606218, 0.202073], [0.05, 0.75, 0.202062, 0.606248], [0.05, 0.75, 0.41833, 0.41833], [0.15, 0.75, 0.35, 0.35], [0.15, 0.75, 0.494975, 0.247487], [0.15, 0.75, 0.247487, 0.494975], [0.15, 0.75, 0.606218, 0.202073], [0.15, 0.75, 0.202062, 0.606248], [0.15, 0.75, 0.41833, 0.41833], [0.25, 0.75, 0.35, 0.35], [0.25, 0.75, 0.494975, 0.247487], [0.25, 0.75, 0.247487, 0.494975], [0.25, 0.75, 0.606218, 0.202073], [0.25, 0.75, 0.202062, 0.606248], [0.25, 0.75, 0.41833, 0.41833], [0.35, 0.75, 0.35, 0.35], [0.35, 0.75, 0.494975, 0.247487], [0.35, 0.75, 0.247487, 0.494975], [0.35, 0.75, 0.606218, 0.202073], [0.35, 0.75, 0.202062, 0.606248], [0.35, 0.75, 0.41833, 0.41833], [0.45, 0.75, 0.35, 0.35], [0.45, 0.75, 0.494975, 0.247487], [0.45, 0.75, 0.247487, 0.494975], [0.45, 0.75, 0.606218, 0.202073], [0.45, 0.75, 0.202062, 0.606248], [0.45, 0.75, 0.41833, 0.41833], [0.55, 0.75, 0.35, 0.35], [0.55, 0.75, 0.494975, 0.247487], [0.55, 0.75, 0.247487, 0.494975], [0.55, 0.75, 0.606218, 0.202073], [0.55, 0.75, 0.202062, 0.606248], [0.55, 0.75, 0.41833, 0.41833], [0.65, 0.75, 0.35, 0.35], [0.65, 0.75, 0.494975, 0.247487], [0.65, 0.75, 0.247487, 0.494975], [0.65, 0.75, 0.606218, 0.202073], [0.65, 0.75, 0.202062, 0.606248], [0.65, 0.75, 0.41833, 0.41833], [0.75, 0.75, 0.35, 0.35], [0.75, 0.75, 0.494975, 0.247487], [0.75, 0.75, 0.247487, 0.494975], [0.75, 0.75, 0.606218, 0.202073], [0.75, 0.75, 0.202062, 0.606248], [0.75, 0.75, 0.41833, 0.41833], [0.85, 0.75, 0.35, 0.35], [0.85, 0.75, 0.494975, 0.247487], [0.85, 0.75, 0.247487, 0.494975], [0.85, 0.75, 0.606218, 0.202073], [0.85, 0.75, 0.202062, 0.606248], [0.85, 0.75, 0.41833, 0.41833], [0.95, 0.75, 0.35, 0.35], [0.95, 0.75, 0.494975, 0.247487], [0.95, 0.75, 0.247487, 0.494975], [0.95, 0.75, 0.606218, 0.202073], [0.95, 0.75, 0.202063, 0.606248], [0.95, 0.75, 0.41833, 0.41833], [0.05, 0.85, 0.35, 0.35], [0.05, 0.85, 0.494975, 0.247487], [0.05, 0.85, 0.247487, 0.494975], [0.05, 0.85, 0.606218, 0.202073], [0.05, 0.85, 0.202062, 0.606248], [0.05, 0.85, 0.41833, 0.41833], [0.15, 0.85, 0.35, 0.35], [0.15, 0.85, 0.494975, 0.247487], [0.15, 0.85, 0.247487, 0.494975], [0.15, 0.85, 0.606218, 0.202073], [0.15, 0.85, 0.202062, 0.606248], [0.15, 0.85, 0.41833, 0.41833], [0.25, 0.85, 0.35, 0.35], [0.25, 0.85, 0.494975, 0.247487], [0.25, 0.85, 0.247487, 0.494975], [0.25, 0.85, 0.606218, 0.202073], [0.25, 0.85, 0.202062, 0.606248], [0.25, 0.85, 0.41833, 0.41833], [0.35, 0.85, 0.35, 0.35], [0.35, 0.85, 0.494975, 0.247487], [0.35, 0.85, 0.247487, 0.494975], [0.35, 0.85, 0.606218, 0.202073], [0.35, 0.85, 0.202062, 0.606248], [0.35, 0.85, 0.41833, 0.41833], [0.45, 0.85, 0.35, 0.35], [0.45, 0.85, 0.494975, 0.247487], [0.45, 0.85, 0.247487, 0.494975], [0.45, 0.85, 0.606218, 0.202073], [0.45, 0.85, 0.202062, 0.606248], [0.45, 0.85, 0.41833, 0.41833], [0.55, 0.85, 0.35, 0.35], [0.55, 0.85, 0.494975, 0.247487], [0.55, 0.85, 0.247487, 0.494975], [0.55, 0.85, 0.606218, 0.202073], [0.55, 0.85, 0.202062, 0.606248], [0.55, 0.85, 0.41833, 0.41833], [0.65, 0.85, 0.35, 0.35], [0.65, 0.85, 0.494975, 0.247487], [0.65, 0.85, 0.247487, 0.494975], [0.65, 0.85, 0.606218, 0.202073], [0.65, 0.85, 0.202062, 0.606248], [0.65, 0.85, 0.41833, 0.41833], [0.75, 0.85, 0.35, 0.35], [0.75, 0.85, 0.494975, 0.247487], [0.75, 0.85, 0.247487, 0.494975], [0.75, 0.85, 0.606218, 0.202073], [0.75, 0.85, 0.202062, 0.606248], [0.75, 0.85, 0.41833, 0.41833], [0.85, 0.85, 0.35, 0.35], [0.85, 0.85, 0.494975, 0.247487], [0.85, 0.85, 0.247487, 0.494975], [0.85, 0.85, 0.606218, 0.202073], [0.85, 0.85, 0.202062, 0.606248], [0.85, 0.85, 0.41833, 0.41833], [0.95, 0.85, 0.35, 0.35], [0.95, 0.85, 0.494975, 0.247487], [0.95, 0.85, 0.247487, 0.494975], [0.95, 0.85, 0.606218, 0.202073], [0.95, 0.85, 0.202063, 0.606248], [0.95, 0.85, 0.41833, 0.41833], [0.05, 0.95, 0.35, 0.35], [0.05, 0.95, 0.494975, 0.247487], [0.05, 0.95, 0.247487, 0.494975], [0.05, 0.95, 0.606218, 0.202073], [0.05, 0.95, 0.202062, 0.606248], [0.05, 0.95, 0.41833, 0.41833], [0.15, 0.95, 0.35, 0.35], [0.15, 0.95, 0.494975, 0.247487], [0.15, 0.95, 0.247487, 0.494975], [0.15, 0.95, 0.606218, 0.202073], [0.15, 0.95, 0.202062, 0.606248], [0.15, 0.95, 0.41833, 0.41833], [0.25, 0.95, 0.35, 0.35], [0.25, 0.95, 0.494975, 0.247487], [0.25, 0.95, 0.247487, 0.494975], [0.25, 0.95, 0.606218, 0.202073], [0.25, 0.95, 0.202062, 0.606248], [0.25, 0.95, 0.41833, 0.41833], [0.35, 0.95, 0.35, 0.35], [0.35, 0.95, 0.494975, 0.247487], [0.35, 0.95, 0.247487, 0.494975], [0.35, 0.95, 0.606218, 0.202073], [0.35, 0.95, 0.202062, 0.606248], [0.35, 0.95, 0.41833, 0.41833], [0.45, 0.95, 0.35, 0.35], [0.45, 0.95, 0.494975, 0.247487], [0.45, 0.95, 0.247487, 0.494975], [0.45, 0.95, 0.606218, 0.202073], [0.45, 0.95, 0.202062, 0.606248], [0.45, 0.95, 0.41833, 0.41833], [0.55, 0.95, 0.35, 0.35], [0.55, 0.95, 0.494975, 0.247487], [0.55, 0.95, 0.247487, 0.494975], [0.55, 0.95, 0.606218, 0.202073], [0.55, 0.95, 0.202062, 0.606248], [0.55, 0.95, 0.41833, 0.41833], [0.65, 0.95, 0.35, 0.35], [0.65, 0.95, 0.494975, 0.247487], [0.65, 0.95, 0.247487, 0.494975], [0.65, 0.95, 0.606218, 0.202073], [0.65, 0.95, 0.202062, 0.606248], [0.65, 0.95, 0.41833, 0.41833], [0.75, 0.95, 0.35, 0.35], [0.75, 0.95, 0.494975, 0.247487], [0.75, 0.95, 0.247487, 0.494975], [0.75, 0.95, 0.606218, 0.202073], [0.75, 0.95, 0.202062, 0.606248], [0.75, 0.95, 0.41833, 0.41833], [0.85, 0.95, 0.35, 0.35], [0.85, 0.95, 0.494975, 0.247487], [0.85, 0.95, 0.247487, 0.494975], [0.85, 0.95, 0.606218, 0.202073], [0.85, 0.95, 0.202062, 0.606248], [0.85, 0.95, 0.41833, 0.41833], [0.95, 0.95, 0.35, 0.35], [0.95, 0.95, 0.494975, 0.247487], [0.95, 0.95, 0.247487, 0.494975], [0.95, 0.95, 0.606218, 0.202073], [0.95, 0.95, 0.202063, 0.606248], [0.95, 0.95, 0.41833, 0.41833], [0.1, 0.1, 0.5, 0.5], [0.1, 0.1, 0.707107, 0.353553], [0.1, 0.1, 0.353553, 0.707107], [0.1, 0.1, 0.866025, 0.288675], [0.1, 0.1, 0.288661, 0.866069], [0.1, 0.1, 0.570088, 0.570088], [0.3, 0.1, 0.5, 0.5], [0.3, 0.1, 0.707107, 0.353553], [0.3, 0.1, 0.353553, 0.707107], [0.3, 0.1, 0.866025, 0.288675], [0.3, 0.1, 0.288661, 0.866069], [0.3, 0.1, 0.570088, 0.570088], [0.5, 0.1, 0.5, 0.5], [0.5, 0.1, 0.707107, 0.353553], [0.5, 0.1, 0.353553, 0.707107], [0.5, 0.1, 0.866025, 0.288675], [0.5, 0.1, 0.288661, 0.866069], [0.5, 0.1, 0.570088, 0.570088], [0.7, 0.1, 0.5, 0.5], [0.7, 0.1, 0.707107, 0.353553], [0.7, 0.1, 0.353553, 0.707107], [0.7, 0.1, 0.866025, 0.288675], [0.7, 0.1, 0.288661, 0.866069], [0.7, 0.1, 0.570088, 0.570088], [0.9, 0.1, 0.5, 0.5], [0.9, 0.1, 0.707107, 0.353553], [0.9, 0.1, 0.353553, 0.707107], [0.9, 0.1, 0.866025, 0.288675], [0.9, 0.1, 0.288661, 0.866069], [0.9, 0.1, 0.570088, 0.570088], [0.1, 0.3, 0.5, 0.5], [0.1, 0.3, 0.707107, 0.353553], [0.1, 0.3, 0.353553, 0.707107], [0.1, 0.3, 0.866025, 0.288675], [0.1, 0.3, 0.288661, 0.866069], [0.1, 0.3, 0.570088, 0.570088], [0.3, 0.3, 0.5, 0.5], [0.3, 0.3, 0.707107, 0.353553], [0.3, 0.3, 0.353553, 0.707107], [0.3, 0.3, 0.866025, 0.288675], [0.3, 0.3, 0.288661, 0.866069], [0.3, 0.3, 0.570088, 0.570088], [0.5, 0.3, 0.5, 0.5], [0.5, 0.3, 0.707107, 0.353553], [0.5, 0.3, 0.353553, 0.707107], [0.5, 0.3, 0.866025, 0.288675], [0.5, 0.3, 0.288661, 0.866069], [0.5, 0.3, 0.570088, 0.570088], [0.7, 0.3, 0.5, 0.5], [0.7, 0.3, 0.707107, 0.353553], [0.7, 0.3, 0.353553, 0.707107], [0.7, 0.3, 0.866025, 0.288675], [0.7, 0.3, 0.288661, 0.866069], [0.7, 0.3, 0.570088, 0.570088], [0.9, 0.3, 0.5, 0.5], [0.9, 0.3, 0.707107, 0.353553], [0.9, 0.3, 0.353553, 0.707107], [0.9, 0.3, 0.866025, 0.288675], [0.9, 0.3, 0.288661, 0.866069], [0.9, 0.3, 0.570088, 0.570088], [0.1, 0.5, 0.5, 0.5], [0.1, 0.5, 0.707107, 0.353553], [0.1, 0.5, 0.353553, 0.707107], [0.1, 0.5, 0.866025, 0.288675], [0.1, 0.5, 0.288661, 0.866069], [0.1, 0.5, 0.570088, 0.570088], [0.3, 0.5, 0.5, 0.5], [0.3, 0.5, 0.707107, 0.353553], [0.3, 0.5, 0.353553, 0.707107], [0.3, 0.5, 0.866025, 0.288675], [0.3, 0.5, 0.288661, 0.866069], [0.3, 0.5, 0.570088, 0.570088], [0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.707107, 0.353553], [0.5, 0.5, 0.353553, 0.707107], [0.5, 0.5, 0.866025, 0.288675], [0.5, 0.5, 0.288661, 0.866069], [0.5, 0.5, 0.570088, 0.570088], [0.7, 0.5, 0.5, 0.5], [0.7, 0.5, 0.707107, 0.353553], [0.7, 0.5, 0.353553, 0.707107], [0.7, 0.5, 0.866025, 0.288675], [0.7, 0.5, 0.288661, 0.866069], [0.7, 0.5, 0.570088, 0.570088], [0.9, 0.5, 0.5, 0.5], [0.9, 0.5, 0.707107, 0.353553], [0.9, 0.5, 0.353553, 0.707107], [0.9, 0.5, 0.866025, 0.288675], [0.9, 0.5, 0.288661, 0.866069], [0.9, 0.5, 0.570088, 0.570088], [0.1, 0.7, 0.5, 0.5], [0.1, 0.7, 0.707107, 0.353553], [0.1, 0.7, 0.353553, 0.707107], [0.1, 0.7, 0.866025, 0.288675], [0.1, 0.7, 0.288661, 0.866069], [0.1, 0.7, 0.570088, 0.570088], [0.3, 0.7, 0.5, 0.5], [0.3, 0.7, 0.707107, 0.353553], [0.3, 0.7, 0.353553, 0.707107], [0.3, 0.7, 0.866025, 0.288675], [0.3, 0.7, 0.288661, 0.866069], [0.3, 0.7, 0.570088, 0.570088], [0.5, 0.7, 0.5, 0.5], [0.5, 0.7, 0.707107, 0.353553], [0.5, 0.7, 0.353553, 0.707107], [0.5, 0.7, 0.866025, 0.288675], [0.5, 0.7, 0.288661, 0.866069], [0.5, 0.7, 0.570088, 0.570088], [0.7, 0.7, 0.5, 0.5], [0.7, 0.7, 0.707107, 0.353553], [0.7, 0.7, 0.353553, 0.707107], [0.7, 0.7, 0.866025, 0.288675], [0.7, 0.7, 0.288661, 0.866069], [0.7, 0.7, 0.570088, 0.570088], [0.9, 0.7, 0.5, 0.5], [0.9, 0.7, 0.707107, 0.353553], [0.9, 0.7, 0.353553, 0.707107], [0.9, 0.7, 0.866025, 0.288675], [0.9, 0.7, 0.288661, 0.866069], [0.9, 0.7, 0.570088, 0.570088], [0.1, 0.9, 0.5, 0.5], [0.1, 0.9, 0.707107, 0.353553], [0.1, 0.9, 0.353553, 0.707107], [0.1, 0.9, 0.866025, 0.288675], [0.1, 0.9, 0.288661, 0.866069], [0.1, 0.9, 0.570088, 0.570088], [0.3, 0.9, 0.5, 0.5], [0.3, 0.9, 0.707107, 0.353553], [0.3, 0.9, 0.353553, 0.707107], [0.3, 0.9, 0.866025, 0.288675], [0.3, 0.9, 0.288661, 0.866069], [0.3, 0.9, 0.570088, 0.570088], [0.5, 0.9, 0.5, 0.5], [0.5, 0.9, 0.707107, 0.353553], [0.5, 0.9, 0.353553, 0.707107], [0.5, 0.9, 0.866025, 0.288675], [0.5, 0.9, 0.288661, 0.866069], [0.5, 0.9, 0.570088, 0.570088], [0.7, 0.9, 0.5, 0.5], [0.7, 0.9, 0.707107, 0.353553], [0.7, 0.9, 0.353553, 0.707107], [0.7, 0.9, 0.866025, 0.288675], [0.7, 0.9, 0.288661, 0.866069], [0.7, 0.9, 0.570088, 0.570088], [0.9, 0.9, 0.5, 0.5], [0.9, 0.9, 0.707107, 0.353553], [0.9, 0.9, 0.353553, 0.707107], [0.9, 0.9, 0.866025, 0.288675], [0.9, 0.9, 0.288661, 0.866069], [0.9, 0.9, 0.570088, 0.570088], [0.166667, 0.166667, 0.65, 0.65], [0.166667, 0.166667, 0.919239, 0.459619], [0.166667, 0.166667, 0.459619, 0.919239], [0.166667, 0.166667, 1.12583, 0.375278], [0.166667, 0.166667, 0.375259, 1.12589], [0.166667, 0.166667, 0.72111, 0.72111], [0.5, 0.166667, 0.65, 0.65], [0.5, 0.166667, 0.919239, 0.459619], [0.5, 0.166667, 0.459619, 0.919239], [0.5, 0.166667, 1.12583, 0.375278], [0.5, 0.166667, 0.375259, 1.12589], [0.5, 0.166667, 0.72111, 0.72111], [0.833333, 0.166667, 0.65, 0.65], [0.833333, 0.166667, 0.919239, 0.459619], [0.833333, 0.166667, 0.459619, 0.919239], [0.833333, 0.166667, 1.12583, 0.375278], [0.833333, 0.166667, 0.375259, 1.12589], [0.833333, 0.166667, 0.72111, 0.72111], [0.166667, 0.5, 0.65, 0.65], [0.166667, 0.5, 0.919239, 0.459619], [0.166667, 0.5, 0.459619, 0.919239], [0.166667, 0.5, 1.12583, 0.375278], [0.166667, 0.5, 0.375259, 1.12589], [0.166667, 0.5, 0.72111, 0.72111], [0.5, 0.5, 0.65, 0.65], [0.5, 0.5, 0.919239, 0.459619], [0.5, 0.5, 0.459619, 0.919239], [0.5, 0.5, 1.12583, 0.375278], [0.5, 0.5, 0.375259, 1.12589], [0.5, 0.5, 0.72111, 0.72111], [0.833333, 0.5, 0.65, 0.65], [0.833333, 0.5, 0.919239, 0.459619], [0.833333, 0.5, 0.459619, 0.919239], [0.833333, 0.5, 1.12583, 0.375278], [0.833333, 0.5, 0.375259, 1.12589], [0.833333, 0.5, 0.72111, 0.72111], [0.166667, 0.833333, 0.65, 0.65], [0.166667, 0.833333, 0.919239, 0.459619], [0.166667, 0.833333, 0.459619, 0.919239], [0.166667, 0.833333, 1.12583, 0.375278], [0.166667, 0.833333, 0.375259, 1.12589], [0.166667, 0.833333, 0.72111, 0.72111], [0.5, 0.833333, 0.65, 0.65], [0.5, 0.833333, 0.919239, 0.459619], [0.5, 0.833333, 0.459619, 0.919239], [0.5, 0.833333, 1.12583, 0.375278], [0.5, 0.833333, 0.375259, 1.12589], [0.5, 0.833333, 0.72111, 0.72111], [0.833333, 0.833333, 0.65, 0.65], [0.833333, 0.833333, 0.919239, 0.459619], [0.833333, 0.833333, 0.459619, 0.919239], [0.833333, 0.833333, 1.12583, 0.375278], [0.833333, 0.833333, 0.375259, 1.12589], [0.833333, 0.833333, 0.72111, 0.72111], [0.25, 0.25, 0.8, 0.8], [0.25, 0.25, 1.13137, 0.565686], [0.25, 0.25, 0.565685, 1.13137], [0.25, 0.25, 1.38564, 0.46188], [0.25, 0.25, 0.461857, 1.38571], [0.25, 0.25, 0.87178, 0.87178], [0.75, 0.25, 0.8, 0.8], [0.75, 0.25, 1.13137, 0.565686], [0.75, 0.25, 0.565685, 1.13137], [0.75, 0.25, 1.38564, 0.46188], [0.75, 0.25, 0.461857, 1.38571], [0.75, 0.25, 0.87178, 0.87178], [0.25, 0.75, 0.8, 0.8], [0.25, 0.75, 1.13137, 0.565686], [0.25, 0.75, 0.565685, 1.13137], [0.25, 0.75, 1.38564, 0.46188], [0.25, 0.75, 0.461857, 1.38571], [0.25, 0.75, 0.87178, 0.87178], [0.75, 0.75, 0.8, 0.8], [0.75, 0.75, 1.13137, 0.565686], [0.75, 0.75, 0.565685, 1.13137], [0.75, 0.75, 1.38564, 0.46188], [0.75, 0.75, 0.461857, 1.38571], [0.75, 0.75, 0.87178, 0.87178], [0.5, 0.5, 0.95, 0.95], [0.5, 0.5, 1.3435, 0.671751], [0.5, 0.5, 0.671751, 1.3435], [0.5, 0.5, 1.64545, 0.548483], [0.5, 0.5, 0.548455, 1.64553], [0.5, 0.5, 0.974679, 0.974679]] \ No newline at end of file diff --git a/pose-detection/src/shared/test_data/image_to_tensor_input.jpg b/pose-detection/src/shared/test_data/image_to_tensor_input.jpg new file mode 100644 index 0000000000..37d6c4b20a Binary files /dev/null and b/pose-detection/src/shared/test_data/image_to_tensor_input.jpg differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect.png b/pose-detection/src/shared/test_data/large_sub_rect.png new file mode 100644 index 0000000000..38a13dabe9 Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect.png differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect_border_zero.png b/pose-detection/src/shared/test_data/large_sub_rect_border_zero.png new file mode 100644 index 0000000000..1a738a50d0 Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect.png b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect.png new file mode 100644 index 0000000000..254dc72ae9 Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect.png differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_border_zero.png b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_border_zero.png new file mode 100644 index 0000000000..5b096cb4de Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation.png b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation.png new file mode 100644 index 0000000000..104cb60915 Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation.png differ diff --git a/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation_border_zero.png b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation_border_zero.png new file mode 100644 index 0000000000..c5512ec0d2 Binary files /dev/null and b/pose-detection/src/shared/test_data/large_sub_rect_keep_aspect_with_rotation_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect.png b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect.png new file mode 100644 index 0000000000..aba8d2591d Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_border_zero.png b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_border_zero.png new file mode 100644 index 0000000000..bfb4615463 Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation.png b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation.png new file mode 100644 index 0000000000..5ce7c3ec36 Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation_border_zero.png b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation_border_zero.png new file mode 100644 index 0000000000..ab14e59546 Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_keep_aspect_with_rotation_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation.png b/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation.png new file mode 100644 index 0000000000..ecfb1e5375 Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation.png differ diff --git a/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation_border_zero.png b/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation_border_zero.png new file mode 100644 index 0000000000..d553011466 Binary files /dev/null and b/pose-detection/src/shared/test_data/medium_sub_rect_with_rotation_border_zero.png differ diff --git a/pose-detection/src/shared/test_data/noop_except_range.png b/pose-detection/src/shared/test_data/noop_except_range.png new file mode 100644 index 0000000000..1486d9f151 Binary files /dev/null and b/pose-detection/src/shared/test_data/noop_except_range.png differ diff --git a/pose-detection/src/shared/test_data/three_people.jpg b/pose-detection/src/shared/test_data/three_people.jpg new file mode 100644 index 0000000000..94d943864e Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people.jpg differ diff --git a/pose-detection/src/shared/test_data/three_people_binary_mask.png b/pose-detection/src/shared/test_data/three_people_binary_mask.png new file mode 100644 index 0000000000..4b1365ebba Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_binary_mask.png differ diff --git a/pose-detection/src/shared/test_data/three_people_binary_segmentation.png b/pose-detection/src/shared/test_data/three_people_binary_segmentation.png new file mode 100644 index 0000000000..f103bed805 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_binary_segmentation.png differ diff --git a/pose-detection/src/shared/test_data/three_people_blur_body_parts.png b/pose-detection/src/shared/test_data/three_people_blur_body_parts.png new file mode 100644 index 0000000000..2f41759ba2 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_blur_body_parts.png differ diff --git a/pose-detection/src/shared/test_data/three_people_bokeh_effect.png b/pose-detection/src/shared/test_data/three_people_bokeh_effect.png new file mode 100644 index 0000000000..2d7a8e9643 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_bokeh_effect.png differ diff --git a/pose-detection/src/shared/test_data/three_people_colored_mask.png b/pose-detection/src/shared/test_data/three_people_colored_mask.png new file mode 100644 index 0000000000..a54cbda122 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_colored_mask.png differ diff --git a/pose-detection/src/shared/test_data/three_people_colored_segmentation.png b/pose-detection/src/shared/test_data/three_people_colored_segmentation.png new file mode 100644 index 0000000000..f2d4648e18 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_colored_segmentation.png differ diff --git a/pose-detection/src/shared/test_data/three_people_draw_mask.png b/pose-detection/src/shared/test_data/three_people_draw_mask.png new file mode 100644 index 0000000000..6276186de5 Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_draw_mask.png differ diff --git a/pose-detection/src/shared/test_data/three_people_pixelated_mask.png b/pose-detection/src/shared/test_data/three_people_pixelated_mask.png new file mode 100644 index 0000000000..471755237a Binary files /dev/null and b/pose-detection/src/shared/test_data/three_people_pixelated_mask.png differ diff --git a/pose-detection/src/shared/test_util.ts b/pose-detection/src/shared/test_util.ts new file mode 100644 index 0000000000..900f537547 --- /dev/null +++ b/pose-detection/src/shared/test_util.ts @@ -0,0 +1,199 @@ +/** + * @license + * Copyright 2021 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +import {Keypoint} from './calculators/interfaces/common_interfaces'; + +/** Karma server directory serving local files. */ +export const KARMA_SERVER = './base/test_data'; + +export async function loadImage( + imagePath: string, width: number, + height: number): Promise { + const img = new Image(width, height); + const promise = new Promise((resolve, reject) => { + img.crossOrigin = ''; + img.onload = () => { + resolve(img); + }; + }); + + img.src = `${KARMA_SERVER}/${imagePath}`; + + return promise; +} + +/** + * Converts an RGBA image to a binary foreground mask based on an RGBA + * threshold. + * + * @param image Input image to convert. + * + * @param r Minimum red value that denotes a foreground mask. + * @param g Minimum green value that denotes a foreground mask. + * @param b Minimum blue value that denotes a foreground mask. + * + * @return A boolean array of size number of pixels. + */ +export function imageToBooleanMask( + rgbaData: Uint8ClampedArray, r: number, g: number, b: number) { + const mask: boolean[] = []; + for (let i = 0; i < rgbaData.length; i += 4) { + mask.push(rgbaData[i] >= r && rgbaData[i + 1] >= g && rgbaData[i + 2] >= b); + } + return mask; +} + +/** + * Given two boolean masks, calculates the IOU percentage. + * + * @param image Input image to convert. + * + * @param expectedMask Expected mask values. + * @param actualMask Actual mask values. + * + * @return A number denoting the IOU. + */ +export function segmentationIOU( + expectedMask: boolean[], actualMask: boolean[]) { + expect(expectedMask.length === actualMask.length); + + const sum = (mask: boolean[]) => mask.reduce((a, b) => a + +b, 0); + + const intersectionMask = + expectedMask.map((value, index) => value && actualMask[index]); + const iou = sum(intersectionMask) / + (sum(expectedMask) + sum(actualMask) - sum(intersectionMask) + + Number.EPSILON); + + return iou; +} + +export async function loadVideo( + videoPath: string, videoFPS: number, + callback: (video: HTMLVideoElement, timestamp: number) => + Promise, + expected: number[][][], skeletonAdjacentPairs: number[][], + simulatedInterval: number): Promise { + // We override video's timestamp with a fake timestamp. + let simulatedTimestamp: number; + // We keep a pointer for the expected array. + let idx: number; + + const actualInterval = 1 / videoFPS; + + // Create a video element on the html page and serve the content through karma + const video = document.createElement('video'); + // Hide video, and use canvas to render the video, so that we can also + // overlay keypoints. + video.style.visibility = 'hidden'; + const source = document.createElement('source'); + source.src = `${KARMA_SERVER}/${videoPath}`; + source.type = 'video/mp4'; + video.appendChild(source); + document.body.appendChild(video); + const canvas = document.createElement('canvas'); + canvas.style.position = 'absolute'; + canvas.style.left = '0'; + canvas.style.top = '0'; + document.body.appendChild(canvas); + const ctx = canvas.getContext('2d'); + + const promise = new Promise((resolve, reject) => { + video.onseeked = async () => { + const keypoints = await callback(video, simulatedTimestamp); + + const expectedKeypoints = expected[idx].map(([x, y]) => { + return {x, y}; + }); + + ctx.drawImage(video, 0, 0); + draw(expectedKeypoints, ctx, skeletonAdjacentPairs, 'Green'); + + draw(keypoints, ctx, skeletonAdjacentPairs, 'Red'); + + const nextTime = video.currentTime + actualInterval; + if (nextTime < video.duration) { + video.currentTime = nextTime; + simulatedTimestamp += simulatedInterval; + idx++; + } else { + resolve(video); + } + }; + }); + + video.onloadedmetadata = () => { + video.currentTime = 0.001; + simulatedTimestamp = 0; + idx = 0; + const videoWidth = video.videoWidth; + const videoHeight = video.videoHeight; + // Must set below two lines, otherwise video width and height are 0. + video.width = videoWidth; + video.height = videoHeight; + // Must set below two lines, otherwise canvas has a different size. + canvas.width = videoWidth; + canvas.height = videoHeight; + }; + + return promise; +} + +export function getXYPerFrame(result: number[][][]): number[][][] { + return result.map(frameResult => { + return frameResult.map(keypoint => [keypoint[0], keypoint[1]]); + }); +} + +function drawKeypoint(keypoint: Keypoint, ctx: CanvasRenderingContext2D): void { + const circle = new Path2D(); + circle.arc( + keypoint.x, keypoint.y, 4 /* radius */, 0 /* startAngle */, 2 * Math.PI); + ctx.fill(circle); + ctx.stroke(circle); +} + +function drawSkeleton( + keypoints: Keypoint[], skeletonAdjacentPairs: number[][], + ctx: CanvasRenderingContext2D, color: string) { + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + + for (const pair of skeletonAdjacentPairs) { + const [i, j] = pair; + const kp1 = keypoints[i]; + const kp2 = keypoints[j]; + + ctx.beginPath(); + ctx.moveTo(kp1.x, kp1.y); + ctx.lineTo(kp2.x, kp2.y); + ctx.stroke(); + } +} + +function draw( + keypoints: Keypoint[], ctx: CanvasRenderingContext2D, + skeletonAdjacentPairs: number[][], color: string) { + ctx.fillStyle = color; + ctx.strokeStyle = color; + + for (const keypoint of keypoints) { + drawKeypoint(keypoint, ctx); + } + drawSkeleton(keypoints, skeletonAdjacentPairs, ctx, color); +} diff --git a/pose-detection/test_data/shared b/pose-detection/test_data/shared deleted file mode 120000 index b02b32bc8f..0000000000 --- a/pose-detection/test_data/shared +++ /dev/null @@ -1 +0,0 @@ -../../shared/test_data \ No newline at end of file diff --git a/pose-detection/test_data/shared/anchor_golden_file_0.json b/pose-detection/test_data/shared/anchor_golden_file_0.json new file mode 100644 index 0000000000..e5cc332330 --- /dev/null +++ b/pose-detection/test_data/shared/anchor_golden_file_0.json @@ -0,0 +1 @@ +[[0.015625, 0.015625, 1.0, 1.0], [0.015625, 0.015625, 1.0, 1.0], [0.046875, 0.015625, 1.0, 1.0], [0.046875, 0.015625, 1.0, 1.0], [0.078125, 0.015625, 1.0, 1.0], [0.078125, 0.015625, 1.0, 1.0], [0.109375, 0.015625, 1.0, 1.0], [0.109375, 0.015625, 1.0, 1.0], [0.140625, 0.015625, 1.0, 1.0], [0.140625, 0.015625, 1.0, 1.0], [0.171875, 0.015625, 1.0, 1.0], [0.171875, 0.015625, 1.0, 1.0], [0.203125, 0.015625, 1.0, 1.0], [0.203125, 0.015625, 1.0, 1.0], [0.234375, 0.015625, 1.0, 1.0], [0.234375, 0.015625, 1.0, 1.0], [0.265625, 0.015625, 1.0, 1.0], [0.265625, 0.015625, 1.0, 1.0], [0.296875, 0.015625, 1.0, 1.0], [0.296875, 0.015625, 1.0, 1.0], [0.328125, 0.015625, 1.0, 1.0], [0.328125, 0.015625, 1.0, 1.0], [0.359375, 0.015625, 1.0, 1.0], [0.359375, 0.015625, 1.0, 1.0], [0.390625, 0.015625, 1.0, 1.0], [0.390625, 0.015625, 1.0, 1.0], [0.421875, 0.015625, 1.0, 1.0], [0.421875, 0.015625, 1.0, 1.0], [0.453125, 0.015625, 1.0, 1.0], [0.453125, 0.015625, 1.0, 1.0], [0.484375, 0.015625, 1.0, 1.0], [0.484375, 0.015625, 1.0, 1.0], [0.515625, 0.015625, 1.0, 1.0], [0.515625, 0.015625, 1.0, 1.0], [0.546875, 0.015625, 1.0, 1.0], [0.546875, 0.015625, 1.0, 1.0], [0.578125, 0.015625, 1.0, 1.0], [0.578125, 0.015625, 1.0, 1.0], [0.609375, 0.015625, 1.0, 1.0], [0.609375, 0.015625, 1.0, 1.0], [0.640625, 0.015625, 1.0, 1.0], [0.640625, 0.015625, 1.0, 1.0], [0.671875, 0.015625, 1.0, 1.0], [0.671875, 0.015625, 1.0, 1.0], [0.703125, 0.015625, 1.0, 1.0], [0.703125, 0.015625, 1.0, 1.0], [0.734375, 0.015625, 1.0, 1.0], [0.734375, 0.015625, 1.0, 1.0], [0.765625, 0.015625, 1.0, 1.0], [0.765625, 0.015625, 1.0, 1.0], [0.796875, 0.015625, 1.0, 1.0], [0.796875, 0.015625, 1.0, 1.0], [0.828125, 0.015625, 1.0, 1.0], [0.828125, 0.015625, 1.0, 1.0], [0.859375, 0.015625, 1.0, 1.0], [0.859375, 0.015625, 1.0, 1.0], [0.890625, 0.015625, 1.0, 1.0], [0.890625, 0.015625, 1.0, 1.0], [0.921875, 0.015625, 1.0, 1.0], [0.921875, 0.015625, 1.0, 1.0], [0.953125, 0.015625, 1.0, 1.0], [0.953125, 0.015625, 1.0, 1.0], [0.984375, 0.015625, 1.0, 1.0], [0.984375, 0.015625, 1.0, 1.0], [0.015625, 0.046875, 1.0, 1.0], [0.015625, 0.046875, 1.0, 1.0], [0.046875, 0.046875, 1.0, 1.0], [0.046875, 0.046875, 1.0, 1.0], [0.078125, 0.046875, 1.0, 1.0], [0.078125, 0.046875, 1.0, 1.0], [0.109375, 0.046875, 1.0, 1.0], [0.109375, 0.046875, 1.0, 1.0], [0.140625, 0.046875, 1.0, 1.0], [0.140625, 0.046875, 1.0, 1.0], [0.171875, 0.046875, 1.0, 1.0], [0.171875, 0.046875, 1.0, 1.0], [0.203125, 0.046875, 1.0, 1.0], [0.203125, 0.046875, 1.0, 1.0], [0.234375, 0.046875, 1.0, 1.0], [0.234375, 0.046875, 1.0, 1.0], [0.265625, 0.046875, 1.0, 1.0], [0.265625, 0.046875, 1.0, 1.0], [0.296875, 0.046875, 1.0, 1.0], [0.296875, 0.046875, 1.0, 1.0], [0.328125, 0.046875, 1.0, 1.0], [0.328125, 0.046875, 1.0, 1.0], [0.359375, 0.046875, 1.0, 1.0], [0.359375, 0.046875, 1.0, 1.0], [0.390625, 0.046875, 1.0, 1.0], [0.390625, 0.046875, 1.0, 1.0], [0.421875, 0.046875, 1.0, 1.0], [0.421875, 0.046875, 1.0, 1.0], [0.453125, 0.046875, 1.0, 1.0], [0.453125, 0.046875, 1.0, 1.0], [0.484375, 0.046875, 1.0, 1.0], [0.484375, 0.046875, 1.0, 1.0], [0.515625, 0.046875, 1.0, 1.0], [0.515625, 0.046875, 1.0, 1.0], [0.546875, 0.046875, 1.0, 1.0], [0.546875, 0.046875, 1.0, 1.0], [0.578125, 0.046875, 1.0, 1.0], [0.578125, 0.046875, 1.0, 1.0], [0.609375, 0.046875, 1.0, 1.0], [0.609375, 0.046875, 1.0, 1.0], [0.640625, 0.046875, 1.0, 1.0], [0.640625, 0.046875, 1.0, 1.0], [0.671875, 0.046875, 1.0, 1.0], [0.671875, 0.046875, 1.0, 1.0], [0.703125, 0.046875, 1.0, 1.0], [0.703125, 0.046875, 1.0, 1.0], [0.734375, 0.046875, 1.0, 1.0], [0.734375, 0.046875, 1.0, 1.0], [0.765625, 0.046875, 1.0, 1.0], [0.765625, 0.046875, 1.0, 1.0], [0.796875, 0.046875, 1.0, 1.0], [0.796875, 0.046875, 1.0, 1.0], [0.828125, 0.046875, 1.0, 1.0], [0.828125, 0.046875, 1.0, 1.0], [0.859375, 0.046875, 1.0, 1.0], [0.859375, 0.046875, 1.0, 1.0], [0.890625, 0.046875, 1.0, 1.0], [0.890625, 0.046875, 1.0, 1.0], [0.921875, 0.046875, 1.0, 1.0], [0.921875, 0.046875, 1.0, 1.0], [0.953125, 0.046875, 1.0, 1.0], [0.953125, 0.046875, 1.0, 1.0], [0.984375, 0.046875, 1.0, 1.0], [0.984375, 0.046875, 1.0, 1.0], [0.015625, 0.078125, 1.0, 1.0], [0.015625, 0.078125, 1.0, 1.0], [0.046875, 0.078125, 1.0, 1.0], [0.046875, 0.078125, 1.0, 1.0], [0.078125, 0.078125, 1.0, 1.0], [0.078125, 0.078125, 1.0, 1.0], [0.109375, 0.078125, 1.0, 1.0], [0.109375, 0.078125, 1.0, 1.0], [0.140625, 0.078125, 1.0, 1.0], [0.140625, 0.078125, 1.0, 1.0], [0.171875, 0.078125, 1.0, 1.0], [0.171875, 0.078125, 1.0, 1.0], [0.203125, 0.078125, 1.0, 1.0], [0.203125, 0.078125, 1.0, 1.0], [0.234375, 0.078125, 1.0, 1.0], [0.234375, 0.078125, 1.0, 1.0], [0.265625, 0.078125, 1.0, 1.0], [0.265625, 0.078125, 1.0, 1.0], [0.296875, 0.078125, 1.0, 1.0], [0.296875, 0.078125, 1.0, 1.0], [0.328125, 0.078125, 1.0, 1.0], [0.328125, 0.078125, 1.0, 1.0], [0.359375, 0.078125, 1.0, 1.0], [0.359375, 0.078125, 1.0, 1.0], [0.390625, 0.078125, 1.0, 1.0], [0.390625, 0.078125, 1.0, 1.0], [0.421875, 0.078125, 1.0, 1.0], [0.421875, 0.078125, 1.0, 1.0], [0.453125, 0.078125, 1.0, 1.0], [0.453125, 0.078125, 1.0, 1.0], [0.484375, 0.078125, 1.0, 1.0], [0.484375, 0.078125, 1.0, 1.0], [0.515625, 0.078125, 1.0, 1.0], [0.515625, 0.078125, 1.0, 1.0], [0.546875, 0.078125, 1.0, 1.0], [0.546875, 0.078125, 1.0, 1.0], [0.578125, 0.078125, 1.0, 1.0], [0.578125, 0.078125, 1.0, 1.0], [0.609375, 0.078125, 1.0, 1.0], [0.609375, 0.078125, 1.0, 1.0], [0.640625, 0.078125, 1.0, 1.0], [0.640625, 0.078125, 1.0, 1.0], [0.671875, 0.078125, 1.0, 1.0], [0.671875, 0.078125, 1.0, 1.0], [0.703125, 0.078125, 1.0, 1.0], [0.703125, 0.078125, 1.0, 1.0], [0.734375, 0.078125, 1.0, 1.0], [0.734375, 0.078125, 1.0, 1.0], [0.765625, 0.078125, 1.0, 1.0], [0.765625, 0.078125, 1.0, 1.0], [0.796875, 0.078125, 1.0, 1.0], [0.796875, 0.078125, 1.0, 1.0], [0.828125, 0.078125, 1.0, 1.0], [0.828125, 0.078125, 1.0, 1.0], [0.859375, 0.078125, 1.0, 1.0], [0.859375, 0.078125, 1.0, 1.0], [0.890625, 0.078125, 1.0, 1.0], [0.890625, 0.078125, 1.0, 1.0], [0.921875, 0.078125, 1.0, 1.0], [0.921875, 0.078125, 1.0, 1.0], [0.953125, 0.078125, 1.0, 1.0], [0.953125, 0.078125, 1.0, 1.0], [0.984375, 0.078125, 1.0, 1.0], [0.984375, 0.078125, 1.0, 1.0], [0.015625, 0.109375, 1.0, 1.0], [0.015625, 0.109375, 1.0, 1.0], [0.046875, 0.109375, 1.0, 1.0], [0.046875, 0.109375, 1.0, 1.0], [0.078125, 0.109375, 1.0, 1.0], [0.078125, 0.109375, 1.0, 1.0], [0.109375, 0.109375, 1.0, 1.0], [0.109375, 0.109375, 1.0, 1.0], [0.140625, 0.109375, 1.0, 1.0], [0.140625, 0.109375, 1.0, 1.0], [0.171875, 0.109375, 1.0, 1.0], [0.171875, 0.109375, 1.0, 1.0], [0.203125, 0.109375, 1.0, 1.0], [0.203125, 0.109375, 1.0, 1.0], [0.234375, 0.109375, 1.0, 1.0], [0.234375, 0.109375, 1.0, 1.0], [0.265625, 0.109375, 1.0, 1.0], [0.265625, 0.109375, 1.0, 1.0], [0.296875, 0.109375, 1.0, 1.0], [0.296875, 0.109375, 1.0, 1.0], [0.328125, 0.109375, 1.0, 1.0], [0.328125, 0.109375, 1.0, 1.0], [0.359375, 0.109375, 1.0, 1.0], [0.359375, 0.109375, 1.0, 1.0], [0.390625, 0.109375, 1.0, 1.0], [0.390625, 0.109375, 1.0, 1.0], [0.421875, 0.109375, 1.0, 1.0], [0.421875, 0.109375, 1.0, 1.0], [0.453125, 0.109375, 1.0, 1.0], [0.453125, 0.109375, 1.0, 1.0], [0.484375, 0.109375, 1.0, 1.0], [0.484375, 0.109375, 1.0, 1.0], [0.515625, 0.109375, 1.0, 1.0], [0.515625, 0.109375, 1.0, 1.0], [0.546875, 0.109375, 1.0, 1.0], [0.546875, 0.109375, 1.0, 1.0], [0.578125, 0.109375, 1.0, 1.0], [0.578125, 0.109375, 1.0, 1.0], [0.609375, 0.109375, 1.0, 1.0], [0.609375, 0.109375, 1.0, 1.0], [0.640625, 0.109375, 1.0, 1.0], [0.640625, 0.109375, 1.0, 1.0], [0.671875, 0.109375, 1.0, 1.0], [0.671875, 0.109375, 1.0, 1.0], [0.703125, 0.109375, 1.0, 1.0], [0.703125, 0.109375, 1.0, 1.0], [0.734375, 0.109375, 1.0, 1.0], [0.734375, 0.109375, 1.0, 1.0], [0.765625, 0.109375, 1.0, 1.0], [0.765625, 0.109375, 1.0, 1.0], [0.796875, 0.109375, 1.0, 1.0], [0.796875, 0.109375, 1.0, 1.0], [0.828125, 0.109375, 1.0, 1.0], [0.828125, 0.109375, 1.0, 1.0], [0.859375, 0.109375, 1.0, 1.0], [0.859375, 0.109375, 1.0, 1.0], [0.890625, 0.109375, 1.0, 1.0], [0.890625, 0.109375, 1.0, 1.0], [0.921875, 0.109375, 1.0, 1.0], [0.921875, 0.109375, 1.0, 1.0], [0.953125, 0.109375, 1.0, 1.0], [0.953125, 0.109375, 1.0, 1.0], [0.984375, 0.109375, 1.0, 1.0], [0.984375, 0.109375, 1.0, 1.0], [0.015625, 0.140625, 1.0, 1.0], [0.015625, 0.140625, 1.0, 1.0], [0.046875, 0.140625, 1.0, 1.0], [0.046875, 0.140625, 1.0, 1.0], [0.078125, 0.140625, 1.0, 1.0], [0.078125, 0.140625, 1.0, 1.0], [0.109375, 0.140625, 1.0, 1.0], [0.109375, 0.140625, 1.0, 1.0], [0.140625, 0.140625, 1.0, 1.0], [0.140625, 0.140625, 1.0, 1.0], [0.171875, 0.140625, 1.0, 1.0], [0.171875, 0.140625, 1.0, 1.0], [0.203125, 0.140625, 1.0, 1.0], [0.203125, 0.140625, 1.0, 1.0], [0.234375, 0.140625, 1.0, 1.0], [0.234375, 0.140625, 1.0, 1.0], [0.265625, 0.140625, 1.0, 1.0], [0.265625, 0.140625, 1.0, 1.0], [0.296875, 0.140625, 1.0, 1.0], [0.296875, 0.140625, 1.0, 1.0], [0.328125, 0.140625, 1.0, 1.0], [0.328125, 0.140625, 1.0, 1.0], [0.359375, 0.140625, 1.0, 1.0], [0.359375, 0.140625, 1.0, 1.0], [0.390625, 0.140625, 1.0, 1.0], [0.390625, 0.140625, 1.0, 1.0], [0.421875, 0.140625, 1.0, 1.0], [0.421875, 0.140625, 1.0, 1.0], [0.453125, 0.140625, 1.0, 1.0], [0.453125, 0.140625, 1.0, 1.0], [0.484375, 0.140625, 1.0, 1.0], [0.484375, 0.140625, 1.0, 1.0], [0.515625, 0.140625, 1.0, 1.0], [0.515625, 0.140625, 1.0, 1.0], [0.546875, 0.140625, 1.0, 1.0], [0.546875, 0.140625, 1.0, 1.0], [0.578125, 0.140625, 1.0, 1.0], [0.578125, 0.140625, 1.0, 1.0], [0.609375, 0.140625, 1.0, 1.0], [0.609375, 0.140625, 1.0, 1.0], [0.640625, 0.140625, 1.0, 1.0], [0.640625, 0.140625, 1.0, 1.0], [0.671875, 0.140625, 1.0, 1.0], [0.671875, 0.140625, 1.0, 1.0], [0.703125, 0.140625, 1.0, 1.0], [0.703125, 0.140625, 1.0, 1.0], [0.734375, 0.140625, 1.0, 1.0], [0.734375, 0.140625, 1.0, 1.0], [0.765625, 0.140625, 1.0, 1.0], [0.765625, 0.140625, 1.0, 1.0], [0.796875, 0.140625, 1.0, 1.0], [0.796875, 0.140625, 1.0, 1.0], [0.828125, 0.140625, 1.0, 1.0], [0.828125, 0.140625, 1.0, 1.0], [0.859375, 0.140625, 1.0, 1.0], [0.859375, 0.140625, 1.0, 1.0], [0.890625, 0.140625, 1.0, 1.0], [0.890625, 0.140625, 1.0, 1.0], [0.921875, 0.140625, 1.0, 1.0], [0.921875, 0.140625, 1.0, 1.0], [0.953125, 0.140625, 1.0, 1.0], [0.953125, 0.140625, 1.0, 1.0], [0.984375, 0.140625, 1.0, 1.0], [0.984375, 0.140625, 1.0, 1.0], [0.015625, 0.171875, 1.0, 1.0], [0.015625, 0.171875, 1.0, 1.0], [0.046875, 0.171875, 1.0, 1.0], [0.046875, 0.171875, 1.0, 1.0], [0.078125, 0.171875, 1.0, 1.0], [0.078125, 0.171875, 1.0, 1.0], [0.109375, 0.171875, 1.0, 1.0], [0.109375, 0.171875, 1.0, 1.0], [0.140625, 0.171875, 1.0, 1.0], [0.140625, 0.171875, 1.0, 1.0], [0.171875, 0.171875, 1.0, 1.0], [0.171875, 0.171875, 1.0, 1.0], [0.203125, 0.171875, 1.0, 1.0], [0.203125, 0.171875, 1.0, 1.0], [0.234375, 0.171875, 1.0, 1.0], [0.234375, 0.171875, 1.0, 1.0], [0.265625, 0.171875, 1.0, 1.0], [0.265625, 0.171875, 1.0, 1.0], [0.296875, 0.171875, 1.0, 1.0], [0.296875, 0.171875, 1.0, 1.0], [0.328125, 0.171875, 1.0, 1.0], [0.328125, 0.171875, 1.0, 1.0], [0.359375, 0.171875, 1.0, 1.0], [0.359375, 0.171875, 1.0, 1.0], [0.390625, 0.171875, 1.0, 1.0], [0.390625, 0.171875, 1.0, 1.0], [0.421875, 0.171875, 1.0, 1.0], [0.421875, 0.171875, 1.0, 1.0], [0.453125, 0.171875, 1.0, 1.0], [0.453125, 0.171875, 1.0, 1.0], [0.484375, 0.171875, 1.0, 1.0], [0.484375, 0.171875, 1.0, 1.0], [0.515625, 0.171875, 1.0, 1.0], [0.515625, 0.171875, 1.0, 1.0], [0.546875, 0.171875, 1.0, 1.0], [0.546875, 0.171875, 1.0, 1.0], [0.578125, 0.171875, 1.0, 1.0], [0.578125, 0.171875, 1.0, 1.0], [0.609375, 0.171875, 1.0, 1.0], [0.609375, 0.171875, 1.0, 1.0], [0.640625, 0.171875, 1.0, 1.0], [0.640625, 0.171875, 1.0, 1.0], [0.671875, 0.171875, 1.0, 1.0], [0.671875, 0.171875, 1.0, 1.0], [0.703125, 0.171875, 1.0, 1.0], [0.703125, 0.171875, 1.0, 1.0], [0.734375, 0.171875, 1.0, 1.0], [0.734375, 0.171875, 1.0, 1.0], [0.765625, 0.171875, 1.0, 1.0], [0.765625, 0.171875, 1.0, 1.0], [0.796875, 0.171875, 1.0, 1.0], [0.796875, 0.171875, 1.0, 1.0], [0.828125, 0.171875, 1.0, 1.0], [0.828125, 0.171875, 1.0, 1.0], [0.859375, 0.171875, 1.0, 1.0], [0.859375, 0.171875, 1.0, 1.0], [0.890625, 0.171875, 1.0, 1.0], [0.890625, 0.171875, 1.0, 1.0], [0.921875, 0.171875, 1.0, 1.0], [0.921875, 0.171875, 1.0, 1.0], [0.953125, 0.171875, 1.0, 1.0], [0.953125, 0.171875, 1.0, 1.0], [0.984375, 0.171875, 1.0, 1.0], [0.984375, 0.171875, 1.0, 1.0], [0.015625, 0.203125, 1.0, 1.0], [0.015625, 0.203125, 1.0, 1.0], [0.046875, 0.203125, 1.0, 1.0], [0.046875, 0.203125, 1.0, 1.0], [0.078125, 0.203125, 1.0, 1.0], [0.078125, 0.203125, 1.0, 1.0], [0.109375, 0.203125, 1.0, 1.0], [0.109375, 0.203125, 1.0, 1.0], [0.140625, 0.203125, 1.0, 1.0], [0.140625, 0.203125, 1.0, 1.0], [0.171875, 0.203125, 1.0, 1.0], [0.171875, 0.203125, 1.0, 1.0], [0.203125, 0.203125, 1.0, 1.0], [0.203125, 0.203125, 1.0, 1.0], [0.234375, 0.203125, 1.0, 1.0], [0.234375, 0.203125, 1.0, 1.0], [0.265625, 0.203125, 1.0, 1.0], [0.265625, 0.203125, 1.0, 1.0], [0.296875, 0.203125, 1.0, 1.0], [0.296875, 0.203125, 1.0, 1.0], [0.328125, 0.203125, 1.0, 1.0], [0.328125, 0.203125, 1.0, 1.0], [0.359375, 0.203125, 1.0, 1.0], [0.359375, 0.203125, 1.0, 1.0], [0.390625, 0.203125, 1.0, 1.0], [0.390625, 0.203125, 1.0, 1.0], [0.421875, 0.203125, 1.0, 1.0], [0.421875, 0.203125, 1.0, 1.0], [0.453125, 0.203125, 1.0, 1.0], [0.453125, 0.203125, 1.0, 1.0], [0.484375, 0.203125, 1.0, 1.0], [0.484375, 0.203125, 1.0, 1.0], [0.515625, 0.203125, 1.0, 1.0], [0.515625, 0.203125, 1.0, 1.0], [0.546875, 0.203125, 1.0, 1.0], [0.546875, 0.203125, 1.0, 1.0], [0.578125, 0.203125, 1.0, 1.0], [0.578125, 0.203125, 1.0, 1.0], [0.609375, 0.203125, 1.0, 1.0], [0.609375, 0.203125, 1.0, 1.0], [0.640625, 0.203125, 1.0, 1.0], [0.640625, 0.203125, 1.0, 1.0], [0.671875, 0.203125, 1.0, 1.0], [0.671875, 0.203125, 1.0, 1.0], [0.703125, 0.203125, 1.0, 1.0], [0.703125, 0.203125, 1.0, 1.0], [0.734375, 0.203125, 1.0, 1.0], [0.734375, 0.203125, 1.0, 1.0], [0.765625, 0.203125, 1.0, 1.0], [0.765625, 0.203125, 1.0, 1.0], [0.796875, 0.203125, 1.0, 1.0], [0.796875, 0.203125, 1.0, 1.0], [0.828125, 0.203125, 1.0, 1.0], [0.828125, 0.203125, 1.0, 1.0], [0.859375, 0.203125, 1.0, 1.0], [0.859375, 0.203125, 1.0, 1.0], [0.890625, 0.203125, 1.0, 1.0], [0.890625, 0.203125, 1.0, 1.0], [0.921875, 0.203125, 1.0, 1.0], [0.921875, 0.203125, 1.0, 1.0], [0.953125, 0.203125, 1.0, 1.0], [0.953125, 0.203125, 1.0, 1.0], [0.984375, 0.203125, 1.0, 1.0], [0.984375, 0.203125, 1.0, 1.0], [0.015625, 0.234375, 1.0, 1.0], [0.015625, 0.234375, 1.0, 1.0], [0.046875, 0.234375, 1.0, 1.0], [0.046875, 0.234375, 1.0, 1.0], [0.078125, 0.234375, 1.0, 1.0], [0.078125, 0.234375, 1.0, 1.0], [0.109375, 0.234375, 1.0, 1.0], [0.109375, 0.234375, 1.0, 1.0], [0.140625, 0.234375, 1.0, 1.0], [0.140625, 0.234375, 1.0, 1.0], [0.171875, 0.234375, 1.0, 1.0], [0.171875, 0.234375, 1.0, 1.0], [0.203125, 0.234375, 1.0, 1.0], [0.203125, 0.234375, 1.0, 1.0], [0.234375, 0.234375, 1.0, 1.0], [0.234375, 0.234375, 1.0, 1.0], [0.265625, 0.234375, 1.0, 1.0], [0.265625, 0.234375, 1.0, 1.0], [0.296875, 0.234375, 1.0, 1.0], [0.296875, 0.234375, 1.0, 1.0], [0.328125, 0.234375, 1.0, 1.0], [0.328125, 0.234375, 1.0, 1.0], [0.359375, 0.234375, 1.0, 1.0], [0.359375, 0.234375, 1.0, 1.0], [0.390625, 0.234375, 1.0, 1.0], [0.390625, 0.234375, 1.0, 1.0], [0.421875, 0.234375, 1.0, 1.0], [0.421875, 0.234375, 1.0, 1.0], [0.453125, 0.234375, 1.0, 1.0], [0.453125, 0.234375, 1.0, 1.0], [0.484375, 0.234375, 1.0, 1.0], [0.484375, 0.234375, 1.0, 1.0], [0.515625, 0.234375, 1.0, 1.0], [0.515625, 0.234375, 1.0, 1.0], [0.546875, 0.234375, 1.0, 1.0], [0.546875, 0.234375, 1.0, 1.0], [0.578125, 0.234375, 1.0, 1.0], [0.578125, 0.234375, 1.0, 1.0], [0.609375, 0.234375, 1.0, 1.0], [0.609375, 0.234375, 1.0, 1.0], [0.640625, 0.234375, 1.0, 1.0], [0.640625, 0.234375, 1.0, 1.0], [0.671875, 0.234375, 1.0, 1.0], [0.671875, 0.234375, 1.0, 1.0], [0.703125, 0.234375, 1.0, 1.0], [0.703125, 0.234375, 1.0, 1.0], [0.734375, 0.234375, 1.0, 1.0], [0.734375, 0.234375, 1.0, 1.0], [0.765625, 0.234375, 1.0, 1.0], [0.765625, 0.234375, 1.0, 1.0], [0.796875, 0.234375, 1.0, 1.0], [0.796875, 0.234375, 1.0, 1.0], [0.828125, 0.234375, 1.0, 1.0], [0.828125, 0.234375, 1.0, 1.0], [0.859375, 0.234375, 1.0, 1.0], [0.859375, 0.234375, 1.0, 1.0], [0.890625, 0.234375, 1.0, 1.0], [0.890625, 0.234375, 1.0, 1.0], [0.921875, 0.234375, 1.0, 1.0], [0.921875, 0.234375, 1.0, 1.0], [0.953125, 0.234375, 1.0, 1.0], [0.953125, 0.234375, 1.0, 1.0], [0.984375, 0.234375, 1.0, 1.0], [0.984375, 0.234375, 1.0, 1.0], [0.015625, 0.265625, 1.0, 1.0], [0.015625, 0.265625, 1.0, 1.0], [0.046875, 0.265625, 1.0, 1.0], [0.046875, 0.265625, 1.0, 1.0], [0.078125, 0.265625, 1.0, 1.0], [0.078125, 0.265625, 1.0, 1.0], [0.109375, 0.265625, 1.0, 1.0], [0.109375, 0.265625, 1.0, 1.0], [0.140625, 0.265625, 1.0, 1.0], [0.140625, 0.265625, 1.0, 1.0], [0.171875, 0.265625, 1.0, 1.0], [0.171875, 0.265625, 1.0, 1.0], [0.203125, 0.265625, 1.0, 1.0], [0.203125, 0.265625, 1.0, 1.0], [0.234375, 0.265625, 1.0, 1.0], [0.234375, 0.265625, 1.0, 1.0], [0.265625, 0.265625, 1.0, 1.0], [0.265625, 0.265625, 1.0, 1.0], [0.296875, 0.265625, 1.0, 1.0], [0.296875, 0.265625, 1.0, 1.0], [0.328125, 0.265625, 1.0, 1.0], [0.328125, 0.265625, 1.0, 1.0], [0.359375, 0.265625, 1.0, 1.0], [0.359375, 0.265625, 1.0, 1.0], [0.390625, 0.265625, 1.0, 1.0], [0.390625, 0.265625, 1.0, 1.0], [0.421875, 0.265625, 1.0, 1.0], [0.421875, 0.265625, 1.0, 1.0], [0.453125, 0.265625, 1.0, 1.0], [0.453125, 0.265625, 1.0, 1.0], [0.484375, 0.265625, 1.0, 1.0], [0.484375, 0.265625, 1.0, 1.0], [0.515625, 0.265625, 1.0, 1.0], [0.515625, 0.265625, 1.0, 1.0], [0.546875, 0.265625, 1.0, 1.0], [0.546875, 0.265625, 1.0, 1.0], [0.578125, 0.265625, 1.0, 1.0], [0.578125, 0.265625, 1.0, 1.0], [0.609375, 0.265625, 1.0, 1.0], [0.609375, 0.265625, 1.0, 1.0], [0.640625, 0.265625, 1.0, 1.0], [0.640625, 0.265625, 1.0, 1.0], [0.671875, 0.265625, 1.0, 1.0], [0.671875, 0.265625, 1.0, 1.0], [0.703125, 0.265625, 1.0, 1.0], [0.703125, 0.265625, 1.0, 1.0], [0.734375, 0.265625, 1.0, 1.0], [0.734375, 0.265625, 1.0, 1.0], [0.765625, 0.265625, 1.0, 1.0], [0.765625, 0.265625, 1.0, 1.0], [0.796875, 0.265625, 1.0, 1.0], [0.796875, 0.265625, 1.0, 1.0], [0.828125, 0.265625, 1.0, 1.0], [0.828125, 0.265625, 1.0, 1.0], [0.859375, 0.265625, 1.0, 1.0], [0.859375, 0.265625, 1.0, 1.0], [0.890625, 0.265625, 1.0, 1.0], [0.890625, 0.265625, 1.0, 1.0], [0.921875, 0.265625, 1.0, 1.0], [0.921875, 0.265625, 1.0, 1.0], [0.953125, 0.265625, 1.0, 1.0], [0.953125, 0.265625, 1.0, 1.0], [0.984375, 0.265625, 1.0, 1.0], [0.984375, 0.265625, 1.0, 1.0], [0.015625, 0.296875, 1.0, 1.0], [0.015625, 0.296875, 1.0, 1.0], [0.046875, 0.296875, 1.0, 1.0], [0.046875, 0.296875, 1.0, 1.0], [0.078125, 0.296875, 1.0, 1.0], [0.078125, 0.296875, 1.0, 1.0], [0.109375, 0.296875, 1.0, 1.0], [0.109375, 0.296875, 1.0, 1.0], [0.140625, 0.296875, 1.0, 1.0], [0.140625, 0.296875, 1.0, 1.0], [0.171875, 0.296875, 1.0, 1.0], [0.171875, 0.296875, 1.0, 1.0], [0.203125, 0.296875, 1.0, 1.0], [0.203125, 0.296875, 1.0, 1.0], [0.234375, 0.296875, 1.0, 1.0], [0.234375, 0.296875, 1.0, 1.0], [0.265625, 0.296875, 1.0, 1.0], [0.265625, 0.296875, 1.0, 1.0], [0.296875, 0.296875, 1.0, 1.0], [0.296875, 0.296875, 1.0, 1.0], [0.328125, 0.296875, 1.0, 1.0], [0.328125, 0.296875, 1.0, 1.0], [0.359375, 0.296875, 1.0, 1.0], [0.359375, 0.296875, 1.0, 1.0], [0.390625, 0.296875, 1.0, 1.0], [0.390625, 0.296875, 1.0, 1.0], [0.421875, 0.296875, 1.0, 1.0], [0.421875, 0.296875, 1.0, 1.0], [0.453125, 0.296875, 1.0, 1.0], [0.453125, 0.296875, 1.0, 1.0], [0.484375, 0.296875, 1.0, 1.0], [0.484375, 0.296875, 1.0, 1.0], [0.515625, 0.296875, 1.0, 1.0], [0.515625, 0.296875, 1.0, 1.0], [0.546875, 0.296875, 1.0, 1.0], [0.546875, 0.296875, 1.0, 1.0], [0.578125, 0.296875, 1.0, 1.0], [0.578125, 0.296875, 1.0, 1.0], [0.609375, 0.296875, 1.0, 1.0], [0.609375, 0.296875, 1.0, 1.0], [0.640625, 0.296875, 1.0, 1.0], [0.640625, 0.296875, 1.0, 1.0], [0.671875, 0.296875, 1.0, 1.0], [0.671875, 0.296875, 1.0, 1.0], [0.703125, 0.296875, 1.0, 1.0], [0.703125, 0.296875, 1.0, 1.0], [0.734375, 0.296875, 1.0, 1.0], [0.734375, 0.296875, 1.0, 1.0], [0.765625, 0.296875, 1.0, 1.0], [0.765625, 0.296875, 1.0, 1.0], [0.796875, 0.296875, 1.0, 1.0], [0.796875, 0.296875, 1.0, 1.0], [0.828125, 0.296875, 1.0, 1.0], [0.828125, 0.296875, 1.0, 1.0], [0.859375, 0.296875, 1.0, 1.0], [0.859375, 0.296875, 1.0, 1.0], [0.890625, 0.296875, 1.0, 1.0], [0.890625, 0.296875, 1.0, 1.0], [0.921875, 0.296875, 1.0, 1.0], [0.921875, 0.296875, 1.0, 1.0], [0.953125, 0.296875, 1.0, 1.0], [0.953125, 0.296875, 1.0, 1.0], [0.984375, 0.296875, 1.0, 1.0], [0.984375, 0.296875, 1.0, 1.0], [0.015625, 0.328125, 1.0, 1.0], [0.015625, 0.328125, 1.0, 1.0], [0.046875, 0.328125, 1.0, 1.0], [0.046875, 0.328125, 1.0, 1.0], [0.078125, 0.328125, 1.0, 1.0], [0.078125, 0.328125, 1.0, 1.0], [0.109375, 0.328125, 1.0, 1.0], [0.109375, 0.328125, 1.0, 1.0], [0.140625, 0.328125, 1.0, 1.0], [0.140625, 0.328125, 1.0, 1.0], [0.171875, 0.328125, 1.0, 1.0], [0.171875, 0.328125, 1.0, 1.0], [0.203125, 0.328125, 1.0, 1.0], [0.203125, 0.328125, 1.0, 1.0], [0.234375, 0.328125, 1.0, 1.0], [0.234375, 0.328125, 1.0, 1.0], [0.265625, 0.328125, 1.0, 1.0], [0.265625, 0.328125, 1.0, 1.0], [0.296875, 0.328125, 1.0, 1.0], [0.296875, 0.328125, 1.0, 1.0], [0.328125, 0.328125, 1.0, 1.0], [0.328125, 0.328125, 1.0, 1.0], [0.359375, 0.328125, 1.0, 1.0], [0.359375, 0.328125, 1.0, 1.0], [0.390625, 0.328125, 1.0, 1.0], [0.390625, 0.328125, 1.0, 1.0], [0.421875, 0.328125, 1.0, 1.0], [0.421875, 0.328125, 1.0, 1.0], [0.453125, 0.328125, 1.0, 1.0], [0.453125, 0.328125, 1.0, 1.0], [0.484375, 0.328125, 1.0, 1.0], [0.484375, 0.328125, 1.0, 1.0], [0.515625, 0.328125, 1.0, 1.0], [0.515625, 0.328125, 1.0, 1.0], [0.546875, 0.328125, 1.0, 1.0], [0.546875, 0.328125, 1.0, 1.0], [0.578125, 0.328125, 1.0, 1.0], [0.578125, 0.328125, 1.0, 1.0], [0.609375, 0.328125, 1.0, 1.0], [0.609375, 0.328125, 1.0, 1.0], [0.640625, 0.328125, 1.0, 1.0], [0.640625, 0.328125, 1.0, 1.0], [0.671875, 0.328125, 1.0, 1.0], [0.671875, 0.328125, 1.0, 1.0], [0.703125, 0.328125, 1.0, 1.0], [0.703125, 0.328125, 1.0, 1.0], [0.734375, 0.328125, 1.0, 1.0], [0.734375, 0.328125, 1.0, 1.0], [0.765625, 0.328125, 1.0, 1.0], [0.765625, 0.328125, 1.0, 1.0], [0.796875, 0.328125, 1.0, 1.0], [0.796875, 0.328125, 1.0, 1.0], [0.828125, 0.328125, 1.0, 1.0], [0.828125, 0.328125, 1.0, 1.0], [0.859375, 0.328125, 1.0, 1.0], [0.859375, 0.328125, 1.0, 1.0], [0.890625, 0.328125, 1.0, 1.0], [0.890625, 0.328125, 1.0, 1.0], [0.921875, 0.328125, 1.0, 1.0], [0.921875, 0.328125, 1.0, 1.0], [0.953125, 0.328125, 1.0, 1.0], [0.953125, 0.328125, 1.0, 1.0], [0.984375, 0.328125, 1.0, 1.0], [0.984375, 0.328125, 1.0, 1.0], [0.015625, 0.359375, 1.0, 1.0], [0.015625, 0.359375, 1.0, 1.0], [0.046875, 0.359375, 1.0, 1.0], [0.046875, 0.359375, 1.0, 1.0], [0.078125, 0.359375, 1.0, 1.0], [0.078125, 0.359375, 1.0, 1.0], [0.109375, 0.359375, 1.0, 1.0], [0.109375, 0.359375, 1.0, 1.0], [0.140625, 0.359375, 1.0, 1.0], [0.140625, 0.359375, 1.0, 1.0], [0.171875, 0.359375, 1.0, 1.0], [0.171875, 0.359375, 1.0, 1.0], [0.203125, 0.359375, 1.0, 1.0], [0.203125, 0.359375, 1.0, 1.0], [0.234375, 0.359375, 1.0, 1.0], [0.234375, 0.359375, 1.0, 1.0], [0.265625, 0.359375, 1.0, 1.0], [0.265625, 0.359375, 1.0, 1.0], [0.296875, 0.359375, 1.0, 1.0], [0.296875, 0.359375, 1.0, 1.0], [0.328125, 0.359375, 1.0, 1.0], [0.328125, 0.359375, 1.0, 1.0], [0.359375, 0.359375, 1.0, 1.0], [0.359375, 0.359375, 1.0, 1.0], [0.390625, 0.359375, 1.0, 1.0], [0.390625, 0.359375, 1.0, 1.0], [0.421875, 0.359375, 1.0, 1.0], [0.421875, 0.359375, 1.0, 1.0], [0.453125, 0.359375, 1.0, 1.0], [0.453125, 0.359375, 1.0, 1.0], [0.484375, 0.359375, 1.0, 1.0], [0.484375, 0.359375, 1.0, 1.0], [0.515625, 0.359375, 1.0, 1.0], [0.515625, 0.359375, 1.0, 1.0], [0.546875, 0.359375, 1.0, 1.0], [0.546875, 0.359375, 1.0, 1.0], [0.578125, 0.359375, 1.0, 1.0], [0.578125, 0.359375, 1.0, 1.0], [0.609375, 0.359375, 1.0, 1.0], [0.609375, 0.359375, 1.0, 1.0], [0.640625, 0.359375, 1.0, 1.0], [0.640625, 0.359375, 1.0, 1.0], [0.671875, 0.359375, 1.0, 1.0], [0.671875, 0.359375, 1.0, 1.0], [0.703125, 0.359375, 1.0, 1.0], [0.703125, 0.359375, 1.0, 1.0], [0.734375, 0.359375, 1.0, 1.0], [0.734375, 0.359375, 1.0, 1.0], [0.765625, 0.359375, 1.0, 1.0], [0.765625, 0.359375, 1.0, 1.0], [0.796875, 0.359375, 1.0, 1.0], [0.796875, 0.359375, 1.0, 1.0], [0.828125, 0.359375, 1.0, 1.0], [0.828125, 0.359375, 1.0, 1.0], [0.859375, 0.359375, 1.0, 1.0], [0.859375, 0.359375, 1.0, 1.0], [0.890625, 0.359375, 1.0, 1.0], [0.890625, 0.359375, 1.0, 1.0], [0.921875, 0.359375, 1.0, 1.0], [0.921875, 0.359375, 1.0, 1.0], [0.953125, 0.359375, 1.0, 1.0], [0.953125, 0.359375, 1.0, 1.0], [0.984375, 0.359375, 1.0, 1.0], [0.984375, 0.359375, 1.0, 1.0], [0.015625, 0.390625, 1.0, 1.0], [0.015625, 0.390625, 1.0, 1.0], [0.046875, 0.390625, 1.0, 1.0], [0.046875, 0.390625, 1.0, 1.0], [0.078125, 0.390625, 1.0, 1.0], [0.078125, 0.390625, 1.0, 1.0], [0.109375, 0.390625, 1.0, 1.0], [0.109375, 0.390625, 1.0, 1.0], [0.140625, 0.390625, 1.0, 1.0], [0.140625, 0.390625, 1.0, 1.0], [0.171875, 0.390625, 1.0, 1.0], [0.171875, 0.390625, 1.0, 1.0], [0.203125, 0.390625, 1.0, 1.0], [0.203125, 0.390625, 1.0, 1.0], [0.234375, 0.390625, 1.0, 1.0], [0.234375, 0.390625, 1.0, 1.0], [0.265625, 0.390625, 1.0, 1.0], [0.265625, 0.390625, 1.0, 1.0], [0.296875, 0.390625, 1.0, 1.0], [0.296875, 0.390625, 1.0, 1.0], [0.328125, 0.390625, 1.0, 1.0], [0.328125, 0.390625, 1.0, 1.0], [0.359375, 0.390625, 1.0, 1.0], [0.359375, 0.390625, 1.0, 1.0], [0.390625, 0.390625, 1.0, 1.0], [0.390625, 0.390625, 1.0, 1.0], [0.421875, 0.390625, 1.0, 1.0], [0.421875, 0.390625, 1.0, 1.0], [0.453125, 0.390625, 1.0, 1.0], [0.453125, 0.390625, 1.0, 1.0], [0.484375, 0.390625, 1.0, 1.0], [0.484375, 0.390625, 1.0, 1.0], [0.515625, 0.390625, 1.0, 1.0], [0.515625, 0.390625, 1.0, 1.0], [0.546875, 0.390625, 1.0, 1.0], [0.546875, 0.390625, 1.0, 1.0], [0.578125, 0.390625, 1.0, 1.0], [0.578125, 0.390625, 1.0, 1.0], [0.609375, 0.390625, 1.0, 1.0], [0.609375, 0.390625, 1.0, 1.0], [0.640625, 0.390625, 1.0, 1.0], [0.640625, 0.390625, 1.0, 1.0], [0.671875, 0.390625, 1.0, 1.0], [0.671875, 0.390625, 1.0, 1.0], [0.703125, 0.390625, 1.0, 1.0], [0.703125, 0.390625, 1.0, 1.0], [0.734375, 0.390625, 1.0, 1.0], [0.734375, 0.390625, 1.0, 1.0], [0.765625, 0.390625, 1.0, 1.0], [0.765625, 0.390625, 1.0, 1.0], [0.796875, 0.390625, 1.0, 1.0], [0.796875, 0.390625, 1.0, 1.0], [0.828125, 0.390625, 1.0, 1.0], [0.828125, 0.390625, 1.0, 1.0], [0.859375, 0.390625, 1.0, 1.0], [0.859375, 0.390625, 1.0, 1.0], [0.890625, 0.390625, 1.0, 1.0], [0.890625, 0.390625, 1.0, 1.0], [0.921875, 0.390625, 1.0, 1.0], [0.921875, 0.390625, 1.0, 1.0], [0.953125, 0.390625, 1.0, 1.0], [0.953125, 0.390625, 1.0, 1.0], [0.984375, 0.390625, 1.0, 1.0], [0.984375, 0.390625, 1.0, 1.0], [0.015625, 0.421875, 1.0, 1.0], [0.015625, 0.421875, 1.0, 1.0], [0.046875, 0.421875, 1.0, 1.0], [0.046875, 0.421875, 1.0, 1.0], [0.078125, 0.421875, 1.0, 1.0], [0.078125, 0.421875, 1.0, 1.0], [0.109375, 0.421875, 1.0, 1.0], [0.109375, 0.421875, 1.0, 1.0], [0.140625, 0.421875, 1.0, 1.0], [0.140625, 0.421875, 1.0, 1.0], [0.171875, 0.421875, 1.0, 1.0], [0.171875, 0.421875, 1.0, 1.0], [0.203125, 0.421875, 1.0, 1.0], [0.203125, 0.421875, 1.0, 1.0], [0.234375, 0.421875, 1.0, 1.0], [0.234375, 0.421875, 1.0, 1.0], [0.265625, 0.421875, 1.0, 1.0], [0.265625, 0.421875, 1.0, 1.0], [0.296875, 0.421875, 1.0, 1.0], [0.296875, 0.421875, 1.0, 1.0], [0.328125, 0.421875, 1.0, 1.0], [0.328125, 0.421875, 1.0, 1.0], [0.359375, 0.421875, 1.0, 1.0], [0.359375, 0.421875, 1.0, 1.0], [0.390625, 0.421875, 1.0, 1.0], [0.390625, 0.421875, 1.0, 1.0], [0.421875, 0.421875, 1.0, 1.0], [0.421875, 0.421875, 1.0, 1.0], [0.453125, 0.421875, 1.0, 1.0], [0.453125, 0.421875, 1.0, 1.0], [0.484375, 0.421875, 1.0, 1.0], [0.484375, 0.421875, 1.0, 1.0], [0.515625, 0.421875, 1.0, 1.0], [0.515625, 0.421875, 1.0, 1.0], [0.546875, 0.421875, 1.0, 1.0], [0.546875, 0.421875, 1.0, 1.0], [0.578125, 0.421875, 1.0, 1.0], [0.578125, 0.421875, 1.0, 1.0], [0.609375, 0.421875, 1.0, 1.0], [0.609375, 0.421875, 1.0, 1.0], [0.640625, 0.421875, 1.0, 1.0], [0.640625, 0.421875, 1.0, 1.0], [0.671875, 0.421875, 1.0, 1.0], [0.671875, 0.421875, 1.0, 1.0], [0.703125, 0.421875, 1.0, 1.0], [0.703125, 0.421875, 1.0, 1.0], [0.734375, 0.421875, 1.0, 1.0], [0.734375, 0.421875, 1.0, 1.0], [0.765625, 0.421875, 1.0, 1.0], [0.765625, 0.421875, 1.0, 1.0], [0.796875, 0.421875, 1.0, 1.0], [0.796875, 0.421875, 1.0, 1.0], [0.828125, 0.421875, 1.0, 1.0], [0.828125, 0.421875, 1.0, 1.0], [0.859375, 0.421875, 1.0, 1.0], [0.859375, 0.421875, 1.0, 1.0], [0.890625, 0.421875, 1.0, 1.0], [0.890625, 0.421875, 1.0, 1.0], [0.921875, 0.421875, 1.0, 1.0], [0.921875, 0.421875, 1.0, 1.0], [0.953125, 0.421875, 1.0, 1.0], [0.953125, 0.421875, 1.0, 1.0], [0.984375, 0.421875, 1.0, 1.0], [0.984375, 0.421875, 1.0, 1.0], [0.015625, 0.453125, 1.0, 1.0], [0.015625, 0.453125, 1.0, 1.0], [0.046875, 0.453125, 1.0, 1.0], [0.046875, 0.453125, 1.0, 1.0], [0.078125, 0.453125, 1.0, 1.0], [0.078125, 0.453125, 1.0, 1.0], [0.109375, 0.453125, 1.0, 1.0], [0.109375, 0.453125, 1.0, 1.0], [0.140625, 0.453125, 1.0, 1.0], [0.140625, 0.453125, 1.0, 1.0], [0.171875, 0.453125, 1.0, 1.0], [0.171875, 0.453125, 1.0, 1.0], [0.203125, 0.453125, 1.0, 1.0], [0.203125, 0.453125, 1.0, 1.0], [0.234375, 0.453125, 1.0, 1.0], [0.234375, 0.453125, 1.0, 1.0], [0.265625, 0.453125, 1.0, 1.0], [0.265625, 0.453125, 1.0, 1.0], [0.296875, 0.453125, 1.0, 1.0], [0.296875, 0.453125, 1.0, 1.0], [0.328125, 0.453125, 1.0, 1.0], [0.328125, 0.453125, 1.0, 1.0], [0.359375, 0.453125, 1.0, 1.0], [0.359375, 0.453125, 1.0, 1.0], [0.390625, 0.453125, 1.0, 1.0], [0.390625, 0.453125, 1.0, 1.0], [0.421875, 0.453125, 1.0, 1.0], [0.421875, 0.453125, 1.0, 1.0], [0.453125, 0.453125, 1.0, 1.0], [0.453125, 0.453125, 1.0, 1.0], [0.484375, 0.453125, 1.0, 1.0], [0.484375, 0.453125, 1.0, 1.0], [0.515625, 0.453125, 1.0, 1.0], [0.515625, 0.453125, 1.0, 1.0], [0.546875, 0.453125, 1.0, 1.0], [0.546875, 0.453125, 1.0, 1.0], [0.578125, 0.453125, 1.0, 1.0], [0.578125, 0.453125, 1.0, 1.0], [0.609375, 0.453125, 1.0, 1.0], [0.609375, 0.453125, 1.0, 1.0], [0.640625, 0.453125, 1.0, 1.0], [0.640625, 0.453125, 1.0, 1.0], [0.671875, 0.453125, 1.0, 1.0], [0.671875, 0.453125, 1.0, 1.0], [0.703125, 0.453125, 1.0, 1.0], [0.703125, 0.453125, 1.0, 1.0], [0.734375, 0.453125, 1.0, 1.0], [0.734375, 0.453125, 1.0, 1.0], [0.765625, 0.453125, 1.0, 1.0], [0.765625, 0.453125, 1.0, 1.0], [0.796875, 0.453125, 1.0, 1.0], [0.796875, 0.453125, 1.0, 1.0], [0.828125, 0.453125, 1.0, 1.0], [0.828125, 0.453125, 1.0, 1.0], [0.859375, 0.453125, 1.0, 1.0], [0.859375, 0.453125, 1.0, 1.0], [0.890625, 0.453125, 1.0, 1.0], [0.890625, 0.453125, 1.0, 1.0], [0.921875, 0.453125, 1.0, 1.0], [0.921875, 0.453125, 1.0, 1.0], [0.953125, 0.453125, 1.0, 1.0], [0.953125, 0.453125, 1.0, 1.0], [0.984375, 0.453125, 1.0, 1.0], [0.984375, 0.453125, 1.0, 1.0], [0.015625, 0.484375, 1.0, 1.0], [0.015625, 0.484375, 1.0, 1.0], [0.046875, 0.484375, 1.0, 1.0], [0.046875, 0.484375, 1.0, 1.0], [0.078125, 0.484375, 1.0, 1.0], [0.078125, 0.484375, 1.0, 1.0], [0.109375, 0.484375, 1.0, 1.0], [0.109375, 0.484375, 1.0, 1.0], [0.140625, 0.484375, 1.0, 1.0], [0.140625, 0.484375, 1.0, 1.0], [0.171875, 0.484375, 1.0, 1.0], [0.171875, 0.484375, 1.0, 1.0], [0.203125, 0.484375, 1.0, 1.0], [0.203125, 0.484375, 1.0, 1.0], [0.234375, 0.484375, 1.0, 1.0], [0.234375, 0.484375, 1.0, 1.0], [0.265625, 0.484375, 1.0, 1.0], [0.265625, 0.484375, 1.0, 1.0], [0.296875, 0.484375, 1.0, 1.0], [0.296875, 0.484375, 1.0, 1.0], [0.328125, 0.484375, 1.0, 1.0], [0.328125, 0.484375, 1.0, 1.0], [0.359375, 0.484375, 1.0, 1.0], [0.359375, 0.484375, 1.0, 1.0], [0.390625, 0.484375, 1.0, 1.0], [0.390625, 0.484375, 1.0, 1.0], [0.421875, 0.484375, 1.0, 1.0], [0.421875, 0.484375, 1.0, 1.0], [0.453125, 0.484375, 1.0, 1.0], [0.453125, 0.484375, 1.0, 1.0], [0.484375, 0.484375, 1.0, 1.0], [0.484375, 0.484375, 1.0, 1.0], [0.515625, 0.484375, 1.0, 1.0], [0.515625, 0.484375, 1.0, 1.0], [0.546875, 0.484375, 1.0, 1.0], [0.546875, 0.484375, 1.0, 1.0], [0.578125, 0.484375, 1.0, 1.0], [0.578125, 0.484375, 1.0, 1.0], [0.609375, 0.484375, 1.0, 1.0], [0.609375, 0.484375, 1.0, 1.0], [0.640625, 0.484375, 1.0, 1.0], [0.640625, 0.484375, 1.0, 1.0], [0.671875, 0.484375, 1.0, 1.0], [0.671875, 0.484375, 1.0, 1.0], [0.703125, 0.484375, 1.0, 1.0], [0.703125, 0.484375, 1.0, 1.0], [0.734375, 0.484375, 1.0, 1.0], [0.734375, 0.484375, 1.0, 1.0], [0.765625, 0.484375, 1.0, 1.0], [0.765625, 0.484375, 1.0, 1.0], [0.796875, 0.484375, 1.0, 1.0], [0.796875, 0.484375, 1.0, 1.0], [0.828125, 0.484375, 1.0, 1.0], [0.828125, 0.484375, 1.0, 1.0], [0.859375, 0.484375, 1.0, 1.0], [0.859375, 0.484375, 1.0, 1.0], [0.890625, 0.484375, 1.0, 1.0], [0.890625, 0.484375, 1.0, 1.0], [0.921875, 0.484375, 1.0, 1.0], [0.921875, 0.484375, 1.0, 1.0], [0.953125, 0.484375, 1.0, 1.0], [0.953125, 0.484375, 1.0, 1.0], [0.984375, 0.484375, 1.0, 1.0], [0.984375, 0.484375, 1.0, 1.0], [0.015625, 0.515625, 1.0, 1.0], [0.015625, 0.515625, 1.0, 1.0], [0.046875, 0.515625, 1.0, 1.0], [0.046875, 0.515625, 1.0, 1.0], [0.078125, 0.515625, 1.0, 1.0], [0.078125, 0.515625, 1.0, 1.0], [0.109375, 0.515625, 1.0, 1.0], [0.109375, 0.515625, 1.0, 1.0], [0.140625, 0.515625, 1.0, 1.0], [0.140625, 0.515625, 1.0, 1.0], [0.171875, 0.515625, 1.0, 1.0], [0.171875, 0.515625, 1.0, 1.0], [0.203125, 0.515625, 1.0, 1.0], [0.203125, 0.515625, 1.0, 1.0], [0.234375, 0.515625, 1.0, 1.0], [0.234375, 0.515625, 1.0, 1.0], [0.265625, 0.515625, 1.0, 1.0], [0.265625, 0.515625, 1.0, 1.0], [0.296875, 0.515625, 1.0, 1.0], [0.296875, 0.515625, 1.0, 1.0], [0.328125, 0.515625, 1.0, 1.0], [0.328125, 0.515625, 1.0, 1.0], [0.359375, 0.515625, 1.0, 1.0], [0.359375, 0.515625, 1.0, 1.0], [0.390625, 0.515625, 1.0, 1.0], [0.390625, 0.515625, 1.0, 1.0], [0.421875, 0.515625, 1.0, 1.0], [0.421875, 0.515625, 1.0, 1.0], [0.453125, 0.515625, 1.0, 1.0], [0.453125, 0.515625, 1.0, 1.0], [0.484375, 0.515625, 1.0, 1.0], [0.484375, 0.515625, 1.0, 1.0], [0.515625, 0.515625, 1.0, 1.0], [0.515625, 0.515625, 1.0, 1.0], [0.546875, 0.515625, 1.0, 1.0], [0.546875, 0.515625, 1.0, 1.0], [0.578125, 0.515625, 1.0, 1.0], [0.578125, 0.515625, 1.0, 1.0], [0.609375, 0.515625, 1.0, 1.0], [0.609375, 0.515625, 1.0, 1.0], [0.640625, 0.515625, 1.0, 1.0], [0.640625, 0.515625, 1.0, 1.0], [0.671875, 0.515625, 1.0, 1.0], [0.671875, 0.515625, 1.0, 1.0], [0.703125, 0.515625, 1.0, 1.0], [0.703125, 0.515625, 1.0, 1.0], [0.734375, 0.515625, 1.0, 1.0], [0.734375, 0.515625, 1.0, 1.0], [0.765625, 0.515625, 1.0, 1.0], [0.765625, 0.515625, 1.0, 1.0], [0.796875, 0.515625, 1.0, 1.0], [0.796875, 0.515625, 1.0, 1.0], [0.828125, 0.515625, 1.0, 1.0], [0.828125, 0.515625, 1.0, 1.0], [0.859375, 0.515625, 1.0, 1.0], [0.859375, 0.515625, 1.0, 1.0], [0.890625, 0.515625, 1.0, 1.0], [0.890625, 0.515625, 1.0, 1.0], [0.921875, 0.515625, 1.0, 1.0], [0.921875, 0.515625, 1.0, 1.0], [0.953125, 0.515625, 1.0, 1.0], [0.953125, 0.515625, 1.0, 1.0], [0.984375, 0.515625, 1.0, 1.0], [0.984375, 0.515625, 1.0, 1.0], [0.015625, 0.546875, 1.0, 1.0], [0.015625, 0.546875, 1.0, 1.0], [0.046875, 0.546875, 1.0, 1.0], [0.046875, 0.546875, 1.0, 1.0], [0.078125, 0.546875, 1.0, 1.0], [0.078125, 0.546875, 1.0, 1.0], [0.109375, 0.546875, 1.0, 1.0], [0.109375, 0.546875, 1.0, 1.0], [0.140625, 0.546875, 1.0, 1.0], [0.140625, 0.546875, 1.0, 1.0], [0.171875, 0.546875, 1.0, 1.0], [0.171875, 0.546875, 1.0, 1.0], [0.203125, 0.546875, 1.0, 1.0], [0.203125, 0.546875, 1.0, 1.0], [0.234375, 0.546875, 1.0, 1.0], [0.234375, 0.546875, 1.0, 1.0], [0.265625, 0.546875, 1.0, 1.0], [0.265625, 0.546875, 1.0, 1.0], [0.296875, 0.546875, 1.0, 1.0], [0.296875, 0.546875, 1.0, 1.0], [0.328125, 0.546875, 1.0, 1.0], [0.328125, 0.546875, 1.0, 1.0], [0.359375, 0.546875, 1.0, 1.0], [0.359375, 0.546875, 1.0, 1.0], [0.390625, 0.546875, 1.0, 1.0], [0.390625, 0.546875, 1.0, 1.0], [0.421875, 0.546875, 1.0, 1.0], [0.421875, 0.546875, 1.0, 1.0], [0.453125, 0.546875, 1.0, 1.0], [0.453125, 0.546875, 1.0, 1.0], [0.484375, 0.546875, 1.0, 1.0], [0.484375, 0.546875, 1.0, 1.0], [0.515625, 0.546875, 1.0, 1.0], [0.515625, 0.546875, 1.0, 1.0], [0.546875, 0.546875, 1.0, 1.0], [0.546875, 0.546875, 1.0, 1.0], [0.578125, 0.546875, 1.0, 1.0], [0.578125, 0.546875, 1.0, 1.0], [0.609375, 0.546875, 1.0, 1.0], [0.609375, 0.546875, 1.0, 1.0], [0.640625, 0.546875, 1.0, 1.0], [0.640625, 0.546875, 1.0, 1.0], [0.671875, 0.546875, 1.0, 1.0], [0.671875, 0.546875, 1.0, 1.0], [0.703125, 0.546875, 1.0, 1.0], [0.703125, 0.546875, 1.0, 1.0], [0.734375, 0.546875, 1.0, 1.0], [0.734375, 0.546875, 1.0, 1.0], [0.765625, 0.546875, 1.0, 1.0], [0.765625, 0.546875, 1.0, 1.0], [0.796875, 0.546875, 1.0, 1.0], [0.796875, 0.546875, 1.0, 1.0], [0.828125, 0.546875, 1.0, 1.0], [0.828125, 0.546875, 1.0, 1.0], [0.859375, 0.546875, 1.0, 1.0], [0.859375, 0.546875, 1.0, 1.0], [0.890625, 0.546875, 1.0, 1.0], [0.890625, 0.546875, 1.0, 1.0], [0.921875, 0.546875, 1.0, 1.0], [0.921875, 0.546875, 1.0, 1.0], [0.953125, 0.546875, 1.0, 1.0], [0.953125, 0.546875, 1.0, 1.0], [0.984375, 0.546875, 1.0, 1.0], [0.984375, 0.546875, 1.0, 1.0], [0.015625, 0.578125, 1.0, 1.0], [0.015625, 0.578125, 1.0, 1.0], [0.046875, 0.578125, 1.0, 1.0], [0.046875, 0.578125, 1.0, 1.0], [0.078125, 0.578125, 1.0, 1.0], [0.078125, 0.578125, 1.0, 1.0], [0.109375, 0.578125, 1.0, 1.0], [0.109375, 0.578125, 1.0, 1.0], [0.140625, 0.578125, 1.0, 1.0], [0.140625, 0.578125, 1.0, 1.0], [0.171875, 0.578125, 1.0, 1.0], [0.171875, 0.578125, 1.0, 1.0], [0.203125, 0.578125, 1.0, 1.0], [0.203125, 0.578125, 1.0, 1.0], [0.234375, 0.578125, 1.0, 1.0], [0.234375, 0.578125, 1.0, 1.0], [0.265625, 0.578125, 1.0, 1.0], [0.265625, 0.578125, 1.0, 1.0], [0.296875, 0.578125, 1.0, 1.0], [0.296875, 0.578125, 1.0, 1.0], [0.328125, 0.578125, 1.0, 1.0], [0.328125, 0.578125, 1.0, 1.0], [0.359375, 0.578125, 1.0, 1.0], [0.359375, 0.578125, 1.0, 1.0], [0.390625, 0.578125, 1.0, 1.0], [0.390625, 0.578125, 1.0, 1.0], [0.421875, 0.578125, 1.0, 1.0], [0.421875, 0.578125, 1.0, 1.0], [0.453125, 0.578125, 1.0, 1.0], [0.453125, 0.578125, 1.0, 1.0], [0.484375, 0.578125, 1.0, 1.0], [0.484375, 0.578125, 1.0, 1.0], [0.515625, 0.578125, 1.0, 1.0], [0.515625, 0.578125, 1.0, 1.0], [0.546875, 0.578125, 1.0, 1.0], [0.546875, 0.578125, 1.0, 1.0], [0.578125, 0.578125, 1.0, 1.0], [0.578125, 0.578125, 1.0, 1.0], [0.609375, 0.578125, 1.0, 1.0], [0.609375, 0.578125, 1.0, 1.0], [0.640625, 0.578125, 1.0, 1.0], [0.640625, 0.578125, 1.0, 1.0], [0.671875, 0.578125, 1.0, 1.0], [0.671875, 0.578125, 1.0, 1.0], [0.703125, 0.578125, 1.0, 1.0], [0.703125, 0.578125, 1.0, 1.0], [0.734375, 0.578125, 1.0, 1.0], [0.734375, 0.578125, 1.0, 1.0], [0.765625, 0.578125, 1.0, 1.0], [0.765625, 0.578125, 1.0, 1.0], [0.796875, 0.578125, 1.0, 1.0], [0.796875, 0.578125, 1.0, 1.0], [0.828125, 0.578125, 1.0, 1.0], [0.828125, 0.578125, 1.0, 1.0], [0.859375, 0.578125, 1.0, 1.0], [0.859375, 0.578125, 1.0, 1.0], [0.890625, 0.578125, 1.0, 1.0], [0.890625, 0.578125, 1.0, 1.0], [0.921875, 0.578125, 1.0, 1.0], [0.921875, 0.578125, 1.0, 1.0], [0.953125, 0.578125, 1.0, 1.0], [0.953125, 0.578125, 1.0, 1.0], [0.984375, 0.578125, 1.0, 1.0], [0.984375, 0.578125, 1.0, 1.0], [0.015625, 0.609375, 1.0, 1.0], [0.015625, 0.609375, 1.0, 1.0], [0.046875, 0.609375, 1.0, 1.0], [0.046875, 0.609375, 1.0, 1.0], [0.078125, 0.609375, 1.0, 1.0], [0.078125, 0.609375, 1.0, 1.0], [0.109375, 0.609375, 1.0, 1.0], [0.109375, 0.609375, 1.0, 1.0], [0.140625, 0.609375, 1.0, 1.0], [0.140625, 0.609375, 1.0, 1.0], [0.171875, 0.609375, 1.0, 1.0], [0.171875, 0.609375, 1.0, 1.0], [0.203125, 0.609375, 1.0, 1.0], [0.203125, 0.609375, 1.0, 1.0], [0.234375, 0.609375, 1.0, 1.0], [0.234375, 0.609375, 1.0, 1.0], [0.265625, 0.609375, 1.0, 1.0], [0.265625, 0.609375, 1.0, 1.0], [0.296875, 0.609375, 1.0, 1.0], [0.296875, 0.609375, 1.0, 1.0], [0.328125, 0.609375, 1.0, 1.0], [0.328125, 0.609375, 1.0, 1.0], [0.359375, 0.609375, 1.0, 1.0], [0.359375, 0.609375, 1.0, 1.0], [0.390625, 0.609375, 1.0, 1.0], [0.390625, 0.609375, 1.0, 1.0], [0.421875, 0.609375, 1.0, 1.0], [0.421875, 0.609375, 1.0, 1.0], [0.453125, 0.609375, 1.0, 1.0], [0.453125, 0.609375, 1.0, 1.0], [0.484375, 0.609375, 1.0, 1.0], [0.484375, 0.609375, 1.0, 1.0], [0.515625, 0.609375, 1.0, 1.0], [0.515625, 0.609375, 1.0, 1.0], [0.546875, 0.609375, 1.0, 1.0], [0.546875, 0.609375, 1.0, 1.0], [0.578125, 0.609375, 1.0, 1.0], [0.578125, 0.609375, 1.0, 1.0], [0.609375, 0.609375, 1.0, 1.0], [0.609375, 0.609375, 1.0, 1.0], [0.640625, 0.609375, 1.0, 1.0], [0.640625, 0.609375, 1.0, 1.0], [0.671875, 0.609375, 1.0, 1.0], [0.671875, 0.609375, 1.0, 1.0], [0.703125, 0.609375, 1.0, 1.0], [0.703125, 0.609375, 1.0, 1.0], [0.734375, 0.609375, 1.0, 1.0], [0.734375, 0.609375, 1.0, 1.0], [0.765625, 0.609375, 1.0, 1.0], [0.765625, 0.609375, 1.0, 1.0], [0.796875, 0.609375, 1.0, 1.0], [0.796875, 0.609375, 1.0, 1.0], [0.828125, 0.609375, 1.0, 1.0], [0.828125, 0.609375, 1.0, 1.0], [0.859375, 0.609375, 1.0, 1.0], [0.859375, 0.609375, 1.0, 1.0], [0.890625, 0.609375, 1.0, 1.0], [0.890625, 0.609375, 1.0, 1.0], [0.921875, 0.609375, 1.0, 1.0], [0.921875, 0.609375, 1.0, 1.0], [0.953125, 0.609375, 1.0, 1.0], [0.953125, 0.609375, 1.0, 1.0], [0.984375, 0.609375, 1.0, 1.0], [0.984375, 0.609375, 1.0, 1.0], [0.015625, 0.640625, 1.0, 1.0], [0.015625, 0.640625, 1.0, 1.0], [0.046875, 0.640625, 1.0, 1.0], [0.046875, 0.640625, 1.0, 1.0], [0.078125, 0.640625, 1.0, 1.0], [0.078125, 0.640625, 1.0, 1.0], [0.109375, 0.640625, 1.0, 1.0], [0.109375, 0.640625, 1.0, 1.0], [0.140625, 0.640625, 1.0, 1.0], [0.140625, 0.640625, 1.0, 1.0], [0.171875, 0.640625, 1.0, 1.0], [0.171875, 0.640625, 1.0, 1.0], [0.203125, 0.640625, 1.0, 1.0], [0.203125, 0.640625, 1.0, 1.0], [0.234375, 0.640625, 1.0, 1.0], [0.234375, 0.640625, 1.0, 1.0], [0.265625, 0.640625, 1.0, 1.0], [0.265625, 0.640625, 1.0, 1.0], [0.296875, 0.640625, 1.0, 1.0], [0.296875, 0.640625, 1.0, 1.0], [0.328125, 0.640625, 1.0, 1.0], [0.328125, 0.640625, 1.0, 1.0], [0.359375, 0.640625, 1.0, 1.0], [0.359375, 0.640625, 1.0, 1.0], [0.390625, 0.640625, 1.0, 1.0], [0.390625, 0.640625, 1.0, 1.0], [0.421875, 0.640625, 1.0, 1.0], [0.421875, 0.640625, 1.0, 1.0], [0.453125, 0.640625, 1.0, 1.0], [0.453125, 0.640625, 1.0, 1.0], [0.484375, 0.640625, 1.0, 1.0], [0.484375, 0.640625, 1.0, 1.0], [0.515625, 0.640625, 1.0, 1.0], [0.515625, 0.640625, 1.0, 1.0], [0.546875, 0.640625, 1.0, 1.0], [0.546875, 0.640625, 1.0, 1.0], [0.578125, 0.640625, 1.0, 1.0], [0.578125, 0.640625, 1.0, 1.0], [0.609375, 0.640625, 1.0, 1.0], [0.609375, 0.640625, 1.0, 1.0], [0.640625, 0.640625, 1.0, 1.0], [0.640625, 0.640625, 1.0, 1.0], [0.671875, 0.640625, 1.0, 1.0], [0.671875, 0.640625, 1.0, 1.0], [0.703125, 0.640625, 1.0, 1.0], [0.703125, 0.640625, 1.0, 1.0], [0.734375, 0.640625, 1.0, 1.0], [0.734375, 0.640625, 1.0, 1.0], [0.765625, 0.640625, 1.0, 1.0], [0.765625, 0.640625, 1.0, 1.0], [0.796875, 0.640625, 1.0, 1.0], [0.796875, 0.640625, 1.0, 1.0], [0.828125, 0.640625, 1.0, 1.0], [0.828125, 0.640625, 1.0, 1.0], [0.859375, 0.640625, 1.0, 1.0], [0.859375, 0.640625, 1.0, 1.0], [0.890625, 0.640625, 1.0, 1.0], [0.890625, 0.640625, 1.0, 1.0], [0.921875, 0.640625, 1.0, 1.0], [0.921875, 0.640625, 1.0, 1.0], [0.953125, 0.640625, 1.0, 1.0], [0.953125, 0.640625, 1.0, 1.0], [0.984375, 0.640625, 1.0, 1.0], [0.984375, 0.640625, 1.0, 1.0], [0.015625, 0.671875, 1.0, 1.0], [0.015625, 0.671875, 1.0, 1.0], [0.046875, 0.671875, 1.0, 1.0], [0.046875, 0.671875, 1.0, 1.0], [0.078125, 0.671875, 1.0, 1.0], [0.078125, 0.671875, 1.0, 1.0], [0.109375, 0.671875, 1.0, 1.0], [0.109375, 0.671875, 1.0, 1.0], [0.140625, 0.671875, 1.0, 1.0], [0.140625, 0.671875, 1.0, 1.0], [0.171875, 0.671875, 1.0, 1.0], [0.171875, 0.671875, 1.0, 1.0], [0.203125, 0.671875, 1.0, 1.0], [0.203125, 0.671875, 1.0, 1.0], [0.234375, 0.671875, 1.0, 1.0], [0.234375, 0.671875, 1.0, 1.0], [0.265625, 0.671875, 1.0, 1.0], [0.265625, 0.671875, 1.0, 1.0], [0.296875, 0.671875, 1.0, 1.0], [0.296875, 0.671875, 1.0, 1.0], [0.328125, 0.671875, 1.0, 1.0], [0.328125, 0.671875, 1.0, 1.0], [0.359375, 0.671875, 1.0, 1.0], [0.359375, 0.671875, 1.0, 1.0], [0.390625, 0.671875, 1.0, 1.0], [0.390625, 0.671875, 1.0, 1.0], [0.421875, 0.671875, 1.0, 1.0], [0.421875, 0.671875, 1.0, 1.0], [0.453125, 0.671875, 1.0, 1.0], [0.453125, 0.671875, 1.0, 1.0], [0.484375, 0.671875, 1.0, 1.0], [0.484375, 0.671875, 1.0, 1.0], [0.515625, 0.671875, 1.0, 1.0], [0.515625, 0.671875, 1.0, 1.0], [0.546875, 0.671875, 1.0, 1.0], [0.546875, 0.671875, 1.0, 1.0], [0.578125, 0.671875, 1.0, 1.0], [0.578125, 0.671875, 1.0, 1.0], [0.609375, 0.671875, 1.0, 1.0], [0.609375, 0.671875, 1.0, 1.0], [0.640625, 0.671875, 1.0, 1.0], [0.640625, 0.671875, 1.0, 1.0], [0.671875, 0.671875, 1.0, 1.0], [0.671875, 0.671875, 1.0, 1.0], [0.703125, 0.671875, 1.0, 1.0], [0.703125, 0.671875, 1.0, 1.0], [0.734375, 0.671875, 1.0, 1.0], [0.734375, 0.671875, 1.0, 1.0], [0.765625, 0.671875, 1.0, 1.0], [0.765625, 0.671875, 1.0, 1.0], [0.796875, 0.671875, 1.0, 1.0], [0.796875, 0.671875, 1.0, 1.0], [0.828125, 0.671875, 1.0, 1.0], [0.828125, 0.671875, 1.0, 1.0], [0.859375, 0.671875, 1.0, 1.0], [0.859375, 0.671875, 1.0, 1.0], [0.890625, 0.671875, 1.0, 1.0], [0.890625, 0.671875, 1.0, 1.0], [0.921875, 0.671875, 1.0, 1.0], [0.921875, 0.671875, 1.0, 1.0], [0.953125, 0.671875, 1.0, 1.0], [0.953125, 0.671875, 1.0, 1.0], [0.984375, 0.671875, 1.0, 1.0], [0.984375, 0.671875, 1.0, 1.0], [0.015625, 0.703125, 1.0, 1.0], [0.015625, 0.703125, 1.0, 1.0], [0.046875, 0.703125, 1.0, 1.0], [0.046875, 0.703125, 1.0, 1.0], [0.078125, 0.703125, 1.0, 1.0], [0.078125, 0.703125, 1.0, 1.0], [0.109375, 0.703125, 1.0, 1.0], [0.109375, 0.703125, 1.0, 1.0], [0.140625, 0.703125, 1.0, 1.0], [0.140625, 0.703125, 1.0, 1.0], [0.171875, 0.703125, 1.0, 1.0], [0.171875, 0.703125, 1.0, 1.0], [0.203125, 0.703125, 1.0, 1.0], [0.203125, 0.703125, 1.0, 1.0], [0.234375, 0.703125, 1.0, 1.0], [0.234375, 0.703125, 1.0, 1.0], [0.265625, 0.703125, 1.0, 1.0], [0.265625, 0.703125, 1.0, 1.0], [0.296875, 0.703125, 1.0, 1.0], [0.296875, 0.703125, 1.0, 1.0], [0.328125, 0.703125, 1.0, 1.0], [0.328125, 0.703125, 1.0, 1.0], [0.359375, 0.703125, 1.0, 1.0], [0.359375, 0.703125, 1.0, 1.0], [0.390625, 0.703125, 1.0, 1.0], [0.390625, 0.703125, 1.0, 1.0], [0.421875, 0.703125, 1.0, 1.0], [0.421875, 0.703125, 1.0, 1.0], [0.453125, 0.703125, 1.0, 1.0], [0.453125, 0.703125, 1.0, 1.0], [0.484375, 0.703125, 1.0, 1.0], [0.484375, 0.703125, 1.0, 1.0], [0.515625, 0.703125, 1.0, 1.0], [0.515625, 0.703125, 1.0, 1.0], [0.546875, 0.703125, 1.0, 1.0], [0.546875, 0.703125, 1.0, 1.0], [0.578125, 0.703125, 1.0, 1.0], [0.578125, 0.703125, 1.0, 1.0], [0.609375, 0.703125, 1.0, 1.0], [0.609375, 0.703125, 1.0, 1.0], [0.640625, 0.703125, 1.0, 1.0], [0.640625, 0.703125, 1.0, 1.0], [0.671875, 0.703125, 1.0, 1.0], [0.671875, 0.703125, 1.0, 1.0], [0.703125, 0.703125, 1.0, 1.0], [0.703125, 0.703125, 1.0, 1.0], [0.734375, 0.703125, 1.0, 1.0], [0.734375, 0.703125, 1.0, 1.0], [0.765625, 0.703125, 1.0, 1.0], [0.765625, 0.703125, 1.0, 1.0], [0.796875, 0.703125, 1.0, 1.0], [0.796875, 0.703125, 1.0, 1.0], [0.828125, 0.703125, 1.0, 1.0], [0.828125, 0.703125, 1.0, 1.0], [0.859375, 0.703125, 1.0, 1.0], [0.859375, 0.703125, 1.0, 1.0], [0.890625, 0.703125, 1.0, 1.0], [0.890625, 0.703125, 1.0, 1.0], [0.921875, 0.703125, 1.0, 1.0], [0.921875, 0.703125, 1.0, 1.0], [0.953125, 0.703125, 1.0, 1.0], [0.953125, 0.703125, 1.0, 1.0], [0.984375, 0.703125, 1.0, 1.0], [0.984375, 0.703125, 1.0, 1.0], [0.015625, 0.734375, 1.0, 1.0], [0.015625, 0.734375, 1.0, 1.0], [0.046875, 0.734375, 1.0, 1.0], [0.046875, 0.734375, 1.0, 1.0], [0.078125, 0.734375, 1.0, 1.0], [0.078125, 0.734375, 1.0, 1.0], [0.109375, 0.734375, 1.0, 1.0], [0.109375, 0.734375, 1.0, 1.0], [0.140625, 0.734375, 1.0, 1.0], [0.140625, 0.734375, 1.0, 1.0], [0.171875, 0.734375, 1.0, 1.0], [0.171875, 0.734375, 1.0, 1.0], [0.203125, 0.734375, 1.0, 1.0], [0.203125, 0.734375, 1.0, 1.0], [0.234375, 0.734375, 1.0, 1.0], [0.234375, 0.734375, 1.0, 1.0], [0.265625, 0.734375, 1.0, 1.0], [0.265625, 0.734375, 1.0, 1.0], [0.296875, 0.734375, 1.0, 1.0], [0.296875, 0.734375, 1.0, 1.0], [0.328125, 0.734375, 1.0, 1.0], [0.328125, 0.734375, 1.0, 1.0], [0.359375, 0.734375, 1.0, 1.0], [0.359375, 0.734375, 1.0, 1.0], [0.390625, 0.734375, 1.0, 1.0], [0.390625, 0.734375, 1.0, 1.0], [0.421875, 0.734375, 1.0, 1.0], [0.421875, 0.734375, 1.0, 1.0], [0.453125, 0.734375, 1.0, 1.0], [0.453125, 0.734375, 1.0, 1.0], [0.484375, 0.734375, 1.0, 1.0], [0.484375, 0.734375, 1.0, 1.0], [0.515625, 0.734375, 1.0, 1.0], [0.515625, 0.734375, 1.0, 1.0], [0.546875, 0.734375, 1.0, 1.0], [0.546875, 0.734375, 1.0, 1.0], [0.578125, 0.734375, 1.0, 1.0], [0.578125, 0.734375, 1.0, 1.0], [0.609375, 0.734375, 1.0, 1.0], [0.609375, 0.734375, 1.0, 1.0], [0.640625, 0.734375, 1.0, 1.0], [0.640625, 0.734375, 1.0, 1.0], [0.671875, 0.734375, 1.0, 1.0], [0.671875, 0.734375, 1.0, 1.0], [0.703125, 0.734375, 1.0, 1.0], [0.703125, 0.734375, 1.0, 1.0], [0.734375, 0.734375, 1.0, 1.0], [0.734375, 0.734375, 1.0, 1.0], [0.765625, 0.734375, 1.0, 1.0], [0.765625, 0.734375, 1.0, 1.0], [0.796875, 0.734375, 1.0, 1.0], [0.796875, 0.734375, 1.0, 1.0], [0.828125, 0.734375, 1.0, 1.0], [0.828125, 0.734375, 1.0, 1.0], [0.859375, 0.734375, 1.0, 1.0], [0.859375, 0.734375, 1.0, 1.0], [0.890625, 0.734375, 1.0, 1.0], [0.890625, 0.734375, 1.0, 1.0], [0.921875, 0.734375, 1.0, 1.0], [0.921875, 0.734375, 1.0, 1.0], [0.953125, 0.734375, 1.0, 1.0], [0.953125, 0.734375, 1.0, 1.0], [0.984375, 0.734375, 1.0, 1.0], [0.984375, 0.734375, 1.0, 1.0], [0.015625, 0.765625, 1.0, 1.0], [0.015625, 0.765625, 1.0, 1.0], [0.046875, 0.765625, 1.0, 1.0], [0.046875, 0.765625, 1.0, 1.0], [0.078125, 0.765625, 1.0, 1.0], [0.078125, 0.765625, 1.0, 1.0], [0.109375, 0.765625, 1.0, 1.0], [0.109375, 0.765625, 1.0, 1.0], [0.140625, 0.765625, 1.0, 1.0], [0.140625, 0.765625, 1.0, 1.0], [0.171875, 0.765625, 1.0, 1.0], [0.171875, 0.765625, 1.0, 1.0], [0.203125, 0.765625, 1.0, 1.0], [0.203125, 0.765625, 1.0, 1.0], [0.234375, 0.765625, 1.0, 1.0], [0.234375, 0.765625, 1.0, 1.0], [0.265625, 0.765625, 1.0, 1.0], [0.265625, 0.765625, 1.0, 1.0], [0.296875, 0.765625, 1.0, 1.0], [0.296875, 0.765625, 1.0, 1.0], [0.328125, 0.765625, 1.0, 1.0], [0.328125, 0.765625, 1.0, 1.0], [0.359375, 0.765625, 1.0, 1.0], [0.359375, 0.765625, 1.0, 1.0], [0.390625, 0.765625, 1.0, 1.0], [0.390625, 0.765625, 1.0, 1.0], [0.421875, 0.765625, 1.0, 1.0], [0.421875, 0.765625, 1.0, 1.0], [0.453125, 0.765625, 1.0, 1.0], [0.453125, 0.765625, 1.0, 1.0], [0.484375, 0.765625, 1.0, 1.0], [0.484375, 0.765625, 1.0, 1.0], [0.515625, 0.765625, 1.0, 1.0], [0.515625, 0.765625, 1.0, 1.0], [0.546875, 0.765625, 1.0, 1.0], [0.546875, 0.765625, 1.0, 1.0], [0.578125, 0.765625, 1.0, 1.0], [0.578125, 0.765625, 1.0, 1.0], [0.609375, 0.765625, 1.0, 1.0], [0.609375, 0.765625, 1.0, 1.0], [0.640625, 0.765625, 1.0, 1.0], [0.640625, 0.765625, 1.0, 1.0], [0.671875, 0.765625, 1.0, 1.0], [0.671875, 0.765625, 1.0, 1.0], [0.703125, 0.765625, 1.0, 1.0], [0.703125, 0.765625, 1.0, 1.0], [0.734375, 0.765625, 1.0, 1.0], [0.734375, 0.765625, 1.0, 1.0], [0.765625, 0.765625, 1.0, 1.0], [0.765625, 0.765625, 1.0, 1.0], [0.796875, 0.765625, 1.0, 1.0], [0.796875, 0.765625, 1.0, 1.0], [0.828125, 0.765625, 1.0, 1.0], [0.828125, 0.765625, 1.0, 1.0], [0.859375, 0.765625, 1.0, 1.0], [0.859375, 0.765625, 1.0, 1.0], [0.890625, 0.765625, 1.0, 1.0], [0.890625, 0.765625, 1.0, 1.0], [0.921875, 0.765625, 1.0, 1.0], [0.921875, 0.765625, 1.0, 1.0], [0.953125, 0.765625, 1.0, 1.0], [0.953125, 0.765625, 1.0, 1.0], [0.984375, 0.765625, 1.0, 1.0], [0.984375, 0.765625, 1.0, 1.0], [0.015625, 0.796875, 1.0, 1.0], [0.015625, 0.796875, 1.0, 1.0], [0.046875, 0.796875, 1.0, 1.0], [0.046875, 0.796875, 1.0, 1.0], [0.078125, 0.796875, 1.0, 1.0], [0.078125, 0.796875, 1.0, 1.0], [0.109375, 0.796875, 1.0, 1.0], [0.109375, 0.796875, 1.0, 1.0], [0.140625, 0.796875, 1.0, 1.0], [0.140625, 0.796875, 1.0, 1.0], [0.171875, 0.796875, 1.0, 1.0], [0.171875, 0.796875, 1.0, 1.0], [0.203125, 0.796875, 1.0, 1.0], [0.203125, 0.796875, 1.0, 1.0], [0.234375, 0.796875, 1.0, 1.0], [0.234375, 0.796875, 1.0, 1.0], [0.265625, 0.796875, 1.0, 1.0], [0.265625, 0.796875, 1.0, 1.0], [0.296875, 0.796875, 1.0, 1.0], [0.296875, 0.796875, 1.0, 1.0], [0.328125, 0.796875, 1.0, 1.0], [0.328125, 0.796875, 1.0, 1.0], [0.359375, 0.796875, 1.0, 1.0], [0.359375, 0.796875, 1.0, 1.0], [0.390625, 0.796875, 1.0, 1.0], [0.390625, 0.796875, 1.0, 1.0], [0.421875, 0.796875, 1.0, 1.0], [0.421875, 0.796875, 1.0, 1.0], [0.453125, 0.796875, 1.0, 1.0], [0.453125, 0.796875, 1.0, 1.0], [0.484375, 0.796875, 1.0, 1.0], [0.484375, 0.796875, 1.0, 1.0], [0.515625, 0.796875, 1.0, 1.0], [0.515625, 0.796875, 1.0, 1.0], [0.546875, 0.796875, 1.0, 1.0], [0.546875, 0.796875, 1.0, 1.0], [0.578125, 0.796875, 1.0, 1.0], [0.578125, 0.796875, 1.0, 1.0], [0.609375, 0.796875, 1.0, 1.0], [0.609375, 0.796875, 1.0, 1.0], [0.640625, 0.796875, 1.0, 1.0], [0.640625, 0.796875, 1.0, 1.0], [0.671875, 0.796875, 1.0, 1.0], [0.671875, 0.796875, 1.0, 1.0], [0.703125, 0.796875, 1.0, 1.0], [0.703125, 0.796875, 1.0, 1.0], [0.734375, 0.796875, 1.0, 1.0], [0.734375, 0.796875, 1.0, 1.0], [0.765625, 0.796875, 1.0, 1.0], [0.765625, 0.796875, 1.0, 1.0], [0.796875, 0.796875, 1.0, 1.0], [0.796875, 0.796875, 1.0, 1.0], [0.828125, 0.796875, 1.0, 1.0], [0.828125, 0.796875, 1.0, 1.0], [0.859375, 0.796875, 1.0, 1.0], [0.859375, 0.796875, 1.0, 1.0], [0.890625, 0.796875, 1.0, 1.0], [0.890625, 0.796875, 1.0, 1.0], [0.921875, 0.796875, 1.0, 1.0], [0.921875, 0.796875, 1.0, 1.0], [0.953125, 0.796875, 1.0, 1.0], [0.953125, 0.796875, 1.0, 1.0], [0.984375, 0.796875, 1.0, 1.0], [0.984375, 0.796875, 1.0, 1.0], [0.015625, 0.828125, 1.0, 1.0], [0.015625, 0.828125, 1.0, 1.0], [0.046875, 0.828125, 1.0, 1.0], [0.046875, 0.828125, 1.0, 1.0], [0.078125, 0.828125, 1.0, 1.0], [0.078125, 0.828125, 1.0, 1.0], [0.109375, 0.828125, 1.0, 1.0], [0.109375, 0.828125, 1.0, 1.0], [0.140625, 0.828125, 1.0, 1.0], [0.140625, 0.828125, 1.0, 1.0], [0.171875, 0.828125, 1.0, 1.0], [0.171875, 0.828125, 1.0, 1.0], [0.203125, 0.828125, 1.0, 1.0], [0.203125, 0.828125, 1.0, 1.0], [0.234375, 0.828125, 1.0, 1.0], [0.234375, 0.828125, 1.0, 1.0], [0.265625, 0.828125, 1.0, 1.0], [0.265625, 0.828125, 1.0, 1.0], [0.296875, 0.828125, 1.0, 1.0], [0.296875, 0.828125, 1.0, 1.0], [0.328125, 0.828125, 1.0, 1.0], [0.328125, 0.828125, 1.0, 1.0], [0.359375, 0.828125, 1.0, 1.0], [0.359375, 0.828125, 1.0, 1.0], [0.390625, 0.828125, 1.0, 1.0], [0.390625, 0.828125, 1.0, 1.0], [0.421875, 0.828125, 1.0, 1.0], [0.421875, 0.828125, 1.0, 1.0], [0.453125, 0.828125, 1.0, 1.0], [0.453125, 0.828125, 1.0, 1.0], [0.484375, 0.828125, 1.0, 1.0], [0.484375, 0.828125, 1.0, 1.0], [0.515625, 0.828125, 1.0, 1.0], [0.515625, 0.828125, 1.0, 1.0], [0.546875, 0.828125, 1.0, 1.0], [0.546875, 0.828125, 1.0, 1.0], [0.578125, 0.828125, 1.0, 1.0], [0.578125, 0.828125, 1.0, 1.0], [0.609375, 0.828125, 1.0, 1.0], [0.609375, 0.828125, 1.0, 1.0], [0.640625, 0.828125, 1.0, 1.0], [0.640625, 0.828125, 1.0, 1.0], [0.671875, 0.828125, 1.0, 1.0], [0.671875, 0.828125, 1.0, 1.0], [0.703125, 0.828125, 1.0, 1.0], [0.703125, 0.828125, 1.0, 1.0], [0.734375, 0.828125, 1.0, 1.0], [0.734375, 0.828125, 1.0, 1.0], [0.765625, 0.828125, 1.0, 1.0], [0.765625, 0.828125, 1.0, 1.0], [0.796875, 0.828125, 1.0, 1.0], [0.796875, 0.828125, 1.0, 1.0], [0.828125, 0.828125, 1.0, 1.0], [0.828125, 0.828125, 1.0, 1.0], [0.859375, 0.828125, 1.0, 1.0], [0.859375, 0.828125, 1.0, 1.0], [0.890625, 0.828125, 1.0, 1.0], [0.890625, 0.828125, 1.0, 1.0], [0.921875, 0.828125, 1.0, 1.0], [0.921875, 0.828125, 1.0, 1.0], [0.953125, 0.828125, 1.0, 1.0], [0.953125, 0.828125, 1.0, 1.0], [0.984375, 0.828125, 1.0, 1.0], [0.984375, 0.828125, 1.0, 1.0], [0.015625, 0.859375, 1.0, 1.0], [0.015625, 0.859375, 1.0, 1.0], [0.046875, 0.859375, 1.0, 1.0], [0.046875, 0.859375, 1.0, 1.0], [0.078125, 0.859375, 1.0, 1.0], [0.078125, 0.859375, 1.0, 1.0], [0.109375, 0.859375, 1.0, 1.0], [0.109375, 0.859375, 1.0, 1.0], [0.140625, 0.859375, 1.0, 1.0], [0.140625, 0.859375, 1.0, 1.0], [0.171875, 0.859375, 1.0, 1.0], [0.171875, 0.859375, 1.0, 1.0], [0.203125, 0.859375, 1.0, 1.0], [0.203125, 0.859375, 1.0, 1.0], [0.234375, 0.859375, 1.0, 1.0], [0.234375, 0.859375, 1.0, 1.0], [0.265625, 0.859375, 1.0, 1.0], [0.265625, 0.859375, 1.0, 1.0], [0.296875, 0.859375, 1.0, 1.0], [0.296875, 0.859375, 1.0, 1.0], [0.328125, 0.859375, 1.0, 1.0], [0.328125, 0.859375, 1.0, 1.0], [0.359375, 0.859375, 1.0, 1.0], [0.359375, 0.859375, 1.0, 1.0], [0.390625, 0.859375, 1.0, 1.0], [0.390625, 0.859375, 1.0, 1.0], [0.421875, 0.859375, 1.0, 1.0], [0.421875, 0.859375, 1.0, 1.0], [0.453125, 0.859375, 1.0, 1.0], [0.453125, 0.859375, 1.0, 1.0], [0.484375, 0.859375, 1.0, 1.0], [0.484375, 0.859375, 1.0, 1.0], [0.515625, 0.859375, 1.0, 1.0], [0.515625, 0.859375, 1.0, 1.0], [0.546875, 0.859375, 1.0, 1.0], [0.546875, 0.859375, 1.0, 1.0], [0.578125, 0.859375, 1.0, 1.0], [0.578125, 0.859375, 1.0, 1.0], [0.609375, 0.859375, 1.0, 1.0], [0.609375, 0.859375, 1.0, 1.0], [0.640625, 0.859375, 1.0, 1.0], [0.640625, 0.859375, 1.0, 1.0], [0.671875, 0.859375, 1.0, 1.0], [0.671875, 0.859375, 1.0, 1.0], [0.703125, 0.859375, 1.0, 1.0], [0.703125, 0.859375, 1.0, 1.0], [0.734375, 0.859375, 1.0, 1.0], [0.734375, 0.859375, 1.0, 1.0], [0.765625, 0.859375, 1.0, 1.0], [0.765625, 0.859375, 1.0, 1.0], [0.796875, 0.859375, 1.0, 1.0], [0.796875, 0.859375, 1.0, 1.0], [0.828125, 0.859375, 1.0, 1.0], [0.828125, 0.859375, 1.0, 1.0], [0.859375, 0.859375, 1.0, 1.0], [0.859375, 0.859375, 1.0, 1.0], [0.890625, 0.859375, 1.0, 1.0], [0.890625, 0.859375, 1.0, 1.0], [0.921875, 0.859375, 1.0, 1.0], [0.921875, 0.859375, 1.0, 1.0], [0.953125, 0.859375, 1.0, 1.0], [0.953125, 0.859375, 1.0, 1.0], [0.984375, 0.859375, 1.0, 1.0], [0.984375, 0.859375, 1.0, 1.0], [0.015625, 0.890625, 1.0, 1.0], [0.015625, 0.890625, 1.0, 1.0], [0.046875, 0.890625, 1.0, 1.0], [0.046875, 0.890625, 1.0, 1.0], [0.078125, 0.890625, 1.0, 1.0], [0.078125, 0.890625, 1.0, 1.0], [0.109375, 0.890625, 1.0, 1.0], [0.109375, 0.890625, 1.0, 1.0], [0.140625, 0.890625, 1.0, 1.0], [0.140625, 0.890625, 1.0, 1.0], [0.171875, 0.890625, 1.0, 1.0], [0.171875, 0.890625, 1.0, 1.0], [0.203125, 0.890625, 1.0, 1.0], [0.203125, 0.890625, 1.0, 1.0], [0.234375, 0.890625, 1.0, 1.0], [0.234375, 0.890625, 1.0, 1.0], [0.265625, 0.890625, 1.0, 1.0], [0.265625, 0.890625, 1.0, 1.0], [0.296875, 0.890625, 1.0, 1.0], [0.296875, 0.890625, 1.0, 1.0], [0.328125, 0.890625, 1.0, 1.0], [0.328125, 0.890625, 1.0, 1.0], [0.359375, 0.890625, 1.0, 1.0], [0.359375, 0.890625, 1.0, 1.0], [0.390625, 0.890625, 1.0, 1.0], [0.390625, 0.890625, 1.0, 1.0], [0.421875, 0.890625, 1.0, 1.0], [0.421875, 0.890625, 1.0, 1.0], [0.453125, 0.890625, 1.0, 1.0], [0.453125, 0.890625, 1.0, 1.0], [0.484375, 0.890625, 1.0, 1.0], [0.484375, 0.890625, 1.0, 1.0], [0.515625, 0.890625, 1.0, 1.0], [0.515625, 0.890625, 1.0, 1.0], [0.546875, 0.890625, 1.0, 1.0], [0.546875, 0.890625, 1.0, 1.0], [0.578125, 0.890625, 1.0, 1.0], [0.578125, 0.890625, 1.0, 1.0], [0.609375, 0.890625, 1.0, 1.0], [0.609375, 0.890625, 1.0, 1.0], [0.640625, 0.890625, 1.0, 1.0], [0.640625, 0.890625, 1.0, 1.0], [0.671875, 0.890625, 1.0, 1.0], [0.671875, 0.890625, 1.0, 1.0], [0.703125, 0.890625, 1.0, 1.0], [0.703125, 0.890625, 1.0, 1.0], [0.734375, 0.890625, 1.0, 1.0], [0.734375, 0.890625, 1.0, 1.0], [0.765625, 0.890625, 1.0, 1.0], [0.765625, 0.890625, 1.0, 1.0], [0.796875, 0.890625, 1.0, 1.0], [0.796875, 0.890625, 1.0, 1.0], [0.828125, 0.890625, 1.0, 1.0], [0.828125, 0.890625, 1.0, 1.0], [0.859375, 0.890625, 1.0, 1.0], [0.859375, 0.890625, 1.0, 1.0], [0.890625, 0.890625, 1.0, 1.0], [0.890625, 0.890625, 1.0, 1.0], [0.921875, 0.890625, 1.0, 1.0], [0.921875, 0.890625, 1.0, 1.0], [0.953125, 0.890625, 1.0, 1.0], [0.953125, 0.890625, 1.0, 1.0], [0.984375, 0.890625, 1.0, 1.0], [0.984375, 0.890625, 1.0, 1.0], [0.015625, 0.921875, 1.0, 1.0], [0.015625, 0.921875, 1.0, 1.0], [0.046875, 0.921875, 1.0, 1.0], [0.046875, 0.921875, 1.0, 1.0], [0.078125, 0.921875, 1.0, 1.0], [0.078125, 0.921875, 1.0, 1.0], [0.109375, 0.921875, 1.0, 1.0], [0.109375, 0.921875, 1.0, 1.0], [0.140625, 0.921875, 1.0, 1.0], [0.140625, 0.921875, 1.0, 1.0], [0.171875, 0.921875, 1.0, 1.0], [0.171875, 0.921875, 1.0, 1.0], [0.203125, 0.921875, 1.0, 1.0], [0.203125, 0.921875, 1.0, 1.0], [0.234375, 0.921875, 1.0, 1.0], [0.234375, 0.921875, 1.0, 1.0], [0.265625, 0.921875, 1.0, 1.0], [0.265625, 0.921875, 1.0, 1.0], [0.296875, 0.921875, 1.0, 1.0], [0.296875, 0.921875, 1.0, 1.0], [0.328125, 0.921875, 1.0, 1.0], [0.328125, 0.921875, 1.0, 1.0], [0.359375, 0.921875, 1.0, 1.0], [0.359375, 0.921875, 1.0, 1.0], [0.390625, 0.921875, 1.0, 1.0], [0.390625, 0.921875, 1.0, 1.0], [0.421875, 0.921875, 1.0, 1.0], [0.421875, 0.921875, 1.0, 1.0], [0.453125, 0.921875, 1.0, 1.0], [0.453125, 0.921875, 1.0, 1.0], [0.484375, 0.921875, 1.0, 1.0], [0.484375, 0.921875, 1.0, 1.0], [0.515625, 0.921875, 1.0, 1.0], [0.515625, 0.921875, 1.0, 1.0], [0.546875, 0.921875, 1.0, 1.0], [0.546875, 0.921875, 1.0, 1.0], [0.578125, 0.921875, 1.0, 1.0], [0.578125, 0.921875, 1.0, 1.0], [0.609375, 0.921875, 1.0, 1.0], [0.609375, 0.921875, 1.0, 1.0], [0.640625, 0.921875, 1.0, 1.0], [0.640625, 0.921875, 1.0, 1.0], [0.671875, 0.921875, 1.0, 1.0], [0.671875, 0.921875, 1.0, 1.0], [0.703125, 0.921875, 1.0, 1.0], [0.703125, 0.921875, 1.0, 1.0], [0.734375, 0.921875, 1.0, 1.0], [0.734375, 0.921875, 1.0, 1.0], [0.765625, 0.921875, 1.0, 1.0], [0.765625, 0.921875, 1.0, 1.0], [0.796875, 0.921875, 1.0, 1.0], [0.796875, 0.921875, 1.0, 1.0], [0.828125, 0.921875, 1.0, 1.0], [0.828125, 0.921875, 1.0, 1.0], [0.859375, 0.921875, 1.0, 1.0], [0.859375, 0.921875, 1.0, 1.0], [0.890625, 0.921875, 1.0, 1.0], [0.890625, 0.921875, 1.0, 1.0], [0.921875, 0.921875, 1.0, 1.0], [0.921875, 0.921875, 1.0, 1.0], [0.953125, 0.921875, 1.0, 1.0], [0.953125, 0.921875, 1.0, 1.0], [0.984375, 0.921875, 1.0, 1.0], [0.984375, 0.921875, 1.0, 1.0], [0.015625, 0.953125, 1.0, 1.0], [0.015625, 0.953125, 1.0, 1.0], [0.046875, 0.953125, 1.0, 1.0], [0.046875, 0.953125, 1.0, 1.0], [0.078125, 0.953125, 1.0, 1.0], [0.078125, 0.953125, 1.0, 1.0], [0.109375, 0.953125, 1.0, 1.0], [0.109375, 0.953125, 1.0, 1.0], [0.140625, 0.953125, 1.0, 1.0], [0.140625, 0.953125, 1.0, 1.0], [0.171875, 0.953125, 1.0, 1.0], [0.171875, 0.953125, 1.0, 1.0], [0.203125, 0.953125, 1.0, 1.0], [0.203125, 0.953125, 1.0, 1.0], [0.234375, 0.953125, 1.0, 1.0], [0.234375, 0.953125, 1.0, 1.0], [0.265625, 0.953125, 1.0, 1.0], [0.265625, 0.953125, 1.0, 1.0], [0.296875, 0.953125, 1.0, 1.0], [0.296875, 0.953125, 1.0, 1.0], [0.328125, 0.953125, 1.0, 1.0], [0.328125, 0.953125, 1.0, 1.0], [0.359375, 0.953125, 1.0, 1.0], [0.359375, 0.953125, 1.0, 1.0], [0.390625, 0.953125, 1.0, 1.0], [0.390625, 0.953125, 1.0, 1.0], [0.421875, 0.953125, 1.0, 1.0], [0.421875, 0.953125, 1.0, 1.0], [0.453125, 0.953125, 1.0, 1.0], [0.453125, 0.953125, 1.0, 1.0], [0.484375, 0.953125, 1.0, 1.0], [0.484375, 0.953125, 1.0, 1.0], [0.515625, 0.953125, 1.0, 1.0], [0.515625, 0.953125, 1.0, 1.0], [0.546875, 0.953125, 1.0, 1.0], [0.546875, 0.953125, 1.0, 1.0], [0.578125, 0.953125, 1.0, 1.0], [0.578125, 0.953125, 1.0, 1.0], [0.609375, 0.953125, 1.0, 1.0], [0.609375, 0.953125, 1.0, 1.0], [0.640625, 0.953125, 1.0, 1.0], [0.640625, 0.953125, 1.0, 1.0], [0.671875, 0.953125, 1.0, 1.0], [0.671875, 0.953125, 1.0, 1.0], [0.703125, 0.953125, 1.0, 1.0], [0.703125, 0.953125, 1.0, 1.0], [0.734375, 0.953125, 1.0, 1.0], [0.734375, 0.953125, 1.0, 1.0], [0.765625, 0.953125, 1.0, 1.0], [0.765625, 0.953125, 1.0, 1.0], [0.796875, 0.953125, 1.0, 1.0], [0.796875, 0.953125, 1.0, 1.0], [0.828125, 0.953125, 1.0, 1.0], [0.828125, 0.953125, 1.0, 1.0], [0.859375, 0.953125, 1.0, 1.0], [0.859375, 0.953125, 1.0, 1.0], [0.890625, 0.953125, 1.0, 1.0], [0.890625, 0.953125, 1.0, 1.0], [0.921875, 0.953125, 1.0, 1.0], [0.921875, 0.953125, 1.0, 1.0], [0.953125, 0.953125, 1.0, 1.0], [0.953125, 0.953125, 1.0, 1.0], [0.984375, 0.953125, 1.0, 1.0], [0.984375, 0.953125, 1.0, 1.0], [0.015625, 0.984375, 1.0, 1.0], [0.015625, 0.984375, 1.0, 1.0], [0.046875, 0.984375, 1.0, 1.0], [0.046875, 0.984375, 1.0, 1.0], [0.078125, 0.984375, 1.0, 1.0], [0.078125, 0.984375, 1.0, 1.0], [0.109375, 0.984375, 1.0, 1.0], [0.109375, 0.984375, 1.0, 1.0], [0.140625, 0.984375, 1.0, 1.0], [0.140625, 0.984375, 1.0, 1.0], [0.171875, 0.984375, 1.0, 1.0], [0.171875, 0.984375, 1.0, 1.0], [0.203125, 0.984375, 1.0, 1.0], [0.203125, 0.984375, 1.0, 1.0], [0.234375, 0.984375, 1.0, 1.0], [0.234375, 0.984375, 1.0, 1.0], [0.265625, 0.984375, 1.0, 1.0], [0.265625, 0.984375, 1.0, 1.0], [0.296875, 0.984375, 1.0, 1.0], [0.296875, 0.984375, 1.0, 1.0], [0.328125, 0.984375, 1.0, 1.0], [0.328125, 0.984375, 1.0, 1.0], [0.359375, 0.984375, 1.0, 1.0], [0.359375, 0.984375, 1.0, 1.0], [0.390625, 0.984375, 1.0, 1.0], [0.390625, 0.984375, 1.0, 1.0], [0.421875, 0.984375, 1.0, 1.0], [0.421875, 0.984375, 1.0, 1.0], [0.453125, 0.984375, 1.0, 1.0], [0.453125, 0.984375, 1.0, 1.0], [0.484375, 0.984375, 1.0, 1.0], [0.484375, 0.984375, 1.0, 1.0], [0.515625, 0.984375, 1.0, 1.0], [0.515625, 0.984375, 1.0, 1.0], [0.546875, 0.984375, 1.0, 1.0], [0.546875, 0.984375, 1.0, 1.0], [0.578125, 0.984375, 1.0, 1.0], [0.578125, 0.984375, 1.0, 1.0], [0.609375, 0.984375, 1.0, 1.0], [0.609375, 0.984375, 1.0, 1.0], [0.640625, 0.984375, 1.0, 1.0], [0.640625, 0.984375, 1.0, 1.0], [0.671875, 0.984375, 1.0, 1.0], [0.671875, 0.984375, 1.0, 1.0], [0.703125, 0.984375, 1.0, 1.0], [0.703125, 0.984375, 1.0, 1.0], [0.734375, 0.984375, 1.0, 1.0], [0.734375, 0.984375, 1.0, 1.0], [0.765625, 0.984375, 1.0, 1.0], [0.765625, 0.984375, 1.0, 1.0], [0.796875, 0.984375, 1.0, 1.0], [0.796875, 0.984375, 1.0, 1.0], [0.828125, 0.984375, 1.0, 1.0], [0.828125, 0.984375, 1.0, 1.0], [0.859375, 0.984375, 1.0, 1.0], [0.859375, 0.984375, 1.0, 1.0], [0.890625, 0.984375, 1.0, 1.0], [0.890625, 0.984375, 1.0, 1.0], [0.921875, 0.984375, 1.0, 1.0], [0.921875, 0.984375, 1.0, 1.0], [0.953125, 0.984375, 1.0, 1.0], [0.953125, 0.984375, 1.0, 1.0], [0.984375, 0.984375, 1.0, 1.0], [0.984375, 0.984375, 1.0, 1.0], [0.03125, 0.03125, 1.0, 1.0], [0.03125, 0.03125, 1.0, 1.0], [0.09375, 0.03125, 1.0, 1.0], [0.09375, 0.03125, 1.0, 1.0], [0.15625, 0.03125, 1.0, 1.0], [0.15625, 0.03125, 1.0, 1.0], [0.21875, 0.03125, 1.0, 1.0], [0.21875, 0.03125, 1.0, 1.0], [0.28125, 0.03125, 1.0, 1.0], [0.28125, 0.03125, 1.0, 1.0], [0.34375, 0.03125, 1.0, 1.0], [0.34375, 0.03125, 1.0, 1.0], [0.40625, 0.03125, 1.0, 1.0], [0.40625, 0.03125, 1.0, 1.0], [0.46875, 0.03125, 1.0, 1.0], [0.46875, 0.03125, 1.0, 1.0], [0.53125, 0.03125, 1.0, 1.0], [0.53125, 0.03125, 1.0, 1.0], [0.59375, 0.03125, 1.0, 1.0], [0.59375, 0.03125, 1.0, 1.0], [0.65625, 0.03125, 1.0, 1.0], [0.65625, 0.03125, 1.0, 1.0], [0.71875, 0.03125, 1.0, 1.0], [0.71875, 0.03125, 1.0, 1.0], [0.78125, 0.03125, 1.0, 1.0], [0.78125, 0.03125, 1.0, 1.0], [0.84375, 0.03125, 1.0, 1.0], [0.84375, 0.03125, 1.0, 1.0], [0.90625, 0.03125, 1.0, 1.0], [0.90625, 0.03125, 1.0, 1.0], [0.96875, 0.03125, 1.0, 1.0], [0.96875, 0.03125, 1.0, 1.0], [0.03125, 0.09375, 1.0, 1.0], [0.03125, 0.09375, 1.0, 1.0], [0.09375, 0.09375, 1.0, 1.0], [0.09375, 0.09375, 1.0, 1.0], [0.15625, 0.09375, 1.0, 1.0], [0.15625, 0.09375, 1.0, 1.0], [0.21875, 0.09375, 1.0, 1.0], [0.21875, 0.09375, 1.0, 1.0], [0.28125, 0.09375, 1.0, 1.0], [0.28125, 0.09375, 1.0, 1.0], [0.34375, 0.09375, 1.0, 1.0], [0.34375, 0.09375, 1.0, 1.0], [0.40625, 0.09375, 1.0, 1.0], [0.40625, 0.09375, 1.0, 1.0], [0.46875, 0.09375, 1.0, 1.0], [0.46875, 0.09375, 1.0, 1.0], [0.53125, 0.09375, 1.0, 1.0], [0.53125, 0.09375, 1.0, 1.0], [0.59375, 0.09375, 1.0, 1.0], [0.59375, 0.09375, 1.0, 1.0], [0.65625, 0.09375, 1.0, 1.0], [0.65625, 0.09375, 1.0, 1.0], [0.71875, 0.09375, 1.0, 1.0], [0.71875, 0.09375, 1.0, 1.0], [0.78125, 0.09375, 1.0, 1.0], [0.78125, 0.09375, 1.0, 1.0], [0.84375, 0.09375, 1.0, 1.0], [0.84375, 0.09375, 1.0, 1.0], [0.90625, 0.09375, 1.0, 1.0], [0.90625, 0.09375, 1.0, 1.0], [0.96875, 0.09375, 1.0, 1.0], [0.96875, 0.09375, 1.0, 1.0], [0.03125, 0.15625, 1.0, 1.0], [0.03125, 0.15625, 1.0, 1.0], [0.09375, 0.15625, 1.0, 1.0], [0.09375, 0.15625, 1.0, 1.0], [0.15625, 0.15625, 1.0, 1.0], [0.15625, 0.15625, 1.0, 1.0], [0.21875, 0.15625, 1.0, 1.0], [0.21875, 0.15625, 1.0, 1.0], [0.28125, 0.15625, 1.0, 1.0], [0.28125, 0.15625, 1.0, 1.0], [0.34375, 0.15625, 1.0, 1.0], [0.34375, 0.15625, 1.0, 1.0], [0.40625, 0.15625, 1.0, 1.0], [0.40625, 0.15625, 1.0, 1.0], [0.46875, 0.15625, 1.0, 1.0], [0.46875, 0.15625, 1.0, 1.0], [0.53125, 0.15625, 1.0, 1.0], [0.53125, 0.15625, 1.0, 1.0], [0.59375, 0.15625, 1.0, 1.0], [0.59375, 0.15625, 1.0, 1.0], [0.65625, 0.15625, 1.0, 1.0], [0.65625, 0.15625, 1.0, 1.0], [0.71875, 0.15625, 1.0, 1.0], [0.71875, 0.15625, 1.0, 1.0], [0.78125, 0.15625, 1.0, 1.0], [0.78125, 0.15625, 1.0, 1.0], [0.84375, 0.15625, 1.0, 1.0], [0.84375, 0.15625, 1.0, 1.0], [0.90625, 0.15625, 1.0, 1.0], [0.90625, 0.15625, 1.0, 1.0], [0.96875, 0.15625, 1.0, 1.0], [0.96875, 0.15625, 1.0, 1.0], [0.03125, 0.21875, 1.0, 1.0], [0.03125, 0.21875, 1.0, 1.0], [0.09375, 0.21875, 1.0, 1.0], [0.09375, 0.21875, 1.0, 1.0], [0.15625, 0.21875, 1.0, 1.0], [0.15625, 0.21875, 1.0, 1.0], [0.21875, 0.21875, 1.0, 1.0], [0.21875, 0.21875, 1.0, 1.0], [0.28125, 0.21875, 1.0, 1.0], [0.28125, 0.21875, 1.0, 1.0], [0.34375, 0.21875, 1.0, 1.0], [0.34375, 0.21875, 1.0, 1.0], [0.40625, 0.21875, 1.0, 1.0], [0.40625, 0.21875, 1.0, 1.0], [0.46875, 0.21875, 1.0, 1.0], [0.46875, 0.21875, 1.0, 1.0], [0.53125, 0.21875, 1.0, 1.0], [0.53125, 0.21875, 1.0, 1.0], [0.59375, 0.21875, 1.0, 1.0], [0.59375, 0.21875, 1.0, 1.0], [0.65625, 0.21875, 1.0, 1.0], [0.65625, 0.21875, 1.0, 1.0], [0.71875, 0.21875, 1.0, 1.0], [0.71875, 0.21875, 1.0, 1.0], [0.78125, 0.21875, 1.0, 1.0], [0.78125, 0.21875, 1.0, 1.0], [0.84375, 0.21875, 1.0, 1.0], [0.84375, 0.21875, 1.0, 1.0], [0.90625, 0.21875, 1.0, 1.0], [0.90625, 0.21875, 1.0, 1.0], [0.96875, 0.21875, 1.0, 1.0], [0.96875, 0.21875, 1.0, 1.0], [0.03125, 0.28125, 1.0, 1.0], [0.03125, 0.28125, 1.0, 1.0], [0.09375, 0.28125, 1.0, 1.0], [0.09375, 0.28125, 1.0, 1.0], [0.15625, 0.28125, 1.0, 1.0], [0.15625, 0.28125, 1.0, 1.0], [0.21875, 0.28125, 1.0, 1.0], [0.21875, 0.28125, 1.0, 1.0], [0.28125, 0.28125, 1.0, 1.0], [0.28125, 0.28125, 1.0, 1.0], [0.34375, 0.28125, 1.0, 1.0], [0.34375, 0.28125, 1.0, 1.0], [0.40625, 0.28125, 1.0, 1.0], [0.40625, 0.28125, 1.0, 1.0], [0.46875, 0.28125, 1.0, 1.0], [0.46875, 0.28125, 1.0, 1.0], [0.53125, 0.28125, 1.0, 1.0], [0.53125, 0.28125, 1.0, 1.0], [0.59375, 0.28125, 1.0, 1.0], [0.59375, 0.28125, 1.0, 1.0], [0.65625, 0.28125, 1.0, 1.0], [0.65625, 0.28125, 1.0, 1.0], [0.71875, 0.28125, 1.0, 1.0], [0.71875, 0.28125, 1.0, 1.0], [0.78125, 0.28125, 1.0, 1.0], [0.78125, 0.28125, 1.0, 1.0], [0.84375, 0.28125, 1.0, 1.0], [0.84375, 0.28125, 1.0, 1.0], [0.90625, 0.28125, 1.0, 1.0], [0.90625, 0.28125, 1.0, 1.0], [0.96875, 0.28125, 1.0, 1.0], [0.96875, 0.28125, 1.0, 1.0], [0.03125, 0.34375, 1.0, 1.0], [0.03125, 0.34375, 1.0, 1.0], [0.09375, 0.34375, 1.0, 1.0], [0.09375, 0.34375, 1.0, 1.0], [0.15625, 0.34375, 1.0, 1.0], [0.15625, 0.34375, 1.0, 1.0], [0.21875, 0.34375, 1.0, 1.0], [0.21875, 0.34375, 1.0, 1.0], [0.28125, 0.34375, 1.0, 1.0], [0.28125, 0.34375, 1.0, 1.0], [0.34375, 0.34375, 1.0, 1.0], [0.34375, 0.34375, 1.0, 1.0], [0.40625, 0.34375, 1.0, 1.0], [0.40625, 0.34375, 1.0, 1.0], [0.46875, 0.34375, 1.0, 1.0], [0.46875, 0.34375, 1.0, 1.0], [0.53125, 0.34375, 1.0, 1.0], [0.53125, 0.34375, 1.0, 1.0], [0.59375, 0.34375, 1.0, 1.0], [0.59375, 0.34375, 1.0, 1.0], [0.65625, 0.34375, 1.0, 1.0], [0.65625, 0.34375, 1.0, 1.0], [0.71875, 0.34375, 1.0, 1.0], [0.71875, 0.34375, 1.0, 1.0], [0.78125, 0.34375, 1.0, 1.0], [0.78125, 0.34375, 1.0, 1.0], [0.84375, 0.34375, 1.0, 1.0], [0.84375, 0.34375, 1.0, 1.0], [0.90625, 0.34375, 1.0, 1.0], [0.90625, 0.34375, 1.0, 1.0], [0.96875, 0.34375, 1.0, 1.0], [0.96875, 0.34375, 1.0, 1.0], [0.03125, 0.40625, 1.0, 1.0], [0.03125, 0.40625, 1.0, 1.0], [0.09375, 0.40625, 1.0, 1.0], [0.09375, 0.40625, 1.0, 1.0], [0.15625, 0.40625, 1.0, 1.0], [0.15625, 0.40625, 1.0, 1.0], [0.21875, 0.40625, 1.0, 1.0], [0.21875, 0.40625, 1.0, 1.0], [0.28125, 0.40625, 1.0, 1.0], [0.28125, 0.40625, 1.0, 1.0], [0.34375, 0.40625, 1.0, 1.0], [0.34375, 0.40625, 1.0, 1.0], [0.40625, 0.40625, 1.0, 1.0], [0.40625, 0.40625, 1.0, 1.0], [0.46875, 0.40625, 1.0, 1.0], [0.46875, 0.40625, 1.0, 1.0], [0.53125, 0.40625, 1.0, 1.0], [0.53125, 0.40625, 1.0, 1.0], [0.59375, 0.40625, 1.0, 1.0], [0.59375, 0.40625, 1.0, 1.0], [0.65625, 0.40625, 1.0, 1.0], [0.65625, 0.40625, 1.0, 1.0], [0.71875, 0.40625, 1.0, 1.0], [0.71875, 0.40625, 1.0, 1.0], [0.78125, 0.40625, 1.0, 1.0], [0.78125, 0.40625, 1.0, 1.0], [0.84375, 0.40625, 1.0, 1.0], [0.84375, 0.40625, 1.0, 1.0], [0.90625, 0.40625, 1.0, 1.0], [0.90625, 0.40625, 1.0, 1.0], [0.96875, 0.40625, 1.0, 1.0], [0.96875, 0.40625, 1.0, 1.0], [0.03125, 0.46875, 1.0, 1.0], [0.03125, 0.46875, 1.0, 1.0], [0.09375, 0.46875, 1.0, 1.0], [0.09375, 0.46875, 1.0, 1.0], [0.15625, 0.46875, 1.0, 1.0], [0.15625, 0.46875, 1.0, 1.0], [0.21875, 0.46875, 1.0, 1.0], [0.21875, 0.46875, 1.0, 1.0], [0.28125, 0.46875, 1.0, 1.0], [0.28125, 0.46875, 1.0, 1.0], [0.34375, 0.46875, 1.0, 1.0], [0.34375, 0.46875, 1.0, 1.0], [0.40625, 0.46875, 1.0, 1.0], [0.40625, 0.46875, 1.0, 1.0], [0.46875, 0.46875, 1.0, 1.0], [0.46875, 0.46875, 1.0, 1.0], [0.53125, 0.46875, 1.0, 1.0], [0.53125, 0.46875, 1.0, 1.0], [0.59375, 0.46875, 1.0, 1.0], [0.59375, 0.46875, 1.0, 1.0], [0.65625, 0.46875, 1.0, 1.0], [0.65625, 0.46875, 1.0, 1.0], [0.71875, 0.46875, 1.0, 1.0], [0.71875, 0.46875, 1.0, 1.0], [0.78125, 0.46875, 1.0, 1.0], [0.78125, 0.46875, 1.0, 1.0], [0.84375, 0.46875, 1.0, 1.0], [0.84375, 0.46875, 1.0, 1.0], [0.90625, 0.46875, 1.0, 1.0], [0.90625, 0.46875, 1.0, 1.0], [0.96875, 0.46875, 1.0, 1.0], [0.96875, 0.46875, 1.0, 1.0], [0.03125, 0.53125, 1.0, 1.0], [0.03125, 0.53125, 1.0, 1.0], [0.09375, 0.53125, 1.0, 1.0], [0.09375, 0.53125, 1.0, 1.0], [0.15625, 0.53125, 1.0, 1.0], [0.15625, 0.53125, 1.0, 1.0], [0.21875, 0.53125, 1.0, 1.0], [0.21875, 0.53125, 1.0, 1.0], [0.28125, 0.53125, 1.0, 1.0], [0.28125, 0.53125, 1.0, 1.0], [0.34375, 0.53125, 1.0, 1.0], [0.34375, 0.53125, 1.0, 1.0], [0.40625, 0.53125, 1.0, 1.0], [0.40625, 0.53125, 1.0, 1.0], [0.46875, 0.53125, 1.0, 1.0], [0.46875, 0.53125, 1.0, 1.0], [0.53125, 0.53125, 1.0, 1.0], [0.53125, 0.53125, 1.0, 1.0], [0.59375, 0.53125, 1.0, 1.0], [0.59375, 0.53125, 1.0, 1.0], [0.65625, 0.53125, 1.0, 1.0], [0.65625, 0.53125, 1.0, 1.0], [0.71875, 0.53125, 1.0, 1.0], [0.71875, 0.53125, 1.0, 1.0], [0.78125, 0.53125, 1.0, 1.0], [0.78125, 0.53125, 1.0, 1.0], [0.84375, 0.53125, 1.0, 1.0], [0.84375, 0.53125, 1.0, 1.0], [0.90625, 0.53125, 1.0, 1.0], [0.90625, 0.53125, 1.0, 1.0], [0.96875, 0.53125, 1.0, 1.0], [0.96875, 0.53125, 1.0, 1.0], [0.03125, 0.59375, 1.0, 1.0], [0.03125, 0.59375, 1.0, 1.0], [0.09375, 0.59375, 1.0, 1.0], [0.09375, 0.59375, 1.0, 1.0], [0.15625, 0.59375, 1.0, 1.0], [0.15625, 0.59375, 1.0, 1.0], [0.21875, 0.59375, 1.0, 1.0], [0.21875, 0.59375, 1.0, 1.0], [0.28125, 0.59375, 1.0, 1.0], [0.28125, 0.59375, 1.0, 1.0], [0.34375, 0.59375, 1.0, 1.0], [0.34375, 0.59375, 1.0, 1.0], [0.40625, 0.59375, 1.0, 1.0], [0.40625, 0.59375, 1.0, 1.0], [0.46875, 0.59375, 1.0, 1.0], [0.46875, 0.59375, 1.0, 1.0], [0.53125, 0.59375, 1.0, 1.0], [0.53125, 0.59375, 1.0, 1.0], [0.59375, 0.59375, 1.0, 1.0], [0.59375, 0.59375, 1.0, 1.0], [0.65625, 0.59375, 1.0, 1.0], [0.65625, 0.59375, 1.0, 1.0], [0.71875, 0.59375, 1.0, 1.0], [0.71875, 0.59375, 1.0, 1.0], [0.78125, 0.59375, 1.0, 1.0], [0.78125, 0.59375, 1.0, 1.0], [0.84375, 0.59375, 1.0, 1.0], [0.84375, 0.59375, 1.0, 1.0], [0.90625, 0.59375, 1.0, 1.0], [0.90625, 0.59375, 1.0, 1.0], [0.96875, 0.59375, 1.0, 1.0], [0.96875, 0.59375, 1.0, 1.0], [0.03125, 0.65625, 1.0, 1.0], [0.03125, 0.65625, 1.0, 1.0], [0.09375, 0.65625, 1.0, 1.0], [0.09375, 0.65625, 1.0, 1.0], [0.15625, 0.65625, 1.0, 1.0], [0.15625, 0.65625, 1.0, 1.0], [0.21875, 0.65625, 1.0, 1.0], [0.21875, 0.65625, 1.0, 1.0], [0.28125, 0.65625, 1.0, 1.0], [0.28125, 0.65625, 1.0, 1.0], [0.34375, 0.65625, 1.0, 1.0], [0.34375, 0.65625, 1.0, 1.0], [0.40625, 0.65625, 1.0, 1.0], [0.40625, 0.65625, 1.0, 1.0], [0.46875, 0.65625, 1.0, 1.0], [0.46875, 0.65625, 1.0, 1.0], [0.53125, 0.65625, 1.0, 1.0], [0.53125, 0.65625, 1.0, 1.0], [0.59375, 0.65625, 1.0, 1.0], [0.59375, 0.65625, 1.0, 1.0], [0.65625, 0.65625, 1.0, 1.0], [0.65625, 0.65625, 1.0, 1.0], [0.71875, 0.65625, 1.0, 1.0], [0.71875, 0.65625, 1.0, 1.0], [0.78125, 0.65625, 1.0, 1.0], [0.78125, 0.65625, 1.0, 1.0], [0.84375, 0.65625, 1.0, 1.0], [0.84375, 0.65625, 1.0, 1.0], [0.90625, 0.65625, 1.0, 1.0], [0.90625, 0.65625, 1.0, 1.0], [0.96875, 0.65625, 1.0, 1.0], [0.96875, 0.65625, 1.0, 1.0], [0.03125, 0.71875, 1.0, 1.0], [0.03125, 0.71875, 1.0, 1.0], [0.09375, 0.71875, 1.0, 1.0], [0.09375, 0.71875, 1.0, 1.0], [0.15625, 0.71875, 1.0, 1.0], [0.15625, 0.71875, 1.0, 1.0], [0.21875, 0.71875, 1.0, 1.0], [0.21875, 0.71875, 1.0, 1.0], [0.28125, 0.71875, 1.0, 1.0], [0.28125, 0.71875, 1.0, 1.0], [0.34375, 0.71875, 1.0, 1.0], [0.34375, 0.71875, 1.0, 1.0], [0.40625, 0.71875, 1.0, 1.0], [0.40625, 0.71875, 1.0, 1.0], [0.46875, 0.71875, 1.0, 1.0], [0.46875, 0.71875, 1.0, 1.0], [0.53125, 0.71875, 1.0, 1.0], [0.53125, 0.71875, 1.0, 1.0], [0.59375, 0.71875, 1.0, 1.0], [0.59375, 0.71875, 1.0, 1.0], [0.65625, 0.71875, 1.0, 1.0], [0.65625, 0.71875, 1.0, 1.0], [0.71875, 0.71875, 1.0, 1.0], [0.71875, 0.71875, 1.0, 1.0], [0.78125, 0.71875, 1.0, 1.0], [0.78125, 0.71875, 1.0, 1.0], [0.84375, 0.71875, 1.0, 1.0], [0.84375, 0.71875, 1.0, 1.0], [0.90625, 0.71875, 1.0, 1.0], [0.90625, 0.71875, 1.0, 1.0], [0.96875, 0.71875, 1.0, 1.0], [0.96875, 0.71875, 1.0, 1.0], [0.03125, 0.78125, 1.0, 1.0], [0.03125, 0.78125, 1.0, 1.0], [0.09375, 0.78125, 1.0, 1.0], [0.09375, 0.78125, 1.0, 1.0], [0.15625, 0.78125, 1.0, 1.0], [0.15625, 0.78125, 1.0, 1.0], [0.21875, 0.78125, 1.0, 1.0], [0.21875, 0.78125, 1.0, 1.0], [0.28125, 0.78125, 1.0, 1.0], [0.28125, 0.78125, 1.0, 1.0], [0.34375, 0.78125, 1.0, 1.0], [0.34375, 0.78125, 1.0, 1.0], [0.40625, 0.78125, 1.0, 1.0], [0.40625, 0.78125, 1.0, 1.0], [0.46875, 0.78125, 1.0, 1.0], [0.46875, 0.78125, 1.0, 1.0], [0.53125, 0.78125, 1.0, 1.0], [0.53125, 0.78125, 1.0, 1.0], [0.59375, 0.78125, 1.0, 1.0], [0.59375, 0.78125, 1.0, 1.0], [0.65625, 0.78125, 1.0, 1.0], [0.65625, 0.78125, 1.0, 1.0], [0.71875, 0.78125, 1.0, 1.0], [0.71875, 0.78125, 1.0, 1.0], [0.78125, 0.78125, 1.0, 1.0], [0.78125, 0.78125, 1.0, 1.0], [0.84375, 0.78125, 1.0, 1.0], [0.84375, 0.78125, 1.0, 1.0], [0.90625, 0.78125, 1.0, 1.0], [0.90625, 0.78125, 1.0, 1.0], [0.96875, 0.78125, 1.0, 1.0], [0.96875, 0.78125, 1.0, 1.0], [0.03125, 0.84375, 1.0, 1.0], [0.03125, 0.84375, 1.0, 1.0], [0.09375, 0.84375, 1.0, 1.0], [0.09375, 0.84375, 1.0, 1.0], [0.15625, 0.84375, 1.0, 1.0], [0.15625, 0.84375, 1.0, 1.0], [0.21875, 0.84375, 1.0, 1.0], [0.21875, 0.84375, 1.0, 1.0], [0.28125, 0.84375, 1.0, 1.0], [0.28125, 0.84375, 1.0, 1.0], [0.34375, 0.84375, 1.0, 1.0], [0.34375, 0.84375, 1.0, 1.0], [0.40625, 0.84375, 1.0, 1.0], [0.40625, 0.84375, 1.0, 1.0], [0.46875, 0.84375, 1.0, 1.0], [0.46875, 0.84375, 1.0, 1.0], [0.53125, 0.84375, 1.0, 1.0], [0.53125, 0.84375, 1.0, 1.0], [0.59375, 0.84375, 1.0, 1.0], [0.59375, 0.84375, 1.0, 1.0], [0.65625, 0.84375, 1.0, 1.0], [0.65625, 0.84375, 1.0, 1.0], [0.71875, 0.84375, 1.0, 1.0], [0.71875, 0.84375, 1.0, 1.0], [0.78125, 0.84375, 1.0, 1.0], [0.78125, 0.84375, 1.0, 1.0], [0.84375, 0.84375, 1.0, 1.0], [0.84375, 0.84375, 1.0, 1.0], [0.90625, 0.84375, 1.0, 1.0], [0.90625, 0.84375, 1.0, 1.0], [0.96875, 0.84375, 1.0, 1.0], [0.96875, 0.84375, 1.0, 1.0], [0.03125, 0.90625, 1.0, 1.0], [0.03125, 0.90625, 1.0, 1.0], [0.09375, 0.90625, 1.0, 1.0], [0.09375, 0.90625, 1.0, 1.0], [0.15625, 0.90625, 1.0, 1.0], [0.15625, 0.90625, 1.0, 1.0], [0.21875, 0.90625, 1.0, 1.0], [0.21875, 0.90625, 1.0, 1.0], [0.28125, 0.90625, 1.0, 1.0], [0.28125, 0.90625, 1.0, 1.0], [0.34375, 0.90625, 1.0, 1.0], [0.34375, 0.90625, 1.0, 1.0], [0.40625, 0.90625, 1.0, 1.0], [0.40625, 0.90625, 1.0, 1.0], [0.46875, 0.90625, 1.0, 1.0], [0.46875, 0.90625, 1.0, 1.0], [0.53125, 0.90625, 1.0, 1.0], [0.53125, 0.90625, 1.0, 1.0], [0.59375, 0.90625, 1.0, 1.0], [0.59375, 0.90625, 1.0, 1.0], [0.65625, 0.90625, 1.0, 1.0], [0.65625, 0.90625, 1.0, 1.0], [0.71875, 0.90625, 1.0, 1.0], [0.71875, 0.90625, 1.0, 1.0], [0.78125, 0.90625, 1.0, 1.0], [0.78125, 0.90625, 1.0, 1.0], [0.84375, 0.90625, 1.0, 1.0], [0.84375, 0.90625, 1.0, 1.0], [0.90625, 0.90625, 1.0, 1.0], [0.90625, 0.90625, 1.0, 1.0], [0.96875, 0.90625, 1.0, 1.0], [0.96875, 0.90625, 1.0, 1.0], [0.03125, 0.96875, 1.0, 1.0], [0.03125, 0.96875, 1.0, 1.0], [0.09375, 0.96875, 1.0, 1.0], [0.09375, 0.96875, 1.0, 1.0], [0.15625, 0.96875, 1.0, 1.0], [0.15625, 0.96875, 1.0, 1.0], [0.21875, 0.96875, 1.0, 1.0], [0.21875, 0.96875, 1.0, 1.0], [0.28125, 0.96875, 1.0, 1.0], [0.28125, 0.96875, 1.0, 1.0], [0.34375, 0.96875, 1.0, 1.0], [0.34375, 0.96875, 1.0, 1.0], [0.40625, 0.96875, 1.0, 1.0], [0.40625, 0.96875, 1.0, 1.0], [0.46875, 0.96875, 1.0, 1.0], [0.46875, 0.96875, 1.0, 1.0], [0.53125, 0.96875, 1.0, 1.0], [0.53125, 0.96875, 1.0, 1.0], [0.59375, 0.96875, 1.0, 1.0], [0.59375, 0.96875, 1.0, 1.0], [0.65625, 0.96875, 1.0, 1.0], [0.65625, 0.96875, 1.0, 1.0], [0.71875, 0.96875, 1.0, 1.0], [0.71875, 0.96875, 1.0, 1.0], [0.78125, 0.96875, 1.0, 1.0], [0.78125, 0.96875, 1.0, 1.0], [0.84375, 0.96875, 1.0, 1.0], [0.84375, 0.96875, 1.0, 1.0], [0.90625, 0.96875, 1.0, 1.0], [0.90625, 0.96875, 1.0, 1.0], [0.96875, 0.96875, 1.0, 1.0], [0.96875, 0.96875, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.0625, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.1875, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.3125, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.4375, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.5625, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.6875, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.8125, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.9375, 0.0625, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.0625, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.1875, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.3125, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.4375, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.5625, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.6875, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.8125, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.9375, 0.1875, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.0625, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.1875, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.3125, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.4375, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.5625, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.6875, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.8125, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.9375, 0.3125, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.0625, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.1875, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.3125, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.4375, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.5625, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.6875, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.8125, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.9375, 0.4375, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.0625, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.1875, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.3125, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.4375, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.5625, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.6875, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.8125, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.9375, 0.5625, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.0625, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.1875, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.3125, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.4375, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.5625, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.6875, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.8125, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.9375, 0.6875, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.0625, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.1875, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.3125, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.4375, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.5625, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.6875, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.8125, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.9375, 0.8125, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.0625, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.1875, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.3125, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.4375, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.5625, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.6875, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.8125, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0], [0.9375, 0.9375, 1.0, 1.0]] \ No newline at end of file diff --git a/pose-detection/test_data/shared/anchor_golden_file_1.json b/pose-detection/test_data/shared/anchor_golden_file_1.json new file mode 100644 index 0000000000..c69a9a0b33 --- /dev/null +++ b/pose-detection/test_data/shared/anchor_golden_file_1.json @@ -0,0 +1 @@ +[[0.0263158, 0.0263158, 0.1, 0.1], [0.0263158, 0.0263158, 0.282843, 0.141421], [0.0263158, 0.0263158, 0.141421, 0.282843], [0.0789474, 0.0263158, 0.1, 0.1], [0.0789474, 0.0263158, 0.282843, 0.141421], [0.0789474, 0.0263158, 0.141421, 0.282843], [0.131579, 0.0263158, 0.1, 0.1], [0.131579, 0.0263158, 0.282843, 0.141421], [0.131579, 0.0263158, 0.141421, 0.282843], [0.184211, 0.0263158, 0.1, 0.1], [0.184211, 0.0263158, 0.282843, 0.141421], [0.184211, 0.0263158, 0.141421, 0.282843], [0.236842, 0.0263158, 0.1, 0.1], [0.236842, 0.0263158, 0.282843, 0.141421], [0.236842, 0.0263158, 0.141421, 0.282843], [0.289474, 0.0263158, 0.1, 0.1], [0.289474, 0.0263158, 0.282843, 0.141421], [0.289474, 0.0263158, 0.141421, 0.282843], [0.342105, 0.0263158, 0.1, 0.1], [0.342105, 0.0263158, 0.282843, 0.141421], [0.342105, 0.0263158, 0.141421, 0.282843], [0.394737, 0.0263158, 0.1, 0.1], [0.394737, 0.0263158, 0.282843, 0.141421], [0.394737, 0.0263158, 0.141421, 0.282843], [0.447368, 0.0263158, 0.1, 0.1], [0.447368, 0.0263158, 0.282843, 0.141421], [0.447368, 0.0263158, 0.141421, 0.282843], [0.5, 0.0263158, 0.1, 0.1], [0.5, 0.0263158, 0.282843, 0.141421], [0.5, 0.0263158, 0.141421, 0.282843], [0.552632, 0.0263158, 0.1, 0.1], [0.552632, 0.0263158, 0.282843, 0.141421], [0.552632, 0.0263158, 0.141421, 0.282843], [0.605263, 0.0263158, 0.1, 0.1], [0.605263, 0.0263158, 0.282843, 0.141421], [0.605263, 0.0263158, 0.141421, 0.282843], [0.657895, 0.0263158, 0.1, 0.1], [0.657895, 0.0263158, 0.282843, 0.141421], [0.657895, 0.0263158, 0.141421, 0.282843], [0.710526, 0.0263158, 0.1, 0.1], [0.710526, 0.0263158, 0.282843, 0.141421], [0.710526, 0.0263158, 0.141421, 0.282843], [0.763158, 0.0263158, 0.1, 0.1], [0.763158, 0.0263158, 0.282843, 0.141421], [0.763158, 0.0263158, 0.141421, 0.282843], [0.81579, 0.0263158, 0.1, 0.1], [0.81579, 0.0263158, 0.282843, 0.141421], [0.81579, 0.0263158, 0.141421, 0.282843], [0.868421, 0.0263158, 0.1, 0.1], [0.868421, 0.0263158, 0.282843, 0.141421], [0.868421, 0.0263158, 0.141421, 0.282843], [0.921053, 0.0263158, 0.1, 0.1], [0.921053, 0.0263158, 0.282843, 0.141421], [0.921053, 0.0263158, 0.141421, 0.282843], [0.973684, 0.0263158, 0.1, 0.1], [0.973684, 0.0263158, 0.282843, 0.141421], [0.973684, 0.0263158, 0.141421, 0.282843], [0.0263158, 0.0789474, 0.1, 0.1], [0.0263158, 0.0789474, 0.282843, 0.141421], [0.0263158, 0.0789474, 0.141421, 0.282843], [0.0789474, 0.0789474, 0.1, 0.1], [0.0789474, 0.0789474, 0.282843, 0.141421], [0.0789474, 0.0789474, 0.141421, 0.282843], [0.131579, 0.0789474, 0.1, 0.1], [0.131579, 0.0789474, 0.282843, 0.141421], [0.131579, 0.0789474, 0.141421, 0.282843], [0.184211, 0.0789474, 0.1, 0.1], [0.184211, 0.0789474, 0.282843, 0.141421], [0.184211, 0.0789474, 0.141421, 0.282843], [0.236842, 0.0789474, 0.1, 0.1], [0.236842, 0.0789474, 0.282843, 0.141421], [0.236842, 0.0789474, 0.141421, 0.282843], [0.289474, 0.0789474, 0.1, 0.1], [0.289474, 0.0789474, 0.282843, 0.141421], [0.289474, 0.0789474, 0.141421, 0.282843], [0.342105, 0.0789474, 0.1, 0.1], [0.342105, 0.0789474, 0.282843, 0.141421], [0.342105, 0.0789474, 0.141421, 0.282843], [0.394737, 0.0789474, 0.1, 0.1], [0.394737, 0.0789474, 0.282843, 0.141421], [0.394737, 0.0789474, 0.141421, 0.282843], [0.447368, 0.0789474, 0.1, 0.1], [0.447368, 0.0789474, 0.282843, 0.141421], [0.447368, 0.0789474, 0.141421, 0.282843], [0.5, 0.0789474, 0.1, 0.1], [0.5, 0.0789474, 0.282843, 0.141421], [0.5, 0.0789474, 0.141421, 0.282843], [0.552632, 0.0789474, 0.1, 0.1], [0.552632, 0.0789474, 0.282843, 0.141421], [0.552632, 0.0789474, 0.141421, 0.282843], [0.605263, 0.0789474, 0.1, 0.1], [0.605263, 0.0789474, 0.282843, 0.141421], [0.605263, 0.0789474, 0.141421, 0.282843], [0.657895, 0.0789474, 0.1, 0.1], [0.657895, 0.0789474, 0.282843, 0.141421], [0.657895, 0.0789474, 0.141421, 0.282843], [0.710526, 0.0789474, 0.1, 0.1], [0.710526, 0.0789474, 0.282843, 0.141421], [0.710526, 0.0789474, 0.141421, 0.282843], [0.763158, 0.0789474, 0.1, 0.1], [0.763158, 0.0789474, 0.282843, 0.141421], [0.763158, 0.0789474, 0.141421, 0.282843], [0.81579, 0.0789474, 0.1, 0.1], [0.81579, 0.0789474, 0.282843, 0.141421], [0.81579, 0.0789474, 0.141421, 0.282843], [0.868421, 0.0789474, 0.1, 0.1], [0.868421, 0.0789474, 0.282843, 0.141421], [0.868421, 0.0789474, 0.141421, 0.282843], [0.921053, 0.0789474, 0.1, 0.1], [0.921053, 0.0789474, 0.282843, 0.141421], [0.921053, 0.0789474, 0.141421, 0.282843], [0.973684, 0.0789474, 0.1, 0.1], [0.973684, 0.0789474, 0.282843, 0.141421], [0.973684, 0.0789474, 0.141421, 0.282843], [0.0263158, 0.131579, 0.1, 0.1], [0.0263158, 0.131579, 0.282843, 0.141421], [0.0263158, 0.131579, 0.141421, 0.282843], [0.0789474, 0.131579, 0.1, 0.1], [0.0789474, 0.131579, 0.282843, 0.141421], [0.0789474, 0.131579, 0.141421, 0.282843], [0.131579, 0.131579, 0.1, 0.1], [0.131579, 0.131579, 0.282843, 0.141421], [0.131579, 0.131579, 0.141421, 0.282843], [0.184211, 0.131579, 0.1, 0.1], [0.184211, 0.131579, 0.282843, 0.141421], [0.184211, 0.131579, 0.141421, 0.282843], [0.236842, 0.131579, 0.1, 0.1], [0.236842, 0.131579, 0.282843, 0.141421], [0.236842, 0.131579, 0.141421, 0.282843], [0.289474, 0.131579, 0.1, 0.1], [0.289474, 0.131579, 0.282843, 0.141421], [0.289474, 0.131579, 0.141421, 0.282843], [0.342105, 0.131579, 0.1, 0.1], [0.342105, 0.131579, 0.282843, 0.141421], [0.342105, 0.131579, 0.141421, 0.282843], [0.394737, 0.131579, 0.1, 0.1], [0.394737, 0.131579, 0.282843, 0.141421], [0.394737, 0.131579, 0.141421, 0.282843], [0.447368, 0.131579, 0.1, 0.1], [0.447368, 0.131579, 0.282843, 0.141421], [0.447368, 0.131579, 0.141421, 0.282843], [0.5, 0.131579, 0.1, 0.1], [0.5, 0.131579, 0.282843, 0.141421], [0.5, 0.131579, 0.141421, 0.282843], [0.552632, 0.131579, 0.1, 0.1], [0.552632, 0.131579, 0.282843, 0.141421], [0.552632, 0.131579, 0.141421, 0.282843], [0.605263, 0.131579, 0.1, 0.1], [0.605263, 0.131579, 0.282843, 0.141421], [0.605263, 0.131579, 0.141421, 0.282843], [0.657895, 0.131579, 0.1, 0.1], [0.657895, 0.131579, 0.282843, 0.141421], [0.657895, 0.131579, 0.141421, 0.282843], [0.710526, 0.131579, 0.1, 0.1], [0.710526, 0.131579, 0.282843, 0.141421], [0.710526, 0.131579, 0.141421, 0.282843], [0.763158, 0.131579, 0.1, 0.1], [0.763158, 0.131579, 0.282843, 0.141421], [0.763158, 0.131579, 0.141421, 0.282843], [0.81579, 0.131579, 0.1, 0.1], [0.81579, 0.131579, 0.282843, 0.141421], [0.81579, 0.131579, 0.141421, 0.282843], [0.868421, 0.131579, 0.1, 0.1], [0.868421, 0.131579, 0.282843, 0.141421], [0.868421, 0.131579, 0.141421, 0.282843], [0.921053, 0.131579, 0.1, 0.1], [0.921053, 0.131579, 0.282843, 0.141421], [0.921053, 0.131579, 0.141421, 0.282843], [0.973684, 0.131579, 0.1, 0.1], [0.973684, 0.131579, 0.282843, 0.141421], [0.973684, 0.131579, 0.141421, 0.282843], [0.0263158, 0.184211, 0.1, 0.1], [0.0263158, 0.184211, 0.282843, 0.141421], [0.0263158, 0.184211, 0.141421, 0.282843], [0.0789474, 0.184211, 0.1, 0.1], [0.0789474, 0.184211, 0.282843, 0.141421], [0.0789474, 0.184211, 0.141421, 0.282843], [0.131579, 0.184211, 0.1, 0.1], [0.131579, 0.184211, 0.282843, 0.141421], [0.131579, 0.184211, 0.141421, 0.282843], [0.184211, 0.184211, 0.1, 0.1], [0.184211, 0.184211, 0.282843, 0.141421], [0.184211, 0.184211, 0.141421, 0.282843], [0.236842, 0.184211, 0.1, 0.1], [0.236842, 0.184211, 0.282843, 0.141421], [0.236842, 0.184211, 0.141421, 0.282843], [0.289474, 0.184211, 0.1, 0.1], [0.289474, 0.184211, 0.282843, 0.141421], [0.289474, 0.184211, 0.141421, 0.282843], [0.342105, 0.184211, 0.1, 0.1], [0.342105, 0.184211, 0.282843, 0.141421], [0.342105, 0.184211, 0.141421, 0.282843], [0.394737, 0.184211, 0.1, 0.1], [0.394737, 0.184211, 0.282843, 0.141421], [0.394737, 0.184211, 0.141421, 0.282843], [0.447368, 0.184211, 0.1, 0.1], [0.447368, 0.184211, 0.282843, 0.141421], [0.447368, 0.184211, 0.141421, 0.282843], [0.5, 0.184211, 0.1, 0.1], [0.5, 0.184211, 0.282843, 0.141421], [0.5, 0.184211, 0.141421, 0.282843], [0.552632, 0.184211, 0.1, 0.1], [0.552632, 0.184211, 0.282843, 0.141421], [0.552632, 0.184211, 0.141421, 0.282843], [0.605263, 0.184211, 0.1, 0.1], [0.605263, 0.184211, 0.282843, 0.141421], [0.605263, 0.184211, 0.141421, 0.282843], [0.657895, 0.184211, 0.1, 0.1], [0.657895, 0.184211, 0.282843, 0.141421], [0.657895, 0.184211, 0.141421, 0.282843], [0.710526, 0.184211, 0.1, 0.1], [0.710526, 0.184211, 0.282843, 0.141421], [0.710526, 0.184211, 0.141421, 0.282843], [0.763158, 0.184211, 0.1, 0.1], [0.763158, 0.184211, 0.282843, 0.141421], [0.763158, 0.184211, 0.141421, 0.282843], [0.81579, 0.184211, 0.1, 0.1], [0.81579, 0.184211, 0.282843, 0.141421], [0.81579, 0.184211, 0.141421, 0.282843], [0.868421, 0.184211, 0.1, 0.1], [0.868421, 0.184211, 0.282843, 0.141421], [0.868421, 0.184211, 0.141421, 0.282843], [0.921053, 0.184211, 0.1, 0.1], [0.921053, 0.184211, 0.282843, 0.141421], [0.921053, 0.184211, 0.141421, 0.282843], [0.973684, 0.184211, 0.1, 0.1], [0.973684, 0.184211, 0.282843, 0.141421], [0.973684, 0.184211, 0.141421, 0.282843], [0.0263158, 0.236842, 0.1, 0.1], [0.0263158, 0.236842, 0.282843, 0.141421], [0.0263158, 0.236842, 0.141421, 0.282843], [0.0789474, 0.236842, 0.1, 0.1], [0.0789474, 0.236842, 0.282843, 0.141421], [0.0789474, 0.236842, 0.141421, 0.282843], [0.131579, 0.236842, 0.1, 0.1], [0.131579, 0.236842, 0.282843, 0.141421], [0.131579, 0.236842, 0.141421, 0.282843], [0.184211, 0.236842, 0.1, 0.1], [0.184211, 0.236842, 0.282843, 0.141421], [0.184211, 0.236842, 0.141421, 0.282843], [0.236842, 0.236842, 0.1, 0.1], [0.236842, 0.236842, 0.282843, 0.141421], [0.236842, 0.236842, 0.141421, 0.282843], [0.289474, 0.236842, 0.1, 0.1], [0.289474, 0.236842, 0.282843, 0.141421], [0.289474, 0.236842, 0.141421, 0.282843], [0.342105, 0.236842, 0.1, 0.1], [0.342105, 0.236842, 0.282843, 0.141421], [0.342105, 0.236842, 0.141421, 0.282843], [0.394737, 0.236842, 0.1, 0.1], [0.394737, 0.236842, 0.282843, 0.141421], [0.394737, 0.236842, 0.141421, 0.282843], [0.447368, 0.236842, 0.1, 0.1], [0.447368, 0.236842, 0.282843, 0.141421], [0.447368, 0.236842, 0.141421, 0.282843], [0.5, 0.236842, 0.1, 0.1], [0.5, 0.236842, 0.282843, 0.141421], [0.5, 0.236842, 0.141421, 0.282843], [0.552632, 0.236842, 0.1, 0.1], [0.552632, 0.236842, 0.282843, 0.141421], [0.552632, 0.236842, 0.141421, 0.282843], [0.605263, 0.236842, 0.1, 0.1], [0.605263, 0.236842, 0.282843, 0.141421], [0.605263, 0.236842, 0.141421, 0.282843], [0.657895, 0.236842, 0.1, 0.1], [0.657895, 0.236842, 0.282843, 0.141421], [0.657895, 0.236842, 0.141421, 0.282843], [0.710526, 0.236842, 0.1, 0.1], [0.710526, 0.236842, 0.282843, 0.141421], [0.710526, 0.236842, 0.141421, 0.282843], [0.763158, 0.236842, 0.1, 0.1], [0.763158, 0.236842, 0.282843, 0.141421], [0.763158, 0.236842, 0.141421, 0.282843], [0.81579, 0.236842, 0.1, 0.1], [0.81579, 0.236842, 0.282843, 0.141421], [0.81579, 0.236842, 0.141421, 0.282843], [0.868421, 0.236842, 0.1, 0.1], [0.868421, 0.236842, 0.282843, 0.141421], [0.868421, 0.236842, 0.141421, 0.282843], [0.921053, 0.236842, 0.1, 0.1], [0.921053, 0.236842, 0.282843, 0.141421], [0.921053, 0.236842, 0.141421, 0.282843], [0.973684, 0.236842, 0.1, 0.1], [0.973684, 0.236842, 0.282843, 0.141421], [0.973684, 0.236842, 0.141421, 0.282843], [0.0263158, 0.289474, 0.1, 0.1], [0.0263158, 0.289474, 0.282843, 0.141421], [0.0263158, 0.289474, 0.141421, 0.282843], [0.0789474, 0.289474, 0.1, 0.1], [0.0789474, 0.289474, 0.282843, 0.141421], [0.0789474, 0.289474, 0.141421, 0.282843], [0.131579, 0.289474, 0.1, 0.1], [0.131579, 0.289474, 0.282843, 0.141421], [0.131579, 0.289474, 0.141421, 0.282843], [0.184211, 0.289474, 0.1, 0.1], [0.184211, 0.289474, 0.282843, 0.141421], [0.184211, 0.289474, 0.141421, 0.282843], [0.236842, 0.289474, 0.1, 0.1], [0.236842, 0.289474, 0.282843, 0.141421], [0.236842, 0.289474, 0.141421, 0.282843], [0.289474, 0.289474, 0.1, 0.1], [0.289474, 0.289474, 0.282843, 0.141421], [0.289474, 0.289474, 0.141421, 0.282843], [0.342105, 0.289474, 0.1, 0.1], [0.342105, 0.289474, 0.282843, 0.141421], [0.342105, 0.289474, 0.141421, 0.282843], [0.394737, 0.289474, 0.1, 0.1], [0.394737, 0.289474, 0.282843, 0.141421], [0.394737, 0.289474, 0.141421, 0.282843], [0.447368, 0.289474, 0.1, 0.1], [0.447368, 0.289474, 0.282843, 0.141421], [0.447368, 0.289474, 0.141421, 0.282843], [0.5, 0.289474, 0.1, 0.1], [0.5, 0.289474, 0.282843, 0.141421], [0.5, 0.289474, 0.141421, 0.282843], [0.552632, 0.289474, 0.1, 0.1], [0.552632, 0.289474, 0.282843, 0.141421], [0.552632, 0.289474, 0.141421, 0.282843], [0.605263, 0.289474, 0.1, 0.1], [0.605263, 0.289474, 0.282843, 0.141421], [0.605263, 0.289474, 0.141421, 0.282843], [0.657895, 0.289474, 0.1, 0.1], [0.657895, 0.289474, 0.282843, 0.141421], [0.657895, 0.289474, 0.141421, 0.282843], [0.710526, 0.289474, 0.1, 0.1], [0.710526, 0.289474, 0.282843, 0.141421], [0.710526, 0.289474, 0.141421, 0.282843], [0.763158, 0.289474, 0.1, 0.1], [0.763158, 0.289474, 0.282843, 0.141421], [0.763158, 0.289474, 0.141421, 0.282843], [0.81579, 0.289474, 0.1, 0.1], [0.81579, 0.289474, 0.282843, 0.141421], [0.81579, 0.289474, 0.141421, 0.282843], [0.868421, 0.289474, 0.1, 0.1], [0.868421, 0.289474, 0.282843, 0.141421], [0.868421, 0.289474, 0.141421, 0.282843], [0.921053, 0.289474, 0.1, 0.1], [0.921053, 0.289474, 0.282843, 0.141421], [0.921053, 0.289474, 0.141421, 0.282843], [0.973684, 0.289474, 0.1, 0.1], [0.973684, 0.289474, 0.282843, 0.141421], [0.973684, 0.289474, 0.141421, 0.282843], [0.0263158, 0.342105, 0.1, 0.1], [0.0263158, 0.342105, 0.282843, 0.141421], [0.0263158, 0.342105, 0.141421, 0.282843], [0.0789474, 0.342105, 0.1, 0.1], [0.0789474, 0.342105, 0.282843, 0.141421], [0.0789474, 0.342105, 0.141421, 0.282843], [0.131579, 0.342105, 0.1, 0.1], [0.131579, 0.342105, 0.282843, 0.141421], [0.131579, 0.342105, 0.141421, 0.282843], [0.184211, 0.342105, 0.1, 0.1], [0.184211, 0.342105, 0.282843, 0.141421], [0.184211, 0.342105, 0.141421, 0.282843], [0.236842, 0.342105, 0.1, 0.1], [0.236842, 0.342105, 0.282843, 0.141421], [0.236842, 0.342105, 0.141421, 0.282843], [0.289474, 0.342105, 0.1, 0.1], [0.289474, 0.342105, 0.282843, 0.141421], [0.289474, 0.342105, 0.141421, 0.282843], [0.342105, 0.342105, 0.1, 0.1], [0.342105, 0.342105, 0.282843, 0.141421], [0.342105, 0.342105, 0.141421, 0.282843], [0.394737, 0.342105, 0.1, 0.1], [0.394737, 0.342105, 0.282843, 0.141421], [0.394737, 0.342105, 0.141421, 0.282843], [0.447368, 0.342105, 0.1, 0.1], [0.447368, 0.342105, 0.282843, 0.141421], [0.447368, 0.342105, 0.141421, 0.282843], [0.5, 0.342105, 0.1, 0.1], [0.5, 0.342105, 0.282843, 0.141421], [0.5, 0.342105, 0.141421, 0.282843], [0.552632, 0.342105, 0.1, 0.1], [0.552632, 0.342105, 0.282843, 0.141421], [0.552632, 0.342105, 0.141421, 0.282843], [0.605263, 0.342105, 0.1, 0.1], [0.605263, 0.342105, 0.282843, 0.141421], [0.605263, 0.342105, 0.141421, 0.282843], [0.657895, 0.342105, 0.1, 0.1], [0.657895, 0.342105, 0.282843, 0.141421], [0.657895, 0.342105, 0.141421, 0.282843], [0.710526, 0.342105, 0.1, 0.1], [0.710526, 0.342105, 0.282843, 0.141421], [0.710526, 0.342105, 0.141421, 0.282843], [0.763158, 0.342105, 0.1, 0.1], [0.763158, 0.342105, 0.282843, 0.141421], [0.763158, 0.342105, 0.141421, 0.282843], [0.81579, 0.342105, 0.1, 0.1], [0.81579, 0.342105, 0.282843, 0.141421], [0.81579, 0.342105, 0.141421, 0.282843], [0.868421, 0.342105, 0.1, 0.1], [0.868421, 0.342105, 0.282843, 0.141421], [0.868421, 0.342105, 0.141421, 0.282843], [0.921053, 0.342105, 0.1, 0.1], [0.921053, 0.342105, 0.282843, 0.141421], [0.921053, 0.342105, 0.141421, 0.282843], [0.973684, 0.342105, 0.1, 0.1], [0.973684, 0.342105, 0.282843, 0.141421], [0.973684, 0.342105, 0.141421, 0.282843], [0.0263158, 0.394737, 0.1, 0.1], [0.0263158, 0.394737, 0.282843, 0.141421], [0.0263158, 0.394737, 0.141421, 0.282843], [0.0789474, 0.394737, 0.1, 0.1], [0.0789474, 0.394737, 0.282843, 0.141421], [0.0789474, 0.394737, 0.141421, 0.282843], [0.131579, 0.394737, 0.1, 0.1], [0.131579, 0.394737, 0.282843, 0.141421], [0.131579, 0.394737, 0.141421, 0.282843], [0.184211, 0.394737, 0.1, 0.1], [0.184211, 0.394737, 0.282843, 0.141421], [0.184211, 0.394737, 0.141421, 0.282843], [0.236842, 0.394737, 0.1, 0.1], [0.236842, 0.394737, 0.282843, 0.141421], [0.236842, 0.394737, 0.141421, 0.282843], [0.289474, 0.394737, 0.1, 0.1], [0.289474, 0.394737, 0.282843, 0.141421], [0.289474, 0.394737, 0.141421, 0.282843], [0.342105, 0.394737, 0.1, 0.1], [0.342105, 0.394737, 0.282843, 0.141421], [0.342105, 0.394737, 0.141421, 0.282843], [0.394737, 0.394737, 0.1, 0.1], [0.394737, 0.394737, 0.282843, 0.141421], [0.394737, 0.394737, 0.141421, 0.282843], [0.447368, 0.394737, 0.1, 0.1], [0.447368, 0.394737, 0.282843, 0.141421], [0.447368, 0.394737, 0.141421, 0.282843], [0.5, 0.394737, 0.1, 0.1], [0.5, 0.394737, 0.282843, 0.141421], [0.5, 0.394737, 0.141421, 0.282843], [0.552632, 0.394737, 0.1, 0.1], [0.552632, 0.394737, 0.282843, 0.141421], [0.552632, 0.394737, 0.141421, 0.282843], [0.605263, 0.394737, 0.1, 0.1], [0.605263, 0.394737, 0.282843, 0.141421], [0.605263, 0.394737, 0.141421, 0.282843], [0.657895, 0.394737, 0.1, 0.1], [0.657895, 0.394737, 0.282843, 0.141421], [0.657895, 0.394737, 0.141421, 0.282843], [0.710526, 0.394737, 0.1, 0.1], [0.710526, 0.394737, 0.282843, 0.141421], [0.710526, 0.394737, 0.141421, 0.282843], [0.763158, 0.394737, 0.1, 0.1], [0.763158, 0.394737, 0.282843, 0.141421], [0.763158, 0.394737, 0.141421, 0.282843], [0.81579, 0.394737, 0.1, 0.1], [0.81579, 0.394737, 0.282843, 0.141421], [0.81579, 0.394737, 0.141421, 0.282843], [0.868421, 0.394737, 0.1, 0.1], [0.868421, 0.394737, 0.282843, 0.141421], [0.868421, 0.394737, 0.141421, 0.282843], [0.921053, 0.394737, 0.1, 0.1], [0.921053, 0.394737, 0.282843, 0.141421], [0.921053, 0.394737, 0.141421, 0.282843], [0.973684, 0.394737, 0.1, 0.1], [0.973684, 0.394737, 0.282843, 0.141421], [0.973684, 0.394737, 0.141421, 0.282843], [0.0263158, 0.447368, 0.1, 0.1], [0.0263158, 0.447368, 0.282843, 0.141421], [0.0263158, 0.447368, 0.141421, 0.282843], [0.0789474, 0.447368, 0.1, 0.1], [0.0789474, 0.447368, 0.282843, 0.141421], [0.0789474, 0.447368, 0.141421, 0.282843], [0.131579, 0.447368, 0.1, 0.1], [0.131579, 0.447368, 0.282843, 0.141421], [0.131579, 0.447368, 0.141421, 0.282843], [0.184211, 0.447368, 0.1, 0.1], [0.184211, 0.447368, 0.282843, 0.141421], [0.184211, 0.447368, 0.141421, 0.282843], [0.236842, 0.447368, 0.1, 0.1], [0.236842, 0.447368, 0.282843, 0.141421], [0.236842, 0.447368, 0.141421, 0.282843], [0.289474, 0.447368, 0.1, 0.1], [0.289474, 0.447368, 0.282843, 0.141421], [0.289474, 0.447368, 0.141421, 0.282843], [0.342105, 0.447368, 0.1, 0.1], [0.342105, 0.447368, 0.282843, 0.141421], [0.342105, 0.447368, 0.141421, 0.282843], [0.394737, 0.447368, 0.1, 0.1], [0.394737, 0.447368, 0.282843, 0.141421], [0.394737, 0.447368, 0.141421, 0.282843], [0.447368, 0.447368, 0.1, 0.1], [0.447368, 0.447368, 0.282843, 0.141421], [0.447368, 0.447368, 0.141421, 0.282843], [0.5, 0.447368, 0.1, 0.1], [0.5, 0.447368, 0.282843, 0.141421], [0.5, 0.447368, 0.141421, 0.282843], [0.552632, 0.447368, 0.1, 0.1], [0.552632, 0.447368, 0.282843, 0.141421], [0.552632, 0.447368, 0.141421, 0.282843], [0.605263, 0.447368, 0.1, 0.1], [0.605263, 0.447368, 0.282843, 0.141421], [0.605263, 0.447368, 0.141421, 0.282843], [0.657895, 0.447368, 0.1, 0.1], [0.657895, 0.447368, 0.282843, 0.141421], [0.657895, 0.447368, 0.141421, 0.282843], [0.710526, 0.447368, 0.1, 0.1], [0.710526, 0.447368, 0.282843, 0.141421], [0.710526, 0.447368, 0.141421, 0.282843], [0.763158, 0.447368, 0.1, 0.1], [0.763158, 0.447368, 0.282843, 0.141421], [0.763158, 0.447368, 0.141421, 0.282843], [0.81579, 0.447368, 0.1, 0.1], [0.81579, 0.447368, 0.282843, 0.141421], [0.81579, 0.447368, 0.141421, 0.282843], [0.868421, 0.447368, 0.1, 0.1], [0.868421, 0.447368, 0.282843, 0.141421], [0.868421, 0.447368, 0.141421, 0.282843], [0.921053, 0.447368, 0.1, 0.1], [0.921053, 0.447368, 0.282843, 0.141421], [0.921053, 0.447368, 0.141421, 0.282843], [0.973684, 0.447368, 0.1, 0.1], [0.973684, 0.447368, 0.282843, 0.141421], [0.973684, 0.447368, 0.141421, 0.282843], [0.0263158, 0.5, 0.1, 0.1], [0.0263158, 0.5, 0.282843, 0.141421], [0.0263158, 0.5, 0.141421, 0.282843], [0.0789474, 0.5, 0.1, 0.1], [0.0789474, 0.5, 0.282843, 0.141421], [0.0789474, 0.5, 0.141421, 0.282843], [0.131579, 0.5, 0.1, 0.1], [0.131579, 0.5, 0.282843, 0.141421], [0.131579, 0.5, 0.141421, 0.282843], [0.184211, 0.5, 0.1, 0.1], [0.184211, 0.5, 0.282843, 0.141421], [0.184211, 0.5, 0.141421, 0.282843], [0.236842, 0.5, 0.1, 0.1], [0.236842, 0.5, 0.282843, 0.141421], [0.236842, 0.5, 0.141421, 0.282843], [0.289474, 0.5, 0.1, 0.1], [0.289474, 0.5, 0.282843, 0.141421], [0.289474, 0.5, 0.141421, 0.282843], [0.342105, 0.5, 0.1, 0.1], [0.342105, 0.5, 0.282843, 0.141421], [0.342105, 0.5, 0.141421, 0.282843], [0.394737, 0.5, 0.1, 0.1], [0.394737, 0.5, 0.282843, 0.141421], [0.394737, 0.5, 0.141421, 0.282843], [0.447368, 0.5, 0.1, 0.1], [0.447368, 0.5, 0.282843, 0.141421], [0.447368, 0.5, 0.141421, 0.282843], [0.5, 0.5, 0.1, 0.1], [0.5, 0.5, 0.282843, 0.141421], [0.5, 0.5, 0.141421, 0.282843], [0.552632, 0.5, 0.1, 0.1], [0.552632, 0.5, 0.282843, 0.141421], [0.552632, 0.5, 0.141421, 0.282843], [0.605263, 0.5, 0.1, 0.1], [0.605263, 0.5, 0.282843, 0.141421], [0.605263, 0.5, 0.141421, 0.282843], [0.657895, 0.5, 0.1, 0.1], [0.657895, 0.5, 0.282843, 0.141421], [0.657895, 0.5, 0.141421, 0.282843], [0.710526, 0.5, 0.1, 0.1], [0.710526, 0.5, 0.282843, 0.141421], [0.710526, 0.5, 0.141421, 0.282843], [0.763158, 0.5, 0.1, 0.1], [0.763158, 0.5, 0.282843, 0.141421], [0.763158, 0.5, 0.141421, 0.282843], [0.81579, 0.5, 0.1, 0.1], [0.81579, 0.5, 0.282843, 0.141421], [0.81579, 0.5, 0.141421, 0.282843], [0.868421, 0.5, 0.1, 0.1], [0.868421, 0.5, 0.282843, 0.141421], [0.868421, 0.5, 0.141421, 0.282843], [0.921053, 0.5, 0.1, 0.1], [0.921053, 0.5, 0.282843, 0.141421], [0.921053, 0.5, 0.141421, 0.282843], [0.973684, 0.5, 0.1, 0.1], [0.973684, 0.5, 0.282843, 0.141421], [0.973684, 0.5, 0.141421, 0.282843], [0.0263158, 0.552632, 0.1, 0.1], [0.0263158, 0.552632, 0.282843, 0.141421], [0.0263158, 0.552632, 0.141421, 0.282843], [0.0789474, 0.552632, 0.1, 0.1], [0.0789474, 0.552632, 0.282843, 0.141421], [0.0789474, 0.552632, 0.141421, 0.282843], [0.131579, 0.552632, 0.1, 0.1], [0.131579, 0.552632, 0.282843, 0.141421], [0.131579, 0.552632, 0.141421, 0.282843], [0.184211, 0.552632, 0.1, 0.1], [0.184211, 0.552632, 0.282843, 0.141421], [0.184211, 0.552632, 0.141421, 0.282843], [0.236842, 0.552632, 0.1, 0.1], [0.236842, 0.552632, 0.282843, 0.141421], [0.236842, 0.552632, 0.141421, 0.282843], [0.289474, 0.552632, 0.1, 0.1], [0.289474, 0.552632, 0.282843, 0.141421], [0.289474, 0.552632, 0.141421, 0.282843], [0.342105, 0.552632, 0.1, 0.1], [0.342105, 0.552632, 0.282843, 0.141421], [0.342105, 0.552632, 0.141421, 0.282843], [0.394737, 0.552632, 0.1, 0.1], [0.394737, 0.552632, 0.282843, 0.141421], [0.394737, 0.552632, 0.141421, 0.282843], [0.447368, 0.552632, 0.1, 0.1], [0.447368, 0.552632, 0.282843, 0.141421], [0.447368, 0.552632, 0.141421, 0.282843], [0.5, 0.552632, 0.1, 0.1], [0.5, 0.552632, 0.282843, 0.141421], [0.5, 0.552632, 0.141421, 0.282843], [0.552632, 0.552632, 0.1, 0.1], [0.552632, 0.552632, 0.282843, 0.141421], [0.552632, 0.552632, 0.141421, 0.282843], [0.605263, 0.552632, 0.1, 0.1], [0.605263, 0.552632, 0.282843, 0.141421], [0.605263, 0.552632, 0.141421, 0.282843], [0.657895, 0.552632, 0.1, 0.1], [0.657895, 0.552632, 0.282843, 0.141421], [0.657895, 0.552632, 0.141421, 0.282843], [0.710526, 0.552632, 0.1, 0.1], [0.710526, 0.552632, 0.282843, 0.141421], [0.710526, 0.552632, 0.141421, 0.282843], [0.763158, 0.552632, 0.1, 0.1], [0.763158, 0.552632, 0.282843, 0.141421], [0.763158, 0.552632, 0.141421, 0.282843], [0.81579, 0.552632, 0.1, 0.1], [0.81579, 0.552632, 0.282843, 0.141421], [0.81579, 0.552632, 0.141421, 0.282843], [0.868421, 0.552632, 0.1, 0.1], [0.868421, 0.552632, 0.282843, 0.141421], [0.868421, 0.552632, 0.141421, 0.282843], [0.921053, 0.552632, 0.1, 0.1], [0.921053, 0.552632, 0.282843, 0.141421], [0.921053, 0.552632, 0.141421, 0.282843], [0.973684, 0.552632, 0.1, 0.1], [0.973684, 0.552632, 0.282843, 0.141421], [0.973684, 0.552632, 0.141421, 0.282843], [0.0263158, 0.605263, 0.1, 0.1], [0.0263158, 0.605263, 0.282843, 0.141421], [0.0263158, 0.605263, 0.141421, 0.282843], [0.0789474, 0.605263, 0.1, 0.1], [0.0789474, 0.605263, 0.282843, 0.141421], [0.0789474, 0.605263, 0.141421, 0.282843], [0.131579, 0.605263, 0.1, 0.1], [0.131579, 0.605263, 0.282843, 0.141421], [0.131579, 0.605263, 0.141421, 0.282843], [0.184211, 0.605263, 0.1, 0.1], [0.184211, 0.605263, 0.282843, 0.141421], [0.184211, 0.605263, 0.141421, 0.282843], [0.236842, 0.605263, 0.1, 0.1], [0.236842, 0.605263, 0.282843, 0.141421], [0.236842, 0.605263, 0.141421, 0.282843], [0.289474, 0.605263, 0.1, 0.1], [0.289474, 0.605263, 0.282843, 0.141421], [0.289474, 0.605263, 0.141421, 0.282843], [0.342105, 0.605263, 0.1, 0.1], [0.342105, 0.605263, 0.282843, 0.141421], [0.342105, 0.605263, 0.141421, 0.282843], [0.394737, 0.605263, 0.1, 0.1], [0.394737, 0.605263, 0.282843, 0.141421], [0.394737, 0.605263, 0.141421, 0.282843], [0.447368, 0.605263, 0.1, 0.1], [0.447368, 0.605263, 0.282843, 0.141421], [0.447368, 0.605263, 0.141421, 0.282843], [0.5, 0.605263, 0.1, 0.1], [0.5, 0.605263, 0.282843, 0.141421], [0.5, 0.605263, 0.141421, 0.282843], [0.552632, 0.605263, 0.1, 0.1], [0.552632, 0.605263, 0.282843, 0.141421], [0.552632, 0.605263, 0.141421, 0.282843], [0.605263, 0.605263, 0.1, 0.1], [0.605263, 0.605263, 0.282843, 0.141421], [0.605263, 0.605263, 0.141421, 0.282843], [0.657895, 0.605263, 0.1, 0.1], [0.657895, 0.605263, 0.282843, 0.141421], [0.657895, 0.605263, 0.141421, 0.282843], [0.710526, 0.605263, 0.1, 0.1], [0.710526, 0.605263, 0.282843, 0.141421], [0.710526, 0.605263, 0.141421, 0.282843], [0.763158, 0.605263, 0.1, 0.1], [0.763158, 0.605263, 0.282843, 0.141421], [0.763158, 0.605263, 0.141421, 0.282843], [0.81579, 0.605263, 0.1, 0.1], [0.81579, 0.605263, 0.282843, 0.141421], [0.81579, 0.605263, 0.141421, 0.282843], [0.868421, 0.605263, 0.1, 0.1], [0.868421, 0.605263, 0.282843, 0.141421], [0.868421, 0.605263, 0.141421, 0.282843], [0.921053, 0.605263, 0.1, 0.1], [0.921053, 0.605263, 0.282843, 0.141421], [0.921053, 0.605263, 0.141421, 0.282843], [0.973684, 0.605263, 0.1, 0.1], [0.973684, 0.605263, 0.282843, 0.141421], [0.973684, 0.605263, 0.141421, 0.282843], [0.0263158, 0.657895, 0.1, 0.1], [0.0263158, 0.657895, 0.282843, 0.141421], [0.0263158, 0.657895, 0.141421, 0.282843], [0.0789474, 0.657895, 0.1, 0.1], [0.0789474, 0.657895, 0.282843, 0.141421], [0.0789474, 0.657895, 0.141421, 0.282843], [0.131579, 0.657895, 0.1, 0.1], [0.131579, 0.657895, 0.282843, 0.141421], [0.131579, 0.657895, 0.141421, 0.282843], [0.184211, 0.657895, 0.1, 0.1], [0.184211, 0.657895, 0.282843, 0.141421], [0.184211, 0.657895, 0.141421, 0.282843], [0.236842, 0.657895, 0.1, 0.1], [0.236842, 0.657895, 0.282843, 0.141421], [0.236842, 0.657895, 0.141421, 0.282843], [0.289474, 0.657895, 0.1, 0.1], [0.289474, 0.657895, 0.282843, 0.141421], [0.289474, 0.657895, 0.141421, 0.282843], [0.342105, 0.657895, 0.1, 0.1], [0.342105, 0.657895, 0.282843, 0.141421], [0.342105, 0.657895, 0.141421, 0.282843], [0.394737, 0.657895, 0.1, 0.1], [0.394737, 0.657895, 0.282843, 0.141421], [0.394737, 0.657895, 0.141421, 0.282843], [0.447368, 0.657895, 0.1, 0.1], [0.447368, 0.657895, 0.282843, 0.141421], [0.447368, 0.657895, 0.141421, 0.282843], [0.5, 0.657895, 0.1, 0.1], [0.5, 0.657895, 0.282843, 0.141421], [0.5, 0.657895, 0.141421, 0.282843], [0.552632, 0.657895, 0.1, 0.1], [0.552632, 0.657895, 0.282843, 0.141421], [0.552632, 0.657895, 0.141421, 0.282843], [0.605263, 0.657895, 0.1, 0.1], [0.605263, 0.657895, 0.282843, 0.141421], [0.605263, 0.657895, 0.141421, 0.282843], [0.657895, 0.657895, 0.1, 0.1], [0.657895, 0.657895, 0.282843, 0.141421], [0.657895, 0.657895, 0.141421, 0.282843], [0.710526, 0.657895, 0.1, 0.1], [0.710526, 0.657895, 0.282843, 0.141421], [0.710526, 0.657895, 0.141421, 0.282843], [0.763158, 0.657895, 0.1, 0.1], [0.763158, 0.657895, 0.282843, 0.141421], [0.763158, 0.657895, 0.141421, 0.282843], [0.81579, 0.657895, 0.1, 0.1], [0.81579, 0.657895, 0.282843, 0.141421], [0.81579, 0.657895, 0.141421, 0.282843], [0.868421, 0.657895, 0.1, 0.1], [0.868421, 0.657895, 0.282843, 0.141421], [0.868421, 0.657895, 0.141421, 0.282843], [0.921053, 0.657895, 0.1, 0.1], [0.921053, 0.657895, 0.282843, 0.141421], [0.921053, 0.657895, 0.141421, 0.282843], [0.973684, 0.657895, 0.1, 0.1], [0.973684, 0.657895, 0.282843, 0.141421], [0.973684, 0.657895, 0.141421, 0.282843], [0.0263158, 0.710526, 0.1, 0.1], [0.0263158, 0.710526, 0.282843, 0.141421], [0.0263158, 0.710526, 0.141421, 0.282843], [0.0789474, 0.710526, 0.1, 0.1], [0.0789474, 0.710526, 0.282843, 0.141421], [0.0789474, 0.710526, 0.141421, 0.282843], [0.131579, 0.710526, 0.1, 0.1], [0.131579, 0.710526, 0.282843, 0.141421], [0.131579, 0.710526, 0.141421, 0.282843], [0.184211, 0.710526, 0.1, 0.1], [0.184211, 0.710526, 0.282843, 0.141421], [0.184211, 0.710526, 0.141421, 0.282843], [0.236842, 0.710526, 0.1, 0.1], [0.236842, 0.710526, 0.282843, 0.141421], [0.236842, 0.710526, 0.141421, 0.282843], [0.289474, 0.710526, 0.1, 0.1], [0.289474, 0.710526, 0.282843, 0.141421], [0.289474, 0.710526, 0.141421, 0.282843], [0.342105, 0.710526, 0.1, 0.1], [0.342105, 0.710526, 0.282843, 0.141421], [0.342105, 0.710526, 0.141421, 0.282843], [0.394737, 0.710526, 0.1, 0.1], [0.394737, 0.710526, 0.282843, 0.141421], [0.394737, 0.710526, 0.141421, 0.282843], [0.447368, 0.710526, 0.1, 0.1], [0.447368, 0.710526, 0.282843, 0.141421], [0.447368, 0.710526, 0.141421, 0.282843], [0.5, 0.710526, 0.1, 0.1], [0.5, 0.710526, 0.282843, 0.141421], [0.5, 0.710526, 0.141421, 0.282843], [0.552632, 0.710526, 0.1, 0.1], [0.552632, 0.710526, 0.282843, 0.141421], [0.552632, 0.710526, 0.141421, 0.282843], [0.605263, 0.710526, 0.1, 0.1], [0.605263, 0.710526, 0.282843, 0.141421], [0.605263, 0.710526, 0.141421, 0.282843], [0.657895, 0.710526, 0.1, 0.1], [0.657895, 0.710526, 0.282843, 0.141421], [0.657895, 0.710526, 0.141421, 0.282843], [0.710526, 0.710526, 0.1, 0.1], [0.710526, 0.710526, 0.282843, 0.141421], [0.710526, 0.710526, 0.141421, 0.282843], [0.763158, 0.710526, 0.1, 0.1], [0.763158, 0.710526, 0.282843, 0.141421], [0.763158, 0.710526, 0.141421, 0.282843], [0.81579, 0.710526, 0.1, 0.1], [0.81579, 0.710526, 0.282843, 0.141421], [0.81579, 0.710526, 0.141421, 0.282843], [0.868421, 0.710526, 0.1, 0.1], [0.868421, 0.710526, 0.282843, 0.141421], [0.868421, 0.710526, 0.141421, 0.282843], [0.921053, 0.710526, 0.1, 0.1], [0.921053, 0.710526, 0.282843, 0.141421], [0.921053, 0.710526, 0.141421, 0.282843], [0.973684, 0.710526, 0.1, 0.1], [0.973684, 0.710526, 0.282843, 0.141421], [0.973684, 0.710526, 0.141421, 0.282843], [0.0263158, 0.763158, 0.1, 0.1], [0.0263158, 0.763158, 0.282843, 0.141421], [0.0263158, 0.763158, 0.141421, 0.282843], [0.0789474, 0.763158, 0.1, 0.1], [0.0789474, 0.763158, 0.282843, 0.141421], [0.0789474, 0.763158, 0.141421, 0.282843], [0.131579, 0.763158, 0.1, 0.1], [0.131579, 0.763158, 0.282843, 0.141421], [0.131579, 0.763158, 0.141421, 0.282843], [0.184211, 0.763158, 0.1, 0.1], [0.184211, 0.763158, 0.282843, 0.141421], [0.184211, 0.763158, 0.141421, 0.282843], [0.236842, 0.763158, 0.1, 0.1], [0.236842, 0.763158, 0.282843, 0.141421], [0.236842, 0.763158, 0.141421, 0.282843], [0.289474, 0.763158, 0.1, 0.1], [0.289474, 0.763158, 0.282843, 0.141421], [0.289474, 0.763158, 0.141421, 0.282843], [0.342105, 0.763158, 0.1, 0.1], [0.342105, 0.763158, 0.282843, 0.141421], [0.342105, 0.763158, 0.141421, 0.282843], [0.394737, 0.763158, 0.1, 0.1], [0.394737, 0.763158, 0.282843, 0.141421], [0.394737, 0.763158, 0.141421, 0.282843], [0.447368, 0.763158, 0.1, 0.1], [0.447368, 0.763158, 0.282843, 0.141421], [0.447368, 0.763158, 0.141421, 0.282843], [0.5, 0.763158, 0.1, 0.1], [0.5, 0.763158, 0.282843, 0.141421], [0.5, 0.763158, 0.141421, 0.282843], [0.552632, 0.763158, 0.1, 0.1], [0.552632, 0.763158, 0.282843, 0.141421], [0.552632, 0.763158, 0.141421, 0.282843], [0.605263, 0.763158, 0.1, 0.1], [0.605263, 0.763158, 0.282843, 0.141421], [0.605263, 0.763158, 0.141421, 0.282843], [0.657895, 0.763158, 0.1, 0.1], [0.657895, 0.763158, 0.282843, 0.141421], [0.657895, 0.763158, 0.141421, 0.282843], [0.710526, 0.763158, 0.1, 0.1], [0.710526, 0.763158, 0.282843, 0.141421], [0.710526, 0.763158, 0.141421, 0.282843], [0.763158, 0.763158, 0.1, 0.1], [0.763158, 0.763158, 0.282843, 0.141421], [0.763158, 0.763158, 0.141421, 0.282843], [0.81579, 0.763158, 0.1, 0.1], [0.81579, 0.763158, 0.282843, 0.141421], [0.81579, 0.763158, 0.141421, 0.282843], [0.868421, 0.763158, 0.1, 0.1], [0.868421, 0.763158, 0.282843, 0.141421], [0.868421, 0.763158, 0.141421, 0.282843], [0.921053, 0.763158, 0.1, 0.1], [0.921053, 0.763158, 0.282843, 0.141421], [0.921053, 0.763158, 0.141421, 0.282843], [0.973684, 0.763158, 0.1, 0.1], [0.973684, 0.763158, 0.282843, 0.141421], [0.973684, 0.763158, 0.141421, 0.282843], [0.0263158, 0.81579, 0.1, 0.1], [0.0263158, 0.81579, 0.282843, 0.141421], [0.0263158, 0.81579, 0.141421, 0.282843], [0.0789474, 0.81579, 0.1, 0.1], [0.0789474, 0.81579, 0.282843, 0.141421], [0.0789474, 0.81579, 0.141421, 0.282843], [0.131579, 0.81579, 0.1, 0.1], [0.131579, 0.81579, 0.282843, 0.141421], [0.131579, 0.81579, 0.141421, 0.282843], [0.184211, 0.81579, 0.1, 0.1], [0.184211, 0.81579, 0.282843, 0.141421], [0.184211, 0.81579, 0.141421, 0.282843], [0.236842, 0.81579, 0.1, 0.1], [0.236842, 0.81579, 0.282843, 0.141421], [0.236842, 0.81579, 0.141421, 0.282843], [0.289474, 0.81579, 0.1, 0.1], [0.289474, 0.81579, 0.282843, 0.141421], [0.289474, 0.81579, 0.141421, 0.282843], [0.342105, 0.81579, 0.1, 0.1], [0.342105, 0.81579, 0.282843, 0.141421], [0.342105, 0.81579, 0.141421, 0.282843], [0.394737, 0.81579, 0.1, 0.1], [0.394737, 0.81579, 0.282843, 0.141421], [0.394737, 0.81579, 0.141421, 0.282843], [0.447368, 0.81579, 0.1, 0.1], [0.447368, 0.81579, 0.282843, 0.141421], [0.447368, 0.81579, 0.141421, 0.282843], [0.5, 0.81579, 0.1, 0.1], [0.5, 0.81579, 0.282843, 0.141421], [0.5, 0.81579, 0.141421, 0.282843], [0.552632, 0.81579, 0.1, 0.1], [0.552632, 0.81579, 0.282843, 0.141421], [0.552632, 0.81579, 0.141421, 0.282843], [0.605263, 0.81579, 0.1, 0.1], [0.605263, 0.81579, 0.282843, 0.141421], [0.605263, 0.81579, 0.141421, 0.282843], [0.657895, 0.81579, 0.1, 0.1], [0.657895, 0.81579, 0.282843, 0.141421], [0.657895, 0.81579, 0.141421, 0.282843], [0.710526, 0.81579, 0.1, 0.1], [0.710526, 0.81579, 0.282843, 0.141421], [0.710526, 0.81579, 0.141421, 0.282843], [0.763158, 0.81579, 0.1, 0.1], [0.763158, 0.81579, 0.282843, 0.141421], [0.763158, 0.81579, 0.141421, 0.282843], [0.81579, 0.81579, 0.1, 0.1], [0.81579, 0.81579, 0.282843, 0.141421], [0.81579, 0.81579, 0.141421, 0.282843], [0.868421, 0.81579, 0.1, 0.1], [0.868421, 0.81579, 0.282843, 0.141421], [0.868421, 0.81579, 0.141421, 0.282843], [0.921053, 0.81579, 0.1, 0.1], [0.921053, 0.81579, 0.282843, 0.141421], [0.921053, 0.81579, 0.141421, 0.282843], [0.973684, 0.81579, 0.1, 0.1], [0.973684, 0.81579, 0.282843, 0.141421], [0.973684, 0.81579, 0.141421, 0.282843], [0.0263158, 0.868421, 0.1, 0.1], [0.0263158, 0.868421, 0.282843, 0.141421], [0.0263158, 0.868421, 0.141421, 0.282843], [0.0789474, 0.868421, 0.1, 0.1], [0.0789474, 0.868421, 0.282843, 0.141421], [0.0789474, 0.868421, 0.141421, 0.282843], [0.131579, 0.868421, 0.1, 0.1], [0.131579, 0.868421, 0.282843, 0.141421], [0.131579, 0.868421, 0.141421, 0.282843], [0.184211, 0.868421, 0.1, 0.1], [0.184211, 0.868421, 0.282843, 0.141421], [0.184211, 0.868421, 0.141421, 0.282843], [0.236842, 0.868421, 0.1, 0.1], [0.236842, 0.868421, 0.282843, 0.141421], [0.236842, 0.868421, 0.141421, 0.282843], [0.289474, 0.868421, 0.1, 0.1], [0.289474, 0.868421, 0.282843, 0.141421], [0.289474, 0.868421, 0.141421, 0.282843], [0.342105, 0.868421, 0.1, 0.1], [0.342105, 0.868421, 0.282843, 0.141421], [0.342105, 0.868421, 0.141421, 0.282843], [0.394737, 0.868421, 0.1, 0.1], [0.394737, 0.868421, 0.282843, 0.141421], [0.394737, 0.868421, 0.141421, 0.282843], [0.447368, 0.868421, 0.1, 0.1], [0.447368, 0.868421, 0.282843, 0.141421], [0.447368, 0.868421, 0.141421, 0.282843], [0.5, 0.868421, 0.1, 0.1], [0.5, 0.868421, 0.282843, 0.141421], [0.5, 0.868421, 0.141421, 0.282843], [0.552632, 0.868421, 0.1, 0.1], [0.552632, 0.868421, 0.282843, 0.141421], [0.552632, 0.868421, 0.141421, 0.282843], [0.605263, 0.868421, 0.1, 0.1], [0.605263, 0.868421, 0.282843, 0.141421], [0.605263, 0.868421, 0.141421, 0.282843], [0.657895, 0.868421, 0.1, 0.1], [0.657895, 0.868421, 0.282843, 0.141421], [0.657895, 0.868421, 0.141421, 0.282843], [0.710526, 0.868421, 0.1, 0.1], [0.710526, 0.868421, 0.282843, 0.141421], [0.710526, 0.868421, 0.141421, 0.282843], [0.763158, 0.868421, 0.1, 0.1], [0.763158, 0.868421, 0.282843, 0.141421], [0.763158, 0.868421, 0.141421, 0.282843], [0.81579, 0.868421, 0.1, 0.1], [0.81579, 0.868421, 0.282843, 0.141421], [0.81579, 0.868421, 0.141421, 0.282843], [0.868421, 0.868421, 0.1, 0.1], [0.868421, 0.868421, 0.282843, 0.141421], [0.868421, 0.868421, 0.141421, 0.282843], [0.921053, 0.868421, 0.1, 0.1], [0.921053, 0.868421, 0.282843, 0.141421], [0.921053, 0.868421, 0.141421, 0.282843], [0.973684, 0.868421, 0.1, 0.1], [0.973684, 0.868421, 0.282843, 0.141421], [0.973684, 0.868421, 0.141421, 0.282843], [0.0263158, 0.921053, 0.1, 0.1], [0.0263158, 0.921053, 0.282843, 0.141421], [0.0263158, 0.921053, 0.141421, 0.282843], [0.0789474, 0.921053, 0.1, 0.1], [0.0789474, 0.921053, 0.282843, 0.141421], [0.0789474, 0.921053, 0.141421, 0.282843], [0.131579, 0.921053, 0.1, 0.1], [0.131579, 0.921053, 0.282843, 0.141421], [0.131579, 0.921053, 0.141421, 0.282843], [0.184211, 0.921053, 0.1, 0.1], [0.184211, 0.921053, 0.282843, 0.141421], [0.184211, 0.921053, 0.141421, 0.282843], [0.236842, 0.921053, 0.1, 0.1], [0.236842, 0.921053, 0.282843, 0.141421], [0.236842, 0.921053, 0.141421, 0.282843], [0.289474, 0.921053, 0.1, 0.1], [0.289474, 0.921053, 0.282843, 0.141421], [0.289474, 0.921053, 0.141421, 0.282843], [0.342105, 0.921053, 0.1, 0.1], [0.342105, 0.921053, 0.282843, 0.141421], [0.342105, 0.921053, 0.141421, 0.282843], [0.394737, 0.921053, 0.1, 0.1], [0.394737, 0.921053, 0.282843, 0.141421], [0.394737, 0.921053, 0.141421, 0.282843], [0.447368, 0.921053, 0.1, 0.1], [0.447368, 0.921053, 0.282843, 0.141421], [0.447368, 0.921053, 0.141421, 0.282843], [0.5, 0.921053, 0.1, 0.1], [0.5, 0.921053, 0.282843, 0.141421], [0.5, 0.921053, 0.141421, 0.282843], [0.552632, 0.921053, 0.1, 0.1], [0.552632, 0.921053, 0.282843, 0.141421], [0.552632, 0.921053, 0.141421, 0.282843], [0.605263, 0.921053, 0.1, 0.1], [0.605263, 0.921053, 0.282843, 0.141421], [0.605263, 0.921053, 0.141421, 0.282843], [0.657895, 0.921053, 0.1, 0.1], [0.657895, 0.921053, 0.282843, 0.141421], [0.657895, 0.921053, 0.141421, 0.282843], [0.710526, 0.921053, 0.1, 0.1], [0.710526, 0.921053, 0.282843, 0.141421], [0.710526, 0.921053, 0.141421, 0.282843], [0.763158, 0.921053, 0.1, 0.1], [0.763158, 0.921053, 0.282843, 0.141421], [0.763158, 0.921053, 0.141421, 0.282843], [0.81579, 0.921053, 0.1, 0.1], [0.81579, 0.921053, 0.282843, 0.141421], [0.81579, 0.921053, 0.141421, 0.282843], [0.868421, 0.921053, 0.1, 0.1], [0.868421, 0.921053, 0.282843, 0.141421], [0.868421, 0.921053, 0.141421, 0.282843], [0.921053, 0.921053, 0.1, 0.1], [0.921053, 0.921053, 0.282843, 0.141421], [0.921053, 0.921053, 0.141421, 0.282843], [0.973684, 0.921053, 0.1, 0.1], [0.973684, 0.921053, 0.282843, 0.141421], [0.973684, 0.921053, 0.141421, 0.282843], [0.0263158, 0.973684, 0.1, 0.1], [0.0263158, 0.973684, 0.282843, 0.141421], [0.0263158, 0.973684, 0.141421, 0.282843], [0.0789474, 0.973684, 0.1, 0.1], [0.0789474, 0.973684, 0.282843, 0.141421], [0.0789474, 0.973684, 0.141421, 0.282843], [0.131579, 0.973684, 0.1, 0.1], [0.131579, 0.973684, 0.282843, 0.141421], [0.131579, 0.973684, 0.141421, 0.282843], [0.184211, 0.973684, 0.1, 0.1], [0.184211, 0.973684, 0.282843, 0.141421], [0.184211, 0.973684, 0.141421, 0.282843], [0.236842, 0.973684, 0.1, 0.1], [0.236842, 0.973684, 0.282843, 0.141421], [0.236842, 0.973684, 0.141421, 0.282843], [0.289474, 0.973684, 0.1, 0.1], [0.289474, 0.973684, 0.282843, 0.141421], [0.289474, 0.973684, 0.141421, 0.282843], [0.342105, 0.973684, 0.1, 0.1], [0.342105, 0.973684, 0.282843, 0.141421], [0.342105, 0.973684, 0.141421, 0.282843], [0.394737, 0.973684, 0.1, 0.1], [0.394737, 0.973684, 0.282843, 0.141421], [0.394737, 0.973684, 0.141421, 0.282843], [0.447368, 0.973684, 0.1, 0.1], [0.447368, 0.973684, 0.282843, 0.141421], [0.447368, 0.973684, 0.141421, 0.282843], [0.5, 0.973684, 0.1, 0.1], [0.5, 0.973684, 0.282843, 0.141421], [0.5, 0.973684, 0.141421, 0.282843], [0.552632, 0.973684, 0.1, 0.1], [0.552632, 0.973684, 0.282843, 0.141421], [0.552632, 0.973684, 0.141421, 0.282843], [0.605263, 0.973684, 0.1, 0.1], [0.605263, 0.973684, 0.282843, 0.141421], [0.605263, 0.973684, 0.141421, 0.282843], [0.657895, 0.973684, 0.1, 0.1], [0.657895, 0.973684, 0.282843, 0.141421], [0.657895, 0.973684, 0.141421, 0.282843], [0.710526, 0.973684, 0.1, 0.1], [0.710526, 0.973684, 0.282843, 0.141421], [0.710526, 0.973684, 0.141421, 0.282843], [0.763158, 0.973684, 0.1, 0.1], [0.763158, 0.973684, 0.282843, 0.141421], [0.763158, 0.973684, 0.141421, 0.282843], [0.81579, 0.973684, 0.1, 0.1], [0.81579, 0.973684, 0.282843, 0.141421], [0.81579, 0.973684, 0.141421, 0.282843], [0.868421, 0.973684, 0.1, 0.1], [0.868421, 0.973684, 0.282843, 0.141421], [0.868421, 0.973684, 0.141421, 0.282843], [0.921053, 0.973684, 0.1, 0.1], [0.921053, 0.973684, 0.282843, 0.141421], [0.921053, 0.973684, 0.141421, 0.282843], [0.973684, 0.973684, 0.1, 0.1], [0.973684, 0.973684, 0.282843, 0.141421], [0.973684, 0.973684, 0.141421, 0.282843], [0.05, 0.05, 0.35, 0.35], [0.05, 0.05, 0.494975, 0.247487], [0.05, 0.05, 0.247487, 0.494975], [0.05, 0.05, 0.606218, 0.202073], [0.05, 0.05, 0.202062, 0.606248], [0.05, 0.05, 0.41833, 0.41833], [0.15, 0.05, 0.35, 0.35], [0.15, 0.05, 0.494975, 0.247487], [0.15, 0.05, 0.247487, 0.494975], [0.15, 0.05, 0.606218, 0.202073], [0.15, 0.05, 0.202062, 0.606248], [0.15, 0.05, 0.41833, 0.41833], [0.25, 0.05, 0.35, 0.35], [0.25, 0.05, 0.494975, 0.247487], [0.25, 0.05, 0.247487, 0.494975], [0.25, 0.05, 0.606218, 0.202073], [0.25, 0.05, 0.202062, 0.606248], [0.25, 0.05, 0.41833, 0.41833], [0.35, 0.05, 0.35, 0.35], [0.35, 0.05, 0.494975, 0.247487], [0.35, 0.05, 0.247487, 0.494975], [0.35, 0.05, 0.606218, 0.202073], [0.35, 0.05, 0.202062, 0.606248], [0.35, 0.05, 0.41833, 0.41833], [0.45, 0.05, 0.35, 0.35], [0.45, 0.05, 0.494975, 0.247487], [0.45, 0.05, 0.247487, 0.494975], [0.45, 0.05, 0.606218, 0.202073], [0.45, 0.05, 0.202062, 0.606248], [0.45, 0.05, 0.41833, 0.41833], [0.55, 0.05, 0.35, 0.35], [0.55, 0.05, 0.494975, 0.247487], [0.55, 0.05, 0.247487, 0.494975], [0.55, 0.05, 0.606218, 0.202073], [0.55, 0.05, 0.202062, 0.606248], [0.55, 0.05, 0.41833, 0.41833], [0.65, 0.05, 0.35, 0.35], [0.65, 0.05, 0.494975, 0.247487], [0.65, 0.05, 0.247487, 0.494975], [0.65, 0.05, 0.606218, 0.202073], [0.65, 0.05, 0.202062, 0.606248], [0.65, 0.05, 0.41833, 0.41833], [0.75, 0.05, 0.35, 0.35], [0.75, 0.05, 0.494975, 0.247487], [0.75, 0.05, 0.247487, 0.494975], [0.75, 0.05, 0.606218, 0.202073], [0.75, 0.05, 0.202062, 0.606248], [0.75, 0.05, 0.41833, 0.41833], [0.85, 0.05, 0.35, 0.35], [0.85, 0.05, 0.494975, 0.247487], [0.85, 0.05, 0.247487, 0.494975], [0.85, 0.05, 0.606218, 0.202073], [0.85, 0.05, 0.202062, 0.606248], [0.85, 0.05, 0.41833, 0.41833], [0.95, 0.05, 0.35, 0.35], [0.95, 0.05, 0.494975, 0.247487], [0.95, 0.05, 0.247487, 0.494975], [0.95, 0.05, 0.606218, 0.202073], [0.95, 0.05, 0.202063, 0.606248], [0.95, 0.05, 0.41833, 0.41833], [0.05, 0.15, 0.35, 0.35], [0.05, 0.15, 0.494975, 0.247487], [0.05, 0.15, 0.247487, 0.494975], [0.05, 0.15, 0.606218, 0.202073], [0.05, 0.15, 0.202062, 0.606248], [0.05, 0.15, 0.41833, 0.41833], [0.15, 0.15, 0.35, 0.35], [0.15, 0.15, 0.494975, 0.247487], [0.15, 0.15, 0.247487, 0.494975], [0.15, 0.15, 0.606218, 0.202073], [0.15, 0.15, 0.202062, 0.606248], [0.15, 0.15, 0.41833, 0.41833], [0.25, 0.15, 0.35, 0.35], [0.25, 0.15, 0.494975, 0.247487], [0.25, 0.15, 0.247487, 0.494975], [0.25, 0.15, 0.606218, 0.202073], [0.25, 0.15, 0.202062, 0.606248], [0.25, 0.15, 0.41833, 0.41833], [0.35, 0.15, 0.35, 0.35], [0.35, 0.15, 0.494975, 0.247487], [0.35, 0.15, 0.247487, 0.494975], [0.35, 0.15, 0.606218, 0.202073], [0.35, 0.15, 0.202062, 0.606248], [0.35, 0.15, 0.41833, 0.41833], [0.45, 0.15, 0.35, 0.35], [0.45, 0.15, 0.494975, 0.247487], [0.45, 0.15, 0.247487, 0.494975], [0.45, 0.15, 0.606218, 0.202073], [0.45, 0.15, 0.202062, 0.606248], [0.45, 0.15, 0.41833, 0.41833], [0.55, 0.15, 0.35, 0.35], [0.55, 0.15, 0.494975, 0.247487], [0.55, 0.15, 0.247487, 0.494975], [0.55, 0.15, 0.606218, 0.202073], [0.55, 0.15, 0.202062, 0.606248], [0.55, 0.15, 0.41833, 0.41833], [0.65, 0.15, 0.35, 0.35], [0.65, 0.15, 0.494975, 0.247487], [0.65, 0.15, 0.247487, 0.494975], [0.65, 0.15, 0.606218, 0.202073], [0.65, 0.15, 0.202062, 0.606248], [0.65, 0.15, 0.41833, 0.41833], [0.75, 0.15, 0.35, 0.35], [0.75, 0.15, 0.494975, 0.247487], [0.75, 0.15, 0.247487, 0.494975], [0.75, 0.15, 0.606218, 0.202073], [0.75, 0.15, 0.202062, 0.606248], [0.75, 0.15, 0.41833, 0.41833], [0.85, 0.15, 0.35, 0.35], [0.85, 0.15, 0.494975, 0.247487], [0.85, 0.15, 0.247487, 0.494975], [0.85, 0.15, 0.606218, 0.202073], [0.85, 0.15, 0.202062, 0.606248], [0.85, 0.15, 0.41833, 0.41833], [0.95, 0.15, 0.35, 0.35], [0.95, 0.15, 0.494975, 0.247487], [0.95, 0.15, 0.247487, 0.494975], [0.95, 0.15, 0.606218, 0.202073], [0.95, 0.15, 0.202063, 0.606248], [0.95, 0.15, 0.41833, 0.41833], [0.05, 0.25, 0.35, 0.35], [0.05, 0.25, 0.494975, 0.247487], [0.05, 0.25, 0.247487, 0.494975], [0.05, 0.25, 0.606218, 0.202073], [0.05, 0.25, 0.202062, 0.606248], [0.05, 0.25, 0.41833, 0.41833], [0.15, 0.25, 0.35, 0.35], [0.15, 0.25, 0.494975, 0.247487], [0.15, 0.25, 0.247487, 0.494975], [0.15, 0.25, 0.606218, 0.202073], [0.15, 0.25, 0.202062, 0.606248], [0.15, 0.25, 0.41833, 0.41833], [0.25, 0.25, 0.35, 0.35], [0.25, 0.25, 0.494975, 0.247487], [0.25, 0.25, 0.247487, 0.494975], [0.25, 0.25, 0.606218, 0.202073], [0.25, 0.25, 0.202062, 0.606248], [0.25, 0.25, 0.41833, 0.41833], [0.35, 0.25, 0.35, 0.35], [0.35, 0.25, 0.494975, 0.247487], [0.35, 0.25, 0.247487, 0.494975], [0.35, 0.25, 0.606218, 0.202073], [0.35, 0.25, 0.202062, 0.606248], [0.35, 0.25, 0.41833, 0.41833], [0.45, 0.25, 0.35, 0.35], [0.45, 0.25, 0.494975, 0.247487], [0.45, 0.25, 0.247487, 0.494975], [0.45, 0.25, 0.606218, 0.202073], [0.45, 0.25, 0.202062, 0.606248], [0.45, 0.25, 0.41833, 0.41833], [0.55, 0.25, 0.35, 0.35], [0.55, 0.25, 0.494975, 0.247487], [0.55, 0.25, 0.247487, 0.494975], [0.55, 0.25, 0.606218, 0.202073], [0.55, 0.25, 0.202062, 0.606248], [0.55, 0.25, 0.41833, 0.41833], [0.65, 0.25, 0.35, 0.35], [0.65, 0.25, 0.494975, 0.247487], [0.65, 0.25, 0.247487, 0.494975], [0.65, 0.25, 0.606218, 0.202073], [0.65, 0.25, 0.202062, 0.606248], [0.65, 0.25, 0.41833, 0.41833], [0.75, 0.25, 0.35, 0.35], [0.75, 0.25, 0.494975, 0.247487], [0.75, 0.25, 0.247487, 0.494975], [0.75, 0.25, 0.606218, 0.202073], [0.75, 0.25, 0.202062, 0.606248], [0.75, 0.25, 0.41833, 0.41833], [0.85, 0.25, 0.35, 0.35], [0.85, 0.25, 0.494975, 0.247487], [0.85, 0.25, 0.247487, 0.494975], [0.85, 0.25, 0.606218, 0.202073], [0.85, 0.25, 0.202062, 0.606248], [0.85, 0.25, 0.41833, 0.41833], [0.95, 0.25, 0.35, 0.35], [0.95, 0.25, 0.494975, 0.247487], [0.95, 0.25, 0.247487, 0.494975], [0.95, 0.25, 0.606218, 0.202073], [0.95, 0.25, 0.202063, 0.606248], [0.95, 0.25, 0.41833, 0.41833], [0.05, 0.35, 0.35, 0.35], [0.05, 0.35, 0.494975, 0.247487], [0.05, 0.35, 0.247487, 0.494975], [0.05, 0.35, 0.606218, 0.202073], [0.05, 0.35, 0.202062, 0.606248], [0.05, 0.35, 0.41833, 0.41833], [0.15, 0.35, 0.35, 0.35], [0.15, 0.35, 0.494975, 0.247487], [0.15, 0.35, 0.247487, 0.494975], [0.15, 0.35, 0.606218, 0.202073], [0.15, 0.35, 0.202062, 0.606248], [0.15, 0.35, 0.41833, 0.41833], [0.25, 0.35, 0.35, 0.35], [0.25, 0.35, 0.494975, 0.247487], [0.25, 0.35, 0.247487, 0.494975], [0.25, 0.35, 0.606218, 0.202073], [0.25, 0.35, 0.202062, 0.606248], [0.25, 0.35, 0.41833, 0.41833], [0.35, 0.35, 0.35, 0.35], [0.35, 0.35, 0.494975, 0.247487], [0.35, 0.35, 0.247487, 0.494975], [0.35, 0.35, 0.606218, 0.202073], [0.35, 0.35, 0.202062, 0.606248], [0.35, 0.35, 0.41833, 0.41833], [0.45, 0.35, 0.35, 0.35], [0.45, 0.35, 0.494975, 0.247487], [0.45, 0.35, 0.247487, 0.494975], [0.45, 0.35, 0.606218, 0.202073], [0.45, 0.35, 0.202062, 0.606248], [0.45, 0.35, 0.41833, 0.41833], [0.55, 0.35, 0.35, 0.35], [0.55, 0.35, 0.494975, 0.247487], [0.55, 0.35, 0.247487, 0.494975], [0.55, 0.35, 0.606218, 0.202073], [0.55, 0.35, 0.202062, 0.606248], [0.55, 0.35, 0.41833, 0.41833], [0.65, 0.35, 0.35, 0.35], [0.65, 0.35, 0.494975, 0.247487], [0.65, 0.35, 0.247487, 0.494975], [0.65, 0.35, 0.606218, 0.202073], [0.65, 0.35, 0.202062, 0.606248], [0.65, 0.35, 0.41833, 0.41833], [0.75, 0.35, 0.35, 0.35], [0.75, 0.35, 0.494975, 0.247487], [0.75, 0.35, 0.247487, 0.494975], [0.75, 0.35, 0.606218, 0.202073], [0.75, 0.35, 0.202062, 0.606248], [0.75, 0.35, 0.41833, 0.41833], [0.85, 0.35, 0.35, 0.35], [0.85, 0.35, 0.494975, 0.247487], [0.85, 0.35, 0.247487, 0.494975], [0.85, 0.35, 0.606218, 0.202073], [0.85, 0.35, 0.202062, 0.606248], [0.85, 0.35, 0.41833, 0.41833], [0.95, 0.35, 0.35, 0.35], [0.95, 0.35, 0.494975, 0.247487], [0.95, 0.35, 0.247487, 0.494975], [0.95, 0.35, 0.606218, 0.202073], [0.95, 0.35, 0.202063, 0.606248], [0.95, 0.35, 0.41833, 0.41833], [0.05, 0.45, 0.35, 0.35], [0.05, 0.45, 0.494975, 0.247487], [0.05, 0.45, 0.247487, 0.494975], [0.05, 0.45, 0.606218, 0.202073], [0.05, 0.45, 0.202062, 0.606248], [0.05, 0.45, 0.41833, 0.41833], [0.15, 0.45, 0.35, 0.35], [0.15, 0.45, 0.494975, 0.247487], [0.15, 0.45, 0.247487, 0.494975], [0.15, 0.45, 0.606218, 0.202073], [0.15, 0.45, 0.202062, 0.606248], [0.15, 0.45, 0.41833, 0.41833], [0.25, 0.45, 0.35, 0.35], [0.25, 0.45, 0.494975, 0.247487], [0.25, 0.45, 0.247487, 0.494975], [0.25, 0.45, 0.606218, 0.202073], [0.25, 0.45, 0.202062, 0.606248], [0.25, 0.45, 0.41833, 0.41833], [0.35, 0.45, 0.35, 0.35], [0.35, 0.45, 0.494975, 0.247487], [0.35, 0.45, 0.247487, 0.494975], [0.35, 0.45, 0.606218, 0.202073], [0.35, 0.45, 0.202062, 0.606248], [0.35, 0.45, 0.41833, 0.41833], [0.45, 0.45, 0.35, 0.35], [0.45, 0.45, 0.494975, 0.247487], [0.45, 0.45, 0.247487, 0.494975], [0.45, 0.45, 0.606218, 0.202073], [0.45, 0.45, 0.202062, 0.606248], [0.45, 0.45, 0.41833, 0.41833], [0.55, 0.45, 0.35, 0.35], [0.55, 0.45, 0.494975, 0.247487], [0.55, 0.45, 0.247487, 0.494975], [0.55, 0.45, 0.606218, 0.202073], [0.55, 0.45, 0.202062, 0.606248], [0.55, 0.45, 0.41833, 0.41833], [0.65, 0.45, 0.35, 0.35], [0.65, 0.45, 0.494975, 0.247487], [0.65, 0.45, 0.247487, 0.494975], [0.65, 0.45, 0.606218, 0.202073], [0.65, 0.45, 0.202062, 0.606248], [0.65, 0.45, 0.41833, 0.41833], [0.75, 0.45, 0.35, 0.35], [0.75, 0.45, 0.494975, 0.247487], [0.75, 0.45, 0.247487, 0.494975], [0.75, 0.45, 0.606218, 0.202073], [0.75, 0.45, 0.202062, 0.606248], [0.75, 0.45, 0.41833, 0.41833], [0.85, 0.45, 0.35, 0.35], [0.85, 0.45, 0.494975, 0.247487], [0.85, 0.45, 0.247487, 0.494975], [0.85, 0.45, 0.606218, 0.202073], [0.85, 0.45, 0.202062, 0.606248], [0.85, 0.45, 0.41833, 0.41833], [0.95, 0.45, 0.35, 0.35], [0.95, 0.45, 0.494975, 0.247487], [0.95, 0.45, 0.247487, 0.494975], [0.95, 0.45, 0.606218, 0.202073], [0.95, 0.45, 0.202063, 0.606248], [0.95, 0.45, 0.41833, 0.41833], [0.05, 0.55, 0.35, 0.35], [0.05, 0.55, 0.494975, 0.247487], [0.05, 0.55, 0.247487, 0.494975], [0.05, 0.55, 0.606218, 0.202073], [0.05, 0.55, 0.202062, 0.606248], [0.05, 0.55, 0.41833, 0.41833], [0.15, 0.55, 0.35, 0.35], [0.15, 0.55, 0.494975, 0.247487], [0.15, 0.55, 0.247487, 0.494975], [0.15, 0.55, 0.606218, 0.202073], [0.15, 0.55, 0.202062, 0.606248], [0.15, 0.55, 0.41833, 0.41833], [0.25, 0.55, 0.35, 0.35], [0.25, 0.55, 0.494975, 0.247487], [0.25, 0.55, 0.247487, 0.494975], [0.25, 0.55, 0.606218, 0.202073], [0.25, 0.55, 0.202062, 0.606248], [0.25, 0.55, 0.41833, 0.41833], [0.35, 0.55, 0.35, 0.35], [0.35, 0.55, 0.494975, 0.247487], [0.35, 0.55, 0.247487, 0.494975], [0.35, 0.55, 0.606218, 0.202073], [0.35, 0.55, 0.202062, 0.606248], [0.35, 0.55, 0.41833, 0.41833], [0.45, 0.55, 0.35, 0.35], [0.45, 0.55, 0.494975, 0.247487], [0.45, 0.55, 0.247487, 0.494975], [0.45, 0.55, 0.606218, 0.202073], [0.45, 0.55, 0.202062, 0.606248], [0.45, 0.55, 0.41833, 0.41833], [0.55, 0.55, 0.35, 0.35], [0.55, 0.55, 0.494975, 0.247487], [0.55, 0.55, 0.247487, 0.494975], [0.55, 0.55, 0.606218, 0.202073], [0.55, 0.55, 0.202062, 0.606248], [0.55, 0.55, 0.41833, 0.41833], [0.65, 0.55, 0.35, 0.35], [0.65, 0.55, 0.494975, 0.247487], [0.65, 0.55, 0.247487, 0.494975], [0.65, 0.55, 0.606218, 0.202073], [0.65, 0.55, 0.202062, 0.606248], [0.65, 0.55, 0.41833, 0.41833], [0.75, 0.55, 0.35, 0.35], [0.75, 0.55, 0.494975, 0.247487], [0.75, 0.55, 0.247487, 0.494975], [0.75, 0.55, 0.606218, 0.202073], [0.75, 0.55, 0.202062, 0.606248], [0.75, 0.55, 0.41833, 0.41833], [0.85, 0.55, 0.35, 0.35], [0.85, 0.55, 0.494975, 0.247487], [0.85, 0.55, 0.247487, 0.494975], [0.85, 0.55, 0.606218, 0.202073], [0.85, 0.55, 0.202062, 0.606248], [0.85, 0.55, 0.41833, 0.41833], [0.95, 0.55, 0.35, 0.35], [0.95, 0.55, 0.494975, 0.247487], [0.95, 0.55, 0.247487, 0.494975], [0.95, 0.55, 0.606218, 0.202073], [0.95, 0.55, 0.202063, 0.606248], [0.95, 0.55, 0.41833, 0.41833], [0.05, 0.65, 0.35, 0.35], [0.05, 0.65, 0.494975, 0.247487], [0.05, 0.65, 0.247487, 0.494975], [0.05, 0.65, 0.606218, 0.202073], [0.05, 0.65, 0.202062, 0.606248], [0.05, 0.65, 0.41833, 0.41833], [0.15, 0.65, 0.35, 0.35], [0.15, 0.65, 0.494975, 0.247487], [0.15, 0.65, 0.247487, 0.494975], [0.15, 0.65, 0.606218, 0.202073], [0.15, 0.65, 0.202062, 0.606248], [0.15, 0.65, 0.41833, 0.41833], [0.25, 0.65, 0.35, 0.35], [0.25, 0.65, 0.494975, 0.247487], [0.25, 0.65, 0.247487, 0.494975], [0.25, 0.65, 0.606218, 0.202073], [0.25, 0.65, 0.202062, 0.606248], [0.25, 0.65, 0.41833, 0.41833], [0.35, 0.65, 0.35, 0.35], [0.35, 0.65, 0.494975, 0.247487], [0.35, 0.65, 0.247487, 0.494975], [0.35, 0.65, 0.606218, 0.202073], [0.35, 0.65, 0.202062, 0.606248], [0.35, 0.65, 0.41833, 0.41833], [0.45, 0.65, 0.35, 0.35], [0.45, 0.65, 0.494975, 0.247487], [0.45, 0.65, 0.247487, 0.494975], [0.45, 0.65, 0.606218, 0.202073], [0.45, 0.65, 0.202062, 0.606248], [0.45, 0.65, 0.41833, 0.41833], [0.55, 0.65, 0.35, 0.35], [0.55, 0.65, 0.494975, 0.247487], [0.55, 0.65, 0.247487, 0.494975], [0.55, 0.65, 0.606218, 0.202073], [0.55, 0.65, 0.202062, 0.606248], [0.55, 0.65, 0.41833, 0.41833], [0.65, 0.65, 0.35, 0.35], [0.65, 0.65, 0.494975, 0.247487], [0.65, 0.65, 0.247487, 0.494975], [0.65, 0.65, 0.606218, 0.202073], [0.65, 0.65, 0.202062, 0.606248], [0.65, 0.65, 0.41833, 0.41833], [0.75, 0.65, 0.35, 0.35], [0.75, 0.65, 0.494975, 0.247487], [0.75, 0.65, 0.247487, 0.494975], [0.75, 0.65, 0.606218, 0.202073], [0.75, 0.65, 0.202062, 0.606248], [0.75, 0.65, 0.41833, 0.41833], [0.85, 0.65, 0.35, 0.35], [0.85, 0.65, 0.494975, 0.247487], [0.85, 0.65, 0.247487, 0.494975], [0.85, 0.65, 0.606218, 0.202073], [0.85, 0.65, 0.202062, 0.606248], [0.85, 0.65, 0.41833, 0.41833], [0.95, 0.65, 0.35, 0.35], [0.95, 0.65, 0.494975, 0.247487], [0.95, 0.65, 0.247487, 0.494975], [0.95, 0.65, 0.606218, 0.202073], [0.95, 0.65, 0.202063, 0.606248], [0.95, 0.65, 0.41833, 0.41833], [0.05, 0.75, 0.35, 0.35], [0.05, 0.75, 0.494975, 0.247487], [0.05, 0.75, 0.247487, 0.494975], [0.05, 0.75, 0.606218, 0.202073], [0.05, 0.75, 0.202062, 0.606248], [0.05, 0.75, 0.41833, 0.41833], [0.15, 0.75, 0.35, 0.35], [0.15, 0.75, 0.494975, 0.247487], [0.15, 0.75, 0.247487, 0.494975], [0.15, 0.75, 0.606218, 0.202073], [0.15, 0.75, 0.202062, 0.606248], [0.15, 0.75, 0.41833, 0.41833], [0.25, 0.75, 0.35, 0.35], [0.25, 0.75, 0.494975, 0.247487], [0.25, 0.75, 0.247487, 0.494975], [0.25, 0.75, 0.606218, 0.202073], [0.25, 0.75, 0.202062, 0.606248], [0.25, 0.75, 0.41833, 0.41833], [0.35, 0.75, 0.35, 0.35], [0.35, 0.75, 0.494975, 0.247487], [0.35, 0.75, 0.247487, 0.494975], [0.35, 0.75, 0.606218, 0.202073], [0.35, 0.75, 0.202062, 0.606248], [0.35, 0.75, 0.41833, 0.41833], [0.45, 0.75, 0.35, 0.35], [0.45, 0.75, 0.494975, 0.247487], [0.45, 0.75, 0.247487, 0.494975], [0.45, 0.75, 0.606218, 0.202073], [0.45, 0.75, 0.202062, 0.606248], [0.45, 0.75, 0.41833, 0.41833], [0.55, 0.75, 0.35, 0.35], [0.55, 0.75, 0.494975, 0.247487], [0.55, 0.75, 0.247487, 0.494975], [0.55, 0.75, 0.606218, 0.202073], [0.55, 0.75, 0.202062, 0.606248], [0.55, 0.75, 0.41833, 0.41833], [0.65, 0.75, 0.35, 0.35], [0.65, 0.75, 0.494975, 0.247487], [0.65, 0.75, 0.247487, 0.494975], [0.65, 0.75, 0.606218, 0.202073], [0.65, 0.75, 0.202062, 0.606248], [0.65, 0.75, 0.41833, 0.41833], [0.75, 0.75, 0.35, 0.35], [0.75, 0.75, 0.494975, 0.247487], [0.75, 0.75, 0.247487, 0.494975], [0.75, 0.75, 0.606218, 0.202073], [0.75, 0.75, 0.202062, 0.606248], [0.75, 0.75, 0.41833, 0.41833], [0.85, 0.75, 0.35, 0.35], [0.85, 0.75, 0.494975, 0.247487], [0.85, 0.75, 0.247487, 0.494975], [0.85, 0.75, 0.606218, 0.202073], [0.85, 0.75, 0.202062, 0.606248], [0.85, 0.75, 0.41833, 0.41833], [0.95, 0.75, 0.35, 0.35], [0.95, 0.75, 0.494975, 0.247487], [0.95, 0.75, 0.247487, 0.494975], [0.95, 0.75, 0.606218, 0.202073], [0.95, 0.75, 0.202063, 0.606248], [0.95, 0.75, 0.41833, 0.41833], [0.05, 0.85, 0.35, 0.35], [0.05, 0.85, 0.494975, 0.247487], [0.05, 0.85, 0.247487, 0.494975], [0.05, 0.85, 0.606218, 0.202073], [0.05, 0.85, 0.202062, 0.606248], [0.05, 0.85, 0.41833, 0.41833], [0.15, 0.85, 0.35, 0.35], [0.15, 0.85, 0.494975, 0.247487], [0.15, 0.85, 0.247487, 0.494975], [0.15, 0.85, 0.606218, 0.202073], [0.15, 0.85, 0.202062, 0.606248], [0.15, 0.85, 0.41833, 0.41833], [0.25, 0.85, 0.35, 0.35], [0.25, 0.85, 0.494975, 0.247487], [0.25, 0.85, 0.247487, 0.494975], [0.25, 0.85, 0.606218, 0.202073], [0.25, 0.85, 0.202062, 0.606248], [0.25, 0.85, 0.41833, 0.41833], [0.35, 0.85, 0.35, 0.35], [0.35, 0.85, 0.494975, 0.247487], [0.35, 0.85, 0.247487, 0.494975], [0.35, 0.85, 0.606218, 0.202073], [0.35, 0.85, 0.202062, 0.606248], [0.35, 0.85, 0.41833, 0.41833], [0.45, 0.85, 0.35, 0.35], [0.45, 0.85, 0.494975, 0.247487], [0.45, 0.85, 0.247487, 0.494975], [0.45, 0.85, 0.606218, 0.202073], [0.45, 0.85, 0.202062, 0.606248], [0.45, 0.85, 0.41833, 0.41833], [0.55, 0.85, 0.35, 0.35], [0.55, 0.85, 0.494975, 0.247487], [0.55, 0.85, 0.247487, 0.494975], [0.55, 0.85, 0.606218, 0.202073], [0.55, 0.85, 0.202062, 0.606248], [0.55, 0.85, 0.41833, 0.41833], [0.65, 0.85, 0.35, 0.35], [0.65, 0.85, 0.494975, 0.247487], [0.65, 0.85, 0.247487, 0.494975], [0.65, 0.85, 0.606218, 0.202073], [0.65, 0.85, 0.202062, 0.606248], [0.65, 0.85, 0.41833, 0.41833], [0.75, 0.85, 0.35, 0.35], [0.75, 0.85, 0.494975, 0.247487], [0.75, 0.85, 0.247487, 0.494975], [0.75, 0.85, 0.606218, 0.202073], [0.75, 0.85, 0.202062, 0.606248], [0.75, 0.85, 0.41833, 0.41833], [0.85, 0.85, 0.35, 0.35], [0.85, 0.85, 0.494975, 0.247487], [0.85, 0.85, 0.247487, 0.494975], [0.85, 0.85, 0.606218, 0.202073], [0.85, 0.85, 0.202062, 0.606248], [0.85, 0.85, 0.41833, 0.41833], [0.95, 0.85, 0.35, 0.35], [0.95, 0.85, 0.494975, 0.247487], [0.95, 0.85, 0.247487, 0.494975], [0.95, 0.85, 0.606218, 0.202073], [0.95, 0.85, 0.202063, 0.606248], [0.95, 0.85, 0.41833, 0.41833], [0.05, 0.95, 0.35, 0.35], [0.05, 0.95, 0.494975, 0.247487], [0.05, 0.95, 0.247487, 0.494975], [0.05, 0.95, 0.606218, 0.202073], [0.05, 0.95, 0.202062, 0.606248], [0.05, 0.95, 0.41833, 0.41833], [0.15, 0.95, 0.35, 0.35], [0.15, 0.95, 0.494975, 0.247487], [0.15, 0.95, 0.247487, 0.494975], [0.15, 0.95, 0.606218, 0.202073], [0.15, 0.95, 0.202062, 0.606248], [0.15, 0.95, 0.41833, 0.41833], [0.25, 0.95, 0.35, 0.35], [0.25, 0.95, 0.494975, 0.247487], [0.25, 0.95, 0.247487, 0.494975], [0.25, 0.95, 0.606218, 0.202073], [0.25, 0.95, 0.202062, 0.606248], [0.25, 0.95, 0.41833, 0.41833], [0.35, 0.95, 0.35, 0.35], [0.35, 0.95, 0.494975, 0.247487], [0.35, 0.95, 0.247487, 0.494975], [0.35, 0.95, 0.606218, 0.202073], [0.35, 0.95, 0.202062, 0.606248], [0.35, 0.95, 0.41833, 0.41833], [0.45, 0.95, 0.35, 0.35], [0.45, 0.95, 0.494975, 0.247487], [0.45, 0.95, 0.247487, 0.494975], [0.45, 0.95, 0.606218, 0.202073], [0.45, 0.95, 0.202062, 0.606248], [0.45, 0.95, 0.41833, 0.41833], [0.55, 0.95, 0.35, 0.35], [0.55, 0.95, 0.494975, 0.247487], [0.55, 0.95, 0.247487, 0.494975], [0.55, 0.95, 0.606218, 0.202073], [0.55, 0.95, 0.202062, 0.606248], [0.55, 0.95, 0.41833, 0.41833], [0.65, 0.95, 0.35, 0.35], [0.65, 0.95, 0.494975, 0.247487], [0.65, 0.95, 0.247487, 0.494975], [0.65, 0.95, 0.606218, 0.202073], [0.65, 0.95, 0.202062, 0.606248], [0.65, 0.95, 0.41833, 0.41833], [0.75, 0.95, 0.35, 0.35], [0.75, 0.95, 0.494975, 0.247487], [0.75, 0.95, 0.247487, 0.494975], [0.75, 0.95, 0.606218, 0.202073], [0.75, 0.95, 0.202062, 0.606248], [0.75, 0.95, 0.41833, 0.41833], [0.85, 0.95, 0.35, 0.35], [0.85, 0.95, 0.494975, 0.247487], [0.85, 0.95, 0.247487, 0.494975], [0.85, 0.95, 0.606218, 0.202073], [0.85, 0.95, 0.202062, 0.606248], [0.85, 0.95, 0.41833, 0.41833], [0.95, 0.95, 0.35, 0.35], [0.95, 0.95, 0.494975, 0.247487], [0.95, 0.95, 0.247487, 0.494975], [0.95, 0.95, 0.606218, 0.202073], [0.95, 0.95, 0.202063, 0.606248], [0.95, 0.95, 0.41833, 0.41833], [0.1, 0.1, 0.5, 0.5], [0.1, 0.1, 0.707107, 0.353553], [0.1, 0.1, 0.353553, 0.707107], [0.1, 0.1, 0.866025, 0.288675], [0.1, 0.1, 0.288661, 0.866069], [0.1, 0.1, 0.570088, 0.570088], [0.3, 0.1, 0.5, 0.5], [0.3, 0.1, 0.707107, 0.353553], [0.3, 0.1, 0.353553, 0.707107], [0.3, 0.1, 0.866025, 0.288675], [0.3, 0.1, 0.288661, 0.866069], [0.3, 0.1, 0.570088, 0.570088], [0.5, 0.1, 0.5, 0.5], [0.5, 0.1, 0.707107, 0.353553], [0.5, 0.1, 0.353553, 0.707107], [0.5, 0.1, 0.866025, 0.288675], [0.5, 0.1, 0.288661, 0.866069], [0.5, 0.1, 0.570088, 0.570088], [0.7, 0.1, 0.5, 0.5], [0.7, 0.1, 0.707107, 0.353553], [0.7, 0.1, 0.353553, 0.707107], [0.7, 0.1, 0.866025, 0.288675], [0.7, 0.1, 0.288661, 0.866069], [0.7, 0.1, 0.570088, 0.570088], [0.9, 0.1, 0.5, 0.5], [0.9, 0.1, 0.707107, 0.353553], [0.9, 0.1, 0.353553, 0.707107], [0.9, 0.1, 0.866025, 0.288675], [0.9, 0.1, 0.288661, 0.866069], [0.9, 0.1, 0.570088, 0.570088], [0.1, 0.3, 0.5, 0.5], [0.1, 0.3, 0.707107, 0.353553], [0.1, 0.3, 0.353553, 0.707107], [0.1, 0.3, 0.866025, 0.288675], [0.1, 0.3, 0.288661, 0.866069], [0.1, 0.3, 0.570088, 0.570088], [0.3, 0.3, 0.5, 0.5], [0.3, 0.3, 0.707107, 0.353553], [0.3, 0.3, 0.353553, 0.707107], [0.3, 0.3, 0.866025, 0.288675], [0.3, 0.3, 0.288661, 0.866069], [0.3, 0.3, 0.570088, 0.570088], [0.5, 0.3, 0.5, 0.5], [0.5, 0.3, 0.707107, 0.353553], [0.5, 0.3, 0.353553, 0.707107], [0.5, 0.3, 0.866025, 0.288675], [0.5, 0.3, 0.288661, 0.866069], [0.5, 0.3, 0.570088, 0.570088], [0.7, 0.3, 0.5, 0.5], [0.7, 0.3, 0.707107, 0.353553], [0.7, 0.3, 0.353553, 0.707107], [0.7, 0.3, 0.866025, 0.288675], [0.7, 0.3, 0.288661, 0.866069], [0.7, 0.3, 0.570088, 0.570088], [0.9, 0.3, 0.5, 0.5], [0.9, 0.3, 0.707107, 0.353553], [0.9, 0.3, 0.353553, 0.707107], [0.9, 0.3, 0.866025, 0.288675], [0.9, 0.3, 0.288661, 0.866069], [0.9, 0.3, 0.570088, 0.570088], [0.1, 0.5, 0.5, 0.5], [0.1, 0.5, 0.707107, 0.353553], [0.1, 0.5, 0.353553, 0.707107], [0.1, 0.5, 0.866025, 0.288675], [0.1, 0.5, 0.288661, 0.866069], [0.1, 0.5, 0.570088, 0.570088], [0.3, 0.5, 0.5, 0.5], [0.3, 0.5, 0.707107, 0.353553], [0.3, 0.5, 0.353553, 0.707107], [0.3, 0.5, 0.866025, 0.288675], [0.3, 0.5, 0.288661, 0.866069], [0.3, 0.5, 0.570088, 0.570088], [0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.707107, 0.353553], [0.5, 0.5, 0.353553, 0.707107], [0.5, 0.5, 0.866025, 0.288675], [0.5, 0.5, 0.288661, 0.866069], [0.5, 0.5, 0.570088, 0.570088], [0.7, 0.5, 0.5, 0.5], [0.7, 0.5, 0.707107, 0.353553], [0.7, 0.5, 0.353553, 0.707107], [0.7, 0.5, 0.866025, 0.288675], [0.7, 0.5, 0.288661, 0.866069], [0.7, 0.5, 0.570088, 0.570088], [0.9, 0.5, 0.5, 0.5], [0.9, 0.5, 0.707107, 0.353553], [0.9, 0.5, 0.353553, 0.707107], [0.9, 0.5, 0.866025, 0.288675], [0.9, 0.5, 0.288661, 0.866069], [0.9, 0.5, 0.570088, 0.570088], [0.1, 0.7, 0.5, 0.5], [0.1, 0.7, 0.707107, 0.353553], [0.1, 0.7, 0.353553, 0.707107], [0.1, 0.7, 0.866025, 0.288675], [0.1, 0.7, 0.288661, 0.866069], [0.1, 0.7, 0.570088, 0.570088], [0.3, 0.7, 0.5, 0.5], [0.3, 0.7, 0.707107, 0.353553], [0.3, 0.7, 0.353553, 0.707107], [0.3, 0.7, 0.866025, 0.288675], [0.3, 0.7, 0.288661, 0.866069], [0.3, 0.7, 0.570088, 0.570088], [0.5, 0.7, 0.5, 0.5], [0.5, 0.7, 0.707107, 0.353553], [0.5, 0.7, 0.353553, 0.707107], [0.5, 0.7, 0.866025, 0.288675], [0.5, 0.7, 0.288661, 0.866069], [0.5, 0.7, 0.570088, 0.570088], [0.7, 0.7, 0.5, 0.5], [0.7, 0.7, 0.707107, 0.353553], [0.7, 0.7, 0.353553, 0.707107], [0.7, 0.7, 0.866025, 0.288675], [0.7, 0.7, 0.288661, 0.866069], [0.7, 0.7, 0.570088, 0.570088], [0.9, 0.7, 0.5, 0.5], [0.9, 0.7, 0.707107, 0.353553], [0.9, 0.7, 0.353553, 0.707107], [0.9, 0.7, 0.866025, 0.288675], [0.9, 0.7, 0.288661, 0.866069], [0.9, 0.7, 0.570088, 0.570088], [0.1, 0.9, 0.5, 0.5], [0.1, 0.9, 0.707107, 0.353553], [0.1, 0.9, 0.353553, 0.707107], [0.1, 0.9, 0.866025, 0.288675], [0.1, 0.9, 0.288661, 0.866069], [0.1, 0.9, 0.570088, 0.570088], [0.3, 0.9, 0.5, 0.5], [0.3, 0.9, 0.707107, 0.353553], [0.3, 0.9, 0.353553, 0.707107], [0.3, 0.9, 0.866025, 0.288675], [0.3, 0.9, 0.288661, 0.866069], [0.3, 0.9, 0.570088, 0.570088], [0.5, 0.9, 0.5, 0.5], [0.5, 0.9, 0.707107, 0.353553], [0.5, 0.9, 0.353553, 0.707107], [0.5, 0.9, 0.866025, 0.288675], [0.5, 0.9, 0.288661, 0.866069], [0.5, 0.9, 0.570088, 0.570088], [0.7, 0.9, 0.5, 0.5], [0.7, 0.9, 0.707107, 0.353553], [0.7, 0.9, 0.353553, 0.707107], [0.7, 0.9, 0.866025, 0.288675], [0.7, 0.9, 0.288661, 0.866069], [0.7, 0.9, 0.570088, 0.570088], [0.9, 0.9, 0.5, 0.5], [0.9, 0.9, 0.707107, 0.353553], [0.9, 0.9, 0.353553, 0.707107], [0.9, 0.9, 0.866025, 0.288675], [0.9, 0.9, 0.288661, 0.866069], [0.9, 0.9, 0.570088, 0.570088], [0.166667, 0.166667, 0.65, 0.65], [0.166667, 0.166667, 0.919239, 0.459619], [0.166667, 0.166667, 0.459619, 0.919239], [0.166667, 0.166667, 1.12583, 0.375278], [0.166667, 0.166667, 0.375259, 1.12589], [0.166667, 0.166667, 0.72111, 0.72111], [0.5, 0.166667, 0.65, 0.65], [0.5, 0.166667, 0.919239, 0.459619], [0.5, 0.166667, 0.459619, 0.919239], [0.5, 0.166667, 1.12583, 0.375278], [0.5, 0.166667, 0.375259, 1.12589], [0.5, 0.166667, 0.72111, 0.72111], [0.833333, 0.166667, 0.65, 0.65], [0.833333, 0.166667, 0.919239, 0.459619], [0.833333, 0.166667, 0.459619, 0.919239], [0.833333, 0.166667, 1.12583, 0.375278], [0.833333, 0.166667, 0.375259, 1.12589], [0.833333, 0.166667, 0.72111, 0.72111], [0.166667, 0.5, 0.65, 0.65], [0.166667, 0.5, 0.919239, 0.459619], [0.166667, 0.5, 0.459619, 0.919239], [0.166667, 0.5, 1.12583, 0.375278], [0.166667, 0.5, 0.375259, 1.12589], [0.166667, 0.5, 0.72111, 0.72111], [0.5, 0.5, 0.65, 0.65], [0.5, 0.5, 0.919239, 0.459619], [0.5, 0.5, 0.459619, 0.919239], [0.5, 0.5, 1.12583, 0.375278], [0.5, 0.5, 0.375259, 1.12589], [0.5, 0.5, 0.72111, 0.72111], [0.833333, 0.5, 0.65, 0.65], [0.833333, 0.5, 0.919239, 0.459619], [0.833333, 0.5, 0.459619, 0.919239], [0.833333, 0.5, 1.12583, 0.375278], [0.833333, 0.5, 0.375259, 1.12589], [0.833333, 0.5, 0.72111, 0.72111], [0.166667, 0.833333, 0.65, 0.65], [0.166667, 0.833333, 0.919239, 0.459619], [0.166667, 0.833333, 0.459619, 0.919239], [0.166667, 0.833333, 1.12583, 0.375278], [0.166667, 0.833333, 0.375259, 1.12589], [0.166667, 0.833333, 0.72111, 0.72111], [0.5, 0.833333, 0.65, 0.65], [0.5, 0.833333, 0.919239, 0.459619], [0.5, 0.833333, 0.459619, 0.919239], [0.5, 0.833333, 1.12583, 0.375278], [0.5, 0.833333, 0.375259, 1.12589], [0.5, 0.833333, 0.72111, 0.72111], [0.833333, 0.833333, 0.65, 0.65], [0.833333, 0.833333, 0.919239, 0.459619], [0.833333, 0.833333, 0.459619, 0.919239], [0.833333, 0.833333, 1.12583, 0.375278], [0.833333, 0.833333, 0.375259, 1.12589], [0.833333, 0.833333, 0.72111, 0.72111], [0.25, 0.25, 0.8, 0.8], [0.25, 0.25, 1.13137, 0.565686], [0.25, 0.25, 0.565685, 1.13137], [0.25, 0.25, 1.38564, 0.46188], [0.25, 0.25, 0.461857, 1.38571], [0.25, 0.25, 0.87178, 0.87178], [0.75, 0.25, 0.8, 0.8], [0.75, 0.25, 1.13137, 0.565686], [0.75, 0.25, 0.565685, 1.13137], [0.75, 0.25, 1.38564, 0.46188], [0.75, 0.25, 0.461857, 1.38571], [0.75, 0.25, 0.87178, 0.87178], [0.25, 0.75, 0.8, 0.8], [0.25, 0.75, 1.13137, 0.565686], [0.25, 0.75, 0.565685, 1.13137], [0.25, 0.75, 1.38564, 0.46188], [0.25, 0.75, 0.461857, 1.38571], [0.25, 0.75, 0.87178, 0.87178], [0.75, 0.75, 0.8, 0.8], [0.75, 0.75, 1.13137, 0.565686], [0.75, 0.75, 0.565685, 1.13137], [0.75, 0.75, 1.38564, 0.46188], [0.75, 0.75, 0.461857, 1.38571], [0.75, 0.75, 0.87178, 0.87178], [0.5, 0.5, 0.95, 0.95], [0.5, 0.5, 1.3435, 0.671751], [0.5, 0.5, 0.671751, 1.3435], [0.5, 0.5, 1.64545, 0.548483], [0.5, 0.5, 0.548455, 1.64553], [0.5, 0.5, 0.974679, 0.974679]] \ No newline at end of file diff --git a/pose-detection/test_data/shared/image_to_tensor_input.jpg b/pose-detection/test_data/shared/image_to_tensor_input.jpg new file mode 100644 index 0000000000..37d6c4b20a Binary files /dev/null and b/pose-detection/test_data/shared/image_to_tensor_input.jpg differ diff --git a/pose-detection/test_data/shared/large_sub_rect.png b/pose-detection/test_data/shared/large_sub_rect.png new file mode 100644 index 0000000000..38a13dabe9 Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect.png differ diff --git a/pose-detection/test_data/shared/large_sub_rect_border_zero.png b/pose-detection/test_data/shared/large_sub_rect_border_zero.png new file mode 100644 index 0000000000..1a738a50d0 Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect_border_zero.png differ diff --git a/pose-detection/test_data/shared/large_sub_rect_keep_aspect.png b/pose-detection/test_data/shared/large_sub_rect_keep_aspect.png new file mode 100644 index 0000000000..254dc72ae9 Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect_keep_aspect.png differ diff --git a/pose-detection/test_data/shared/large_sub_rect_keep_aspect_border_zero.png b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_border_zero.png new file mode 100644 index 0000000000..5b096cb4de Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_border_zero.png differ diff --git a/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation.png b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation.png new file mode 100644 index 0000000000..104cb60915 Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation.png differ diff --git a/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation_border_zero.png b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation_border_zero.png new file mode 100644 index 0000000000..c5512ec0d2 Binary files /dev/null and b/pose-detection/test_data/shared/large_sub_rect_keep_aspect_with_rotation_border_zero.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_keep_aspect.png b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect.png new file mode 100644 index 0000000000..aba8d2591d Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_border_zero.png b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_border_zero.png new file mode 100644 index 0000000000..bfb4615463 Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_border_zero.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation.png b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation.png new file mode 100644 index 0000000000..5ce7c3ec36 Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation_border_zero.png b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation_border_zero.png new file mode 100644 index 0000000000..ab14e59546 Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_keep_aspect_with_rotation_border_zero.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_with_rotation.png b/pose-detection/test_data/shared/medium_sub_rect_with_rotation.png new file mode 100644 index 0000000000..ecfb1e5375 Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_with_rotation.png differ diff --git a/pose-detection/test_data/shared/medium_sub_rect_with_rotation_border_zero.png b/pose-detection/test_data/shared/medium_sub_rect_with_rotation_border_zero.png new file mode 100644 index 0000000000..d553011466 Binary files /dev/null and b/pose-detection/test_data/shared/medium_sub_rect_with_rotation_border_zero.png differ diff --git a/pose-detection/test_data/shared/noop_except_range.png b/pose-detection/test_data/shared/noop_except_range.png new file mode 100644 index 0000000000..1486d9f151 Binary files /dev/null and b/pose-detection/test_data/shared/noop_except_range.png differ diff --git a/pose-detection/test_data/shared/three_people.jpg b/pose-detection/test_data/shared/three_people.jpg new file mode 100644 index 0000000000..94d943864e Binary files /dev/null and b/pose-detection/test_data/shared/three_people.jpg differ diff --git a/pose-detection/test_data/shared/three_people_binary_mask.png b/pose-detection/test_data/shared/three_people_binary_mask.png new file mode 100644 index 0000000000..4b1365ebba Binary files /dev/null and b/pose-detection/test_data/shared/three_people_binary_mask.png differ diff --git a/pose-detection/test_data/shared/three_people_binary_segmentation.png b/pose-detection/test_data/shared/three_people_binary_segmentation.png new file mode 100644 index 0000000000..f103bed805 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_binary_segmentation.png differ diff --git a/pose-detection/test_data/shared/three_people_blur_body_parts.png b/pose-detection/test_data/shared/three_people_blur_body_parts.png new file mode 100644 index 0000000000..2f41759ba2 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_blur_body_parts.png differ diff --git a/pose-detection/test_data/shared/three_people_bokeh_effect.png b/pose-detection/test_data/shared/three_people_bokeh_effect.png new file mode 100644 index 0000000000..2d7a8e9643 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_bokeh_effect.png differ diff --git a/pose-detection/test_data/shared/three_people_colored_mask.png b/pose-detection/test_data/shared/three_people_colored_mask.png new file mode 100644 index 0000000000..a54cbda122 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_colored_mask.png differ diff --git a/pose-detection/test_data/shared/three_people_colored_segmentation.png b/pose-detection/test_data/shared/three_people_colored_segmentation.png new file mode 100644 index 0000000000..f2d4648e18 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_colored_segmentation.png differ diff --git a/pose-detection/test_data/shared/three_people_draw_mask.png b/pose-detection/test_data/shared/three_people_draw_mask.png new file mode 100644 index 0000000000..6276186de5 Binary files /dev/null and b/pose-detection/test_data/shared/three_people_draw_mask.png differ diff --git a/pose-detection/test_data/shared/three_people_pixelated_mask.png b/pose-detection/test_data/shared/three_people_pixelated_mask.png new file mode 100644 index 0000000000..471755237a Binary files /dev/null and b/pose-detection/test_data/shared/three_people_pixelated_mask.png differ