diff --git a/.github/workflows/vision-productSearch.yaml b/.github/workflows/vision-productSearch.yaml new file mode 100644 index 0000000000..18f2909dd6 --- /dev/null +++ b/.github/workflows/vision-productSearch.yaml @@ -0,0 +1,68 @@ +name: vision-productSearch +on: + push: + branches: + - main + paths: + - 'vision/productSearch/**' + pull_request: + paths: + - 'vision/productSearch/**' + pull_request_target: + types: [labeled] + paths: + - 'vision/productSearch/**' + schedule: + - cron: '0 0 * * 0' +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: 'write' + pull-requests: 'write' + id-token: 'write' + steps: + - uses: actions/checkout@v3.1.0 + with: + ref: ${{github.event.pull_request.head.sha}} + - uses: 'google-github-actions/auth@v1.0.0' + with: + workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' + service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' + create_credentials_file: 'true' + access_token_lifetime: 600s + - uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + - run: npm install + working-directory: vision/productSearch + - run: npm test + working-directory: vision/productSearch + env: + MOCHA_REPORTER_SUITENAME: vision_productSearch + MOCHA_REPORTER_OUTPUT: vision_productSearch_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule'}} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/vision.yaml b/.github/workflows/vision.yaml new file mode 100644 index 0000000000..556fe45735 --- /dev/null +++ b/.github/workflows/vision.yaml @@ -0,0 +1,69 @@ +name: vision +on: + push: + branches: + - main + paths: + - 'vision/**' + pull_request: + paths: + - 'vision/**' + - '!vision/productSearch/**' + pull_request_target: + types: [labeled] + paths: + - 'vision/**' + schedule: + - cron: '0 0 * * 0' +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: 'write' + pull-requests: 'write' + id-token: 'write' + steps: + - uses: actions/checkout@v3.1.0 + with: + ref: ${{github.event.pull_request.head.sha}} + - uses: 'google-github-actions/auth@v1.0.0' + with: + workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' + service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' + create_credentials_file: 'true' + access_token_lifetime: 600s + - uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + - run: npm install + working-directory: vision + - run: npm test + working-directory: vision + env: + MOCHA_REPORTER_SUITENAME: vision + MOCHA_REPORTER_OUTPUT: vision_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule'}} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/workflows.json b/.github/workflows/workflows.json index 0894cd4a93..2a2396b673 100644 --- a/.github/workflows/workflows.json +++ b/.github/workflows/workflows.json @@ -70,6 +70,8 @@ "texttospeech", "translate", "video-intelligence", + "vision", + "vision/productSearch", "contact-center-insights", "workflows" ] diff --git a/vision/.eslintrc.yml b/vision/.eslintrc.yml new file mode 100644 index 0000000000..98634adbef --- /dev/null +++ b/vision/.eslintrc.yml @@ -0,0 +1,4 @@ +--- +rules: + no-console: off + node/no-unsupported-features/node-builtins: off diff --git a/vision/async-batch-annotate-images.js b/vision/async-batch-annotate-images.js new file mode 100644 index 0000000000..9760e31410 --- /dev/null +++ b/vision/async-batch-annotate-images.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( + inputImageUri = 'gs://cloud-samples-data/vision/label/wakeupcat.jpg', + outputUri = 'gs://YOUR_BUCKET_ID/path/to/save/results/' +) { + // [START vision_async_batch_annotate_images] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const inputImageUri = 'gs://cloud-samples-data/vision/label/wakeupcat.jpg'; + // const outputUri = 'gs://YOUR_BUCKET_ID/path/to/save/results/'; + + // Imports the Google Cloud client libraries + const {ImageAnnotatorClient} = require('@google-cloud/vision').v1; + + // Instantiates a client + const client = new ImageAnnotatorClient(); + + // You can send multiple images to be annotated, this sample demonstrates how to do this with + // one image. If you want to use multiple images, you have to create a request object for each image that you want annotated. + async function asyncBatchAnnotateImages() { + // Set the type of annotation you want to perform on the image + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.Feature.Type + const features = [{type: 'LABEL_DETECTION'}]; + + // Build the image request object for that one image. Note: for additional images you have to create + // additional image request objects and store them in a list to be used below. + const imageRequest = { + image: { + source: { + imageUri: inputImageUri, + }, + }, + features: features, + }; + + // Set where to store the results for the images that will be annotated. + const outputConfig = { + gcsDestination: { + uri: outputUri, + }, + batchSize: 2, // The max number of responses to output in each JSON file + }; + + // Add each image request object to the batch request and add the output config. + const request = { + requests: [ + imageRequest, // add additional request objects here + ], + outputConfig, + }; + + // Make the asynchronous batch request. + const [operation] = await client.asyncBatchAnnotateImages(request); + + // Wait for the operation to complete + const [filesResponse] = await operation.promise(); + + // The output is written to GCS with the provided output_uri as prefix + const destinationUri = filesResponse.outputConfig.gcsDestination.uri; + console.log(`Output written to GCS with prefix: ${destinationUri}`); + } + + asyncBatchAnnotateImages(); + // [END vision_async_batch_annotate_images] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/batch-annotate-files-gcs.js b/vision/batch-annotate-files-gcs.js new file mode 100644 index 0000000000..b35d190bab --- /dev/null +++ b/vision/batch-annotate-files-gcs.js @@ -0,0 +1,108 @@ +// 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( + gcsSourceUri = 'gs://cloud-samples-data/vision/document_understanding/kafka.pdf' +) { + // [START vision_batch_annotate_files_gcs] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const gcsSourceUri = 'gs://cloud-samples-data/vision/document_understanding/kafka.pdf'; + + // Imports the Google Cloud client libraries + const {ImageAnnotatorClient} = require('@google-cloud/vision').v1; + + // Instantiates a client + const client = new ImageAnnotatorClient(); + + // You can send multiple files to be annotated, this sample demonstrates how to do this with + // one file. If you want to use multiple files, you have to create a request object for each file that you want annotated. + async function batchAnnotateFiles() { + // First Specify the input config with the file's uri and its type. + // Supported mime_type: application/pdf, image/tiff, image/gif + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#inputconfig + const inputConfig = { + mimeType: 'application/pdf', + gcsSource: { + uri: gcsSourceUri, + }, + }; + + // Set the type of annotation you want to perform on the file + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.Feature.Type + const features = [{type: 'DOCUMENT_TEXT_DETECTION'}]; + + // Build the request object for that one file. Note: for additional files you have to create + // additional file request objects and store them in a list to be used below. + // Since we are sending a file of type `application/pdf`, we can use the `pages` field to + // specify which pages to process. The service can process up to 5 pages per document file. + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.AnnotateFileRequest + const fileRequest = { + inputConfig: inputConfig, + features: features, + // Annotate the first two pages and the last one (max 5 pages) + // First page starts at 1, and not 0. Last page is -1. + pages: [1, 2, -1], + }; + + // Add each `AnnotateFileRequest` object to the batch request. + const request = { + requests: [fileRequest], + }; + + // Make the synchronous batch request. + const [result] = await client.batchAnnotateFiles(request); + + // Process the results, just get the first result, since only one file was sent in this + // sample. + const responses = result.responses[0].responses; + + for (const response of responses) { + console.log(`Full text: ${response.fullTextAnnotation.text}`); + for (const page of response.fullTextAnnotation.pages) { + for (const block of page.blocks) { + console.log(`Block confidence: ${block.confidence}`); + for (const paragraph of block.paragraphs) { + console.log(` Paragraph confidence: ${paragraph.confidence}`); + for (const word of paragraph.words) { + const symbol_texts = word.symbols.map(symbol => symbol.text); + const word_text = symbol_texts.join(''); + console.log( + ` Word text: ${word_text} (confidence: ${word.confidence})` + ); + for (const symbol of word.symbols) { + console.log( + ` Symbol: ${symbol.text} (confidence: ${symbol.confidence})` + ); + } + } + } + } + } + } + } + + batchAnnotateFiles(); + // [END vision_batch_annotate_files_gcs] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/batch-annotate-files.js b/vision/batch-annotate-files.js new file mode 100644 index 0000000000..347be88d09 --- /dev/null +++ b/vision/batch-annotate-files.js @@ -0,0 +1,105 @@ +// 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(fileName = 'path/to/your/file.pdf') { + // [START vision_batch_annotate_files] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const fileName = 'path/to/your/file.pdf'; + + // Imports the Google Cloud client libraries + const {ImageAnnotatorClient} = require('@google-cloud/vision').v1; + const fs = require('fs').promises; + + // Instantiates a client + const client = new ImageAnnotatorClient(); + + // You can send multiple files to be annotated, this sample demonstrates how to do this with + // one file. If you want to use multiple files, you have to create a request object for each file that you want annotated. + async function batchAnnotateFiles() { + // First Specify the input config with the file's path and its type. + // Supported mime_type: application/pdf, image/tiff, image/gif + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#inputconfig + const inputConfig = { + mimeType: 'application/pdf', + content: await fs.readFile(fileName), + }; + + // Set the type of annotation you want to perform on the file + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.Feature.Type + const features = [{type: 'DOCUMENT_TEXT_DETECTION'}]; + + // Build the request object for that one file. Note: for additional files you have to create + // additional file request objects and store them in a list to be used below. + // Since we are sending a file of type `application/pdf`, we can use the `pages` field to + // specify which pages to process. The service can process up to 5 pages per document file. + // https://cloud.google.com/vision/docs/reference/rpc/google.cloud.vision.v1#google.cloud.vision.v1.AnnotateFileRequest + const fileRequest = { + inputConfig: inputConfig, + features: features, + // Annotate the first two pages and the last one (max 5 pages) + // First page starts at 1, and not 0. Last page is -1. + pages: [1, 2, -1], + }; + + // Add each `AnnotateFileRequest` object to the batch request. + const request = { + requests: [fileRequest], + }; + + // Make the synchronous batch request. + const [result] = await client.batchAnnotateFiles(request); + + // Process the results, just get the first result, since only one file was sent in this + // sample. + const responses = result.responses[0].responses; + + for (const response of responses) { + console.log(`Full text: ${response.fullTextAnnotation.text}`); + for (const page of response.fullTextAnnotation.pages) { + for (const block of page.blocks) { + console.log(`Block confidence: ${block.confidence}`); + for (const paragraph of block.paragraphs) { + console.log(` Paragraph confidence: ${paragraph.confidence}`); + for (const word of paragraph.words) { + const symbol_texts = word.symbols.map(symbol => symbol.text); + const word_text = symbol_texts.join(''); + console.log( + ` Word text: ${word_text} (confidence: ${word.confidence})` + ); + for (const symbol of word.symbols) { + console.log( + ` Symbol: ${symbol.text} (confidence: ${symbol.confidence})` + ); + } + } + } + } + } + } + } + + batchAnnotateFiles(); + // [END vision_batch_annotate_files] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/detect.js b/vision/detect.js new file mode 100644 index 0000000000..c728c8569f --- /dev/null +++ b/vision/detect.js @@ -0,0 +1,920 @@ +// Copyright 2017 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'; + +async function detectFaces(fileName) { + // [START vision_face_detection] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + const [result] = await client.faceDetection(fileName); + const faces = result.faceAnnotations; + console.log('Faces:'); + faces.forEach((face, i) => { + console.log(` Face #${i + 1}:`); + console.log(` Joy: ${face.joyLikelihood}`); + console.log(` Anger: ${face.angerLikelihood}`); + console.log(` Sorrow: ${face.sorrowLikelihood}`); + console.log(` Surprise: ${face.surpriseLikelihood}`); + }); + // [END vision_face_detection] +} + +async function detectFacesGCS(bucketName, fileName) { + // [START vision_face_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs face detection on the gcs file + const [result] = await client.faceDetection(`gs://${bucketName}/${fileName}`); + const faces = result.faceAnnotations; + console.log('Faces:'); + faces.forEach((face, i) => { + console.log(` Face #${i + 1}:`); + console.log(` Joy: ${face.joyLikelihood}`); + console.log(` Anger: ${face.angerLikelihood}`); + console.log(` Sorrow: ${face.sorrowLikelihood}`); + console.log(` Surprise: ${face.surpriseLikelihood}`); + }); + // [END vision_face_detection_gcs] +} + +async function detectLabels(fileName) { + // [START vision_label_detection] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs label detection on the local file + const [result] = await client.labelDetection(fileName); + const labels = result.labelAnnotations; + console.log('Labels:'); + labels.forEach(label => console.log(label.description)); + // [END vision_label_detection] +} + +async function detectLabelsGCS(bucketName, fileName) { + // [START vision_label_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs label detection on the gcs file + const [result] = await client.labelDetection( + `gs://${bucketName}/${fileName}` + ); + const labels = result.labelAnnotations; + console.log('Labels:'); + labels.forEach(label => console.log(label.description)); + // [END vision_label_detection_gcs] +} + +async function detectLandmarks(fileName) { + // [START vision_landmark_detection] + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs landmark detection on the local file + const [result] = await client.landmarkDetection(fileName); + const landmarks = result.landmarkAnnotations; + console.log('Landmarks:'); + landmarks.forEach(landmark => console.log(landmark)); + // [END vision_landmark_detection] +} + +async function detectLandmarksGCS(bucketName, fileName) { + // [START vision_landmark_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs landmark detection on the gcs file + const [result] = await client.landmarkDetection( + `gs://${bucketName}/${fileName}` + ); + const landmarks = result.landmarkAnnotations; + console.log('Landmarks:'); + landmarks.forEach(landmark => console.log(landmark)); + // [END vision_landmark_detection_gcs] +} + +async function detectText(fileName) { + // [START vision_text_detection] + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs text detection on the local file + const [result] = await client.textDetection(fileName); + const detections = result.textAnnotations; + console.log('Text:'); + detections.forEach(text => console.log(text)); + // [END vision_text_detection] +} + +async function detectTextGCS(bucketName, fileName) { + // [START vision_text_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs text detection on the gcs file + const [result] = await client.textDetection(`gs://${bucketName}/${fileName}`); + const detections = result.textAnnotations; + console.log('Text:'); + detections.forEach(text => console.log(text)); + // [END vision_text_detection_gcs] +} + +async function detectLogos(fileName) { + // [START vision_logo_detection] + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs logo detection on the local file + const [result] = await client.logoDetection(fileName); + const logos = result.logoAnnotations; + console.log('Logos:'); + logos.forEach(logo => console.log(logo)); + // [END vision_logo_detection] +} + +async function detectLogosGCS(bucketName, fileName) { + // [START vision_logo_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs logo detection on the gcs file + const [result] = await client.logoDetection(`gs://${bucketName}/${fileName}`); + const logos = result.logoAnnotations; + console.log('Logos:'); + logos.forEach(logo => console.log(logo)); + // [END vision_logo_detection_gcs] +} + +async function detectProperties(fileName) { + // [START vision_image_property_detection] + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs property detection on the local file + const [result] = await client.imageProperties(fileName); + const colors = result.imagePropertiesAnnotation.dominantColors.colors; + colors.forEach(color => console.log(color)); + // [END vision_image_property_detection] +} + +async function detectPropertiesGCS(bucketName, fileName) { + // [START vision_image_property_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs property detection on the gcs file + const [result] = await client.imageProperties( + `gs://${bucketName}/${fileName}` + ); + const colors = result.imagePropertiesAnnotation.dominantColors.colors; + colors.forEach(color => console.log(color)); + // [END vision_image_property_detection_gcs] +} + +async function detectSafeSearch(fileName) { + // [START vision_safe_search_detection] + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs safe search detection on the local file + const [result] = await client.safeSearchDetection(fileName); + const detections = result.safeSearchAnnotation; + console.log('Safe search:'); + console.log(`Adult: ${detections.adult}`); + console.log(`Medical: ${detections.medical}`); + console.log(`Spoof: ${detections.spoof}`); + console.log(`Violence: ${detections.violence}`); + console.log(`Racy: ${detections.racy}`); + // [END vision_safe_search_detection] +} + +async function detectSafeSearchGCS(bucketName, fileName) { + // [START vision_safe_search_detection_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Performs safe search property detection on the remote file + const [result] = await client.safeSearchDetection( + `gs://${bucketName}/${fileName}` + ); + const detections = result.safeSearchAnnotation; + console.log(`Adult: ${detections.adult}`); + console.log(`Spoof: ${detections.spoof}`); + console.log(`Medical: ${detections.medical}`); + console.log(`Violence: ${detections.violence}`); + // [END vision_safe_search_detection_gcs] +} + +async function detectCropHints(fileName) { + // [START vision_crop_hint_detection] + + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Find crop hints for the local file + const [result] = await client.cropHints(fileName); + const cropHints = result.cropHintsAnnotation; + cropHints.cropHints.forEach((hintBounds, hintIdx) => { + console.log(`Crop Hint ${hintIdx}:`); + hintBounds.boundingPoly.vertices.forEach((bound, boundIdx) => { + console.log(` Bound ${boundIdx}: (${bound.x}, ${bound.y})`); + }); + }); + // [END vision_crop_hint_detection] +} + +async function detectCropHintsGCS(bucketName, fileName) { + // [START vision_crop_hint_detection_gcs] + + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Find crop hints for the remote file + const [result] = await client.cropHints(`gs://${bucketName}/${fileName}`); + const cropHints = result.cropHintsAnnotation; + cropHints.cropHints.forEach((hintBounds, hintIdx) => { + console.log(`Crop Hint ${hintIdx}:`); + hintBounds.boundingPoly.vertices.forEach((bound, boundIdx) => { + console.log(` Bound ${boundIdx}: (${bound.x}, ${bound.y})`); + }); + }); + // [END vision_crop_hint_detection_gcs] +} + +async function detectWeb(fileName) { + // [START vision_web_detection] + + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Detect similar images on the web to a local file + const [result] = await client.webDetection(fileName); + const webDetection = result.webDetection; + if (webDetection.fullMatchingImages.length) { + console.log( + `Full matches found: ${webDetection.fullMatchingImages.length}` + ); + webDetection.fullMatchingImages.forEach(image => { + console.log(` URL: ${image.url}`); + console.log(` Score: ${image.score}`); + }); + } + + if (webDetection.partialMatchingImages.length) { + console.log( + `Partial matches found: ${webDetection.partialMatchingImages.length}` + ); + webDetection.partialMatchingImages.forEach(image => { + console.log(` URL: ${image.url}`); + console.log(` Score: ${image.score}`); + }); + } + + if (webDetection.webEntities.length) { + console.log(`Web entities found: ${webDetection.webEntities.length}`); + webDetection.webEntities.forEach(webEntity => { + console.log(` Description: ${webEntity.description}`); + console.log(` Score: ${webEntity.score}`); + }); + } + + if (webDetection.bestGuessLabels.length) { + console.log( + `Best guess labels found: ${webDetection.bestGuessLabels.length}` + ); + webDetection.bestGuessLabels.forEach(label => { + console.log(` Label: ${label.label}`); + }); + } + // [END vision_web_detection] +} + +async function detectWebGCS(bucketName, fileName) { + // [START vision_web_detection_gcs] + + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Detect similar images on the web to a remote file + const [result] = await client.webDetection(`gs://${bucketName}/${fileName}`); + const webDetection = result.webDetection; + if (webDetection.fullMatchingImages.length) { + console.log( + `Full matches found: ${webDetection.fullMatchingImages.length}` + ); + webDetection.fullMatchingImages.forEach(image => { + console.log(` URL: ${image.url}`); + console.log(` Score: ${image.score}`); + }); + } + + if (webDetection.partialMatchingImages.length) { + console.log( + `Partial matches found: ${webDetection.partialMatchingImages.length}` + ); + webDetection.partialMatchingImages.forEach(image => { + console.log(` URL: ${image.url}`); + console.log(` Score: ${image.score}`); + }); + } + + if (webDetection.webEntities.length) { + console.log(`Web entities found: ${webDetection.webEntities.length}`); + webDetection.webEntities.forEach(webEntity => { + console.log(` Description: ${webEntity.description}`); + console.log(` Score: ${webEntity.score}`); + }); + } + + if (webDetection.bestGuessLabels.length) { + console.log( + `Best guess labels found: ${webDetection.bestGuessLabels.length}` + ); + webDetection.bestGuessLabels.forEach(label => { + console.log(` Label: ${label.label}`); + }); + } + // [END vision_web_detection_gcs] +} + +async function detectWebGeo(fileName) { + // [START vision_web_detection_include_geo] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + const request = { + image: { + source: { + filename: fileName, + }, + }, + imageContext: { + webDetectionParams: { + includeGeoResults: true, + }, + }, + }; + + // Detect similar images on the web to a local file + const [result] = await client.webDetection(request); + const webDetection = result.webDetection; + webDetection.webEntities.forEach(entity => { + console.log(`Score: ${entity.score}`); + console.log(`Description: ${entity.description}`); + }); + // [END vision_web_detection_include_geo] +} + +async function detectWebGeoGCS(bucketName, fileName) { + // [START vision_web_detection_include_geo_gcs] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + const request = { + image: { + source: { + imageUri: `gs://${bucketName}/${fileName}`, + }, + }, + imageContext: { + webDetectionParams: { + includeGeoResults: true, + }, + }, + }; + + // Detect similar images on the web to a remote file + const [result] = await client.webDetection(request); + const webDetection = result.webDetection; + webDetection.webEntities.forEach(entity => { + console.log(`Score: ${entity.score}`); + console.log(`Description: ${entity.description}`); + }); + // [END vision_web_detection_include_geo_gcs] +} + +async function detectFulltext(fileName) { + // [START vision_fulltext_detection] + + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Read a local image as a text document + const [result] = await client.documentTextDetection(fileName); + const fullTextAnnotation = result.fullTextAnnotation; + console.log(`Full text: ${fullTextAnnotation.text}`); + fullTextAnnotation.pages.forEach(page => { + page.blocks.forEach(block => { + console.log(`Block confidence: ${block.confidence}`); + block.paragraphs.forEach(paragraph => { + console.log(`Paragraph confidence: ${paragraph.confidence}`); + paragraph.words.forEach(word => { + const wordText = word.symbols.map(s => s.text).join(''); + console.log(`Word text: ${wordText}`); + console.log(`Word confidence: ${word.confidence}`); + word.symbols.forEach(symbol => { + console.log(`Symbol text: ${symbol.text}`); + console.log(`Symbol confidence: ${symbol.confidence}`); + }); + }); + }); + }); + }); + // [END vision_fulltext_detection] +} + +async function detectFulltextGCS(bucketName, fileName) { + // [START vision_fulltext_detection_gcs] + + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'Bucket where the file resides, e.g. my-bucket'; + // const fileName = 'Path to file within bucket, e.g. path/to/image.png'; + + // Read a remote image as a text document + const [result] = await client.documentTextDetection( + `gs://${bucketName}/${fileName}` + ); + const fullTextAnnotation = result.fullTextAnnotation; + console.log(fullTextAnnotation.text); + // [END vision_fulltext_detection_gcs] +} + +async function detectPdfText(bucketName, fileName, outputPrefix) { + // [START vision_text_detection_pdf_gcs] + + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision').v1; + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // Bucket where the file resides + // const bucketName = 'my-bucket'; + // Path to PDF file within bucket + // const fileName = 'path/to/document.pdf'; + // The folder to store the results + // const outputPrefix = 'results' + + const gcsSourceUri = `gs://${bucketName}/${fileName}`; + const gcsDestinationUri = `gs://${bucketName}/${outputPrefix}/`; + + const inputConfig = { + // Supported mime_types are: 'application/pdf' and 'image/tiff' + mimeType: 'application/pdf', + gcsSource: { + uri: gcsSourceUri, + }, + }; + const outputConfig = { + gcsDestination: { + uri: gcsDestinationUri, + }, + }; + const features = [{type: 'DOCUMENT_TEXT_DETECTION'}]; + const request = { + requests: [ + { + inputConfig: inputConfig, + features: features, + outputConfig: outputConfig, + }, + ], + }; + + const [operation] = await client.asyncBatchAnnotateFiles(request); + const [filesResponse] = await operation.promise(); + const destinationUri = + filesResponse.responses[0].outputConfig.gcsDestination.uri; + console.log('Json saved to: ' + destinationUri); + // [END vision_text_detection_pdf_gcs] +} + +async function localizeObjects(fileName) { + // [START vision_localize_objects] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + const fs = require('fs'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = `/path/to/localImage.png`; + const request = { + image: {content: fs.readFileSync(fileName)}, + }; + + const [result] = await client.objectLocalization(request); + const objects = result.localizedObjectAnnotations; + objects.forEach(object => { + console.log(`Name: ${object.name}`); + console.log(`Confidence: ${object.score}`); + const vertices = object.boundingPoly.normalizedVertices; + vertices.forEach(v => console.log(`x: ${v.x}, y:${v.y}`)); + }); + // [END vision_localize_objects] +} + +async function localizeObjectsGCS(gcsUri) { + // [START vision_localize_objects_gcs] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const gcsUri = `gs://bucket/bucketImage.png`; + + const [result] = await client.objectLocalization(gcsUri); + const objects = result.localizedObjectAnnotations; + objects.forEach(object => { + console.log(`Name: ${object.name}`); + console.log(`Confidence: ${object.score}`); + const veritices = object.boundingPoly.normalizedVertices; + veritices.forEach(v => console.log(`x: ${v.x}, y:${v.y}`)); + }); + // [END vision_localize_objects_gcs] +} + +require(`yargs`) // eslint-disable-line + .demand(1) + .command( + 'faces ', + 'Detects faces in a local image file.', + {}, + opts => detectFaces(opts.fileName) + ) + .command( + 'faces-gcs ', + 'Detects faces in an image in Google Cloud Storage.', + {}, + opts => detectFacesGCS(opts.bucketName, opts.fileName) + ) + .command( + 'labels ', + 'Detects labels in a local image file.', + {}, + opts => detectLabels(opts.fileName) + ) + .command( + 'labels-gcs ', + 'Detects labels in an image in Google Cloud Storage.', + {}, + opts => detectLabelsGCS(opts.bucketName, opts.fileName) + ) + .command( + 'landmarks ', + 'Detects landmarks in a local image file.', + {}, + opts => detectLandmarks(opts.fileName) + ) + .command( + 'landmarks-gcs ', + 'Detects landmarks in an image in Google Cloud Storage.', + {}, + opts => detectLandmarksGCS(opts.bucketName, opts.fileName) + ) + .command('text ', 'Detects text in a local image file.', {}, opts => + detectText(opts.fileName) + ) + .command( + 'text-gcs ', + 'Detects text in an image in Google Cloud Storage.', + {}, + opts => detectTextGCS(opts.bucketName, opts.fileName) + ) + .command( + 'logos ', + 'Detects logos in a local image file.', + {}, + opts => detectLogos(opts.fileName) + ) + .command( + 'logos-gcs ', + 'Detects logos in an image in Google Cloud Storage.', + {}, + opts => detectLogosGCS(opts.bucketName, opts.fileName) + ) + .command( + 'properties ', + 'Detects image properties in a local image file.', + {}, + opts => detectProperties(opts.fileName) + ) + .command( + 'properties-gcs ', + 'Detects image properties in an image in Google Cloud Storage.', + {}, + opts => detectPropertiesGCS(opts.bucketName, opts.fileName) + ) + .command( + 'safe-search ', + 'Detects safe search properties in a local image file.', + {}, + opts => detectSafeSearch(opts.fileName) + ) + .command( + 'safe-search-gcs ', + 'Detects safe search properties in an image in Google Cloud Storage.', + {}, + opts => detectSafeSearchGCS(opts.bucketName, opts.fileName) + ) + .command( + 'crops ', + 'Detects crop hints in a local image file.', + {}, + opts => detectCropHints(opts.fileName) + ) + .command( + 'crops-gcs ', + 'Detects crop hints in an image in Google Cloud Storage.', + {}, + opts => detectCropHintsGCS(opts.bucketName, opts.fileName) + ) + .command( + 'web ', + 'Finds similar photos on the web for a local image file.', + {}, + opts => detectWeb(opts.fileName) + ) + .command( + 'web-gcs ', + 'Finds similar photos on the web for an image in Google Cloud Storage.', + {}, + opts => detectWebGCS(opts.bucketName, opts.fileName) + ) + .command( + 'web-geo ', + 'Detects web entities with improved results using geographic metadata', + {}, + opts => detectWebGeo(opts.fileName) + ) + .command( + 'web-geo-gcs ', + 'Detects web entities with improved results using geographic metadata', + {}, + opts => detectWebGeoGCS(opts.bucketName, opts.fileName) + ) + .command( + 'fulltext ', + 'Extracts full text from a local image file.', + {}, + opts => detectFulltext(opts.fileName) + ) + .command( + 'fulltext-gcs ', + 'Extracts full text from an image in Google Cloud Storage.', + {}, + opts => detectFulltextGCS(opts.bucketName, opts.fileName) + ) + .command( + 'pdf ', + 'Extracts full text from a pdf file', + {}, + opts => detectPdfText(opts.bucketName, opts.fileName, opts.outputPrefix) + ) + .command( + 'localize-objects ', + 'Detects Objects in a local image file', + {}, + opts => localizeObjects(opts.fileName) + ) + .command( + 'localize-objects-gcs ', + 'Detects Objects Google Cloud Storage Bucket', + {}, + opts => localizeObjectsGCS(opts.gcsUri) + ) + .example('node $0 faces ./resources/face_no_surprise.jpg') + .example('node $0 faces-gcs my-bucket your-image.jpg') + .example('node $0 labels ./resources/wakeupcat.jpg') + .example('node $0 labels-gcs my-bucket your-image.jpg') + .example('node $0 landmarks ./resources/landmark.jpg') + .example('node $0 landmarks-gcs my-bucket your-image.jpg') + .example('node $0 text ./resources/wakeupcat.jpg') + .example('node $0 text-gcs my-bucket your-image.jpg') + .example('node $0 logos ./resources/logos.png') + .example('node $0 logos-gcs my-bucket your-image.jpg.png') + .example('node $0 properties ./resources/landmark.jpg') + .example('node $0 properties-gcs my-bucket your-image.jpg') + .example('node $0 safe-search ./resources/wakeupcat.jpg') + .example('node $0 safe-search-gcs my-bucket your-image.jpg') + .example('node $0 crops ./resources/wakeupcat.jpg') + .example('node $0 crops-gcs my-bucket your-image.jpg') + .example('node $0 web ./resources/wakeupcat.jpg') + .example('node $0 web-gcs my-bucket your-image.jpg') + .example('node $0 web-geo ./resources/city.jpg') + .example('node $0 web-geo-gcs my-bucket your-image.jpg') + .example('node $0 fulltext ./resources/wakeupcat.jpg') + .example('node $0 fulltext-gcs my-bucket your-image.jpg') + .example('node $0 pdf my-bucket my-pdf.pdf results') + .example('node $0 localize-objects ./resources/duck_and_truck.jpg') + .example('node $0 localize-objects-gcs gs://bucket/bucketImage.png') + .wrap(120) + .recommendCommands() + .epilogue('For more information, see https://cloud.google.com/vision/docs') + .help() + .strict().argv; diff --git a/vision/detect.v1p1beta1.js b/vision/detect.v1p1beta1.js new file mode 100644 index 0000000000..4d223ac7e6 --- /dev/null +++ b/vision/detect.v1p1beta1.js @@ -0,0 +1,80 @@ +// Copyright 2017 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'; + +async function detectFulltext(fileName) { + // [START vision_detect_document] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision').v1p1beta1; + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + // Performs label detection on the local file + const [result] = await client.textDetection(fileName); + const pages = result.fullTextAnnotation.pages; + pages.forEach(page => { + page.blocks.forEach(block => { + const blockWords = []; + block.paragraphs.forEach(paragraph => { + paragraph.words.forEach(word => blockWords.push(word)); + console.log(`Paragraph confidence: ${paragraph.confidence}`); + }); + + let blockText = ''; + const blockSymbols = []; + blockWords.forEach(word => { + word.symbols.forEach(symbol => blockSymbols.push(symbol)); + let wordText = ''; + word.symbols.forEach(symbol => { + wordText = wordText + symbol.text; + console.log(`Symbol text: ${symbol.text}`); + console.log(`Symbol confidence: ${symbol.confidence}`); + }); + console.log(`Word text: ${wordText}`); + console.log(`Word confidence: ${word.confidence}`); + blockText = blockText + ` ${wordText}`; + }); + + console.log(`Block text: ${blockText}`); + console.log(`Block confidence: ${block.confidence}`); + }); + }); + // [END vision_detect_document] +} + +//.usage('$0 ', 'Cloud Vision Beta API Samples') +require(`yargs`) // eslint-disable-line + .demand(1) + .command( + 'fulltext ', + 'Extracts full text from an image file including new confidence scores', + {}, + opts => detectFulltext(opts.fileName) + ) + .example('node $0 safe-search ./resources/wakeupcat.jpg') + .example('node $0 web-entities-geo ./resources/city.jpg') + .example('node $0 web ./resources/wakeupcat.jpg') + .example('node $0 fulltext ./resources/wakeupcat.jpg') + .wrap(120) + .recommendCommands() + .epilogue('For more information, see https://cloud.google.com/vision/docs') + .help() + .strict().argv; diff --git a/vision/detect.v1p3beta1.js b/vision/detect.v1p3beta1.js new file mode 100644 index 0000000000..bed9f9add3 --- /dev/null +++ b/vision/detect.v1p3beta1.js @@ -0,0 +1,111 @@ +// Copyright 2018 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'; + +async function detectHandwritingOCR(fileName) { + // [START vision_handwritten_ocr_beta] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision').v1p3beta1; + const fs = require('fs'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = `/path/to/localImage.png`; + + const request = { + image: { + content: fs.readFileSync(fileName), + }, + feature: { + languageHints: ['en-t-i0-handwrit'], + }, + }; + + const [result] = await client.documentTextDetection(request); + const fullTextAnnotation = result.fullTextAnnotation; + console.log(`Full text: ${fullTextAnnotation.text}`); + // [END vision_handwritten_ocr_beta] +} + +async function detectHandwritingGCS(uri) { + // [START vision_handwritten_ocr_gcs_beta] + // Imports the Google Cloud client libraries + const vision = require('@google-cloud/vision').v1p3beta1; + const fs = require('fs'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const uri = `gs://bucket/bucketImage.png`; + + const request = { + image: { + content: fs.readFileSync(uri), + }, + feature: { + languageHints: ['en-t-i0-handwrit'], + }, + }; + + const [result] = await client.documentTextDetection(request); + const fullTextAnnotation = result.fullTextAnnotation; + console.log(`Full text: ${fullTextAnnotation.text}`); + // [END vision_handwritten_ocr_gcs_beta] +} + +require('yargs') + .demand(1) + .command( + 'detectHandwriting', + 'Detects handwriting in a local image file.', + {}, + opts => detectHandwritingOCR(opts.handwritingSample) + ) + .command( + 'detectHandwritingGCS', + 'Detects handwriting from Google Cloud Storage Bucket.', + {}, + opts => detectHandwritingGCS(opts.handwritingSample) + ) + .options({ + handwritingSample: { + alias: 'h', + default: './resources/handwritten.jpg', + global: true, + requiresArg: true, + type: 'string', + }, + handwritingGcsUri: { + alias: 'u', + default: 'gs://cloud-samples-data/vision/handwritten.jpg', + global: true, + requiresArg: true, + type: 'string', + }, + }) + .example('node $0 detectHandwriting ./resources/handwritten.jpg') + .example('node $0 detectHandwritingGCS gs://my-bucket/image.jpg') + .wrap(120) + .recommendCommands() + .epilogue('For more information, see https://cloud.google.com/vision/docs') + .help() + .strict().argv; diff --git a/vision/faceDetection.js b/vision/faceDetection.js new file mode 100644 index 0000000000..5b45fb1247 --- /dev/null +++ b/vision/faceDetection.js @@ -0,0 +1,106 @@ +// Copyright 2016 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'; + +// [START vision_face_detection_tutorial_imports] +// By default, the client will authenticate using the service account file +// specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use +// the project specified by the GCLOUD_PROJECT environment variable. See +// https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication +const vision = require('@google-cloud/vision'); +// [END vision_face_detection_tutorial_imports] +// [START vision_face_detection_tutorial_client] +// Creates a client +const client = new vision.ImageAnnotatorClient(); + +const fs = require('fs'); +// [END vision_face_detection_tutorial_client] + +/** + * Uses the Vision API to detect faces in the given file. + */ +// [START vision_face_detection_tutorial_send_request] +async function detectFaces(inputFile) { + // Make a call to the Vision API to detect the faces + const request = {image: {source: {filename: inputFile}}}; + const results = await client.faceDetection(request); + const faces = results[0].faceAnnotations; + const numFaces = faces.length; + console.log(`Found ${numFaces} face${numFaces === 1 ? '' : 's'}.`); + return faces; +} +// [END vision_face_detection_tutorial_send_request] + +/** + * Draws a polygon around the faces, then saves to outputFile. + */ +// [START vision_face_detection_tutorial_process_response] +async function highlightFaces(inputFile, faces, outputFile, PImage) { + // Open the original image + const stream = fs.createReadStream(inputFile); + let promise; + if (inputFile.match(/\.jpg$/)) { + promise = PImage.decodeJPEGFromStream(stream); + } else if (inputFile.match(/\.png$/)) { + promise = PImage.decodePNGFromStream(stream); + } else { + throw new Error(`Unknown filename extension ${inputFile}`); + } + const img = await promise; + const context = img.getContext('2d'); + context.drawImage(img, 0, 0, img.width, img.height, 0, 0); + + // Now draw boxes around all the faces + context.strokeStyle = 'rgba(0,255,0,0.8)'; + context.lineWidth = '5'; + + faces.forEach(face => { + context.beginPath(); + let origX = 0; + let origY = 0; + face.boundingPoly.vertices.forEach((bounds, i) => { + if (i === 0) { + origX = bounds.x; + origY = bounds.y; + context.moveTo(bounds.x, bounds.y); + } else { + context.lineTo(bounds.x, bounds.y); + } + }); + context.lineTo(origX, origY); + context.stroke(); + }); + + // Write the result to a file + console.log(`Writing to file ${outputFile}`); + const writeStream = fs.createWriteStream(outputFile); + await PImage.encodePNGToStream(img, writeStream); +} +// [END vision_face_detection_tutorial_process_response] + +// Run the example +// [START vision_face_detection_tutorial_run_application] +async function main(inputFile, outputFile) { + const PImage = require('pureimage'); + outputFile = outputFile || 'out.png'; + const faces = await detectFaces(inputFile); + console.log('Highlighting...'); + await highlightFaces(inputFile, faces, outputFile, PImage); + console.log('Finished!'); +} +// [END vision_face_detection_tutorial_run_application] + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/vision/package.json b/vision/package.json new file mode 100644 index 0000000000..28afa6da3a --- /dev/null +++ b/vision/package.json @@ -0,0 +1,28 @@ +{ + "name": "nodejs-docs-samples-vision", + "private": true, + "license": "Apache-2.0", + "author": "Google LLC", + "engines": { + "node": ">=12.0.0" + }, + "files": [ + "*.js" + ], + "scripts": { + "test": "mocha system-test --timeout 600000" + }, + "dependencies": { + "@google-cloud/vision": "^3.0.1", + "natural": "^5.0.0", + "pureimage": "^0.3.0", + "redis": "~4.3.0", + "yargs": "^16.0.0" + }, + "devDependencies": { + "@google-cloud/storage": "^6.0.0", + "chai": "^4.2.0", + "mocha": "^10.0.0", + "uuid": "^9.0.0" + } +} diff --git a/vision/productSearch/addProductToProductSet.js b/vision/productSearch/addProductToProductSet.js new file mode 100644 index 0000000000..11fc156267 --- /dev/null +++ b/vision/productSearch/addProductToProductSet.js @@ -0,0 +1,55 @@ +// Copyright 2020 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, location, productId, productSetId) { + // [START vision_product_search_add_product_to_product_set] + const vision = require('@google-cloud/vision'); + const client = new vision.ProductSearchClient(); + + async function addProductToProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const productSetId = 'Id of the product set'; + + const productPath = client.productPath(projectId, location, productId); + const productSetPath = client.productSetPath( + projectId, + location, + productSetId + ); + + const request = { + name: productSetPath, + product: productPath, + }; + + await client.addProductToProductSet(request); + console.log('Product added to product set.'); + } + addProductToProductSet(); + // [END vision_product_search_add_product_to_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/createProduct.js b/vision/productSearch/createProduct.js new file mode 100644 index 0000000000..0bd6df925b --- /dev/null +++ b/vision/productSearch/createProduct.js @@ -0,0 +1,65 @@ +// Copyright 2020 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, + location, + productId, + productDisplayName, + productCategory +) { + // [START vision_product_search_create_product] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + async function createProduct() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const productDisplayName = 'Display name of the product'; + // const productCategory = 'Catoegory of the product'; + + // Resource path that represents Google Cloud Platform location. + const locationPath = client.locationPath(projectId, location); + + const product = { + displayName: productDisplayName, + productCategory: productCategory, + }; + + const request = { + parent: locationPath, + product: product, + productId: productId, + }; + + const [createdProduct] = await client.createProduct(request); + console.log(`Product name: ${createdProduct.name}`); + } + createProduct(); + // [END vision_product_search_create_product] +} +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/createProductSet.js b/vision/productSearch/createProductSet.js new file mode 100644 index 0000000000..b330962de5 --- /dev/null +++ b/vision/productSearch/createProductSet.js @@ -0,0 +1,59 @@ +// Copyright 2020 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, location, productSetId, productSetDisplayName) { + // [START vision_product_search_create_product_set] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function createProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + // const productSetDisplayName = 'Display name of the product set'; + + // Resource path that represents Google Cloud Platform location. + const locationPath = client.locationPath(projectId, location); + + const productSet = { + displayName: productSetDisplayName, + }; + + const request = { + parent: locationPath, + productSet: productSet, + productSetId: productSetId, + }; + + const [createdProductSet] = await client.createProductSet(request); + console.log(`Product Set name: ${createdProductSet.name}`); + } + createProductSet(); + // [END vision_product_search_create_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/createReferenceImage.js b/vision/productSearch/createReferenceImage.js new file mode 100644 index 0000000000..1404c431bd --- /dev/null +++ b/vision/productSearch/createReferenceImage.js @@ -0,0 +1,58 @@ +// Copyright 2020 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, location, productId, referenceImageId, gcsUri) { + // [START vision_product_search_create_reference_image] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function createReferenceImage() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const referenceImageId = 'Id of the reference image'; + // const gcsUri = 'Google Cloud Storage path of the input image'; + + const formattedParent = client.productPath(projectId, location, productId); + + const referenceImage = { + uri: gcsUri, + }; + + const request = { + parent: formattedParent, + referenceImage: referenceImage, + referenceImageId: referenceImageId, + }; + + const [response] = await client.createReferenceImage(request); + console.log(`response.name: ${response.name}`); + console.log(`response.uri: ${response.uri}`); + } + createReferenceImage(); + // [END vision_product_search_create_reference_image] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/deleteProduct.js b/vision/productSearch/deleteProduct.js new file mode 100644 index 0000000000..48924b8dc9 --- /dev/null +++ b/vision/productSearch/deleteProduct.js @@ -0,0 +1,48 @@ +// Copyright 2020 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, location, productId) { + // [START vision_product_search_delete_product] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function deleteProduct() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + + // Resource path that represents full path to the product. + const productPath = client.productPath(projectId, location, productId); + + await client.deleteProduct({name: productPath}); + console.log('Product deleted.'); + } + deleteProduct(); + // [END vision_product_search_delete_product] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/deleteProductSet.js b/vision/productSearch/deleteProductSet.js new file mode 100644 index 0000000000..d039b506e3 --- /dev/null +++ b/vision/productSearch/deleteProductSet.js @@ -0,0 +1,52 @@ +// Copyright 2020 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, location, productSetId) { + // [START vision_product_search_delete_product_set] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function deleteProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + + // Resource path that represents full path to the product set. + const productSetPath = client.productSetPath( + projectId, + location, + productSetId + ); + + await client.deleteProductSet({name: productSetPath}); + console.log('Product set deleted.'); + } + deleteProductSet(); + // [END vision_product_search_delete_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/deleteReferenceImage.js b/vision/productSearch/deleteReferenceImage.js new file mode 100644 index 0000000000..d5dc312e9c --- /dev/null +++ b/vision/productSearch/deleteReferenceImage.js @@ -0,0 +1,55 @@ +// Copyright 2020 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, location, productId, referenceImageId) { + // [START vision_product_search_delete_reference_image] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function deleteReferenceImage() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const referenceImageId = 'Id of the reference image'; + + const formattedName = client.referenceImagePath( + projectId, + location, + productId, + referenceImageId + ); + + const request = { + name: formattedName, + }; + + await client.deleteReferenceImage(request); + console.log('Reference image deleted from product.'); + } + deleteReferenceImage(); + // [END vision_product_search_delete_reference_image] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/getProduct.js b/vision/productSearch/getProduct.js new file mode 100644 index 0000000000..f2779515aa --- /dev/null +++ b/vision/productSearch/getProduct.js @@ -0,0 +1,53 @@ +// Copyright 2020 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, location, productId) { + // [START vision_product_search_get_product] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function getProduct() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + + // Resource path that represents Google Cloud Platform location. + const productPath = client.productPath(projectId, location, productId); + + const [product] = await client.getProduct({name: productPath}); + console.log(`Product name: ${product.name}`); + console.log(`Product id: ${product.name.split('/').pop()}`); + console.log(`Product display name: ${product.displayName}`); + console.log(`Product description: ${product.description}`); + console.log(`Product category: ${product.productCategory}`); + console.log(`Product labels: ${product.productLabels}`); + } + getProduct(); + // [END vision_product_search_get_product] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/getProductSet.js b/vision/productSearch/getProductSet.js new file mode 100644 index 0000000000..d3edfa2a46 --- /dev/null +++ b/vision/productSearch/getProductSet.js @@ -0,0 +1,53 @@ +// Copyright 2020 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, location, productSetId) { + // [START vision_product_search_get_product_set] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function getProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + + // Resource path that represents Google Cloud Platform location. + const productSetPath = client.productSetPath( + projectId, + location, + productSetId + ); + + const [productSet] = await client.getProductSet({name: productSetPath}); + console.log(`Product Set name: ${productSet.name}`); + console.log(`Product Set display name: ${productSet.displayName}`); + } + getProductSet(); + // [END vision_product_search_get_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/getReferenceImage.js b/vision/productSearch/getReferenceImage.js new file mode 100644 index 0000000000..db5a123a73 --- /dev/null +++ b/vision/productSearch/getReferenceImage.js @@ -0,0 +1,56 @@ +// Copyright 2020 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, location, productId, referenceImageId) { + // [START vision_product_search_get_reference_image] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function getReferenceImage() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const referenceImageId = 'Id of the reference image'; + + const formattedName = client.referenceImagePath( + projectId, + location, + productId, + referenceImageId + ); + + const request = { + name: formattedName, + }; + + const response = await client.getReferenceImage(request); + console.log(`response.name: ${response.name}`); + console.log(`response.uri: ${response.uri}`); + } + getReferenceImage(); + // [END vision_product_search_get_reference_image] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/getSimilarProducts.js b/vision/productSearch/getSimilarProducts.js new file mode 100644 index 0000000000..d1e95eb83e --- /dev/null +++ b/vision/productSearch/getSimilarProducts.js @@ -0,0 +1,88 @@ +// Copyright 2020 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, + location, + productSetId, + productCategory, + filePath, + filter +) { + // [START vision_product_search_get_similar_products] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + const fs = require('fs'); + // Creates a client + const productSearchClient = new vision.ProductSearchClient(); + const imageAnnotatorClient = new vision.ImageAnnotatorClient(); + + async function getSimilarProductsFile() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'nodejs-docs-samples'; + // const location = 'us-west1'; + // const productSetId = 'indexed_product_set_id_for_testing'; + // const productCategory = 'apparel'; + // const filePath = './resources/shoes_1.jpg'; + // const filter = ''; + const productSetPath = productSearchClient.productSetPath( + projectId, + location, + productSetId + ); + const content = fs.readFileSync(filePath, 'base64'); + const request = { + // The input image can be a GCS link or HTTPS link or Raw image bytes. + // Example: + // To use GCS link replace with below code + // image: {source: {gcsImageUri: filePath}} + // To use HTTP link replace with below code + // image: {source: {imageUri: filePath}} + image: {content: content}, + features: [{type: 'PRODUCT_SEARCH'}], + imageContext: { + productSearchParams: { + productSet: productSetPath, + productCategories: [productCategory], + filter: filter, + }, + }, + }; + const [response] = await imageAnnotatorClient.batchAnnotateImages({ + requests: [request], + }); + console.log('Search Image:', filePath); + const results = response['responses'][0]['productSearchResults']['results']; + console.log('\nSimilar product information:'); + results.forEach(result => { + console.log('Product id:', result['product'].name.split('/').pop(-1)); + console.log('Product display name:', result['product'].displayName); + console.log('Product description:', result['product'].description); + console.log('Product category:', result['product'].productCategory); + }); + } + getSimilarProductsFile(); + // [END vision_product_search_get_similar_products] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/getSimilarProductsGcs.js b/vision/productSearch/getSimilarProductsGcs.js new file mode 100644 index 0000000000..dbb926a817 --- /dev/null +++ b/vision/productSearch/getSimilarProductsGcs.js @@ -0,0 +1,90 @@ +// Copyright 2020 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() { + // [START vision_product_search_get_similar_products_gcs] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + // Creates a client + const productSearchClient = new vision.ProductSearchClient(); + const imageAnnotatorClient = new vision.ImageAnnotatorClient(); + + async function getSimilarProductsGcs( + projectId, + location, + productSetId, + productCategory, + filePath, + filter + ) { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + // const productCategory = 'Category of the product'; + // const filePath = 'Local file path of the image to be searched'; + // const filter = 'Condition to be applied on the labels'; + const productSetPath = productSearchClient.productSetPath( + projectId, + location, + productSetId + ); + + const request = { + // The input image can be a GCS link or HTTPS link or Raw image bytes. + // Example: + // To use GCS link replace with below code + // image: {source: {gcsImageUri: filePath}} + // To use HTTP link replace with below code + // image: {source: {imageUri: filePath}} + image: {source: {gcsImageUri: filePath}}, + features: [{type: 'PRODUCT_SEARCH'}], + imageContext: { + productSearchParams: { + productSet: productSetPath, + productCategories: [productCategory], + filter: filter, + }, + }, + }; + console.log(request.image); + + const [response] = await imageAnnotatorClient.batchAnnotateImages({ + requests: [request], + }); + console.log('Search Image:', filePath); + console.log('\nSimilar product information:'); + + const results = response['responses'][0]['productSearchResults']['results']; + results.forEach(result => { + console.log('Product id:', result['product'].name.split('/').pop(-1)); + console.log('Product display name:', result['product'].displayName); + console.log('Product description:', result['product'].description); + console.log('Product category:', result['product'].productCategory); + }); + } + getSimilarProductsGcs(); + // [END vision_product_search_get_similar_products_gcs] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/importProductSets.js b/vision/productSearch/importProductSets.js new file mode 100644 index 0000000000..3435c6a64c --- /dev/null +++ b/vision/productSearch/importProductSets.js @@ -0,0 +1,82 @@ +// Copyright 2020 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, location, gcsUri) { + // [START vision_product_search_import_product_images] + // Imports the Google Cloud client library + // [START vision_product_search_tutorial_import] + const vision = require('@google-cloud/vision'); + // [END vision_product_search_tutorial_import] + // Creates a client + const client = new vision.ProductSearchClient(); + + async function importProductSets() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const gcsUri = 'Google Cloud Storage URI. Target files must be in Product Search CSV format'; + + // A resource that represents Google Cloud Platform location. + const projectLocation = client.locationPath(projectId, location); + + // Set the input configuration along with Google Cloud Storage URI + const inputConfig = { + gcsSource: { + csvFileUri: gcsUri, + }, + }; + + // Import the product sets from the input URI. + const [response, operation] = await client.importProductSets({ + parent: projectLocation, + inputConfig: inputConfig, + }); + + console.log('Processing operation name: ', operation.name); + + // synchronous check of operation status + const [result] = await response.promise(); + console.log('Processing done.'); + console.log('Results of the processing:'); + + for (const i in result.statuses) { + console.log( + 'Status of processing ', + i, + 'of the csv:', + result.statuses[i] + ); + + // Check the status of reference image + if (result.statuses[i].code === 0) { + console.log(result.referenceImages[i]); + } else { + console.log('No reference image.'); + } + } + } + importProductSets(); + // [END vision_product_search_import_product_images] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/listProductSets.js b/vision/productSearch/listProductSets.js new file mode 100644 index 0000000000..5032d3477b --- /dev/null +++ b/vision/productSearch/listProductSets.js @@ -0,0 +1,50 @@ +// Copyright 2020 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, location) { + // [START vision_product_search_list_product_sets] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function listProductSets() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + + // Resource path that represents Google Cloud Platform location. + const locationPath = client.locationPath(projectId, location); + + const [productSets] = await client.listProductSets({parent: locationPath}); + productSets.forEach(productSet => { + console.log(`Product Set name: ${productSet.name}`); + console.log(`Product Set display name: ${productSet.displayName}`); + }); + } + listProductSets(); + // [END vision_product_search_list_product_sets] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/listProducts.js b/vision/productSearch/listProducts.js new file mode 100644 index 0000000000..bdd1b2cddc --- /dev/null +++ b/vision/productSearch/listProducts.js @@ -0,0 +1,59 @@ +// Copyright 2020 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, location) { + // [START vision_product_search_list_products] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function listProducts() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + + // Resource path that represents Google Cloud Platform location. + const locationPath = client.locationPath(projectId, location); + + const [products] = await client.listProducts({parent: locationPath}); + products.forEach(product => { + console.log(`Product name: ${product.name}`); + console.log(`Product id: ${product.name.split('/').pop()}`); + console.log(`Product display name: ${product.displayName}`); + console.log(`Product description: ${product.description}`); + console.log(`Product category: ${product.productCategory}`); + if (product.productLabels.length) { + console.log('Product labels:'); + product.productLabels.forEach(productLabel => { + console.log(`${productLabel.key}: ${productLabel.value}`); + }); + } + }); + } + listProducts(); + // [END vision_product_search_list_products] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/listProductsInProductSet.js b/vision/productSearch/listProductsInProductSet.js new file mode 100644 index 0000000000..7c3adc9c22 --- /dev/null +++ b/vision/productSearch/listProductsInProductSet.js @@ -0,0 +1,54 @@ +// Copyright 2020 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, location, productSetId) { + // [START vision_product_search_list_products_in_product_set] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function listProductsInProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + const productSetPath = client.productSetPath( + projectId, + location, + productSetId + ); + const request = { + name: productSetPath, + }; + + const [products] = await client.listProductsInProductSet(request); + products.forEach(product => { + console.log(`Product name: ${product.name}`); + console.log(`Product display name: ${product.displayName}`); + }); + } + listProductsInProductSet(); + // [END vision_product_search_list_products_in_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/listReferenceImage.js b/vision/productSearch/listReferenceImage.js new file mode 100644 index 0000000000..2f7cdac8a6 --- /dev/null +++ b/vision/productSearch/listReferenceImage.js @@ -0,0 +1,52 @@ +// Copyright 2020 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, location, productId) { + // [START vision_product_search_list_reference_images] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function listReferenceImage() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + + // const formattedParent = client.productPath(projectId, location, productId); + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + const formattedParent = client.productPath(projectId, location, productId); + const request = { + parent: formattedParent, + }; + + const [response] = await client.listReferenceImages(request); + response.forEach(image => { + console.log(`image.name: ${image.name}`); + console.log(`image.uri: ${image.uri}`); + }); + } + listReferenceImage(); + // [END vision_product_search_list_reference_images] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/purgeOrphanProducts.js b/vision/productSearch/purgeOrphanProducts.js new file mode 100644 index 0000000000..9bcf2624a2 --- /dev/null +++ b/vision/productSearch/purgeOrphanProducts.js @@ -0,0 +1,62 @@ +// Copyright 2020 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, location) { + // [START vision_product_search_purge_orphan_products] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function purgeOrphanProducts() { + // Deletes all products not in any product sets. + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + + const formattedParent = client.locationPath(projectId, location); + + // The operation is irreversible and removes multiple products. + // The user is required to pass in force=true to actually perform the purge. + // If force is not set to True, the service raises an error. + const force = true; + + try { + const [operation] = await client.purgeProducts({ + parent: formattedParent, + deleteOrphanProducts: true, + force: force, + }); + await operation.promise(); + console.log('Orphan products deleted.'); + } catch (err) { + console.log(err); + } + } + purgeOrphanProducts(); + // [END vision_product_search_purge_orphan_products] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/purgeProductsInProductSet.js b/vision/productSearch/purgeProductsInProductSet.js new file mode 100644 index 0000000000..054233fe46 --- /dev/null +++ b/vision/productSearch/purgeProductsInProductSet.js @@ -0,0 +1,64 @@ +// Copyright 2020 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, location, productSetId) { + // [START vision_product_search_purge_products_in_product_set] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function purgeProductsInProductSet() { + // Deletes all products in a product set. + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productSetId = 'Id of the product set'; + + const formattedParent = client.locationPath(projectId, location); + const purgeConfig = {productSetId: productSetId}; + + // The operation is irreversible and removes multiple products. + // The user is required to pass in force=true to actually perform the purge. + // If force is not set to True, the service raises an error. + const force = true; + + try { + const [operation] = await client.purgeProducts({ + parent: formattedParent, + productSetPurgeConfig: purgeConfig, + force: force, + }); + await operation.promise(); + console.log('Products removed from product set.'); + } catch (err) { + console.log(err); + } + } + purgeProductsInProductSet(); + // [END vision_product_search_purge_products_in_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/removeProductFromProductSet.js b/vision/productSearch/removeProductFromProductSet.js new file mode 100644 index 0000000000..c490aa82a5 --- /dev/null +++ b/vision/productSearch/removeProductFromProductSet.js @@ -0,0 +1,52 @@ +// Copyright 2020 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, location, productId, productSetId) { + // [START vision_product_search_remove_product_from_product_set] + const vision = require('@google-cloud/vision'); + + const client = new vision.ProductSearchClient(); + + async function removeProductFromProductSet() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + const productSetPath = client.productSetPath( + projectId, + location, + productSetId + ); + + const productPath = client.productPath(projectId, location, productId); + + const request = { + name: productSetPath, + product: productPath, + }; + + await client.removeProductFromProductSet(request); + console.log('Product removed from product set.'); + } + removeProductFromProductSet(); + // [END vision_product_search_remove_product_from_product_set] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/productSearch/updateProductLabels.js b/vision/productSearch/updateProductLabels.js new file mode 100644 index 0000000000..94da3e0f2b --- /dev/null +++ b/vision/productSearch/updateProductLabels.js @@ -0,0 +1,75 @@ +// Copyright 2020 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, location, productId, key, value) { + // [START vision_product_search_update_product_labels] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ProductSearchClient(); + + async function updateProductLabels() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = 'Your Google Cloud project Id'; + // const location = 'A compute region name'; + // const productId = 'Id of the product'; + // const key = 'The key of the label'; + // const value = 'The value of the label'; + + // Resource path that represents full path to the product. + const productPath = client.productPath(projectId, location, productId); + + const product = { + name: productPath, + productLabels: [ + { + key: key, + value: value, + }, + ], + }; + + const updateMask = { + paths: ['product_labels'], + }; + + const request = { + product: product, + updateMask: updateMask, + }; + + const [updatedProduct] = await client.updateProduct(request); + console.log(`Product name: ${updatedProduct.name}`); + console.log(`Product display name: ${updatedProduct.displayName}`); + console.log(`Product description: ${updatedProduct.description}`); + console.log(`Product category: ${updatedProduct.productCategory}`); + console.log( + `Product Labels: ${updatedProduct.productLabels[0].key}: ${updatedProduct.productLabels[0].value}` + ); + } + updateProductLabels(); + // [END vision_product_search_update_product_labels] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/quickstart.js b/vision/quickstart.js new file mode 100644 index 0000000000..db1fabce97 --- /dev/null +++ b/vision/quickstart.js @@ -0,0 +1,46 @@ +// Copyright 2017 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'; + +// sample-metadata: +// title: Cloud Vision Quickstart +// description: Performs label detection of an image with a cat. +// usage: node quickstart.js + +function main() { + // [START vision_quickstart] + async function quickstart() { + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + // Performs label detection on the image file + const [result] = await client.labelDetection('./resources/wakeupcat.jpg'); + const labels = result.labelAnnotations; + console.log('Labels:'); + labels.forEach(label => console.log(label.description)); + } + quickstart(); + // [END vision_quickstart] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/resources/bicycle.jpg b/vision/resources/bicycle.jpg new file mode 100644 index 0000000000..fd536e689b Binary files /dev/null and b/vision/resources/bicycle.jpg differ diff --git a/vision/resources/bonito.gif b/vision/resources/bonito.gif new file mode 100644 index 0000000000..bea0b6ebd4 Binary files /dev/null and b/vision/resources/bonito.gif differ diff --git a/vision/resources/cat.jpg b/vision/resources/cat.jpg new file mode 100644 index 0000000000..76af906f0a Binary files /dev/null and b/vision/resources/cat.jpg differ diff --git a/vision/resources/city.jpg b/vision/resources/city.jpg new file mode 100644 index 0000000000..b14282e753 Binary files /dev/null and b/vision/resources/city.jpg differ diff --git a/vision/resources/duck_and_truck.jpg b/vision/resources/duck_and_truck.jpg new file mode 100644 index 0000000000..5c560fe774 Binary files /dev/null and b/vision/resources/duck_and_truck.jpg differ diff --git a/vision/resources/face.png b/vision/resources/face.png new file mode 100644 index 0000000000..b613a94e34 Binary files /dev/null and b/vision/resources/face.png differ diff --git a/vision/resources/face_no_surprise.jpg b/vision/resources/face_no_surprise.jpg new file mode 100644 index 0000000000..0e2894adb8 Binary files /dev/null and b/vision/resources/face_no_surprise.jpg differ diff --git a/vision/resources/faulkner.jpg b/vision/resources/faulkner.jpg new file mode 100644 index 0000000000..93b8ac3ad2 Binary files /dev/null and b/vision/resources/faulkner.jpg differ diff --git a/vision/resources/google.png b/vision/resources/google.png new file mode 100644 index 0000000000..333bda9371 Binary files /dev/null and b/vision/resources/google.png differ diff --git a/vision/resources/handwritten.jpg b/vision/resources/handwritten.jpg new file mode 100644 index 0000000000..4cc24d7bc9 Binary files /dev/null and b/vision/resources/handwritten.jpg differ diff --git a/vision/resources/landmark.jpg b/vision/resources/landmark.jpg new file mode 100644 index 0000000000..41c3d0fc93 Binary files /dev/null and b/vision/resources/landmark.jpg differ diff --git a/vision/resources/logos.png b/vision/resources/logos.png new file mode 100644 index 0000000000..dcfb4ac955 Binary files /dev/null and b/vision/resources/logos.png differ diff --git a/vision/resources/mountain.jpg b/vision/resources/mountain.jpg new file mode 100644 index 0000000000..f9505df38f Binary files /dev/null and b/vision/resources/mountain.jpg differ diff --git a/vision/resources/no-text.jpg b/vision/resources/no-text.jpg new file mode 100644 index 0000000000..8b77575de7 Binary files /dev/null and b/vision/resources/no-text.jpg differ diff --git a/vision/resources/pdf-ocr.pdf b/vision/resources/pdf-ocr.pdf new file mode 100644 index 0000000000..c66ce0e4c3 Binary files /dev/null and b/vision/resources/pdf-ocr.pdf differ diff --git a/vision/resources/sabertooth.gif b/vision/resources/sabertooth.gif new file mode 100644 index 0000000000..2cee28eeb1 Binary files /dev/null and b/vision/resources/sabertooth.gif differ diff --git a/vision/resources/shoes_1.jpg b/vision/resources/shoes_1.jpg new file mode 100644 index 0000000000..78318eeff6 Binary files /dev/null and b/vision/resources/shoes_1.jpg differ diff --git a/vision/resources/succulents.jpg b/vision/resources/succulents.jpg new file mode 100644 index 0000000000..197fe6ac16 Binary files /dev/null and b/vision/resources/succulents.jpg differ diff --git a/vision/resources/sunbeamkitties.jpg b/vision/resources/sunbeamkitties.jpg new file mode 100644 index 0000000000..b9a584ef55 Binary files /dev/null and b/vision/resources/sunbeamkitties.jpg differ diff --git a/vision/resources/text.jpg b/vision/resources/text.jpg new file mode 100644 index 0000000000..3b17d55de0 Binary files /dev/null and b/vision/resources/text.jpg differ diff --git a/vision/resources/wakeupcat.jpg b/vision/resources/wakeupcat.jpg new file mode 100644 index 0000000000..139cf461ec Binary files /dev/null and b/vision/resources/wakeupcat.jpg differ diff --git a/vision/resources/water.jpg b/vision/resources/water.jpg new file mode 100644 index 0000000000..1554b63df0 Binary files /dev/null and b/vision/resources/water.jpg differ diff --git a/vision/setEndpoint.js b/vision/setEndpoint.js new file mode 100644 index 0000000000..d209e53ce1 --- /dev/null +++ b/vision/setEndpoint.js @@ -0,0 +1,49 @@ +// 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 +// +// 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. + +'use strict'; + +// sample-metadata: +// title: Cloud Vision Custom API Endpoint +// description: Demonstrates using a custom API endpoint for the Cloud Vision API. +// usage: node setEndpoint.js + +function main() { + // [START vision_set_endpoint] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + async function setEndpoint() { + // Specifies the location of the api endpoint + const clientOptions = {apiEndpoint: 'eu-vision.googleapis.com'}; + + // Creates a client + const client = new vision.ImageAnnotatorClient(clientOptions); + + // Performs text detection on the image file + const [result] = await client.textDetection('./resources/wakeupcat.jpg'); + const labels = result.textAnnotations; + console.log('Text:'); + labels.forEach(label => console.log(label.description)); + } + setEndpoint(); + // [END vision_set_endpoint] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2)); diff --git a/vision/system-test/async-batch-annotate-images.test.js b/vision/system-test/async-batch-annotate-images.test.js new file mode 100644 index 0000000000..ba67a3d899 --- /dev/null +++ b/vision/system-test/async-batch-annotate-images.test.js @@ -0,0 +1,55 @@ +// 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 path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const uuid = require('uuid'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const storage = new Storage(); +const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; +const cmd = 'node async-batch-annotate-images.js'; + +const files = ['pdf-ocr.pdf', 'landmark.jpg'].map(name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; +}); + +describe('detect v1 p4 beta1', () => { + before(async () => { + const [bucket] = await storage.createBucket(bucketName); + await Promise.all(files.map(file => bucket.upload(file.localPath))); + }); + + after(async () => { + const bucket = storage.bucket(bucketName); + await bucket.deleteFiles({force: true}); + await bucket.delete(); + }); + + it('should annotate the remote landmark.jpg sample', async () => { + const output = execSync( + `${cmd} gs://${bucketName}/${files[1].name} gs://${bucketName}/out/` + ); + assert.match(output, /Output written to GCS with prefix: gs:\/\//); + }); +}); diff --git a/vision/system-test/batch-annotate-files-gcs.test.js b/vision/system-test/batch-annotate-files-gcs.test.js new file mode 100644 index 0000000000..bccd16f648 --- /dev/null +++ b/vision/system-test/batch-annotate-files-gcs.test.js @@ -0,0 +1,54 @@ +// 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 path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const uuid = require('uuid'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const storage = new Storage(); +const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; +const cmd = 'node batch-annotate-files-gcs.js'; + +const files = ['pdf-ocr.pdf', 'landmark.jpg'].map(name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; +}); + +describe('detect v1 p4 beta1', () => { + before(async () => { + const [bucket] = await storage.createBucket(bucketName); + await Promise.all(files.map(file => bucket.upload(file.localPath))); + }); + + after(async () => { + const bucket = storage.bucket(bucketName); + await bucket.deleteFiles({force: true}); + await bucket.delete(); + }); + + it('should annotate the remote pdf-ocr.pdf in GCS bucket', async () => { + const output = execSync(`${cmd} gs://${bucketName}/${files[0].name}`); + assert.match(output, /Word text: Boring/); + assert.match(output, /Symbol: p/); + }); +}); diff --git a/vision/system-test/batch-annotate-files.test.js b/vision/system-test/batch-annotate-files.test.js new file mode 100644 index 0000000000..09eabf36c5 --- /dev/null +++ b/vision/system-test/batch-annotate-files.test.js @@ -0,0 +1,54 @@ +// 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 path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const uuid = require('uuid'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const storage = new Storage(); +const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; +const cmd = 'node batch-annotate-files.js'; + +const files = ['pdf-ocr.pdf', 'landmark.jpg'].map(name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; +}); + +describe('detect v1 p4 beta1', () => { + before(async () => { + const [bucket] = await storage.createBucket(bucketName); + await Promise.all(files.map(file => bucket.upload(file.localPath))); + }); + + after(async () => { + const bucket = storage.bucket(bucketName); + await bucket.deleteFiles({force: true}); + await bucket.delete(); + }); + + it('should annotate the local pdf-ocr.pdf sample', async () => { + const output = execSync(`${cmd} ${files[0].localPath}`); + assert.match(output, /Word text: Boring/); + assert.match(output, /Symbol: p/); + }); +}); diff --git a/vision/system-test/detect.test.js b/vision/system-test/detect.test.js new file mode 100644 index 0000000000..47513329c4 --- /dev/null +++ b/vision/system-test/detect.test.js @@ -0,0 +1,265 @@ +// Copyright 2018 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 path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const cp = require('child_process'); +const uuid = require('uuid'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const vision = require('@google-cloud/vision'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const client = new vision.ImageAnnotatorClient(); + +const storage = new Storage(); +const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; +const prefix = 'results'; +const cmd = 'node detect.js'; +const files = [ + 'face_no_surprise.jpg', + 'landmark.jpg', + 'logos.png', + 'text.jpg', + 'wakeupcat.jpg', + 'faulkner.jpg', + 'city.jpg', + 'pdf-ocr.pdf', + 'duck_and_truck.jpg', + 'google.png', +].map(name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; +}); + +describe('detect', () => { + before(async () => { + const [bucket] = await storage.createBucket(bucketName); + await Promise.all(files.map(file => bucket.upload(file.localPath))); + }); + + after(async () => { + const bucket = storage.bucket(bucketName); + await bucket.deleteFiles({force: true}); + await bucket.deleteFiles({force: true}); // Try a second time... + await bucket.delete(); + }); + + it('should detect faces in a local file', async () => { + const output = execSync(`${cmd} faces ${files[0].localPath}`); + assert.match(output, /Faces:/); + assert.match(output, /Face #1:/); + }); + + it('should detect faces in a remote file', async () => { + const output = execSync(`${cmd} faces-gcs ${bucketName} ${files[0].name}`); + assert.match(output, /Faces:/); + assert.match(output, /Face #1:/); + }); + + it('should detect labels in a local file', async () => { + const output = execSync(`${cmd} labels ${files[4].localPath}`); + assert.match(output, /Labels:/); + assert.match(output, /cat/); + }); + + it('should detect labels in a remote file', async () => { + const output = execSync(`${cmd} labels-gcs ${bucketName} ${files[4].name}`); + assert.match(output, /Labels:/); + assert.match(output, /cat/); + }); + + it('should detect landmarks in a local file', async () => { + const output = execSync(`${cmd} landmarks ${files[1].localPath}`); + assert.match(output, /Landmarks:/); + assert.match(output, /Palace of Fine Arts/); + }); + + it('should detect landmarks in a remote file', async () => { + const output = execSync( + `${cmd} landmarks-gcs ${bucketName} ${files[1].name}` + ); + assert.match(output, /Landmarks:/); + assert.match(output, /Palace of Fine Arts/); + }); + + it('should detect text in a local file', async () => { + const output = execSync(`${cmd} text ${files[3].localPath}`); + assert.match(output, /Text:/); + assert.match(output, /System Software Update/); + }); + + it('should detect text in a remote file', async () => { + const output = execSync(`${cmd} text-gcs ${bucketName} ${files[3].name}`); + assert.match(output, /Text:/); + assert.match(output, /System Software Update/); + }); + + it('should detect logos in a local file', async () => { + const output = execSync(`${cmd} logos ${files[9].localPath}`); + assert.match(output, /Logos:/); + assert.match(output, /Google/); + }); + + it('should detect logos in a remote file', async () => { + const output = execSync(`${cmd} logos-gcs ${bucketName} ${files[9].name}`); + assert.match(output, /Logos:/); + assert.match(output, /Google/); + }); + + it('should detect properties in a local file', async () => { + const output = execSync(`${cmd} properties ${files[1].localPath}`); + assert.match(output, /color: { red: 69, green: 42, blue: 27/); + assert.ok(output.split('\n').length > 4, 'Multiple colors were detected.'); + }); + + it('should detect properties in a remote file', async () => { + const output = execSync( + `${cmd} properties-gcs ${bucketName} ${files[1].name}` + ); + assert.match(output, /color: { red: 69, green: 42, blue: 27/); + assert.ok(output.split('\n').length > 4, 'Multiple colors were detected.'); + }); + + it('should detect safe-search in a local file', async () => { + const output = execSync(`${cmd} safe-search ${files[4].localPath}`); + assert.match(output, /VERY_LIKELY/); + assert.match(output, /Racy:/); + }); + + it('should detect safe-search in a remote file', async () => { + const output = execSync( + `${cmd} safe-search-gcs ${bucketName} ${files[4].name}` + ); + assert.match(output, /Medical:/); + }); + + it('should detect crop hints in a local file', async () => { + const output = execSync(`${cmd} crops ${files[2].localPath}`); + assert.match(output, /Crop Hint 0:/); + assert.match(output, /Bound 2:/); + }); + + it('should detect crop hints in a remote file', async () => { + const output = execSync(`${cmd} crops-gcs ${bucketName} ${files[2].name}`); + assert.match(output, /Crop Hint 0:/); + assert.match(output, /Bound 2:/); + }); + + it('should detect similar web images in a local file', async () => { + const output = execSync(`${cmd} web ${files[5].localPath}`); + + const [results] = await client.webDetection(files[5].localPath); + const webDetection = results.webDetection; + + if (webDetection.fullMatchingImages.length) { + assert.match(output, /Full matches found:/); + } + + if (webDetection.partialMatchingImages.length) { + assert.match(output, /Partial matches found:/); + } + + if (webDetection.webEntities.length) { + assert.match(output, /Web entities found:/); + assert.match(output, /Description:/); + } + + if (webDetection.bestGuessLabels.length) { + assert.match(output, /Best guess labels found/); + assert.match(output, /Label:/); + } + }); + + it('should detect similar web images in a remote file', async () => { + const output = execSync(`${cmd} web-gcs ${bucketName} ${files[5].name}`); + + const [results] = await client.webDetection( + `gs://${bucketName}/${files[5].name}` + ); + const webDetection = results.webDetection; + + if (webDetection.fullMatchingImages.length) { + assert.match(output, /Full matches found:/); + } + + if (webDetection.partialMatchingImages.length) { + assert.match(output, /Partial matches found:/); + } + + if (webDetection.webEntities.length) { + assert.match(output, /Web entities found:/); + assert.match(output, /Description:/); + } + + if (webDetection.bestGuessLabels.length) { + assert.match(output, /Best guess labels found/); + assert.match(output, /Label:/); + } + }); + + it('should detect web entities with geo metadata in local file', async () => { + const output = execSync(`${cmd} web-geo ${files[1].localPath}`); + assert.match(output, /Description:/); + assert.match(output, /Score:/); + assert.match(output, /Rome/); + }); + + it('should detect web entities with geo metadata in remote file', async () => { + const output = execSync( + `${cmd} web-geo-gcs ${bucketName} ${files[1].name}` + ); + assert.match(output, /Description:/); + assert.match(output, /Score:/); + assert.match(output, /Rome/); + }); + + it('should read a document from a local file', async () => { + const output = execSync(`${cmd} fulltext ${files[2].localPath}`); + assert.match(output, /Google Cloud Platform/); + assert.match(output, /Word text: Cloud/); + assert.match(output, /Word confidence: 0.9/); + }); + + it('should read a document from a remote file', async () => { + const output = execSync( + `${cmd} fulltext-gcs ${bucketName} ${files[2].name}` + ); + assert.match(output, /Google Cloud Platform/); + }); + + it('should extract text from pdf file', async () => { + const output = execSync( + `${cmd} pdf ${bucketName} ${files[7].name} ${prefix}` + ); + assert.match(output, /results/); + }); + + it('should detect objects in a local file', async () => { + const output = execSync(`${cmd} localize-objects ${files[8].localPath}`); + assert.match(output, /Name: Toy/); + }); + + it('should detect objects in a remote file', async () => { + const output = execSync( + `${cmd} localize-objects-gcs gs://${bucketName}/${files[8].name}` + ); + assert.match(output, /Name: Toy/); + }); +}); diff --git a/vision/system-test/detect.v1p1beta1.test.js b/vision/system-test/detect.v1p1beta1.test.js new file mode 100644 index 0000000000..8a0d5606a7 --- /dev/null +++ b/vision/system-test/detect.v1p1beta1.test.js @@ -0,0 +1,53 @@ +// Copyright 2017 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 path = require('path'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node detect.v1p1beta1.js'; +const files = ['text.jpg', 'wakeupcat.jpg', 'landmark.jpg', 'city.jpg'].map( + name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; + } +); + +describe('detect v1 p1 beta1', () => { + it('should extract text from image file and print confidence', async () => { + const output = execSync(`${cmd} fulltext ${files[0].localPath}`); + assert.match(output, /Word text: class/); + assert.match(output, /Word confidence:/); + }); + + // Refs: https://github.com/googleapis/nodejs-vision/issues/1025 + it.skip('should detect safe search properties from image file', async () => { + const output = execSync(`${cmd} safe-search ${files[1].localPath}`); + assert.match(output, /VERY_LIKELY/); + assert.match(output, /Racy:/); + }); + + // Refs: https://github.com/googleapis/nodejs-vision/issues/1025 + it.skip('should detect web entities including best guess labels', async () => { + const output = execSync(`${cmd} web ${files[2].localPath}`); + assert.match(output, /Best guess label: palace of fine arts/); + }); +}); diff --git a/vision/system-test/detect.v1p3beta1.test.js b/vision/system-test/detect.v1p3beta1.test.js new file mode 100644 index 0000000000..05b0274663 --- /dev/null +++ b/vision/system-test/detect.v1p3beta1.test.js @@ -0,0 +1,65 @@ +// Copyright 2017 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 path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const cp = require('child_process'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const uuid = require('uuid'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const storage = new Storage(); +const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; +const cmd = 'node detect.v1p3beta1.js'; + +const files = ['duck_and_truck.jpg', 'handwritten.jpg', 'bicycle.jpg'].map( + name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; + } +); + +describe('detect v1 p3 beta1', () => { + before(async () => { + const [bucket] = await storage.createBucket(bucketName); + await Promise.all(files.map(file => bucket.upload(file.localPath))); + }); + + after(async () => { + const bucket = storage.bucket(bucketName); + await bucket.deleteFiles({force: true}); + await bucket.deleteFiles({force: true}); // Try a second time... + await bucket.delete(); + }); + + it('should read handwriting in local handwritten.jpg sample', async () => { + const output = execSync( + `${cmd} detectHandwriting -h ${files[1].localPath}` + ); + assert.match(output, /hand written message/); + }); + + it('should read handwriting from handwritten.jpg in GCS bucket', async () => { + const output = execSync( + `${cmd} detectHandwritingGCS -u gs://${bucketName}/${files[1].name}` + ); + assert.match(output, /hand written message/); + }); +}); diff --git a/vision/system-test/faceDetection.test.js b/vision/system-test/faceDetection.test.js new file mode 100644 index 0000000000..496c2933c8 --- /dev/null +++ b/vision/system-test/faceDetection.test.js @@ -0,0 +1,35 @@ +// Copyright 2016 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 path = require('path'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node faceDetection.js'; +const inputFile = path.join(__dirname, '../resources', 'face.png'); +const outputFile = path.join(__dirname, '../../', 'out.png'); + +describe('face detection', () => { + it('should detect faces', async () => { + const output = execSync(`${cmd} ${inputFile} ${outputFile}`); + assert.match(output, /Found 1 face/); + assert.match(output, /Highlighting.../); + assert.match(output, /Finished!/); + }); +}); diff --git a/vision/system-test/importProductSets.test.js b/vision/system-test/importProductSets.test.js new file mode 100644 index 0000000000..43472229c6 --- /dev/null +++ b/vision/system-test/importProductSets.test.js @@ -0,0 +1,47 @@ +// Copyright 2018 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 {describe, it, before} = require('mocha'); +const cp = require('child_process'); +const vision = require('@google-cloud/vision'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node productSearch/importProductSets'; +const productSearchClient = new vision.ProductSearchClient(); +describe('import product sets', () => { + let testImportProductSets; + let projectId; + + before(async () => { + projectId = await productSearchClient.getProjectId(); + + //Shared fixture data for product tests + testImportProductSets = { + projectId, + location: 'us-west1', + gcsUri: 'gs://cloud-samples-data/vision/product_search/product_sets.csv', + }; + }); + + it('should import a Product Set', async () => { + const output = execSync( + `${cmd} ${projectId} ${testImportProductSets.location} ${testImportProductSets.gcsUri}` + ); + assert.match(output, /Processing done./); + }); +}); diff --git a/vision/system-test/productSearch.test.js b/vision/system-test/productSearch.test.js new file mode 100644 index 0000000000..9cb5b30ba8 --- /dev/null +++ b/vision/system-test/productSearch.test.js @@ -0,0 +1,113 @@ +// Copyright 2018 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 uuid = require('uuid'); +const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const productSearchClient = new vision.ProductSearchClient(); +const cmd = 'node productSearch'; + +// Refs: https://github.com/googleapis/nodejs-vision/issues/1025 +describe.skip('product search', () => { + let projectId; + let testProductSet; + + before(async () => { + projectId = await productSearchClient.getProjectId(); + + // Shared fixture data for product tests + const testProductSet = { + projectId, + location: 'us-west1', + productCategory: 'homegoods', + productId: `test_product_id${uuid.v4()}`, + productDisplayName: 'test_product_display_name_1', + productSetId: `test_product_set_id${uuid.v4()}`, + productSetDisplayName: 'test_product_set_display_name_1', + }; + + testProductSet.productSetPath = productSearchClient.productSetPath( + testProductSet.projectId, + testProductSet.location, + testProductSet.productSetId + ); + + // Create a test product set for each test + await productSearchClient.createProduct({ + parent: productSearchClient.locationPath( + testProductSet.projectId, + testProductSet.location + ), + productId: testProductSet.productId, + product: { + displayName: testProductSet.productDisplayName, + productCategory: testProductSet.productCategory, + }, + }); + testProductSet.createdProductPaths.push(testProductSet.productPath); + + await productSearchClient.createProductSet({ + parent: productSearchClient.locationPath( + testProductSet.projectId, + testProductSet.location + ), + productSetId: testProductSet.productSetId, + productSet: { + displayName: testProductSet.productSetDisplayName, + }, + }); + testProductSet.createdProductSetPaths.push( + testProductSet.createdProductSetPaths + ); + }); + + after(async () => { + // Delete products sets after each test + testProductSet.createdProductSetPaths.forEach(async path => { + try { + await productSearchClient.deleteProductSet({name: path}); + } catch (err) { + // ignore error + } + }); + testProductSet.createdProductPaths.forEach(async path => { + try { + await productSearchClient.deleteProduct({name: path}); + } catch (err) { + // ignore error + } + }); + }); + + it('should add product to product set', async () => { + const output = execSync( + `${cmd}/addProductToProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${testProductSet.productId}" "${testProductSet.productSetId}"` + ); + assert.match(output, /Product added to product set./); + }); + + it('should remove a product from a product set', async () => { + const output = execSync( + `${cmd}/removeProductFromProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${testProductSet.productId}" "${testProductSet.productSetId}"` + ); + assert.match(output, /Product removed from product set./); + }); +}); diff --git a/vision/system-test/productSets.test.js b/vision/system-test/productSets.test.js new file mode 100644 index 0000000000..29bf95c6a5 --- /dev/null +++ b/vision/system-test/productSets.test.js @@ -0,0 +1,173 @@ +// Copyright 2018 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 uuid = require('uuid'); +const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const productSearch = new vision.ProductSearchClient(); +const cmd = 'node productSearch'; +let testProductSet; + +// Helper function: returns product set if exists else false +async function getProductSetOrFalse(productSetPath) { + try { + const response = await productSearch.getProductSet({name: productSetPath}); + return response[0]; + } catch (err) { + if (err.message.includes('Not found')) { + return false; + } else { + throw err; + } + } +} + +describe('product sets', () => { + let projectId; + + before(async () => { + projectId = await productSearch.getProjectId(); + // Shared fixture data for product tests + testProductSet = { + projectId, + location: 'us-west1', + productSetId: `test_product_set_id${uuid.v4()}`, + productSetDisplayName: 'test_product_set_display_name_1', + }; + testProductSet.productSetPath = productSearch.productSetPath( + testProductSet.projectId, + testProductSet.location, + testProductSet.productSetId + ); + testProductSet.createdProductSetPaths = []; + // Create a test product set for each test + await productSearch.createProductSet({ + parent: productSearch.locationPath( + testProductSet.projectId, + testProductSet.location + ), + productSetId: testProductSet.productSetId, + productSet: { + displayName: testProductSet.productSetDisplayName, + }, + }); + testProductSet.createdProductSetPaths.push( + testProductSet.createdProductSetPaths + ); + }); + + after(async () => { + // Delete products sets after each test + testProductSet.createdProductSetPaths.forEach(async path => { + try { + await productSearch.deleteProductSet({name: path}); + } catch (err) { + // ignore error + } + }); + }); + + it('should create product set', async () => { + const newProductSetId = `ProductSetId${uuid.v4()}`; + const newProductSetPath = productSearch.productSetPath( + testProductSet.projectId, + testProductSet.location, + newProductSetId + ); + assert.strictEqual(await getProductSetOrFalse(newProductSetPath), false); + testProductSet.createdProductSetPaths.push(newProductSetPath); + + const output = execSync( + `${cmd}/createProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${newProductSetId}" "${testProductSet.productSetDisplayName}"` + ); + + assert.match(output, new RegExp(`Product Set name: ${newProductSetPath}`)); + + const newProductSet = await getProductSetOrFalse(newProductSetPath); + assert.strictEqual( + newProductSet.displayName, + testProductSet.productSetDisplayName + ); + }); + + it('should get product set', async () => { + const output = execSync( + `${cmd}/getProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${testProductSet.productSetId}"` + ); + + assert.match( + output, + new RegExp(`Product Set name: ${testProductSet.productSetPath}`) + ); + assert.match( + output, + new RegExp( + `Product Set display name: ${testProductSet.productSetDisplayName}` + ) + ); + }); + + it('should list product sets', async () => { + const output = execSync( + `${cmd}/listProductSets "${testProductSet.projectId}" "${testProductSet.location}"` + ); + + assert.match( + output, + new RegExp(`Product Set name: ${testProductSet.productSetPath}`) + ); + assert.match( + output, + new RegExp( + `Product Set display name: ${testProductSet.productSetDisplayName}` + ) + ); + }); + + it('should purge a product set', async () => { + const output = execSync( + `${cmd}/purgeProductsInProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${testProductSet.productSetId}"` + ); + + assert.match(output, new RegExp('Products removed from product set.')); + }); + + it('should delete product sets', async () => { + const productSet = await productSearch.getProductSet({ + name: `${testProductSet.productSetPath}`, + }); + assert.ok(productSet); + + const output = execSync( + `${cmd}/deleteProductSet "${testProductSet.projectId}" "${testProductSet.location}" "${testProductSet.productSetId}"` + ); + + assert.match(output, /deleted/); + try { + await productSearch.getProductSet({ + name: `${testProductSet.productSetPath}`, + }); + assert.fail('Product set was not deleted'); + } catch (err) { + assert.ok(err.message.includes('Not found')); + } + }); +}); diff --git a/vision/system-test/products.test.js b/vision/system-test/products.test.js new file mode 100644 index 0000000000..895b35bf34 --- /dev/null +++ b/vision/system-test/products.test.js @@ -0,0 +1,245 @@ +// Copyright 2018 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 uuid = require('uuid'); +const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node productSearch'; + +const productSearch = new vision.ProductSearchClient(); + +let testProduct; + +// Helper function: returns product if exists else false +async function getProductOrFalse(productPath) { + try { + const response = await productSearch.getProduct({name: productPath}); + return response[0]; + } catch (err) { + if (err.message.includes('Not found')) { + return false; + } + } +} + +describe('products', () => { + let projectId; + + before(async () => { + projectId = await productSearch.getProjectId(); + // Shared fixture data for product tests + testProduct = { + projectId, + location: 'us-west1', + productId: `test_products_id${uuid.v4()}`, + productDisplayName: 'test_product_display_name_1', + productCategory: 'homegoods', + productKey: 'myKey', + productValue: 'myValue', + }; + testProduct.productPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + testProduct.productId + ); + testProduct.createdProductPaths = []; + + // Create a test product set for each test + await productSearch.createProduct({ + parent: productSearch.locationPath( + testProduct.projectId, + testProduct.location + ), + productId: testProduct.productId, + product: { + displayName: testProduct.productDisplayName, + productCategory: testProduct.productCategory, + }, + }); + testProduct.createdProductPaths.push(testProduct.productPath); + }); + + after(async () => { + // Delete products sets after each test + testProduct.createdProductPaths.forEach(async path => { + try { + await productSearch.deleteProduct({name: path}); + } catch (err) { + // ignore error + } + }); + }); + it('should create product', async () => { + const newProductId = `ProductId${uuid.v4()}`; + const newProductPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + newProductId + ); + const maybeProduct = await getProductOrFalse(newProductPath); + assert.strictEqual(maybeProduct, false); + + let output = execSync( + `${cmd}/createProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productDisplayName}" "${testProduct.productCategory}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + + const newProduct = await getProductOrFalse(newProductPath); + assert.ok(newProduct.displayName === testProduct.productDisplayName); + assert.ok(newProduct.productCategory === testProduct.productCategory); + + output = execSync( + `${cmd}/deleteProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}"` + ); + assert.match(output, /Product deleted./); + }); + + it('should get product', async () => { + const newProductId = `ProductId${uuid.v4()}`; + const newProductPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + newProductId + ); + assert.strictEqual(await getProductOrFalse(newProductPath), false); + let output = execSync( + `${cmd}/createProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productDisplayName}" "${testProduct.productCategory}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + + output = execSync( + `${cmd}/getProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + assert.match(output, new RegExp(`Product id: ${newProductId}`)); + assert.match(output, /Product display name:/); + assert.match(output, /Product description:/); + assert.match( + output, + new RegExp(`Product category: ${testProduct.productCategory}`) + ); + assert.match(output, /Product labels:/); + + output = execSync( + `${cmd}/deleteProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}"` + ); + assert.match(output, /Product deleted./); + }); + + it('should list products', async () => { + const output = execSync( + `${cmd}/listProducts "${testProduct.projectId}" "${testProduct.location}"` + ); + assert.match(output, new RegExp(`Product id: ${testProduct.productId}`)); + assert.match(output, /Product labels:/); + }); + + it('should update product label', async () => { + const newProductId = `ProductId${uuid.v4()}`; + const newProductPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + newProductId + ); + let output = execSync( + `${cmd}/createProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productDisplayName}" "${testProduct.productCategory}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + output = execSync( + `${cmd}/updateProductLabels "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productKey}" "${testProduct.productValue}"` + ); + + assert.match( + output, + new RegExp( + `Product Labels: ${testProduct.productKey}: ${testProduct.productValue}` + ) + ); + assert.match( + output, + new RegExp(`Product display name: ${testProduct.productDisplayName}`) + ); + assert.match(output, /Product description:/); + assert.match( + output, + new RegExp(`Product category: ${testProduct.productCategory}`) + ); + }); + + it('should delete product', async () => { + const newProductId = `ProductId${uuid.v4()}`; + const newProductPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + newProductId + ); + assert.strictEqual(await getProductOrFalse(newProductPath), false); + let output = execSync( + `${cmd}/createProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productDisplayName}" "${testProduct.productCategory}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + + output = execSync( + `${cmd}/deleteProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}"` + ); + assert.match(output, /Product deleted./); + + try { + await productSearch.getProduct({name: `${newProductPath}`}); + assert.fail('Product was not deleted'); + } catch (err) { + assert.ok(err.message.includes('Not found')); + } + }); + + it('should remove orphaned products', async () => { + const newProductId = `ProductId${uuid.v4()}`; + const newProductPath = productSearch.productPath( + testProduct.projectId, + testProduct.location, + newProductId + ); + + assert.strictEqual(await getProductOrFalse(newProductPath), false); + let output = execSync( + `${cmd}/createProduct "${testProduct.projectId}" "${testProduct.location}" "${newProductId}" "${testProduct.productDisplayName}" "${testProduct.productCategory}"` + ); + + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + + output = execSync( + `${cmd}/purgeOrphanProducts "${testProduct.projectId}" "${testProduct.location}"` + ); + + assert.match(output, new RegExp('Orphan products deleted.')); + try { + await productSearch.getProduct({name: `${newProductPath}`}); + assert.fail('Product was not deleted'); + } catch (err) { + assert.ok(err.message.includes('Not found')); + } + }); +}); diff --git a/vision/system-test/quickstart.test.js b/vision/system-test/quickstart.test.js new file mode 100644 index 0000000000..806e71cabe --- /dev/null +++ b/vision/system-test/quickstart.test.js @@ -0,0 +1,29 @@ +// Copyright 2017 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 {describe, it} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('quickstart', () => { + it('should detect labels in a remote file', async () => { + const stdout = execSync('node quickstart.js'); + assert.match(stdout, /Labels:/); + assert.match(stdout, /cat/); + }); +}); diff --git a/vision/system-test/referenceImages.test.js b/vision/system-test/referenceImages.test.js new file mode 100644 index 0000000000..d0aee4e576 --- /dev/null +++ b/vision/system-test/referenceImages.test.js @@ -0,0 +1,92 @@ +// Copyright 2018 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 uuid = require('uuid'); +const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const productSearchClient = new vision.ProductSearchClient(); +const cmd = 'node productSearch'; + +let testProduct; + +describe('reference images', () => { + let projectId; + before(async () => { + projectId = await productSearchClient.getProjectId(); + // Shared fixture data for product tests + testProduct = { + projectId, + location: 'us-west1', + productId: 'test_product_ref_image_id_1', + productDisplayName: 'test_product_display_name_1', + productCategory: 'homegoods', + productReferenceImageId: `ReferenceImage${uuid.v4()}`, + productImageUri: + 'gs://cloud-samples-data/vision/product_search/shoes_1.jpg', + }; + testProduct.productPath = productSearchClient.productPath( + testProduct.projectId, + testProduct.location, + testProduct.productId + ); + testProduct.createdProductPaths = []; + + // Create a test product for each test + await productSearchClient.createProduct({ + parent: productSearchClient.locationPath( + testProduct.projectId, + testProduct.location + ), + productId: testProduct.productId, + product: { + displayName: testProduct.productDisplayName, + productCategory: testProduct.productCategory, + }, + }); + testProduct.createdProductPaths.push(testProduct.productPath); + }); + + after(async () => { + // Delete products after each test + testProduct.createdProductPaths.forEach(async path => { + try { + await productSearchClient.deleteProduct({name: path}); + } catch (err) { + // ignore error + } + }); + }); + + it('should create reference image', async () => { + const output = execSync( + `${cmd}/createReferenceImage "${testProduct.projectId}" "${testProduct.location}" "${testProduct.productId}" "${testProduct.productReferenceImageId}" "${testProduct.productImageUri}"` + ); + assert.match(output, /response.uri: gs:\/\//); + }); + + it('should delete reference image', async () => { + const output = execSync( + `${cmd}/deleteReferenceImage "${testProduct.projectId}" "${testProduct.location}" "${testProduct.productId}" "${testProduct.productReferenceImageId}"` + ); + + assert.match(output, /Reference image deleted from product./); + }); +}); diff --git a/vision/system-test/setEndpoint.test.js b/vision/system-test/setEndpoint.test.js new file mode 100644 index 0000000000..e823c5782e --- /dev/null +++ b/vision/system-test/setEndpoint.test.js @@ -0,0 +1,27 @@ +// 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 +// +// 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. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const cp = require('child_process'); +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +describe('set endpoint for vision api call', () => { + it('should detect text in a remote file from a pre-set api endpoint', () => { + const stdout = execSync('node setEndpoint.js'); + assert.match(stdout, /human/); + }); +}); diff --git a/vision/system-test/similarProducts.test.js b/vision/system-test/similarProducts.test.js new file mode 100644 index 0000000000..acb97c1e66 --- /dev/null +++ b/vision/system-test/similarProducts.test.js @@ -0,0 +1,104 @@ +// Copyright 2018 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 vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const {describe, it, before} = require('mocha'); +const cp = require('child_process'); +const path = require('path'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node productSearch'; +const filter = ['', 'style=womens']; +const localPath = path.join(__dirname, '../resources/shoes_1.jpg'); +const gcsUri = 'gs://cloud-samples-data/vision/product_search/shoes_1.jpg'; + +const productSearch = new vision.ProductSearchClient(); + +let testSimilarProducts; + +// Refs: https://github.com/googleapis/nodejs-vision/issues/1025 +describe.skip('similar products', () => { + let projectId; + before(async () => { + projectId = await productSearch.getProjectId(); + + // Shared fixture data for product tests + //Need to have a product set already imported and indexed + // (gs://nodejs-docs-samples/product-search/indexed_product_sets.csv) + testSimilarProducts = { + projectId, + location: 'us-west1', + productSetId: 'indexed_product_set_id_for_testing', + productCategory: 'apparel', + }; + testSimilarProducts.productPath = productSearch.productSetPath( + testSimilarProducts.projectId, + testSimilarProducts.location, + testSimilarProducts.productSetId + ); + }); + it('should check if similar product exists to one provided in local file with no filter', async () => { + const output = execSync( + `${cmd}/getSimilarProducts "${testSimilarProducts.projectId}" "${testSimilarProducts.location}" "${testSimilarProducts.productSetId}" "${testSimilarProducts.productCategory}" "${localPath}" "${filter[0]}"` + ); + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) + ); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + assert.match(output, /Product id: indexed_product_id_for_testing_2/); + }); + + it('should check if similar product exists to one provided in local file with filter', async () => { + const output = execSync( + `${cmd}/getSimilarProducts "${testSimilarProducts.projectId}" "${testSimilarProducts.location}" "${testSimilarProducts.productSetId}" "${testSimilarProducts.productCategory}" "${localPath}" "${filter[1]}"` + ); + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) + ); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + }); + + it('should check if similar product exists to one provided in GCS file with no filter', async () => { + const output = execSync( + `${cmd}/getSimilarProductsGcs "${testSimilarProducts.projectId}" "${testSimilarProducts.location}" "${testSimilarProducts.productSetId}" "${testSimilarProducts.productCategory}" "${gcsUri}" "${filter[0]}"` + ); + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) + ); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + assert.match(output, /Product id: indexed_product_id_for_testing_2/); + }); + + it('should check if similar product exists to one provided in GCS file with filter', async () => { + const output = execSync( + `${cmd}/getSimilarProductsGcs "${testSimilarProducts.projectId}" "${testSimilarProducts.location}" "${testSimilarProducts.productSetId}" "${testSimilarProducts.productCategory}" "${gcsUri}" "${filter[1]}"` + ); + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) + ); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + }); +}); diff --git a/vision/system-test/textDetection.test.js b/vision/system-test/textDetection.test.js new file mode 100644 index 0000000000..7116056488 --- /dev/null +++ b/vision/system-test/textDetection.test.js @@ -0,0 +1,43 @@ +// Copyright 2016 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 path = require('path'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +// Refs: https://github.com/googleapis/nodejs-vision/issues/1025 +describe.skip('Text Detection', () => { + it('should detect texts', async () => { + const inputDir = path.join(__dirname, '../resources'); + try { + execSync(`node textDetection analyze ${inputDir}`); + } catch (err) { + if (err.stderr.match(/connect ECONNREFUSED/)) { + console.error( + '☣️ Redis is unavailable. Skipping vision textDetection test.' + ); + return; + } + throw new Error(err.stderr); + } + + const stdout = execSync('node textDetection lookup sunbeams'); + assert.match(stdout, /sunbeamkitties/); + }); +}); diff --git a/vision/textDetection.js b/vision/textDetection.js new file mode 100644 index 0000000000..7c67a16d01 --- /dev/null +++ b/vision/textDetection.js @@ -0,0 +1,300 @@ +// Copyright 2016 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 fs = require('fs').promises; +const path = require('path'); +const {promisify} = require('util'); +const vision = require('@google-cloud/vision'); +const natural = require('natural'); +const redis = require('redis'); + +// By default, the client will authenticate using the service account file +// specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use +// the project specified by the GCLOUD_PROJECT environment variable. See +// https://cloud.google.com/docs/authentication/getting-started#setting_the_environment_variable + +// Instantiate a vision client +const client = new vision.ImageAnnotatorClient(); + +/** + * State manager for text processing. Stores and reads results from Redis. + */ +class Index { + /** + * Create a new Index object. + */ + constructor() { + // Connect to a redis server. + const TOKEN_DB = 0; + const DOCS_DB = 1; + const PORT = process.env.REDIS_PORT || '6379'; + const HOST = process.env.REDIS_HOST || '127.0.0.1'; + + this.tokenClient = redis + .createClient(PORT, HOST, { + db: TOKEN_DB, + }) + .on('error', err => { + console.error('ERR:REDIS: ' + err); + throw err; + }); + this.docsClient = redis + .createClient(PORT, HOST, { + db: DOCS_DB, + }) + .on('error', err => { + console.error('ERR:REDIS: ' + err); + throw err; + }); + } + + /** + * Close all active redis server connections. + */ + quit() { + this.tokenClient.quit(); + this.docsClient.quit(); + } + + /** + * Tokenize the given document. + * @param {string} filename - key for the storage in redis + * @param {string} document - Collection of words to be tokenized + * @returns {Promise} + */ + async add(filename, document) { + const PUNCTUATION = ['.', ',', ':', '']; + const tokenizer = new natural.WordTokenizer(); + const tokens = tokenizer.tokenize(document); + // filter out punctuation, then add all tokens to a redis set. + await Promise.all( + tokens + .filter(token => PUNCTUATION.indexOf(token) === -1) + .map(token => { + const sadd = promisify(this.tokenClient.sadd).bind(this.tokenClient); + return sadd(token, filename); + }) + ); + const set = promisify(this.docsClient.set).bind(this.docsClient); + await set(filename, document); + } + + /** + * Lookup files that contain a given set of words in redis + * @param {string[]} words An array of words to lookup + * @returns {Promise} Words and their arrays of matching filenames + */ + async lookup(words) { + return Promise.all( + words + .map(word => word.toLowerCase()) + .map(word => { + const smembers = promisify(this.tokenClient.smembers).bind( + this.tokenClient + ); + return smembers(word); + }) + ); + } + + /** + * Check to see if a Document is already stored in redis. + * @param {string} filename + * @returns {Promise} + */ + async documentIsProcessed(filename) { + const get = promisify(this.docsClient.get).bind(this.docsClient); + const value = await get(filename); + if (value) { + console.log(`${filename} already added to index.`); + return true; + } + if (value === '') { + console.log(`${filename} was already checked, and contains no text.`); + return true; + } + return false; + } + + /** + * Updates a given doc to have no text in redis. + * @param {string} filename + */ + async setContainsNoText(filename) { + const set = promisify(this.docsClient.set).bind(this.docsClient); + await set(filename, ''); + } +} + +/** + * Given a list of words, lookup any matches in the database. + * @param {string[]} words + * @returns {Promise} + */ +async function lookup(words) { + const index = new Index(); + const hits = await index.lookup(words); + index.quit(); + words.forEach((word, i) => { + console.log(`hits for "${word}":`, hits[i].join(', ')); + }); + return hits; +} + +/** + * Provide a joined string with all descriptions from the response data + * @param {TextAnnotation[]} texts Response data from the Vision API + * @returns {string} A joined string containing al descriptions + */ +function extractDescription(texts) { + let document = ''; + texts.forEach(text => { + document += text.description || ''; + }); + return document.toLowerCase(); +} + +/** + * Grab the description, and push it into redis. + * @param {string} filename Name of the file being processed + * @param {Index} index The Index object that wraps Redis + * @param {*} response Individual response from the Cloud Vision API + * @returns {Promise} + */ +async function extractDescriptions(filename, index, response) { + if (response.textAnnotations.length) { + const words = extractDescription(response.textAnnotations); + await index.add(filename, words); + } else { + console.log(`${filename} had no discernable text.`); + await index.setContainsNoText(filename); + } +} + +/** + * Given a set of image file paths, extract the text and run them through the + * Cloud Vision API. + * @param {Index} index The stateful `Index` Object. + * @param {string[]} inputFiles The list of files to process. + * @returns {Promise} + */ +async function getTextFromFiles(index, inputFiles) { + // Read all of the given files and provide request objects that will be + // passed to the Cloud Vision API in a batch request. + const requests = await Promise.all( + inputFiles.map(async filename => { + const content = await fs.readFile(filename); + console.log(` 👉 ${filename}`); + return { + image: { + content: content.toString('base64'), + }, + features: [{type: 'TEXT_DETECTION'}], + }; + }) + ); + + // Make a call to the Vision API to detect text + const results = await client.batchAnnotateImages({requests}); + const detections = results[0].responses; + await Promise.all( + inputFiles.map(async (filename, i) => { + const response = detections[i]; + if (response.error) { + console.info(`API Error for ${filename}`, response.error); + return; + } + await extractDescriptions(filename, index, response); + }) + ); +} + +/** + * Main entry point for the program. + * @param {string} inputDir The directory in which to run the sample. + * @returns {Promise} + */ +async function main(inputDir) { + const index = new Index(); + try { + const files = await fs.readdir(inputDir); + + // Get a list of all files in the directory (filter out other directories) + const allImageFiles = ( + await Promise.all( + files.map(async file => { + const filename = path.join(inputDir, file); + const stats = await fs.stat(filename); + if (!stats.isDirectory()) { + return filename; + } + }) + ) + ).filter(f => !!f); + + // Figure out which files have already been processed + let imageFilesToProcess = ( + await Promise.all( + allImageFiles.map(async filename => { + const processed = await index.documentIsProcessed(filename); + if (!processed) { + // Forward this filename on for further processing + return filename; + } + }) + ) + ).filter(file => !!file); + + // The batch endpoint won't handle + if (imageFilesToProcess.length > 15) { + console.log( + 'Maximum of 15 images allowed. Analyzing the first 15 found.' + ); + imageFilesToProcess = imageFilesToProcess.slice(0, 15); + } + + // Analyze any remaining unprocessed files + if (imageFilesToProcess.length > 0) { + console.log('Files to process: '); + await getTextFromFiles(index, imageFilesToProcess); + } + console.log('All files processed!'); + } catch (e) { + console.error(e); + } + index.quit(); +} + +const usage = + 'Usage: node textDetection ... \n\n Commands: analyze, lookup'; +if (process.argv.length < 3) { + throw new Error(usage); +} +const args = process.argv.slice(2); +const command = args.shift(); +if (command === 'analyze') { + if (!args.length) { + throw new Error('Usage: node textDetection analyze '); + } + main(args[0]).catch(console.error); +} else if (command === 'lookup') { + if (!args.length) { + throw new Error('Usage: node textDetection lookup ...'); + } + lookup(args).catch(console.error); +} else { + throw new Error(usage); +} diff --git a/vision/vision-face-detection.js b/vision/vision-face-detection.js new file mode 100644 index 0000000000..a2ca4fbce1 --- /dev/null +++ b/vision/vision-face-detection.js @@ -0,0 +1,56 @@ +// Copyright 2020 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'; + +// sample-metadata: +// title: Cloud Vision Face Detection +// description: Identify faces in an image using the Cloud Vision API. +// usage: node vision-face-detection.js + +function main(fileName) { + // [START vision_face_detection] + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); + + // Creates a client + const client = new vision.ImageAnnotatorClient(); + + async function detectFaces() { + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const fileName = 'Local image file, e.g. /path/to/image.png'; + + const [result] = await client.faceDetection(fileName); + const faces = result.faceAnnotations; + console.log('Faces:'); + faces.forEach((face, i) => { + console.log(` Face #${i + 1}:`); + console.log(` Joy: ${face.joyLikelihood}`); + console.log(` Anger: ${face.angerLikelihood}`); + console.log(` Sorrow: ${face.sorrowLikelihood}`); + console.log(` Surprise: ${face.surpriseLikelihood}`); + }); + } + detectFaces(); + // [END vision_face_detection] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); + +main(...process.argv.slice(2));