From b0d713be4fe813834c479d2848080c638945af15 Mon Sep 17 00:00:00 2001 From: Nirupa Anantha Kumar Date: Fri, 5 Apr 2019 12:02:05 -0700 Subject: [PATCH] docs(samples): add vision beta samples (#157) * Vision beta samples * Vision beta samples * Vision beta samples * Vision beta samples * fixing tests * fixing tests * fixing tests * fixing tests * fixing tests --- automl/package.json | 5 +- ...sionObjectDetectionDataset.v1beta1.test.js | 82 +++++++++++ ...VisionObjectDetectionModel.v1beta1.test.js | 127 ++++++++++++++++ ...sionObjectDetectionPredict.v1beta1.test.js | 40 +++++ .../create-dataset.v1beta1.js | 71 +++++++++ .../create-model.v1beta1,js.js | 61 ++++++++ .../delete-dataset.v1beta1.js | 60 ++++++++ .../object-detection/delete-model.v1beta1.js | 59 ++++++++ .../object-detection/deploy-model.v1beta1.js | 58 ++++++++ .../display-evaluation.v1beta1.js | 119 +++++++++++++++ .../object-detection/export-data.v1beta1.js | 74 ++++++++++ .../object-detection/get-dataset.v1beta1.js | 62 ++++++++ .../get-model-evaluations.v1beta1.js | 136 +++++++++++++++++ .../object-detection/get-model.v1beta1.js | 104 +++++++++++++ .../get-operation-status.v1beta1.js | 57 ++++++++ .../object-detection/import-data.v1beta1.js | 70 +++++++++ .../object-detection/list-datasets.v1beta1.js | 65 +++++++++ .../list-model-evaluations.v1beta1.js | 138 ++++++++++++++++++ .../object-detection/list-models.v1beta1.js | 99 +++++++++++++ .../list-operation-status.v1beta1.js | 56 +++++++ .../object-detection/predict.v1beta1.js | 88 +++++++++++ .../undeploy-model.v1beta1.js | 59 ++++++++ automl/vision/resources/songbird.jpg | Bin 0 -> 24322 bytes 23 files changed, 1688 insertions(+), 2 deletions(-) create mode 100644 automl/test/automlVisionObjectDetectionDataset.v1beta1.test.js create mode 100644 automl/test/automlVisionObjectDetectionModel.v1beta1.test.js create mode 100644 automl/test/automlVisionObjectDetectionPredict.v1beta1.test.js create mode 100644 automl/vision/object-detection/create-dataset.v1beta1.js create mode 100644 automl/vision/object-detection/create-model.v1beta1,js.js create mode 100644 automl/vision/object-detection/delete-dataset.v1beta1.js create mode 100644 automl/vision/object-detection/delete-model.v1beta1.js create mode 100644 automl/vision/object-detection/deploy-model.v1beta1.js create mode 100644 automl/vision/object-detection/display-evaluation.v1beta1.js create mode 100644 automl/vision/object-detection/export-data.v1beta1.js create mode 100644 automl/vision/object-detection/get-dataset.v1beta1.js create mode 100644 automl/vision/object-detection/get-model-evaluations.v1beta1.js create mode 100644 automl/vision/object-detection/get-model.v1beta1.js create mode 100644 automl/vision/object-detection/get-operation-status.v1beta1.js create mode 100644 automl/vision/object-detection/import-data.v1beta1.js create mode 100644 automl/vision/object-detection/list-datasets.v1beta1.js create mode 100644 automl/vision/object-detection/list-model-evaluations.v1beta1.js create mode 100644 automl/vision/object-detection/list-models.v1beta1.js create mode 100644 automl/vision/object-detection/list-operation-status.v1beta1.js create mode 100644 automl/vision/object-detection/predict.v1beta1.js create mode 100644 automl/vision/object-detection/undeploy-model.v1beta1.js create mode 100644 automl/vision/resources/songbird.jpg diff --git a/automl/package.json b/automl/package.json index 5f3c0b548df..3f6d9d5086b 100644 --- a/automl/package.json +++ b/automl/package.json @@ -12,12 +12,13 @@ "repository": "googleapis/nodejs-automl", "private": true, "scripts": { - "test": "mocha --timeout 600000" + "test": "mocha --timeout 600000 --recursive" }, "dependencies": { "@google-cloud/automl": "^0.2.0", "mathjs": "^5.5.0", - "yargs": "^13.2.1" + "yargs": "^13.2.1", + "execa":"^1.0.0" }, "devDependencies": { "mocha": "^6.0.1", diff --git a/automl/test/automlVisionObjectDetectionDataset.v1beta1.test.js b/automl/test/automlVisionObjectDetectionDataset.v1beta1.test.js new file mode 100644 index 00000000000..2c484873471 --- /dev/null +++ b/automl/test/automlVisionObjectDetectionDataset.v1beta1.test.js @@ -0,0 +1,82 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +'use strict'; + +const {assert} = require('chai'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +/** Tests for AutoML Vision Object Detection "Dataset API" sample. */ +// TODO(developer): Before running the test cases, +// set the environment variables PROJECT_ID, REGION_NAME and +// change the value of datasetId +const projectId = 'nodejs-docs-samples'; +const computeRegion = 'us-central1'; +const outputPath = 'gs://nodejs-docs-samples/VISION_OBJECT_DETECTION/'; +const datasetName = 'test_vision_create_dataset'; +const filter = 'imageObjectDetectionDatasetMetadata:*'; +const datasetId = 'ICN3946265060617537378'; +const importDataCsv = 'gs://nodejs-docs-samples-vcm/flowerTraindata20lines.csv'; + +describe('Vision Object Detection DatasetAPI', () => { + it.skip(`should create, import and delete a dataset`, async () => { + // Create dataset + let output = await execSync( + `node vision/object-detection/create-dataset.v1beta1.js "${projectId}" "${computeRegion}" "${datasetName}"` + ); + const parsedOut = output.split('\n'); + const outputDatasetId = parsedOut[1].split(':')[1].trim(); + assert.match(output, /Dataset display name:/); + + // Import data + output = await execSync( + `node vision/object-detection/import-data.v1beta1.js "${projectId}" "${computeRegion}" "${outputDatasetId}" "${importDataCsv}"` + ); + assert.match(output, /Processing import.../); + + // Delete dataset + output = await execSync( + `node vision/object-detection/delete-dataset.v1beta1.js "${projectId}" "${computeRegion}" "${outputDatasetId}"` + ); + assert.match(output, /Dataset delete details:/); + }); + + it.skip(`should list datasets`, async () => { + // List datasets + const output = await execSync( + `node vision/object-detection/list-datasets.v1beta1.js "${projectId}" "${computeRegion}" "${filter}"` + ); + assert.match(output, /List of datasets:/); + }); + + it.skip(`should get preexisting dataset`, async () => { + // Get dataset + const output = await execSync( + `node vision/object-detection/get-dataset.v1beta1.js "${projectId}" "${computeRegion}" "${datasetId}"` + ); + assert.match(output, /Dataset display name:/); + }); + + it.skip(`should export dataset`, async () => { + // Export data + const outputUri = outputPath + datasetId; + const output = await execSync( + `node vision/object-detection/export-data.v1beta1.js "${projectId}" "${computeRegion}" "${datasetId}" "${outputUri}"` + ); + assert.match(output, /Processing export.../); + }); +}); diff --git a/automl/test/automlVisionObjectDetectionModel.v1beta1.test.js b/automl/test/automlVisionObjectDetectionModel.v1beta1.test.js new file mode 100644 index 00000000000..183e1a2dce0 --- /dev/null +++ b/automl/test/automlVisionObjectDetectionModel.v1beta1.test.js @@ -0,0 +1,127 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +'use strict'; + +const {assert} = require('chai'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +/** Tests for AutoML Vision Object Detection "Model API" sample. */ +// TODO(developer): Before running the test cases, +// set the environment variables PROJECT_ID, REGION_NAME and +// change the value of datasetId, deployModelId and undeployModelId +const projectId = 'nodejs-docs-samples'; +const computeRegion = 'us-central1'; +const filter = 'imageObjectDetectionModelMetadata:*'; +const datasetId = 'ICN3217071205693347964'; +const testModelName = 'birds2_201804101601_base'; +const deployModelId = 'IOD1728502647608049664'; +const undeployModelId = 'IOD3348109663601164288'; + +describe(' Vision Object Detection ModelAPI', () => { + it.skip(`should create a model`, async () => { + let output = await execSync( + `node vision/object-detection/create-model.v1beta1.js "${projectId}" "${computeRegion}" "${datasetId}" "${testModelName}"` + ); + const operationName = output + .split('\n')[0] + .split(':')[1] + .trim(); + assert.match(output, /Training started.../); + + output = await execSync( + `node vision/object-detection/get-operation-status.v1beta1.js "${operationName}"` + ); + assert.match(output, /Operation details:/); + }); + + it.skip(`should list models, get and delete a model. list, get and display model + evaluations from preexisting models`, async () => { + // List models + let output = await execSync( + `node vision/object-detection/list-models.v1beta1.js "${projectId}" "${computeRegion}" "${filter}"` + ); + const parsedOut = output.split('\n'); + const outputModelId = parsedOut[3].split(':')[1].trim(); + assert.match(output, /List of models:/); + + // Get Model + output = await execSync( + `node vision/object-detection/get-model.v1beta1.js "${projectId}" "${computeRegion}" "${outputModelId}"` + ); + assert.match(output, /Model name:/); + + // List model evaluation + output = await execSync( + `node vision/object-detection/list-model-evaluations.v1beta1.js "${projectId}" "${computeRegion}" "${outputModelId}"` + ); + const parsedModelEvaluation = output.split('\n'); + const modelEvaluationId = parsedModelEvaluation[3].split(':')[1].trim(); + assert.match(output, /Model evaluation Id:/); + + // Get model evaluation + output = await execSync( + `node vision/object-detection/get-model-evaluation.v1beta1.js "${projectId}" "${computeRegion}" "${outputModelId}"` + + ` "${modelEvaluationId}"` + ); + assert.match(output, /Model evaluation Id:/); + + // Display evaluation + output = await execSync( + `node vision/object-detection/display-evaluation.v1beta1.js "${projectId}" "${computeRegion}" "${outputModelId}" ` + ); + assert.match(output, /Model Evaluation ID:/); + + // Delete Model + output = await execSync( + `node vision/object-detection/delete-model.v1beta1.js "${projectId}" "${computeRegion}" "${outputModelId}"` + ); + assert.match(output, /Model delete details:/); + }); + + it.skip(`should list and get operation status`, async () => { + // List operation status + let output = await execSync( + `node vision/object-detection/list-operations-status.v1beta1.js` + ); + const parsedOut = output.split('\n'); + const operationFullId = parsedOut[3].split(':')[1].trim(); + + // Get operation status + // Poll operation status, here confirming that operation is not complete yet + output = await execSync( + `node vision/object-detection/get-operation-status.v1beta1.js "${operationFullId}"` + ); + assert.match(output, /Operation details:/); + }); + + it.skip(`should deploy the model`, async () => { + // Deploy the model + const output = await execSync( + `node vision/object-detection/deploy-model.v1beta1.js "${projectId}" "${computeRegion}" ${deployModelId}` + ); + assert.match(output, /Name:/); + }); + + it.skip(`should undeploy the model`, async () => { + // Undeploy the model + const output = await execSync( + `node vision/object-detection/undeploy-model.v1beta1.js "${projectId}" "${computeRegion}" ${undeployModelId}` + ); + assert.match(output, /Name:/); + }); +}); diff --git a/automl/test/automlVisionObjectDetectionPredict.v1beta1.test.js b/automl/test/automlVisionObjectDetectionPredict.v1beta1.test.js new file mode 100644 index 00000000000..9953227bb1b --- /dev/null +++ b/automl/test/automlVisionObjectDetectionPredict.v1beta1.test.js @@ -0,0 +1,40 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +'use strict'; + +const {assert} = require('chai'); +const execa = require('execa'); + +/** Tests for AutoML Vision Object Detection "Prediction API" sample. */ + +// TODO(developer): Before running the test cases, +// set the environment variables PROJECT_ID, REGION_NAME and change the value of modelId +const projectId = 'nodejs-docs-samples'; +const computeRegion = 'us-central1'; +const modelId = ''; +const filePath = './resource/songbird.jpg'; + +const exec = async cmd => (await execa.shell(cmd)).stdout; + +describe.skip('Vision Object Detection PredictionAPI', () => { + it(`should run prediction from preexisting model`, async () => { + // Run prediction on 'salad.jpg' in resource folder + const output = await exec( + `node vision/object-detection/predict.v1beta1.js "${projectId}" "${computeRegion}" "${modelId}" "${filePath}"` + ); + assert.match(output, /Prediction results:/); + }); +}); diff --git a/automl/vision/object-detection/create-dataset.v1beta1.js b/automl/vision/object-detection/create-dataset.v1beta1.js new file mode 100644 index 00000000000..1e15b9737da --- /dev/null +++ b/automl/vision/object-detection/create-dataset.v1beta1.js @@ -0,0 +1,71 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetName = 'YOUR_DATASET_NAME' +) { + // [START automl_vision_object_detection_create_dataset] + /** + * Demonstrates using the AutoML client to create a dataset. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetName = '[DATASET_NAME]' e.g., "myDataset"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + const util = require(`util`); + + async function createDataset() { + // A resource that represents Google Cloud Platform location. + const projectLocation = automlClient.locationPath(projectId, computeRegion); + + // Set dataset name and metadata. + const myDataset = { + displayName: datasetName, + imageObjectDetectionDatasetMetadata: {}, + }; + + // Create a dataset with the dataset metadata in the region. + const [response] = await automlClient.createDataset({ + parent: projectLocation, + dataset: myDataset, + }); + + //const dataset = response[0]; + // Display the dataset information. + console.log(`Dataset name: ${response.name}`); + console.log(`Dataset Id: ${response.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${response.displayName}`); + console.log(`Dataset example count: ${response.exampleCount}`); + console.log( + `Image object detection dataset metadata: ${util.inspect( + response.imageObjectDetectionDatasetMetadata, + false, + null + )}` + ); + } + createDataset(); + // [END automl_vision_object_detection_create_dataset] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/create-model.v1beta1,js.js b/automl/vision/object-detection/create-model.v1beta1,js.js new file mode 100644 index 00000000000..4b2198f0bd8 --- /dev/null +++ b/automl/vision/object-detection/create-model.v1beta1,js.js @@ -0,0 +1,61 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetId = 'YOUR_DATASET_ID', + modelName = 'MODEL_NAME' +) { + // [START automl_vision_object_detection_create_model] + /** + * Demonstrates using the AutoML client to create a model. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetId = '[DATASET_ID]' e.g., "IOD34216801856389120"; + // const modelName = '[MODEL_NAME]' e.g., "myModel"; + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function createModel() { + // A resource that represents Google Cloud Platform location. + const projectLocation = automlClient.locationPath(projectId, computeRegion); + + // Set datasetId, model name and model metadata for the dataset. + const myModel = { + displayName: modelName, + datasetId: datasetId, + imageObjectDetectionModelMetadata: {}, + }; + + // Create a model with the model metadata in the region. + const [response] = await automlClient.createModel({ + parent: projectLocation, + model: myModel, + }); + + const initialApiResponse = response[1]; + console.log(`Training operation name: ${initialApiResponse.name}`); + console.log(`Training started...`); + } + createModel(); + // [END automl_vision_object_detection_create_model] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/delete-dataset.v1beta1.js b/automl/vision/object-detection/delete-dataset.v1beta1.js new file mode 100644 index 00000000000..cd65f170e9f --- /dev/null +++ b/automl/vision/object-detection/delete-dataset.v1beta1.js @@ -0,0 +1,60 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetId = 'YOUR_DATASET_ID' +) { + // [START automl_vision_object_detection_delete_dsataset] + /** + * Demonstrates using the AutoML client to delete a dataset. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetId = '[DATASET_ID]' e.g., "IOD34216801856389120"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + async function deleteDataset() { + // Get the full path of the dataset. + const datasetFullId = automlClient.datasetPath( + projectId, + computeRegion, + datasetId + ); + + // Delete a dataset. + const [operation] = await automlClient.deleteDataset({name: datasetFullId}); + + const [response] = await operation.promise(); + const operationDetails = response[2]; + + // Get the Dataset delete details. + console.log('Dataset delete details:'); + console.log(` Operation details:`); + console.log(` Name: ${operationDetails.name}`); + console.log(` Done: ${operationDetails.done}`); + } + deleteDataset(); + // [END automl_vision_object_detection_delete_dataset] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/delete-model.v1beta1.js b/automl/vision/object-detection/delete-model.v1beta1.js new file mode 100644 index 00000000000..73a7e2f3a79 --- /dev/null +++ b/automl/vision/object-detection/delete-model.v1beta1.js @@ -0,0 +1,59 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'YOUR_MODEL_ID' +) { + // [START automl_vision_object_detection_delete_model] + /** + * Demonstrates using the AutoML client to delete a model. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD1187015161160925184"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function deleteModel() { + // Get the full path of the model. + const modelFullId = automlClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // Delete a model. + const [operation] = await automlClient.deleteModel({name: modelFullId}); + + const [response] = await operation.promise(); + const operationDetails = response[2]; + + // Get the Model delete details. + console.log('Model delete details:'); + console.log(` Operation details:`); + console.log(` Name: ${operationDetails.name}`); + console.log(` Done: ${operationDetails.done}`); + } + deleteModel(); + // [END automl_vision_object_detection_delete_model] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/deploy-model.v1beta1.js b/automl/vision/object-detection/deploy-model.v1beta1.js new file mode 100644 index 00000000000..803e39f8335 --- /dev/null +++ b/automl/vision/object-detection/deploy-model.v1beta1.js @@ -0,0 +1,58 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID' +) { + // [START automl_vision_object_detection_deploy_model] + + /** + * Demonstrates using the AutoML client to deploy model. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "TEN5200971474357190656"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function deployModel() { + // Get the full path of the model. + const modelFullId = automlClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // Deploy a model with the deploy model request. + const [operation] = await automlClient.deployModel({name: modelFullId}); + + const [response] = await operation.promise(); + console.log(`Deployment Details:`); + console.log(` Name: ${response.name}`); + console.log(` Metadata:`); + console.log(` Type Url: ${response.metadata.typeUrl}`); + console.log(` Done: ${response.done}`); + } + deployModel(); + // [END automl_vision_object_detection_deploy_model] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/display-evaluation.v1beta1.js b/automl/vision/object-detection/display-evaluation.v1beta1.js new file mode 100644 index 00000000000..838aeb8df85 --- /dev/null +++ b/automl/vision/object-detection/display-evaluation.v1beta1.js @@ -0,0 +1,119 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID', + filter = 'FILTER_EXPRESSION' +) { + // [START automl_vision_object_detection_display_evaluation] + /** + * Demonstrates using the AutoML client to display model evaluation. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD2122286140026257408"; + // const filter = '[FILTER_EXPRESSIONS]' + // e.g., "imageObjectDetectionModelMetadata:*"; + + const math = require(`mathjs`); + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + // Get the full path of the model. + const modelFullId = automlClient.modelPath(projectId, computeRegion, modelId); + + // List all the model evaluations in the model by applying filter. + automlClient + .listModelEvaluations({parent: modelFullId, filter: filter}) + .then(respond => { + const response = respond[0]; + // Iterate through the results. + let modelEvaluationId = ``; + for (const element of response) { + // There is evaluation for each class in a model and for overall model. + // Get only the evaluation of overall model. + if (!element.annotationSpecId) { + modelEvaluationId = element.name.split(`/`).pop(-1); + } + } + console.log(`Model Evaluation ID: ${modelEvaluationId}`); + + // Resource name for the model evaluation. + const modelEvaluationFullId = automlClient.modelEvaluationPath( + projectId, + computeRegion, + modelId, + modelEvaluationId + ); + + // Get a model evaluation. + automlClient + .getModelEvaluation({name: modelEvaluationFullId}) + .then(responses => { + const modelEvaluation = responses[0]; + const classMetrics = + modelEvaluation.imageObjectDetectionEvaluationMetrics; + const boundingBoxMetricsEntries = + classMetrics.boundingBoxMetricsEntries; + + for (const boundingBoxMetricsEntry of boundingBoxMetricsEntries) { + const confidenceMetricsEntries = + boundingBoxMetricsEntry.confidenceMetricsEntries; + // Showing model score based on threshold of 0.5 + for (const confidenceMetricsEntry of confidenceMetricsEntries) { + if (confidenceMetricsEntry.confidenceThreshold === 0.5) { + console.log( + `Precision and recall are based on a score ` + + `threshold of 0.5` + ); + console.log( + `Model precision: ${math.round( + confidenceMetricsEntry.precision * 100, + 2 + )} %` + ); + console.log( + `Model recall: ${math.round( + confidenceMetricsEntry.recall * 100, + 2 + )} %` + ); + console.log( + `Model f1 score: ${math.round( + confidenceMetricsEntry.f1Score * 100, + 2 + )} %` + ); + } + } + } + }) + .catch(err => { + console.error(err); + }); + }) + .catch(err => { + console.error(err); + }); + // [END automl_vision_object_detection_display_evaluation] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/export-data.v1beta1.js b/automl/vision/object-detection/export-data.v1beta1.js new file mode 100644 index 00000000000..e5d3b190b5d --- /dev/null +++ b/automl/vision/object-detection/export-data.v1beta1.js @@ -0,0 +1,74 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetId = 'YOUR_DATASET_ID', + gcsOutputUri = 'GCS_DIRECTORY' +) { + // [START automl_vision_object_detection_export_data] + /** + * Demonstrates using the AutoML client to export a dataset to a + * Google Cloud Storage bucket. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetId = '[DATASET_ID]' e.g.,"IOD34216801856389120"; + // const gcsOutputUri = '[GCS_OUTPUT_URI]' e.g., "gs:///", + // `Google Cloud Storage URI for the export directory`; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + async function exportData() { + // Get the full path of the dataset. + const datasetFullId = automlClient.datasetPath( + projectId, + computeRegion, + datasetId + ); + + // Set the output URI + const outputConfig = { + gcsDestination: { + outputUriPrefix: gcsOutputUri, + }, + }; + + // Set the request + const request = { + name: datasetFullId, + outputConfig: outputConfig, + }; + + // Export the data to the output URI. + const [operation] = await automlClient.exportData(request); + + const [response] = await operation.promise(); + console.log('Data export details:'); + console.log(` Operation details:`); + console.log(` Name: ${response.name}`); + console.log(` Done: ${response.done}`); + } + exportData(); + // [END automl_vision_object_detection_export_data] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/get-dataset.v1beta1.js b/automl/vision/object-detection/get-dataset.v1beta1.js new file mode 100644 index 00000000000..99c9d587e21 --- /dev/null +++ b/automl/vision/object-detection/get-dataset.v1beta1.js @@ -0,0 +1,62 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetId = 'YOUR_DATASET_ID' +) { + // [START automl_vision_object_detection_get_dataset] + /** + * Demonstrates using the AutoML client to get a dataset by ID. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetId = '[DATASET_ID]' e.g.,"IOD34216801856389120"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + const util = require(`util`); + async function getDataset() { + // Get the full path of the dataset. + const datasetFullId = automlClient.datasetPath( + projectId, + computeRegion, + datasetId + ); + + // Get a dataset. + const [response] = await automlClient.getDataset({name: datasetFullId}); + console.log(`Got dataset: ${response.name}`); + console.log(`Dataset Id: ${response.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${response.displayName}`); + console.log(`Dataset example count: ${response.exampleCount}`); + console.log( + `Image object detection dataset metadata: ${util.inspect( + response.imageObjectDetectionDatasetMetadata, + false, + null + )}` + ); + } + getDataset(); + // [END automl_vision_object_detection_get_dataset] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/get-model-evaluations.v1beta1.js b/automl/vision/object-detection/get-model-evaluations.v1beta1.js new file mode 100644 index 00000000000..11a45bbd69d --- /dev/null +++ b/automl/vision/object-detection/get-model-evaluations.v1beta1.js @@ -0,0 +1,136 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID', + modelEvaluationId = 'MODEL_EVALUATION_ID' +) { + // [START automl_vision_object_detection_get_model_evaluation] + /** + * Demonstrates using the AutoML client to get model evaluations. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD2122286140026257408"; + // const modelEvaluationId = '[MODEL_EVALUATION_ID]' + // e.g., "3806191078210741236"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + const math = require(`mathjs`); + + async function getModelEvaluations() { + // Get the full path of the model evaluation. + const modelEvaluationFullId = automlClient.modelEvaluationPath( + projectId, + computeRegion, + modelId, + modelEvaluationId + ); + + // Get complete detail of the model evaluation. + const [response] = await automlClient.getModelEvaluation({ + name: modelEvaluationFullId, + }); + + const detectMetrics = response.imageObjectDetectionEvaluationMetrics; + const boundingBoxMetricsEntries = detectMetrics.boundingBoxMetricsEntries; + + // Display the model evaluations information. + console.log(`\nModel evaluation name: ${response.name}`); + console.log( + `Model evaluation Id: ${response.name + .split(`/`) + .slice(-1) + .pop()}` + ); + console.log( + `Model evaluation annotation spec Id: ${response.annotationSpecId}` + ); + console.log(`Model evaluation display name: ${response.displayName}`); + console.log( + `Model evaluation example count: ${response.evaluatedExampleCount}` + ); + console.log(`Image object detection evaluation metrics:`); + console.log( + ` Evaluated bounding box count: ${ + detectMetrics.evaluatedBoundingBoxCount + }` + ); + console.log( + ` Bounding box mean average precision: ${math.round( + detectMetrics.boundingBoxMeanAveragePrecision, + 6 + )}` + ); + + for (const boundingBoxMetricsEntry of boundingBoxMetricsEntries) { + console.log(`\tBounding box metrics entries:`); + console.log( + ` Iou threshold: ${math.round( + boundingBoxMetricsEntry.iouThreshold, + 2 + )}` + ); + console.log( + ` Mean average precision: ${math.round( + boundingBoxMetricsEntry.meanAveragePrecision, + 6 + )}` + ); + console.log(` Confidence metrics entries:`); + const confidenceMetricsEntries = + boundingBoxMetricsEntry.confidenceMetricsEntries; + + for (const confidenceMetricsEntry of confidenceMetricsEntries) { + console.log( + ` Model confidence threshold: ${math.round( + confidenceMetricsEntry.confidenceThreshold * 100, + 6 + )}` + ); + console.log( + ` Model recall: ${math.round( + confidenceMetricsEntry.recall * 100, + 2 + )} %` + ); + console.log( + `\t\t\tModel precision: ${math.round( + confidenceMetricsEntry.precision * 100, + 2 + )} %` + ); + console.log( + ` Model f1 score: ${math.round( + confidenceMetricsEntry.f1Score * 100, + 2 + )} % \n` + ); + } + } + } + getModelEvaluations(); + // [END automl_vision_object_detection_get_model_evaluation] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/get-model.v1beta1.js b/automl/vision/object-detection/get-model.v1beta1.js new file mode 100644 index 00000000000..b92e7b8cac9 --- /dev/null +++ b/automl/vision/object-detection/get-model.v1beta1.js @@ -0,0 +1,104 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID' +) { + // [START automl_vision_object_detection_get_model] + /** + * Demonstrates using the AutoML client to get model details. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD1187015161160925184"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + async function getModel() { + // Get the full path of the model. + const modelFullId = automlClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // Get complete detail of the model. + const [response] = await automlClient.getModel({name: modelFullId}); + + // Display the model information. + console.log(`Model name: ${response.name}`); + console.log(`Model Id: ${response.name.split(`/`).pop(-1)}`); + console.log(`Model display name: ${response.displayName}`); + console.log(`Dataset Id: ${response.datasetId}`); + + if (response.modelMetadata === `translationModelMetadata`) { + console.log(`Translation model metadata:`); + console.log( + `\tBase model: ${response.translationModelMetadata.baseModel}` + ); + console.log( + `\tSource language code: ${ + response.translationModelMetadata.sourceLanguageCode + }` + ); + console.log( + `\tTarget language code: ${ + response.translationModelMetadata.targetLanguageCode + }` + ); + } else if (response.modelMetadata === `textClassificationModelMetadata`) { + console.log( + `Text classification model metadata: , ${ + response.textClassificationModelMetadata + }` + ); + } else if (response.modelMetadata === `imageClassificationModelMetadata`) { + console.log(`Image classification model metadata:`); + console.log( + `\tBase model Id: ${ + response.imageClassificationModelMetadata.baseModelId + }` + ); + console.log( + `\tTrain budget: ${ + response.imageClassificationModelMetadata.trainBudget + }` + ); + console.log( + `\tTrain cost: ${response.imageClassificationModelMetadata.trainCost}` + ); + console.log( + `\tStop reason: ${response.imageClassificationModelMetadata.stopReason}` + ); + } else if (response.modelMetadata === `imageObjectDetectionModelMetadata`) { + console.log(`Image Object Detection Model metadata:`); + console.log( + `\tModel Type: ${response.imageObjectDetectionModelMetadata.modelType}` + ); + } + console.log(`Model deployment state: ${response.deploymentState}`); + } + getModel(); + // [END automl_vision_object_detection_get_model] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/get-operation-status.v1beta1.js b/automl/vision/object-detection/get-operation-status.v1beta1.js new file mode 100644 index 00000000000..571f0eb936b --- /dev/null +++ b/automl/vision/object-detection/get-operation-status.v1beta1.js @@ -0,0 +1,57 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main(operationFullId = 'OPERATION_FULL_ID') { + // [START automl_vision_object_detection_get_operation_status] + /** + * Demonstrates using the AutoML client to get operation status. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const operationFullId = '[OPERATION_FULL_ID]' + // eg., "projects//locations/us-central1/operations/", + // `Full name of an operation`; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function getOperationStatus() { + // Get the latest state of a long-running operation. + const [response] = await automlClient.operationsClient.getOperation({ + name: operationFullId, + }); + console.log(`Operation details:`); + console.log(`\tName: ${response.name}`); + console.log(`\tMetadata:`); + console.log(`\t\tType Url: ${response.metadata.typeUrl}`); + console.log(`\tDone: ${response.done}`); + + if (response.response) { + console.log(`\tResponse:`); + console.log(`\t\tType Url: ${response.response.typeUrl}`); + } + + if (response.error) { + console.log(`\tResponse:`); + console.log(`\t\tError code: ${response.error.code}`); + console.log(`\t\tError message: ${response.error.message}`); + } + } + getOperationStatus(); + // [END automl_vision_object_detection_get_operation_status] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/import-data.v1beta1.js b/automl/vision/object-detection/import-data.v1beta1.js new file mode 100644 index 00000000000..d607f01bbd6 --- /dev/null +++ b/automl/vision/object-detection/import-data.v1beta1.js @@ -0,0 +1,70 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + datasetId = 'YOUR_DATASET_ID', + gcsPath = 'GCS_PATH' +) { + // [START automl_vision_object_detection_import_data] + /** + * Demonstrates using the AutoML client to import labeled items. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const datasetId = '[DATASET_ID]' e.g.,"IOD34216801856389120"; + // const gcsPath = '[GCS_PATH]' e.g., "gs:///", + // `.csv paths in AutoML Vision Object Detection CSV format`; + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function importData() { + // Get the full path of the dataset. + const datasetFullId = automlClient.datasetPath( + projectId, + computeRegion, + datasetId + ); + + // Get the multiple Google Cloud Storage URIs. + const inputUris = gcsPath.split(`,`); + const inputConfig = { + gcsSource: { + inputUris: inputUris, + }, + }; + + // Import the data from the input URI. + const [operation] = await automlClient.importData({ + name: datasetFullId, + inputConfig: inputConfig, + }); + + const [response] = await operation.promise(); + // Get the data import details. + console.log('Data import details:'); + console.log(` Operation details:`); + console.log(` Name: ${response.name}`); + console.log(` Done: ${response.done}`); + } + importData(); + // [END automl_vision_object_detection_import_data] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/list-datasets.v1beta1.js b/automl/vision/object-detection/list-datasets.v1beta1.js new file mode 100644 index 00000000000..b9eecc2ae1c --- /dev/null +++ b/automl/vision/object-detection/list-datasets.v1beta1.js @@ -0,0 +1,65 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + filter = 'FILTER_EXPRESSION' +) { + // [START automl_vision_object_detection_list_datasets] + /** + * Demonstrates using the AutoML client to list all Datasets. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const filter_ = '[FILTER_EXPRESSIONS]' + // e.g., "imageObjectDetectionDatasetMetadata:*"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + const util = require(`util`); + + async function listDatasets() { + const projectLocation = automlClient.locationPath(projectId, computeRegion); + + // List all the datasets available in the region by applying filter. + const [response] = await automlClient.listDatasets({ + parent: projectLocation, + filter: filter, + }); + console.log('List of datasets:'); + for (const dataset of response) { + console.log(`\nDataset name: ${dataset.name}`); + console.log(`Dataset Id: ${dataset.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${dataset.displayName}`); + console.log(`Dataset example count: ${dataset.exampleCount}`); + console.log( + `Image object detection dataset metadata: ${util.inspect( + dataset.imageObjectDetectionDatasetMetadata, + false, + null + )}` + ); + } + } + listDatasets(); + // [END automl_vision_object_detection_list_datasets] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/list-model-evaluations.v1beta1.js b/automl/vision/object-detection/list-model-evaluations.v1beta1.js new file mode 100644 index 00000000000..16bdf7612b3 --- /dev/null +++ b/automl/vision/object-detection/list-model-evaluations.v1beta1.js @@ -0,0 +1,138 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID', + filter = 'FILTER_EXPRESSION' +) { + // [START automl_vision_object_detection_list_model_evaluations] + /** + * Demonstrates using the AutoML client to list model evaluations. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD1187015161160925184"; + // const filter_ = '[FILTER_EXPRESSIONS]' + // e.g., "imageObjectDetectionModelMetadata:*"; + + const math = require(`mathjs`); + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + async function listModelEvaluations() { + // Get the full path of the model. + const modelFullId = automlClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // List all the model evaluations in the model by applying filter. + const [response] = await automlClient.listModelEvaluations({ + parent: modelFullId, + filter: filter, + }); + console.log(`List of model evaluations:`); + for (const element of response) { + const detectMetrics = element.imageObjectDetectionEvaluationMetrics; + const boundingBoxMetricsEntries = detectMetrics.boundingBoxMetricsEntries; + + // Display the model evaluations information. + console.log(`\nModel evaluation name: ${element.name}`); + console.log( + `Model evaluation Id: ${element.name + .split(`/`) + .slice(-1) + .pop()}` + ); + console.log( + `Model evaluation annotation spec Id: ${element.annotationSpecId}` + ); + console.log(`Model evaluation display name: ${element.displayName}`); + console.log( + `Model evaluation example count: ${element.evaluatedExampleCount}` + ); + console.log(`Image object detection evaluation metrics:`); + console.log( + `Evaluated bounding box count: ${ + detectMetrics.evaluatedBoundingBoxCount + }` + ); + console.log( + `Bounding box mean average precision: ${math.round( + detectMetrics.boundingBoxMeanAveragePrecision, + 6 + )}` + ); + + for (const boundingBoxMetricsEntry of boundingBoxMetricsEntries) { + console.log(`\tBounding box metrics entries:`); + console.log( + `Iou threshold: ${math.round( + boundingBoxMetricsEntry.iouThreshold, + 2 + )}` + ); + console.log( + `Mean average precision: ${math.round( + boundingBoxMetricsEntry.meanAveragePrecision, + 6 + )}` + ); + console.log(`Confidence metrics entries:`); + const confidenceMetricsEntries = + boundingBoxMetricsEntry.confidenceMetricsEntries; + + for (const confidenceMetricsEntry of confidenceMetricsEntries) { + console.log( + `Model confidence threshold: ${math.round( + confidenceMetricsEntry.confidenceThreshold * 100, + 6 + )}` + ); + console.log( + `\t\t\tModel recall: ${math.round( + confidenceMetricsEntry.recall * 100, + 2 + )} %` + ); + console.log( + `Model precision: ${math.round( + confidenceMetricsEntry.precision * 100, + 2 + )} %` + ); + console.log( + `Model f1 score: ${math.round( + confidenceMetricsEntry.f1Score * 100, + 2 + )} % \n` + ); + } + } + } + } + listModelEvaluations(); + // [END automl_vision_object_detection_list_model_evaluations] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/list-models.v1beta1.js b/automl/vision/object-detection/list-models.v1beta1.js new file mode 100644 index 00000000000..8b1b26c71fd --- /dev/null +++ b/automl/vision/object-detection/list-models.v1beta1.js @@ -0,0 +1,99 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + filter = 'FILTER_EXPRESSION' +) { + // [START automl_vision_object_detection_list_models] + /** + * Demonstrates using the AutoML client to list all models. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const filter_ = '[FILTER_EXPRESSIONS]' + // e.g., "imageObjectDetectionModelMetadata:*"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function listModels() { + // A resource that represents Google Cloud Platform location. + const projectLocation = automlClient.locationPath(projectId, computeRegion); + + // List all the models available in the region by applying filter. + const [response] = await automlClient.listModels({ + parent: projectLocation, + filter: filter, + }); + console.log(`List of models:`); + for (const model of response) { + console.log(`\nModel name: ${model.name}`); + console.log(`Model Id: ${model.name.split(`/`).pop(-1)}`); + console.log(`Model display name: ${model.displayName}`); + console.log(`Dataset Id: ${model.datasetId}`); + + if (model.modelMetadata === `translationModelMetadata`) { + console.log(`Translation model metadata:`); + console.log(`Base model: ${model.translationModelMetadata.baseModel}`); + console.log( + `Source language code: ${ + model.translationModelMetadata.sourceLanguageCode + }` + ); + console.log( + `Target language code: ${ + model.translationModelMetadata.targetLanguageCode + }` + ); + } else if (model.modelMetadata === `textClassificationModelMetadata`) { + console.log( + `Text classification model metadata: , ${ + model.textClassificationModelMetadata + }` + ); + } else if (model.modelMetadata === `imageClassificationModelMetadata`) { + console.log(`Image classification model metadata:`); + console.log( + `Base model Id: ${model.imageClassificationModelMetadata.baseModelId}` + ); + console.log( + `Train budget: ${model.imageClassificationModelMetadata.trainBudget}` + ); + console.log( + `Train cost: ${model.imageClassificationModelMetadata.trainCost}` + ); + console.log( + `Stop reason: ${model.imageClassificationModelMetadata.stopReason}` + ); + } else if (model.modelMetadata === `imageObjectDetectionModelMetadata`) { + console.log(`Image Object Detection Model metadata:`); + console.log( + `Model Type: ${model.imageObjectDetectionModelMetadata.modelType}` + ); + } + + console.log(`Model deployment state: ${model.deploymentState}`); + } + } + listModels(); + // [END automl_vision_object_detection_list_models] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/list-operation-status.v1beta1.js b/automl/vision/object-detection/list-operation-status.v1beta1.js new file mode 100644 index 00000000000..a7f3719f06b --- /dev/null +++ b/automl/vision/object-detection/list-operation-status.v1beta1.js @@ -0,0 +1,56 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + filter = 'FILTER_EXPRESSION' +) { + // [START automl_vision_object_detection_list_operations_status] + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const filter = '[FILTER_EXPRESSIONS]'; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + async function listOperationStatus() { + // A resource that represents Google Cloud Platform location. + const projectLocation = automlClient.locationPath(projectId, computeRegion); + + // List all the operations available in the region by applying filter. + const [operations] = await automlClient.operationsClient.listOperations({ + name: projectLocation, + filter: filter, + }); + for (const element of operations) { + console.log(`\nOperation details:`); + console.log(`Name: ${element.name}`); + console.log(`Metadata:`); + console.log(`Type Url: ${element.metadata.typeUrl}`); + console.log(`Done: ${element.done}`); + } + } + listOperationStatus(); + // [END automl_vision_object_detection_list_operations_status] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/predict.v1beta1.js b/automl/vision/object-detection/predict.v1beta1.js new file mode 100644 index 00000000000..040a002bbf3 --- /dev/null +++ b/automl/vision/object-detection/predict.v1beta1.js @@ -0,0 +1,88 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'YOUR_MODEL_ID', + filePath = 'GCS_PATH', + scoreThreshold = 'SCORE_THRESHOLD' +) { + // [START automl_vision_object_detection_predict] + + /** + * Demonstrates using the AutoML client to detect the object in an image. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "IOD1187015161160925184"; + // const filePath = '[GCS_PATH]' e.g., "/home/ubuntu/salad.jpg", + // `local text file path of content to be extracted`; + // const scoreThreshold = '[SCORE_THRESHOLD]', e.g, 0.50 , + // `Set the score threshold for Prediction of the created model`; + + //Imports the Google Cloud Automl library + const {PredictionServiceClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const predictionServiceClient = new PredictionServiceClient(); + + const fs = require(`fs`); + + async function predict() { + // Get the full path of the model. + const modelFullId = predictionServiceClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // Read the file content for prediction. + const content = fs.readFileSync(filePath, `base64`); + let params = {}; + if (scoreThreshold) { + params = { + score_threshold: scoreThreshold, + }; + } + + // Set the payload by giving the content and type of the file. + const payload = { + image: { + imageBytes: content, + }, + }; + + // params is additional domain-specific parameters. + // currently there is no additional parameters supported. + const [response] = await predictionServiceClient.predict({ + name: modelFullId, + payload: payload, + params: params, + }); + console.log(`Prediction results:`); + for (const result of response[0].payload) { + console.log(`\nPredicted class name: ${result.displayName}`); + console.log( + `Predicted class score: ${result.imageObjectDetection.score}` + ); + } + } + predict(); + // [END automl_vision_object_detection_predict] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/object-detection/undeploy-model.v1beta1.js b/automl/vision/object-detection/undeploy-model.v1beta1.js new file mode 100644 index 00000000000..e3eba9c92b7 --- /dev/null +++ b/automl/vision/object-detection/undeploy-model.v1beta1.js @@ -0,0 +1,59 @@ +/** + * Copyright 2019, Google LLC + * 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. + */ + +`use strict`; +function main( + projectId = 'YOUR_PROJECT_ID', + computeRegion = 'YOUR_REGION_NAME', + modelId = 'MODEL_ID' +) { + // [START automl_vision_object_detection_undeploy_model] + /** + * Demonstrates using the AutoML client to undeploy model. + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const projectId = '[PROJECT_ID]' e.g., "my-gcloud-project"; + // const computeRegion = '[REGION_NAME]' e.g., "us-central1"; + // const modelId = '[MODEL_ID]' e.g., "TEN5200971474357190656"; + + //Imports the Google Cloud Automl library + const {AutoMlClient} = require('@google-cloud/automl').v1beta1; + + // Instantiates a client + const automlClient = new AutoMlClient(); + + async function undeployModel() { + // Get the full path of the model. + const modelFullId = automlClient.modelPath( + projectId, + computeRegion, + modelId + ); + + // Deploy a model with the deploy model request. + const [operation] = await automlClient.undeployModel({name: modelFullId}); + const [response] = await operation.promise(); + for (const element of response) { + console.log(`Undeployment Details:`); + console.log(`\tName: ${element.name}`); + console.log(`\tMetadata:`); + console.log(`\t\tType Url: ${element.metadata.typeUrl}`); + console.log(`\tDone: ${element.done}`); + } + } + undeployModel(); + // [END automl_vision_object_detection_undeploy_model] +} +main(...process.argv.slice(2)); diff --git a/automl/vision/resources/songbird.jpg b/automl/vision/resources/songbird.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f10312d084e9c3e91230cae195683ea5238af3c2 GIT binary patch literal 24322 zcmb4qWmFtZu=e5>T!RN#ba8ighs7n_Vg8N)$N&IXSU8yfWc~kuNBGx9L;@hA{Ogv+0>Hq*!owlL zA;QDMBOv_uM6hu1*a$eE*b&9mk#MO@T|+ohO7J9_2N!OrHO%&qIo(3>Q%i4YBsI<5 z2?)8Mi~oipfdTx-@xNFAF#m=@Kt%f2$&39@gnulsD6j}{{}}!O1{NC*{vU-nf;u8D z6~{l0lEIW_w}ri%TTTfLBs_e{)W4g64{-m4!G^;IhyqU3wWxA5+Dh1UmR9f&M+cT6E>w)&eqiv%<#^*K}=Y0tt%-L4rO^Ao{HMgpNrjs#J6Qcvu zLCnL?hJ7+Z0)4hVRx`XIdr2aFUkX#WB6jUMIFAP7bOmQ5o4-Wk321Jk&Sk@| zX?vi^DA8(3gY*g9HRX=S@7DTxCbiZ-lk`zBzXoT63M{V%G&_%hdK&}9E!kR_5G+6& z0Ga90SZxg(v8gybG_6ArL9sNyPOx@n^^wZJDSCtfDRSmmi|3F>Dab5OAG%-bA|2n0 zA9|#gqPtu~)xxjUEYpmogCHntY*`PZLd)n-MWR`vHb+2(<9bJHcu@&R;pj7ZfV z(;dxIyHHn%6vO8HV($jQR=$4IgI1Fv$pj8f1Zs$4dN7u?l$2HgO&eVsO=MU~R!dD7 zUpPi)-KqLdW)t{icyjofeX5-+@;8nAE&i~awIm!5>8IZ96OxC0Q%WhC1r~DErtvwt z8>wquvR}cG*+*$qBpeb`ZSIWoL$jfb8Scu9m%<&YHD=J1(&}B!YwVc>6Ecq0*b==E zJu;5Zr&?|ilEvO^z>LPz_(FqMN;}e$2?Q;4$<7TEO)A6^<(zhPb+sB(O@D3T<(z_+ zQ44&w6(-Pzf^7DKCn&~WH%DEnr1TlVN>2?0h4RA7@Q}oC$Ktff|CCgw`oT`HE6`qC z>Bi`*mL|ju7H`ta56&+Gih;Qe)j1GSxgELu7>ygt-4A9zB@LHsX3)HGpr40<5slY~I%fc|6DcnHTg2{4AT68rvOsP4wL?)?3U(V@KUmsfe zj0~Le?2j;V^fmAw(j@XE?j@QikvFWS6k*_mn#u{I#jv4+sEWuRmz69nMsfux`<^~` z(yy!8F1|wC7aYdmxss`Eh$katdl;^yVBk+BATS0E)xIh;RTBMqlftEhu(^Bb78$vI z>>hWaf>d=j){*LZ92TYss#H95MvTnxgQ#q>mQwnlZ|WAa*jkDMigx4*GxvtuYG-)u zoH)Evglu;21y(JZH)y0->T`-zz4b>?iNQNUG6H{ghIt>I8wZT&8)?Tc{BfrAx zho8#O4p!q)9#qH>7}kthX^a`!oJ>rE^3~Y*lu&3k zk%rv>>Oe>us<^lu9t@?n#L(j+Lr(YOGAGE0jm|RyKd%^LB}GX>tFk~Zxk0luxI7+` zzn_qM8clf8N~f}}(%zlKGt@;CA2iTYHjZm5>)rzX4Z z@Y2$HJ>5f+QlKLa$sK&AhPDAJkAR>Z_>MU;I1b-3rre`q6Hs8<%cHVKdr>2=*Rc1? zz0d>AbMG2=y+CzAXt0TGrB!_Zaf)}Nbo>}yGAx)!=TEkWj@BAtTq;3V>@9s;UTNm6 zhNOHs3`^t4I^wf_n=zv*7g`07CaPz`)+E}*0^-RA1~R6?&{B@%mWVW>0Oj;YPO|&e zAW~EKJo-c&lni>G5hz2*(LPxqFi|2$DLKRg-^%Aa}`^_wFFC z5*_iXYrn(JYm*-JEIwJPUHxwNOI4jLPJ*OwXMxcRK0dn#VfEVji5IIn*C$=PoO%Tv zc%mDxJ^e{QkJZW@Ocx1aV&c}B%F-b+Bod&fgW7V<=XNk^14%*uW z_s`*G4fSCVoRGnu#+8D7u>~+8v|(`WiPLUFzq}#q3{A%os*IO5&BRYA!@Ewn%`&ho z@_^%HbH00nS&OUO3UMeHkYOeyL(-O65pz&6E>&}f+oeSWHH=(u=Q}a+X#W_amWZdM zhF^u82eM1kN{GS67trR?zym5tbCi~H(W*D@qe#sfASltJ##%zGG_a@eKWfG>Xa!l` z=~ywNPUtC(;xuRbo))A@6ypV}m=C3}o7(CbNGS)`8c1nRk^xXx)q$4uxrZJI*krKl z(m2l>YKI!ZxdM1f9=3Uun(P&w6)?^l;5?NiW;H0tj?#*ov^odt+C_=}J9VulH=(#R zimc`m3wSK&+2`w-waY^`ajyU`%UK1la7MQtKi-1N>4=Y_=5 zHvQ-~5$D->|U2IX3U_2IDlQ1!5lV(=xx)gD;R7&zuYxWZ=ky^Kp#%46E zGiXhvDUrklR;M-Y4V_wZVZ;qCnd#e_eX}k5>rB#NPsd^6UjXL(H=PXgsFf$h8Ka>9 zy8JNUz~MqkhYq>qbddB$laO$o4FG^d380XQi){LhR`8Nh=C$-4j)BOu(%nSE0j0^` zjC49`$k+@0$Uo!dO9xNKIJY7q%NMlm*!v*Nq+cS_no-KdV+R;G7c5tQ0f9IIJJ_zm z!dzDJg%g|nJ&G?}P6vl9Z7g#=Iu(r>w-OuT30>Duk*#q@i*qz2$)1P(3M2HlsKY<{ z+Bkpi^XDUpB5#yUAguj{AF*+H^_~@4XfIX0|LA664s$b}7O-}Ug(M0*vlm7mEL~7g z(U{CxjE$1o88m2&YfRa*eUn_X`i`$&`91CAY$Nbn#b?;l;Q4;fvA=+ka@c($U~)oS zoVo4O$`5<#(XS)Q{@Xqy*t{>2oI6I-`OZgG8oiF9#XlH|vJfctZ38pDHa9q9ElK}= zzU%md+2E=~&Oj`=zJ?JF_qFg~nZkz4V81Z4ym@<^U)C+(AHt6UW;ZaaD{`-jS^BFY0$! zo+R1(Yp`ov>tWpuxye1Bd%hCiw9q=Ox^&r|ocbjY#K?zP@KCFLRl32X7~SW)C|}ch zF~MBF_3S6T>05X<31SW7KL3CO@kS(ubtmN!+t*64d;Q4cI>vyX_wTjR%_d#UcP49} zRvB?6KtY{e(y{PgkCd@#__ zu@r5`ii{w|RGYeL_$}ivz>*9%a|qw9iSBc9wOHR)nX%ekx-O0OdPZG>bfPv=cwKo6 z^0JZwYYrw*O<{_~T}y?YzgEp{4Q~n9oSq6#dJm6Xqm||)1Vfuk_g_*aGEG1uMLyT5 z3=XfCoIvI4LSxq^Kwtn{!x(6U_@_3Je+H{&3^up+K9ELfc)`syL`2^3*&zC6e><}! z{0qn#+o9CquiLL7n!C<^#{NFqoN|5qEM)&B*amjha>S}{(mdw)@>=u}r}yn{dHN5x z;+b`A#dx{N`E*g*yX?H{!rtZYh2Es&G~R`B%u@ChkFKCdhOjO|n9O&pk;Ves+sU!* zo%W*Chb>7_)wIyNqpH=(`=FXjN!n1R7}lj%vR9DjF3ATz8wdXdw_OY)4m6(M_xcxt zGD+GiT#~fiQAh(~#~U)aclLEUXtf71<|1rQJNCi5fupNmM}_ouIERko0+c7_RkJqf%+Y1Kp)d$N!vB0>M157oxG^MV795U>e4Xz3;_%Yv z9Br|Dv?EfGW!~pyt!$vkUtLRuYNPvh?P{gxv`D;OC!d^VZRl}t=$+6UqOQ(0pey&x zhsksnB0ze||d@34jGF z{ydr_*l_E4bZHx^%mGjG5Lft1kTbWO zqIDw*c!j1UMk$Myn=eUv&rm^|xN;y_cc~Ak{ftL#2%e6W*2_U{n9@rRwT&RRuyYL2 z!mn8>7W|f_k>IvJ+E9v7P!&~eaHxb1FebWNS5nx zPh}y%P|*kXP7Cj2DU<-^R3_ape`;weWXfwtrdF2@HbH9UIfd1^CCaBVcee=eHQK}* zv=NJI0ofbi4Fz)$vh1LxeC$*$5$WO@S}5|xw1m(a+X#cAJQ?bFBAU#8HR!%C`fE|q zH_Nq!kgs`&9;;d?r8U) zWs>5nCs|e8PyfD{i_vA~05~SGrg_H+H?)+Xs6?{sUCg%pzX0t1KyBY{`2|s=~Z-G5A$K=UkWWM&DlmG$NaSMMBTWvoy zorqGe^HUs}#-)THbIeU}x>+%{#bU8lnTe(aL_bv1Zj*+IUXzTSSyEn+ z(Kd5dT`3}v!$OO=D?yH(r@oXr6}zeC6h)UUK}jYS3twapJuyU&_?uS@$QWIXD>e`H z*(=7JKvCUNjm<2sTt_Y5tTq`1`l1qebHlM&{Or!IQ+@d zNS9bh1WUCY2qZvArH=#ATjDd$wQ6QjbJ4akw$Z0G5F4XuAsEWN5hqSD{q=4FY?hW% znXE@dThTATh0bBe?JkbrvTsPgY4dw^y`V3KQN7~EH`|<*vbfmiOXqx~`YGtxuRiOd zq(1u7eT#VUnUEkP9qpn_X1Lg>(~ZB@=429|0QLj~XmGFtI*9AqiNZ|(;N?l^uU1>J zC&HT(EM0K%*SFpvD{_T*j_097wt+p^o5u(Y$lRZ5{M{IR<3yc{=B-KzmshlAq@zCI z@4^g_$K8@{aEH6(d2O70GnwyGm^caBRDQXC7Fm)!@FBIQgT9Y1s#L?SJ%IOpYIPS+ zu;9d5Xo3O6=L#>+1XsI7ce{WP(;or{%Wc7!kYhv5Q7wF#du8g7jj&;&yAQ+x*Igdu zp?3H3LXQX!&Zk*NWy-3Fw#|*!n?V>wweMBpSN84L?ChHB!JlOv1_Ih7ff3X z=}i8Z0DtarohJ0Q?W@RV0AEIQ+j`kPSal?#@L|&Bp8C|NIrYW>Le)T+QfBXtk$o zh@DV_CX?8VPfk^MOHxaNU{S9CQY?{{jL9$<*t9K|9oucQVbWv_5Qmeq)xZ&wfcLGD zR8~typ$!p#c%?qKM8%pyl}0@*C&T(yVSsiZF^gKRDAT7S73@XY?)#Y$BZM|I!D!jW zwq|t!$bq9TjgFDv+-3Yh`M6Y4Bt?-3h7prKW>-Q1Re~wwl%KT0uVzq?Hdf&VRSFcG zO`<~cyVxx9tT^<1Sbo8MlEVgrKBG(PcC7rV!UGT?Ut%RwC*=pxK*Yt8ie`xluS}2c z4pce>oJ&WIv}3D;K}tU<%6Q6rRmag&huxqJVX94lli-+U+E0#PFxOcyopaqlyp^gs z><)}DwYZKOyip?<{QzLxIkYR)l9qsF6oZX#S@+P-BE1#$#AX(u^C5BcbP@RNe5Zmw zxkyr8pWo9Kprov^JJar@yD4+sF`}3lQnChkAa-X`_LuFX<7{+2MLLd6J$`4(I=%CP z-5+}2>i%@t)$--S38_(3>qDgLn588*HnS%4dna!qc@;u1wv&ecxO=qF_cU1Fwylt8^3aed~a=J{XA&Tcs4>#dL zF>1tzt;h`3tBbq%v{)JFxn(u6<1X6YfXG@RF?ft5nj^6mj1X(;eFk}~Yy>IN`P3@h z@P_8vLTMNw$|JY_lBvuwZO5F6{!H+1Ot-ZW`AkCLR((;|<>|?bVgias z{H01oKaNRO%UsR5uxChvKRRrKEQ8E8OJ7)Y%@uTbv-gOQ|5LUGj1&`!5~h&_zZ9q( zAthb$u&=kPI9Kb+TMC`9$+rYTd5pLI2b}kBp3-QyZV>$ni;`Rc{7yhM1L4?Z6Ze5U zi37fNfT6JUR0q#cz(eWCcgrT1C~Trc9UNViGPj*`awCbgBSBxr0l}Jda0YvotX<+~ zG0ISk;zUy;TS$n}|7GL<2isv7-D zaw;JMjmuu58=y>up_hX1ApQ)WPNQh*(!o+W)Vmv9w?{`E_Z>(ERM!j=Cdw!&?-t0;!S$n4hA1tgJ;deo;+Y2Wa|=kM!c?hh3v_=-nKl^4BE z*SX2wP&u~-FDx(rc{KFC7DbXZTo$joY00yCb+xm=sXeAt?v_!o+Ld;cvR0okjzB^# zQ;&E-(9E@$I-^|V*~Z{fQTi&PTO#NM=`WzfNWeMpZ9z+VWyp2xjMY7-C6AdL$VN%Jf9?qCjj*#6ks{KREQvvjp(QZ$CRa zo^`X-&4m8~q`_{1;P0#pu<)Qy*ip*n7caH>qUx`otYbIt7=jfK`twKsjGq%$7xH+G z7fBo2x3zDXJZe@pZ*Bf2kN6fz;Tn7*W4fZX%!2MZ_KFtu;=Mpjh5#B7)Y2592)7nLC`i_H zpxbnQ*nAH!lv>qkbBeb|u*M)aYCsh8F+!6c+kz9jb?8EnnbOxBDD_XV7ix;(FWFQ~ z@I-$ZfQ85dboSjh?0ni;5T|e*_$2jnzq`xZH#@~}t%R2paBPds9+K_-3Ymp$+1QB_ zv-m0;-i6kM^GYafg-6?UhKMg}Zxb1L0xMYxQ_nn>51EtMHMIbrm@{cVnJxPpMEznf zI!fKS=XwuuYFXN_+b$pVv;GT^PuQU|xYsw`x6P5y|K~j-1B0`_H|kFGUBNS!F@fCT zva@mg8aXDI;EO+483Rpy0Y3Q2eU^9twPoMCKlrRn*b{YblG5T+mggMY6vI9?nc7mu z>m!NiGZ*V7j$H*&xjC~EEE=a>jO=dyYMdqy+EFH|S*P87vZk8PP97Ri*-+3lfl>Rr znH&ARV>7@MHTtGktehWS8yA1J@F$Ai^MR+OB!R=vd@8z2*k;^7!wlss7bbCD(m9(kOB?eKO~D(`(99m-n%uX;Z=UrSTcZ85ebQ5~Tx= zJQKC3ayFLr89oybb+=q%nrBGXGM&Hj??+GOq`Rt-++pPR+361R=O+Z+T zbspKi^8|@M&8XkxW((~2qcMisodyCMNcEqQ%0)M|ulq=X}(R!9!gVc-2XRuOmaZQ-%@O5pK-Jy#kdoOAkJDsE(L z`~|T7ocNuDxZ?L_VDTce7yJ1}X_$0joZ8SyG8d!IZ%28>anA=1-Z7!hTsD{sCjG~5 zVs0&E;pqgO3ED-AxTA)rxAytl>BCWML7C*i2Nb9@jT@(+gWwbIJ~IWnk}%MjC*e|7 z@vUXk8&M=x=A0(dv=GJMW@2aZKxx**VLurMF7Fol5-`p~K^KUO;Fh@P5?z2Bo~r@w zzxHe^sikAGg1a7h_OY;8&%0;33dFy?%ngXe%B#ylvN1rB z-TlXzT0=pxOhkpCfwjpa)kvKN^;Ch9Tf928vVfsBblSc;grTldET@5w6+U2DS~}Z3 z)A1u0JT|r_-j6g?IF0&DuIgOeZal#uM=l%G%Ff# zWz$Z{-bVRtG%%8!BX&D%O~Lom8q@yxi7AeYwZe+oT}~hrnYx;4gau{v7h26Bvq0Zwa_7?UM#rJikC+EcL&z_S5UiNR2d*Y;tZPyErvVNVL7b zj1j-BS)XlHpF4?u+519rAc8AQZjHj4^2$;q#5t%!eLT^|!f8(3b7$T++KHlm0WdBmMhpxZRH`xemc}yB%A`eZ^l{qDc15H-1;5 zHFFnbXD=p_6^mf_IXY(joIkj+@@uZ&BvDWsyAXU6b!2d1qckIMRbu^yHA;2)W02UC zD(zrPqDujED9U!$nv`fJBaEJs640zJ-`L3L|F}@WzeIWN7BOC^MpYy98^)@iJ>_d7^DfWg(V1nnLHv+8W3$ z-xxRjLh-$fL26+oEvCQfM`O|AZx)1DY<@*0iauhPToVVRewLF}*CC7jPP*xZzN4Ak zRrO68zuWa4DgAkdoG`%r#nAVaVtmE6!FYtWy#6D%l(>djv>s!)_%wn=L})fS+Nh~T zu?(mjPg@O9Aq}y2bwj#Bf!9PeH7-<)B2*{)2RT2vKbD8Pa{n1I#btl~WlyaC zDq^Y=!luemkNqEI5hQ=Ie8Q>V`lS|QDt0+Ud$drNfKT7;7s`E%j?ABRzWGV+v9IoW z{$z>j+USU5fINMT=!6P%MjV&8rtZ8ZlhH;$B6RRPp7-lE7P4!cNPfBK1qPo= zk~(K?K0TQ@m`J+%+sLYUlkWxiZmwbc$!yRaA5is0`3v|S6!Y~iuVW+dFQB-4g^pS1 zwc_&4JfN}6>uXFA?->RXl%IO><>fEHWQalvd-km;x?2G9t5-1imOf0_7b2N!+c6b`@H9bo9PBZc6{_cEqDqV#7r6d zW;@-9bo)LH8J64*Apvm{!uk7_ZxfGGWK*D+vMrH@l*~2J7M7?Z*7 zbh?(xV>S4KxTx+H(!@r2%xp45EFnV${>-6;2vA@&kNdh{w4%AuW zXy9?j>S06GA^xS!h!|8F*Xj-lQf;;{_^qen%Vfxh;)h*pc)M1FwzXuns#%i9gtAk$ zwUwB*_7Q`BwmtWzo5QqS@5)jAM})Y3UGH*oyz;sA50o=4#HVMJ3Xn7F!pJqsRZDvr z>oNV)$zb9}|Y30NtHVPG`1D`av-dpR;s*7CA30VL)+ztQ;I zqJMwPl`vlA_4C+hC=Ls8gyj9-D?!SF*3t<6lQH5ORqifK@_oC(+*; z=Q~!t27jhYcXhMH3WM|6Mv1^hs?qR6&=Ho@G}vn*L1X1by& zn!Neuu9;xRra*3=G&s;nn;&fu=e>p zl(^es` zo#M5Ei7M`q`{YflVaVE~T{a5}O_KYe=fDi{^eJ=mqp-Hlp-q-hbmva=VMby?=7*TI z&h7CpVU~4ku7v?PcUfGzw$7DP*R=H@_kGx-v7sN9z#t^j4&Ro7>Eb_HL$#ZDozLRd ze)TEaW(8SF#SzxBbedc@WqT2R1f^wmIP~o~PX;XbVAR!3Z1A0)j>PO}1G2*EX;^t< zH%%WGnX)%l`Ei)LLpwjW0=vx>lrhFeQxljPo1@;+TD+~IU=j8)7Mf{aH>uPtsS25G zMn7H8mQ|`7KASu&SlUbcF=5NGtQ)bsswI%)(8l!{$gTXKw#y46)Y^x&J*k}Yk>Lk^ zC>(@GEgiI4Jj~k7oh^rE^@SXu$OMGZmx;rX_|IG0uqiPJ)7G6O(sDe^7X6PiaKVZ(&OtquxS*HDLk_VvL(I)urH_*Y~KOsSq1 z!zRpkh?TQALYqp;M8kGkGbj(<8iut(c35!y*%$h z7omZl;k}vrU|r=mm=pD9W@hIp;?4^%7h97V*?JJG*>w70!q?72v1n3L#kQaHiJLf zw^XK+xhUiptwL4;=!dl<)<(J6(GYh4T$Dx5V4dw+5pc6pHdIg9I`Y_u@cEhP%bo)c zvDMKON=L?fGz;};?)SqkVT2An`F0O!`UfN{(4C5-a;uFf%}`>T1o29#KZ}GgtEcmBR*73g2nLSKW3qTTH4!SK1{OBvE|AxaT4 zagOzSKWR#Sp(qv~U|XcTIYpJ;4^Fy>g9FvMi zoIw{a*e=NJs}GX87n`f+qa=g`m?gH^F7#}iYo|1ONr+e%y5FA}W~jA!#>fFovVQ>) z;JM$@B-`_w!^a*5_$s1U$=qf9gk7#MUG0m%N+{v?BKWO{I|zGMQ?s{SCZ!=f0OQ`i zoxvx+azm8e8O!B@%$sxQYJvhqG)3Pqp*<>fr}i8gm$s~HCl33XsB^Ec>?rTs1;AbK zFmtac5*m%cMXY zhM%3>2J@r<`RYop-vsJ}DNQg8O)>!XFs&GX{w?*wkZyx>V3x1>UHPh5pde;-^)NZ-W4t+MD_MZ=fkV3~8|s#w<e+alu}FqV1hczw!E$816ev8X9-Qb4 zXQ^qf?N+P1JTpznqja|8(O-z89NZ;^C-Wuy*mfa$hW2xuRGvb?|yGX5&WUSo0k$=0gJlt8Ja{XyC z_~1k4S`&81p5uT>Um%XQ@)LJ}B91HJ>g@fmuY#-^=1HI}Z*wDF*Rw{TMRU|iswy0h z&AF&Xvi8BHW9}+ihs?v{ojxm-0sgHq{j(5hJN&6osO`|NxHy$4<|hdwiQ)JQ@$m(bK zHpR!L>-*?@o|KJGe=OVZY6fIn@9 zr5ugfsRz%oL*A~rH#o06N0}PBpxypG!rxBcx3TD~b77-n!GXw{$i8vEF_lm7Bj5oH zUTm#r$%+jjDg8c*eqPa}5LGabpqvV5BbAJirOGU%Bhyg&xL^iBDPXqFJ#x9FKaX|B zg_#u|5-7XH!g4nsU@_7eIe|zUjLJf0+XV$4RdEw*2&hMy&%2~mvH0#4tHxBpF9e4k zcGuJh_PXsnsLK{3F&g2=+pv!zh=fX)bzP!T*P+$oA3yN2_84AWO}wS72NHUsQ2G?I zU{=fhfZODOKX!Z-*raf)^~|MTn=uLAO)4}j-ss()aGwe6@~!`l`13Qc(cIxTB23vd zbr)fKFpfLP0}6#z+l#S0p^ZaAP~Nm-Rl!Ro7nDHikNYGLdMwb&zbbI|W9mD6!j|?V zNm;;W-u`HqkDu-ZxxeSki6Z%Uejwo)DZAN{gWg7l?kaj=SiT_M`;wU0?|4=$OPo$m z`jCC|TD+3Cb(eQB?Oa_26tTo8ld~57J^}!c(5>x zmztB>aacHo^A{&eHxPsT?hHxdZgZ86makk);bc3T(%ipX^{k)m_veOu(Yc-D{n1y~ zAAX8{57?faOR#PG_QQA0o;e6}uj3naoEWv+fxnfg2aJD8U!&z35pbHHW~&IXCkdQI zk`N60BPiz{ynEYp@!U-@-3a-9+yB9z80E|7ZID6ct7A}Mt6w%6NiOgc0< za9c(xCGn(D#qZ`bb=mM{16xJ0`=jlT%jB8TE*HUpbGtsi>-W$1Dk@zJEaT#rjetZn zWnlb>k9ae766=M~_O$7W=LbIq zY=sC5< z0x1zJ50T~WMzo29J!lfuwtG$) zn9ET_S6$DEp+z5VI|$G$zi}*)oC67vK6}4>QPddjQMQMR4?XNDJbv#DMM~;y5@9!)u#ZBEKcX8>8}2Z!X^)C;+uW{)LT`v#f^f+cbB*GSl`D0AKWO&fI*9 z7Pw2RbNaC}hNmCE#fx-q&M8;QOcgEqpoK2Mq6Nk=Hk$*CR0)7n*zD?~N+MILVE5kY zyj@oZ-dfiy|JXp9`SSZ^v?oeGyp~p?dkis#ynkf(?@BE<02c~>a>GP^D$%!?JWE?j z%JRI=ykPXziyC*3#3i9ImNLe-Y^c6Y?Br)~$FQBQxpap!|wjKi2F##LR z62F2QY|xhSJxK$Rkf#G%jY=?hd(GTHN-4IwH5_OyITfF5;M^FOI&a!lqT)V1#ZX9F4p!S0b6?yYm%y%J!u1b}6#qgw{STTnm{lzZRjIQ4Gd*#7xMGB& z%Lm_b^+XVt932qq9We@2MQT{FWtrT&4_kG6Qv-D79Qb(#43yDxGx0jqs!|tZV8k{c zXom9N91+KUcmLjksVeAZIg^YSC)KfcOCTX40Wf=~^wi(nop)iH2e znt6?olk5V3|K&UkF|N5iOj%?hZPD4Xhq!srkmHtQxx2dpDQo(qnPbhZ4Z`aylM95u zJXI*(eQTa9XZ5pVG_Si#CLk`D`&s|WXy&`y?#itn!7-f}SF2)~jwedzE(&1js%e_U6SS0`ZENIj^s?F!moxvCOh<2IvQ`sTEAd5!0K_!bU-8+Ie zZION!me=HLgY4Ln?9ehwF#b?EsxrwFLVSp$_L=m^(6H9penJAvrks181 zsI~Wck2jMXxMu&JRu(mB{vnQwBKP1tAwLkj@T3jHq}shv5>ZupNXnUnh8{?8CY?xP zq7|;ZLpy3DhPvkZ0gflwWPvM=7#__|73v+i|2cCbi!M+UTmhW7BOCP*K__Dp9OuFb zXb1>KFej}yb(e?Z{5owp`bVY4+wRwH%xo9o4)YV4>TauFw;r8KGGB;Gz{5td$!SaJ zQj2hdIR-+?Rn};gu|8%Ibd_BQ+$&>ACtl*&X!>)Oef&(3_(a~jvp^Kk3XFX!l`N&JlTB8H7o!;F}_K3vkCDuEtK8!(DSSq3!;4( zwM*e~STL<~1@N~E7KR<>ueKAB?0I|Wi>aLH@Z7(;O67x-1h=h3ts8-DRNws zH+#rwJ`C8nn@pE3-^Q-?7BG8!OQ5n4YvHlpF`cyI$h+4`M5`&3zn5aPTphM#BzD_x zQB+bHI;=m5U?b6++o_^#OOyX7!!w%Aii)#);=m34adyUhnN%4JFBc6mtXpB^&gDn| zqAM)>>vyH$hw&M)R40LVi|vM*)o{%idCU*gPqA50RsR$Y_WTIZ;cy)#n zH9^C?&nr9(_PKa_^Tp)}b04*Sq7aa&du%}KvCS?e$ke=)&#Ak$I33h};bXaL3ZXKE z%2rL%<>!$AeRS{$NE4d?d4*r4^WyBo3_Vw}$8R@uPq@FEsAOJLan~u@X8{boN$Wa< z{2CafF9C2t+~t7zedpqJI>b1;5C}c#9qB&u@6D=TG@-(+Kc3o)@48j-%9I~@p|rAg zt?30sa|*)f%^4l?d{9~biZX`2E~8hnsDGB;T(hXLuwGWDipWEEs%OoZ<#=9#&k^$i zA3b{F^mqU0ljUB)+nN^BUf2h6>Ir2er9Zp<1)y=7(;0a>GettLoWg3Li_LyzOkC*9 zHi#!)StO#vfpP>7fEtk=etRhSS%bYk??Sud#IYR7BhX{~UA3k(4Z>4MTIG-d@+f0* zqlviwScWa@;y<+OX*0#Cw3*v~U#Pv@$;24wRs9g;!(^^q5ORR3Jot$?N)Z2CEF-;u)#0}!!s46nc>GN`fpPyB_a#}q0 z{F>Dld)(HPnYz>?Z~KTiZNK|LYE;PS(2)4&LytuLRUGm)Bwu!zQ!3$cW5;CFobbF& zMYy~^V9lg?fz=uHi%Md|dUVWuj3sabCB79_`Fs1eI7veo561>M95K<@o!9kI6EVWj zZ}hQgng9WPb=NN{PLbOv6rrbW@dyK4U1P65GdEx9GVy8c6TR{Hi?Dq&XL)SAqjy~A zF~Sx+?B=K)*o}hu4|U1KzPta)A|4^zz6gBBJ`VV*6B<*v^1=qVL;EOa(C&YDVO7_HYg|S8*!zMdvuQjt_wDt>c zn-3ioZ^G6atk$SPyktx&Fr&#StAvCS_z_0!Q&``kQzLAPrkQgtrfHYR5Os`1z3PgW zXtXT=XI4h!G>=nciWx13HOekji5G*EHbZ5EC_Pg7RT9jFAs4;MzVQnBlr!g6irhM+ ztFcdLs;;zARG(ngnk^9Q++vdGO6d8oQA&-Ow+V@w)kM1uu54DN#n>~L;bWt=TTV9> zfhXB&IINE(0!ag8LxyP{VkxYV%@(<}?16?;eyEID$6~3VmfG`LrNFu{%q|jMFQ%F% zaxqg#{MziVa?2w#$wc5Nwv-%bsLI8n5+@}vA=&_32)%{%7E5sF9_X+S8!pm4?hd?& z=9@L@sV#fhU#~lKO;57|j5H131+cx%F|p3i-T0hK zSXfD8Ty$`dY6v&I*FAHQoIi#(M~1#(40(i-7jq3avq%JoX|M_IzNXO2*_)3EIfB60 z=4GO3CgwswG7>MFDRFtVw>@lnO?;UHqaxeG{;Nt8LD5J_#t^wfjMr#Kf7nH_#jdCm=pE{q4;KEw>|JnmRU)9UQ$ zjID5Vd@GBZ*xm%KYhD8{D_rY8LPK6|Lq>$S+V>Va+=^TiFcX}wFL&_V!y`e~=ET1- z9}(A0NatnEg-c}G!Hs?1GnSVhziBLXAT;T|y4iFN4trc80q$>kaLb1tFJV+>lBFbltkFOw^8Bbl5K3$3{aYc4_Bw+8@SJOSATvR$@5Ck<=`wASb6 z=im5_NW)oXe6rx9 zW#2frv#NRD-Z8GH9vsb#G1qCnXNcTb1A!o0FbAk5Xq6q3)?V?ouVMcHE#=LOy`)?o z?Z-1uA`ZuyS?p&lu{6$mm3RfY*LG!I#<|X>lm_XbYll0VwZ}oH&1)Ez9VAZ6BOJms zvwu>3*xOcF#o{ z+4g&lT61)5$K%au2Tsl5j^k&@(QXL!3!-+nAALhjR5xJ^fBS`x{X^=x^enb!9piCf z_9QL)grx7Ns_BlTG|L$D)Z5$-nyqww%&YyaevgJ$8F46iuev$64c7N_#USM7-_Ypc z^(*NKD@z-2z&t(?&Bx34&3wBMxIS91s5l<5O-}>JFLR@9{EJ3Y&!|_cjik*qlj{+7 z7r~OtA)b93Yqh%QJ^7*WOc{(0d!1&w;h^rf=qk;e9790R4!^#OoA!%N=UB8^t@`^} zbK{$lwpggTLcjQV5u0es@64n#ml1aoLv(NJWal2@Kq=V^b$PYGzKdJ0#1D4sfpcIy zy_V67GIB_+GVs$Na2DImD#o!lNn-E1K4k-0Qsbq~xdbSGRnVhcobE@XJkd_-Z{!dm z%Gm0aUu*YIRbG-Enq~Vz0&~oX? za(X$6dq(#K&nsr}XH}-F1v#dG5V?*Qi4nTou&iWTbt4~5LKd@*(hYQ53o$mHX>DN+ zLt#fSs(9#wY}Qn|tpPn&t0Z&IL+<@iB&Z5`?ulb=tssjBs4AvRikg`%mNE^L$Rrrq zRh{Amj#if}8J{Jua-wIbxM*jeW&wDL` zowo}{;p)2)b-qx&2C3Miiz!EUs^=5cTQ#)Gd9IJz8&7r!HUQlFE^WjzTwd0=wu#ZK zdI{?04)rJ~on?C|k1lFdH2Gb34@J79h~=9m3rA>+j--_W5x69dEpXdQp-5cWI+Kxd zd8|_fNCjJ#R!b}|RXxh7Bvd+7$!sC-Qi@c)@~a_wbU?+xI9UGxf9#Lp*_|DjZIBna zr|&h+*6#AY{ZiUOnZTjR#M>O2HabbcjlkSFu0A4klv?7=AlUM_zdt3h_G61UIgXC! zE8QVwvo|YjAE6=57S~O4S!hCXHSrtev2Lzoo3R68-mNQj?DDEXJaWU5Hp??(nYL)j zX9)|ePVOxurH4Hhv67M37i!Mo#@LijXmGhN4ISAn9nd2+o<1#(TOC0>?v~S0G5Jl2 zI{??4<=6nw({f1C{wpf@vxY&>DACeL$2<$Txvd#m8>UwHiF=1K2SA0v@nIVXYZ;lq z$9KOV8(8MOn{R|2?sprjeMF^5d5wU=&{`Pa)1#aTZ-+K#YC|ujns>h1Y0J-TA}KNv z;yw`Vz&luxvrV@vS>rnQw7v4Z?vc-7jfVNJU<))P3pM7>Hy#&TLJH;>%bMA48gl{; zR{(rG^vp-OM&0j{eP-T=Y2p4pj!e=^nd32YhM;C7hd7pya)5IYV0+#v&9o-g`hgY*KpW3HLM65fwj%LE>p%lR`_JeJ9Gqn+|j%M zLtHO*xw@Y_fzdiwWWE&~8xhiT(m)Tn{^360s=n@`Gq7>U@ASXIZn{SbVOVW^(Z==& z$;@8nm&W#hZ+6(^*7{}yY_{B6v8*^q?&`8&VRJNlSV=DN0CWMvVVS>*>D?Jc#hCiG znvy9Oz+XEhK~O%$z+z0w!P1NHv^ErUQ?bFH>ZGTjOcHx^SQ;P0 zqV~Cs;Cbp>WS+H+zJ~YFG~JUZ@FjU(5!`^U$zx)zWEq`dv0B-c3~($h&Lj;+>4>0k z+hjU*wXSqIbW}$&D^27`4Ga zzg0D3iYDwu{{VHRIl?5Ai;-l8qZJqMiv?qGr;d6jwGA816mp&P{rajW#)}=4{$H6| z6;?GZ@0j}_<*FxANhvI7H?pBIUd*Fq7c5daL#SlcK7=h%kFtM;)ZAGVX#ty9Q1$dT z38fH3P83ZVG-+>+$}Vz$UrG`hh=?l&jM^%?taQwPD=UXCj zuTaJ^3k^=_jH?`+vpJ%Yt9re`R^YM~0n8B32)Pi_6-ddH5UqeHPO>@X+CxKX~_l<>hF& zZSHdczJq1Lv|Lu_$;$AI0GBze1co~7adn%mtTSAe+ecra)pZUV#A;cdo<;_VgIY4U z7O~zVb{kpkaMa}{#Pm7=#`w8}nre0i7kC`jjKS>9@iou;u4r$ILIH5n>s9{%>SqsH#E*;3x^C09yRWW06UAbvfSYBK1#YSQW*VWvM=q z6y#+jf>Qni&}RLo-Y-=%x?DC4<}{sb;zov zaJKh&wxC#m9C?GyahY)f4&+~Q3Fey7@f?f>(i>@}_qNy8>S{%9M)>~9YF(YMx}JBi zW-N|h9#gRbMz=8ZC2E+j2USGtO$5<-y^an40Gn3*Q1o4-PaCr}#iaa2r&2UFzT;)T zaK~=X(UQEDkXqpWVBY%JgQryaU4cm){-TFa+uy0=o#6D){qv|@2NJ<(YGdz1-A?nD zE7;KJ%bln3SkqwJ>R#No`YuPtxJ^wYnI>Tu_#WH3IR_^(_OTY$+7Zkp%Lz1wIP6gy zrz3UpcDn=7M1X7(owjy zGEhi-k@&$o<^#kIq?_~7`mXOy#PtuQsLhqFyCHx#D9r~Yv()IXf1NJj1vo{bKNBGo?ACCU;^ho&Q^nFw)ehmwCAGL z`%K2Z(N*4u64nMVIO%Z4hTlyCKyICRp{j9*5Z0Rj>-hP*qfWOQ0evhJj2#1?Chf%) zlx}!zQ9213VCe|~?r=7<;~ob!wy}|?#nEjYo$)w=Xq}mW&J0c(hMOJyjl4h}q%CW* zNvWRE9Yr^JD_`LR5*`i7%CAM*MqAjsu-t9d#Cc}HEZ`!75 zwW!(H__q&akGYUNvwT3g#g;tm4ulN?`Gd54BvVZI*_$YLgq_Pu=^d7FIL=~_wZg=- z^S>?mj$Y~CYWx}Ym@~(2^|#7TF}_kqUS4ZRHPFf3A3yNLdstlO3-q^CY*>?>CC%;0 zp$CM}WpkSU0Ek}Wp$VQJJ6nA%)AUYlSfZT(`X<HfP7B*YPX7q*(1bfLz@an3ka=r1mrpqYaK^lp&p%X zx7u#dnKXUPR84Rs8}b$wxaQO7S2U5z1uj8I-EocsyL-w*-n877TGBLcp(fIMYwEPA z*jh*+f;1rLN0A3)ehZAgA}mQ4y{>KT=DKwpuvf(F?IrQ*UEvY}2gF}&#D7mRsXGet zIeVj379!+By+SZ4a8Myw;GvZ=kqfNmSSq)anl8g>>dI?PQ+2OIi-eG~WZfG)M(DVp zTKyGWw_6nzKp`Z?Vpx)lLX;J;6b)NdplH)&r68>LYh?R63abw2sS`q|+Ki`Fn26jf zA}O;rO14tZ7~E`yt8oK>eu?)Dd_Ktr zKMBu!ZfuNdofj^FTqy&cax}KK=f5 zkuG5>Y^SookUpm%NQ7+Je&Dz=6ukOEi+)IhvT*=aTI# z=X5r;+sGAe5l=kf@rOEQk)6#10@)6ku3c@sCjLq!t=XhyTo1Ufhs7kBgF%V+$s+}e zUPfy>kOy#iu7lZ@KTj)K1H;Q$T=u!am$$^ydWUo77cbxpb=ludQ6=F4!QJJ^Wrkfy z*23YwUQ1BM+UhEp-{V&>?YJzFfL;R=kX*F5oq@`dl1TwZbJr`D8?6qV!N*T-MzblXQ#43o)+{}|}g!xfV#xt%K*B* zx#ZQdD9DSQwo9t!vdV~LUR6>~0uD)uQED5aOC^@-u%(q`1gw^OD)p6ts|710oJ)A& zycffn`d?slLU5h}+`a6FZHMo2n%2nYi~KDEKsqLozAR6xH@?W|BV@(Wxx098Z_RZ6 zFyg30d0OC0^Z;tUZ)QAL+}5~mo21UioMmf?`ksD^ROQg!(YtZgPDM-rL-p7B{5c5 z-Ncd$Nz>x7=2ev0+}+Ol0)(!o%5Awe&2F65(WSwZuc^547ZVq>#@!6AE_k(uT?+x~ zdH$(>6~yk0WM~Y^Zb-9BtZoC3I~_nMD(Qg@bc9{`gIouQ1`7ghYu`reuo^F6^ldJT zXSQHVfENw(^FOFfMOPti_&SFJVUpFbz9yXH+E=&+v^0lY6KuuL*cOXg_gYtI6pegV zEev>ZXlNvW8ZDSyUs4FRmRm0kVNVjZrg>s*ym{^&ndIDn#|JgKhgrl8^ibpO7^Q17 z#Zlh9z8L^+j&VEOUgUKrbGnr!bZFx1l6{KtJK0V4SKKkr%GZ-`>J4e+E;sgFa-#*N zs)g{>gM6M6(o1du%J%`OAnSGV_9KbN#t7U<4ESxPm%lB|{{RK_S6~&wx+Z43UeH)w ze~7ecrLOt3D3aN2!V%S9cK7<@KOp$X8#U@F(S z4%aFw>MRu^TPwsWY_(iYs_Oh!xg(ZUW=)Zi=t<;LnaEehRT9}-o~e7)pJys21j(|Z zU{D%LqjGTe({!Y~^*ynii+_Uen^ zOf7`wla|Bx%&m_VV*$3lmfOqqDx$p2VUN2OLj#`M*oL%d6!6z(1Rl1q=$O=144P;V z7)*^9d9mm>KP7T!L{KGH4w){%O}ARq*lS|U9^zg${q$R8n2gPFumCuCe}9tad_jK^ zal7xY{kkiSz6hTkh7;3gXfAg80r(}dJV}4p(D!E+<;}L_b_3lYWp+|SNdr+}NZeTYY`6Z&FxE>|34UW7Ks!PMjzMtI z@5`R3d=5PDZ~QsgT||(H43F<8&EAK9D~^Ek%r`#W7Vk;e|wa=0HE@j4i68OA9g|`{YFQtz84bF`>79~4v2`0Hd zsl}j*wvEhb4)0?nr+Zvn74X(Sf<75v6oXbci;bKC(Zkhtp2qmDUg*Nd*n$W3cYEo7 zJr}!F!CN>oG!pg?h<)MC{)oFoxYIbTB`RqJIwlHW5lx9^GP-u78st0lRK8Tuo1nRB zER|QzRB9@aGEH?!894&a7ga;p5mFUNjWGh2sFtE!)l98SK(AEDQ4b1-g-DjlOP83W L=BdQcn2-P2+Meq8 literal 0 HcmV?d00001