├── .eslintrc.json ├── .gitignore ├── .tool-versions ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── clean-slate.sh ├── deployment ├── build-open-source-dist.sh ├── build-s3-dist.sh ├── common.sh ├── custom-ppe-detection.yaml ├── deploy-s3-dist.sh ├── images │ ├── analysis-result-01.jpg │ ├── analysis-result-02.jpg │ ├── analysis-result-03.jpg │ ├── analysis.gif │ ├── cfn-01-create-stack.jpg │ ├── cfn-02-specify-details.jpg │ ├── cfn-03-review-stack.jpg │ ├── custom-ppe-detection-analysis.jpg │ ├── custom-ppe-detection-architecture.jpg │ ├── custom-ppe-detection-training.jpg │ ├── custom-ppe-detection.drawio │ ├── invitation-email.jpg │ ├── training-option-1.gif │ └── training-option-2.gif └── samples │ ├── sample-input.json │ └── training-dataset.sh └── source ├── .version ├── api ├── .eslintrc.json ├── README.md ├── index.js ├── lib │ ├── index.js │ ├── mxValidation.js │ └── s3utils.js └── package.json ├── build ├── package.json └── post-build.js ├── custom-resources ├── .eslintrc.json ├── README.md ├── index.js ├── lib │ ├── cognito │ │ └── index.js │ ├── rekognition │ │ └── index.js │ ├── s3 │ │ └── index.js │ ├── shared │ │ ├── cfResponse.js │ │ └── mxBaseResponse.js │ └── web │ │ ├── index.js │ │ ├── solutionManifest.js │ │ └── webcontent.js └── package.json ├── layers └── image-utils │ ├── index.js │ └── package.json ├── tools ├── analyze-with-custom-labels-model.js ├── package.json └── prepare-training-dataset.js └── webapp ├── .eslintrc.json ├── 404.html ├── README.md ├── css └── app.css ├── favicon.ico ├── images ├── option-1 │ ├── pexels-photo-1108101.jpg │ ├── pexels-photo-159306.jpg │ ├── pexels-photo-901941.jpg │ ├── unsplash-photo-1529088746738-c4c0a152fb2c.jpg │ ├── unsplash-photo-1553429938-0c318ee3de7a.jpg │ ├── unsplash-photo-1560872952-142f67294080.jpg │ ├── unsplash-photo-1588931294038-079a39173e8c.jpg │ └── unsplash-photo-1589939705384-5185137a7f0f.jpg └── option-2 │ ├── pexels-photo-1108101-355.00,590.00,386.00,65.00.jpg │ ├── pexels-photo-1216589-425.00,571.00,290.00,84.00.jpg │ ├── pexels-photo-1216589-460.00,607.00,502.00,52.00.jpg │ ├── pexels-photo-2760243-389.00,430.00,292.00,226.00.jpg │ ├── pexels-photo-3680959-316.00,535.00,167.00,119.00.jpg │ ├── pexels-photo-3680959-513.00,577.00,309.00,82.00.jpg │ ├── pexels-photo-544966-481.00,476.00,304.00,43.00.jpg │ ├── pexels-photo-591.00,486.00,274.00,101.00.jpg │ ├── unsplash-photo-1550496236-ebe36e6f7a92-457.00,433.00,295.00,92.00.jpg │ └── unsplash-photo-1580810734868-7ea4e9130c01-153.00,427.00,308.00,134.00.jpg ├── index.html ├── package.json ├── src └── lib │ └── js │ ├── app.js │ ├── mainView │ ├── mainView.js │ └── tabControllers │ │ ├── analysis │ │ ├── dropzoneSlideComponent.js │ │ └── previewSlideComponent.js │ │ ├── analysisTab.js │ │ ├── baseSlideComponent.js │ │ ├── baseTab.js │ │ ├── projectTab.js │ │ ├── training │ │ ├── dataset.js │ │ ├── labelDatasetSlideComponent.js │ │ ├── prepareDatasetSlideComponent.js │ │ ├── selectTrainOptionsSlideComponent.js │ │ └── trainModelSlideComponent.js │ │ └── trainingTab.js │ ├── mixins │ ├── mxAlert.js │ ├── mxDropzone.js │ ├── mxReadable.js │ └── mxSpinner.js │ ├── shared │ ├── apiHelper.js │ ├── appUtils.js │ ├── awsConsole.js │ ├── fileItem.js │ ├── localCache.js │ ├── localization.js │ ├── projectVersion.js │ ├── s3utils.js │ └── signer.js │ └── signIn │ ├── cognitoConnector.js │ └── signInFlow.js └── third_party ├── amazon-cognito-identity.min.js └── aws-sdk-2.680.0.min.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "strict": "error", 5 | "func-names": ["error", "never"], 6 | "comma-dangle": ["error", { 7 | "arrays": "always-multiline", 8 | "objects": "always-multiline", 9 | "imports": "always-multiline", 10 | "exports": "always-multiline", 11 | "functions": "never" 12 | }], 13 | "no-use-before-define": ["error", { 14 | "functions": false, 15 | "classes": true, 16 | "variables": true 17 | }], 18 | "prefer-arrow-callback": 0, 19 | "prefer-object-spread": 0, 20 | "implicit-arrow-linebreak": 0, 21 | "arrow-parens": 0, 22 | "operator-linebreak": 0, 23 | "class-methods-use-this": 0, 24 | "import/no-extraneous-dependencies": 0, 25 | "no-nested-ternary": 0, 26 | "one-var-declaration-per-line": 0, 27 | "one-var": 0, 28 | "no-plusplus": 0, 29 | "no-await-in-loop": 0, 30 | "no-console": 0, 31 | "global-require": 0, 32 | "no-continue": 0 33 | }, 34 | "env": { 35 | "es6": true, 36 | "browser": false, 37 | "node": true, 38 | "jquery": false, 39 | "mocha": true 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #### Compiled source 2 | *.com 3 | *.class 4 | *.dll 5 | *.exe 6 | *.o 7 | 8 | #### Packages 9 | #### it's better to unpack these files and commit the raw source 10 | #### git has its own built in compression methods 11 | *.7z 12 | *.dmg 13 | *.gz 14 | *.iso 15 | *.jar 16 | *.rar 17 | *.tar 18 | *.zip 19 | 20 | #### Logs and databases 21 | *.log 22 | *.sql 23 | *.sqlite 24 | 25 | 26 | #### OS generated files 27 | .DS_Store 28 | .DS_Store? 29 | ._* 30 | .Spotlight-V100 31 | .Trashes 32 | ehthumbs.db 33 | Thumbs.db 34 | 35 | 36 | #### Node modules 37 | node_modules 38 | release 39 | not-used 40 | dist 41 | .vscode 42 | doc 43 | package-lock.json 44 | global-s3-assets 45 | regional-s3-assets 46 | open-source 47 | 48 | #### Misc 49 | *.env 50 | *.pem 51 | *.key 52 | *.vtt 53 | 54 | ### ignore webapp compiled JS files 55 | source/webapp/common-bundle-dev.js 56 | source/webapp/solution-manifest.js 57 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 10.16.3 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.0.0] - 2019-05-14 8 | ### Added 9 | - initial version 10 | 11 | ### Changed 12 | 13 | ### Removed 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/awslabs/amazon-rekognition-custom-ppe-detection-with-custom-labels/issues), or [recently closed](https://github.com/awslabs/amazon-rekognition-custom-ppe-detection-with-custom-labels/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/amazon-rekognition-custom-ppe-detection-with-custom-labels/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/awslabs/amazon-rekognition-custom-ppe-detection-with-custom-labels/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Custom PPE Detection Using Amazon Rekognition Custom Labels 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | SPDX-License-Identifier: MIT-0 5 | 6 | ********************** 7 | THIRD PARTY COMPONENTS 8 | ********************** 9 | This software includes third party software subject to the following copyrights: 10 | 11 | AWS SDK under the Apache License Version 2.0 12 | AWS Amplify under the Apache License Version 2.0 13 | JQuery under the Massachusetts Institute of Technology (MIT) license 14 | Bootstrap under the Massachusetts Institute of Technology (MIT) license 15 | Popper under the Massachusetts Institute of Technology (MIT) license 16 | adm-zip under the Massachusetts Institute of Technology (MIT) license 17 | Jimp under the Massachusetts Institute of Technology (MIT) license 18 | Mime under the Massachusetts Institute of Technology (MIT) license 19 | Crypto-js under the Massachusetts Institute of Technology (MIT) license 20 | Fontawesome Fonts under SIL OFL 1.1 license 21 | Terser under BSD license 22 | Glob under ISC license 23 | -------------------------------------------------------------------------------- /clean-slate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # @function usage 5 | # 6 | function usage() { 7 | echo -e " 8 | ------------------------------------------------------------------------------ 9 | 10 | This script helps to clean up files and directories created by build script 11 | 12 | ------------------------------------------------------------------------------ 13 | 14 | bash ./clean-slate.sh [--path ] 15 | 16 | where 17 | --path [optional] specify the root directory to start cleaning. 18 | " 19 | return 0 20 | } 21 | 22 | ROOTDIR= 23 | while [[ $# -gt 0 ]]; do 24 | key="$1" 25 | case $key in 26 | -p|--path) 27 | ROOTDIR="$2" 28 | shift # past key 29 | shift # past value 30 | ;; 31 | *) 32 | shift 33 | ;; 34 | esac 35 | done 36 | 37 | [ -z "$ROOTDIR" ] && \ 38 | ROOTDIR="." 39 | 40 | function clean_deployment() { 41 | echo "------------------------------------------------------------------------------" 42 | echo "Cleaning deployment folder" 43 | echo "------------------------------------------------------------------------------" 44 | local startDir=$1 45 | for dir in "global-s3-assets" "regional-s3-assets" "open-source"; do 46 | find "${startDir}" -name "${dir}" -type d -exec rm -rfv "{}" \; 47 | done 48 | } 49 | 50 | function clean_source() { 51 | echo "------------------------------------------------------------------------------" 52 | echo "Cleaning source folder" 53 | echo "------------------------------------------------------------------------------" 54 | local startDir=$1 55 | for dir in "node_modules" "dist"; do 56 | find "${startDir}" -name "${dir}" -type d -exec rm -rfv "{}" \; 57 | done 58 | 59 | for file in "package-lock.json" ".DS_Store"; do 60 | find "${startDir}" -name "${file}" -type f -exec rm -rfv "{}" \; 61 | done 62 | } 63 | 64 | usage 65 | clean_deployment "${ROOTDIR}" 66 | clean_source "${ROOTDIR}" 67 | -------------------------------------------------------------------------------- /deployment/build-open-source-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################## 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: MIT-0 6 | ######################################################################################## 7 | source ./common.sh 8 | 9 | ###################################################################### 10 | # 11 | # optional flags 12 | # 13 | while [[ $# -gt 0 ]]; do 14 | key="$1" 15 | case $key in 16 | -s|--solution) 17 | SOLUTION="$2" 18 | shift # past key 19 | shift # past value 20 | ;; 21 | *) 22 | shift 23 | ;; 24 | esac 25 | done 26 | 27 | [ -z "$SOLUTION" ] && \ 28 | SOLUTION="custom-ppe-detection" 29 | 30 | echo "------------------------------------------------------------------------------" 31 | echo "Building open-source Folder" 32 | echo "------------------------------------------------------------------------------" 33 | 34 | # Setting up diretory 35 | DEPLOYMENT_DIR="$PWD" 36 | OPENSRC_DIR="$DEPLOYMENT_DIR/open-source" 37 | OPENSRC_DIST_DIR="$OPENSRC_DIR/dist" 38 | SRC_DIR="$DEPLOYMENT_DIR/../source/" 39 | 40 | # 41 | # @function usage 42 | # 43 | function usage() { 44 | echo -e " 45 | ------------------------------------------------------------------------------ 46 | 47 | This script helps you to build open source code package 48 | It should be run from the repo's deployment directory 49 | 50 | ------------------------------------------------------------------------------ 51 | cd deployment 52 | bash ./build-open-source-dist.sh [--solution SOLUTION] 53 | 54 | where 55 | --solution SOLUTION [optional] if not specified, use 'solution name' from package.json 56 | " 57 | return 0 58 | } 59 | 60 | # 61 | # @function clear_start 62 | # 63 | function clear_start() { 64 | runcmd rm -rf "$OPENSRC_DIR" 65 | runcmd mkdir -p "$OPENSRC_DIST_DIR/deployment" 66 | # in case build system is macosx, delete any .DS_Store file 67 | find "$DEPLOYMENT_DIR" -name '.DS_Store' -type f -delete 68 | find "$SRC_DIR" -name '.DS_Store' -type f -delete 69 | } 70 | 71 | # 72 | # @function copy_deployment_folder 73 | # 74 | function copy_deployment_folder() { 75 | echo "------------------------------------------------------------------------------" 76 | echo "Copying Deployment Folder" 77 | echo "------------------------------------------------------------------------------" 78 | local files=(\ 79 | "build-s3-dist.sh" \ 80 | "build-open-source-dist.sh" \ 81 | "common.sh" \ 82 | "deploy-s3-dist.sh" \ 83 | ) 84 | cp -rv "$DEPLOYMENT_DIR"/*.yaml "$OPENSRC_DIST_DIR/deployment/" 85 | for file in "${files[@]}"; do 86 | runcmd cp -r "$DEPLOYMENT_DIR/$file" "$OPENSRC_DIST_DIR/deployment/" 87 | done 88 | # copy tutorials folder 89 | cp -rv "$DEPLOYMENT_DIR"/images "$OPENSRC_DIST_DIR"/deployment/images 90 | cp -rv "$DEPLOYMENT_DIR"/samples "$OPENSRC_DIST_DIR"/deployment/samples 91 | # copy .github PULL_REQUEST_TEMPLATE file 92 | cp -rv "$DEPLOYMENT_DIR"/../.github "$OPENSRC_DIST_DIR/" 93 | } 94 | 95 | function copy_standard_documents() { 96 | echo "------------------------------------------------------------------------------" 97 | echo "Copying Legal Related Documents" 98 | echo "------------------------------------------------------------------------------" 99 | local files=(\ 100 | "CHANGELOG.md" \ 101 | "CODE_OF_CONDUCT.md" \ 102 | "CONTRIBUTING.md" \ 103 | "LICENSE.txt" \ 104 | "NOTICE.txt" \ 105 | "README.md" \ 106 | ) 107 | pushd "$DEPLOYMENT_DIR"/.. 108 | pwd 109 | for file in "${files[@]}"; do 110 | runcmd cp -r "$file" "$OPENSRC_DIST_DIR"/ 111 | done 112 | popd 113 | } 114 | 115 | function copy_source_folder() { 116 | echo "------------------------------------------------------------------------------" 117 | echo "Copying Source Folder" 118 | echo "------------------------------------------------------------------------------" 119 | runcmd cp -r "$SRC_DIR" "$OPENSRC_DIST_DIR/source/" 120 | 121 | # clean up dist 122 | pushd "$OPENSRC_DIST_DIR/source" 123 | find . -name "dist" -type d -exec rm -rf "{}" \; 2> /dev/null 124 | find . -name "node_modules" -type d -exec rm -rf "{}" \; 2> /dev/null 125 | find . -name "package-lock.json" -type f -delete 126 | rm -v ./webapp/solution-manifest.js 127 | popd 128 | } 129 | 130 | function create_github_zip() { 131 | echo "------------------------------------------------------------------------------" 132 | echo "Create GitHub zip File" 133 | echo "------------------------------------------------------------------------------" 134 | cd "$OPENSRC_DIST_DIR" || exit 135 | # zip -q -r9 ../${SOLUTION}.zip * .github .gitignore .tool-versions 136 | zip -q -r9 ../${SOLUTION}.zip * .github 137 | cd "$DEPLOYMENT_DIR" || exit 138 | rm -rf "$OPENSRC_DIST_DIR" 139 | } 140 | 141 | function on_complete() { 142 | echo "------------------------------------------------------------------------------" 143 | echo "Open Source Packaging Complete" 144 | echo "------------------------------------------------------------------------------" 145 | } 146 | 147 | clear_start 148 | copy_deployment_folder 149 | copy_standard_documents 150 | copy_source_folder 151 | create_github_zip 152 | on_complete 153 | -------------------------------------------------------------------------------- /deployment/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################## 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: MIT-0 6 | ######################################################################################## 7 | 8 | # 9 | # @function runcmd 10 | # @description print command before run 11 | # 12 | function runcmd() { 13 | echo "> ${@}" 14 | "${@}" || exit 1 15 | } 16 | 17 | # 18 | # @function grep_package_version 19 | # @description grep package version from package.json version field 20 | # 21 | function grep_package_version() { 22 | local list=($(grep "version" "$1")) 23 | local tmp=${list[1]%\"*} 24 | local version=v${tmp#\"} 25 | echo $version 26 | } 27 | 28 | # 29 | # @function grep_package_name 30 | # @description grep package name from package.json name field 31 | # 32 | function grep_package_name() { 33 | local list=($(grep "name" "$1")) 34 | local tmp=${list[1]%\"*} 35 | local package=${tmp#\"} 36 | echo $package 37 | } 38 | 39 | # 40 | # @function grep_zip_name 41 | # @description grep package zip name from package.json name field 42 | # 43 | function grep_zip_name() { 44 | local package=$(grep_package_name "$1") 45 | local version=$(grep_package_version "$1") 46 | echo "${package}_${version}.zip" 47 | } 48 | 49 | # 50 | # @function grep_solution_name 51 | # @description grep solution name from solution.js 52 | # 53 | function grep_solution_name() { 54 | local str=$(grep "Name:" "$1") 55 | [[ "$str" =~ \s*Name:.+\'(.+)\', ]] 56 | echo ${BASH_REMATCH[1]} 57 | } 58 | 59 | # 60 | # @function grep_solution_version 61 | # @description grep solution version from solution.js 62 | # 63 | function grep_solution_version() { 64 | local str=$(grep "Version:" "$1") 65 | [[ "$str" =~ \s*Version:.+\'(.+)\', ]] 66 | echo "v${BASH_REMATCH[1]}" 67 | } 68 | 69 | # 70 | # REGIONS := these are the regions that supports custom-ppe-detection demo solution. 71 | # 72 | REGIONS=( \ 73 | us-east-1 \ 74 | us-east-2 \ 75 | us-west-2 \ 76 | eu-west-1 \ 77 | ) 78 | 79 | # 80 | # BUCKET := bucket base name. The 'actual' bucket name will be concatenated 81 | # with region. The bucket stores cloudformation template and package. 82 | # For example, if BUCKET is 'solutions' and is deployed in eu-west-1 83 | # Then, the actual deployment bucket will be 'solutions-eu-west-1'. 84 | # (Mandatory) 85 | BUCKET= 86 | -------------------------------------------------------------------------------- /deployment/deploy-s3-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ######################################################################################## 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: MIT-0 6 | ######################################################################################## 7 | 8 | # include shared configuration file 9 | source ./common.sh 10 | 11 | NODEJS_VERSION=$(node --version) 12 | DEPLOY_DIR="$PWD" 13 | SOURCE_DIR="$DEPLOY_DIR/../source" 14 | TEMPLATE_DIST_DIR="global-s3-assets" 15 | BUID_DIST_DIR="regional-s3-assets" 16 | 17 | # 18 | # @function usage 19 | # 20 | function usage() { 21 | echo -e " 22 | ------------------------------------------------------------------------------ 23 | 24 | This script helps you to deploy CloudFormation templates to the bucket(s). 25 | It should be run from the repo's deployment directory 26 | 27 | ------------------------------------------------------------------------------ 28 | cd deployment 29 | bash ./deploy-s3-dist.sh --bucket BUCKET_NAME [--acl ACL_SETTING] [--profile AWS_PROFILE] [--region AWS_REGION] 30 | 31 | where 32 | --bucket BUCKET_NAME specify the bucket name where the templates and packages deployed to. 33 | 34 | --acl ACL_SETTING [optional] if not specified, it deploys with 'bucket-owner-full-control' access 35 | control setting. You could specify 'public-read' if you plan to share the solution 36 | with other AWS accounts. Note that it requires your bucket to be configured to permit 37 | 'public-read' acl settings 38 | 39 | --profile AWS_PROFILE [optional] specify the AWS CLI profile. If not specified, it assumes 'default' 40 | 41 | --region AWS_REGION [optional] specify AWS_REGION. If not specified, it assumes 'us-east-1' 42 | " 43 | return 0 44 | } 45 | 46 | ###################################################################### 47 | # 48 | # optional flags 49 | # 50 | while [[ $# -gt 0 ]]; do 51 | key="$1" 52 | case $key in 53 | -b|--bucket) 54 | BUCKET="$2" 55 | shift # past argument 56 | shift # past value 57 | ;; 58 | -s|--solution) 59 | SOLUTION="$2" 60 | shift # past key 61 | shift # past value 62 | ;; 63 | -v|--version) 64 | VERSION="$2" 65 | shift # past argument 66 | shift # past value 67 | ;; 68 | --single-region) 69 | SINGLE_REGION=true 70 | shift # past argument 71 | ;; 72 | -a|--acl) 73 | ACL_SETTING="$2" 74 | shift # past argument 75 | shift # past value 76 | ;; 77 | -p|--profile) 78 | PROFILE="$2" 79 | shift # past argument 80 | shift # past value 81 | ;; 82 | -r|--region) 83 | REGION="$2" 84 | shift # past argument 85 | shift # past value 86 | ;; 87 | *) 88 | shift 89 | ;; 90 | esac 91 | done 92 | 93 | [ -z "$BUCKET" ] && \ 94 | echo "error: missing --bucket parameter..." && \ 95 | usage && \ 96 | exit 1 97 | 98 | [ -z "$VERSION" ] && \ 99 | VERSION=$(cat "$SOURCE_DIR/.version") 100 | 101 | [ -z "$VERSION" ] && \ 102 | echo "error: can't find the versioning, please use --version parameter..." && \ 103 | usage && \ 104 | exit 1 105 | 106 | [ -z "$SOLUTION" ] && \ 107 | SOLUTION="custom-ppe-detection" 108 | 109 | [ -z "$SINGLE_REGION" ] && \ 110 | SINGLE_REGION=true 111 | 112 | [ -z "$ACL_SETTING" ] && \ 113 | ACL_SETTING="bucket-owner-full-control" 114 | 115 | [ -z "$PROFILE" ] && \ 116 | PROFILE="default" 117 | 118 | [ -z "$REGION" ] && \ 119 | REGION="us-east-1" 120 | 121 | # 122 | # @function copy_to_bucket 123 | # @description copy solution to regional bucket 124 | # 125 | function copy_to_bucket() { 126 | local source=$1 127 | local bucket=$2 128 | 129 | aws s3api get-bucket-location \ 130 | --bucket ${bucket} \ 131 | --profile ${PROFILE} > /dev/null 2>&1 132 | 133 | local status=$? 134 | [ $status -ne 0 ] && \ 135 | echo "bucket '${bucket}' not exists. skipping..." && \ 136 | return 0 137 | 138 | echo "uploading package to '${bucket}' in '${REGION}' (${ACL_SETTING}) [${PROFILE}]..." 139 | aws s3 cp $source s3://${bucket}/${SOLUTION}/${VERSION}/ \ 140 | --recursive \ 141 | --acl ${ACL_SETTING} \ 142 | --profile ${PROFILE} \ 143 | --region ${REGION} 144 | } 145 | 146 | if [ "$SINGLE_REGION" == "true" ]; then 147 | # deploy to a single region 148 | echo "** '${SOLUTION} ($VERSION)' package will be deployed to '${BUCKET}' bucket in ${REGION} region **" 149 | copy_to_bucket ${BUID_DIST_DIR} "${BUCKET}" 150 | else 151 | echo "'${SOLUTION} ($VERSION)' package will be deployed to '${BUCKET}-[region]' buckets: ${REGIONS[*]} regions" 152 | # special case, deploy to main bucket (without region suffix) 153 | copy_to_bucket ${BUID_DIST_DIR} "${BUCKET}" "us-east-1" 154 | 155 | # now, deploy to regional based buckets 156 | for region in ${REGIONS[@]}; do 157 | copy_to_bucket ${BUID_DIST_DIR} "${BUCKET}-${region}" "${region}" 158 | done 159 | fi 160 | -------------------------------------------------------------------------------- /deployment/images/analysis-result-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/analysis-result-01.jpg -------------------------------------------------------------------------------- /deployment/images/analysis-result-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/analysis-result-02.jpg -------------------------------------------------------------------------------- /deployment/images/analysis-result-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/analysis-result-03.jpg -------------------------------------------------------------------------------- /deployment/images/analysis.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/analysis.gif -------------------------------------------------------------------------------- /deployment/images/cfn-01-create-stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/cfn-01-create-stack.jpg -------------------------------------------------------------------------------- /deployment/images/cfn-02-specify-details.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/cfn-02-specify-details.jpg -------------------------------------------------------------------------------- /deployment/images/cfn-03-review-stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/cfn-03-review-stack.jpg -------------------------------------------------------------------------------- /deployment/images/custom-ppe-detection-analysis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/custom-ppe-detection-analysis.jpg -------------------------------------------------------------------------------- /deployment/images/custom-ppe-detection-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/custom-ppe-detection-architecture.jpg -------------------------------------------------------------------------------- /deployment/images/custom-ppe-detection-training.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/custom-ppe-detection-training.jpg -------------------------------------------------------------------------------- /deployment/images/invitation-email.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/invitation-email.jpg -------------------------------------------------------------------------------- /deployment/images/training-option-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/training-option-1.gif -------------------------------------------------------------------------------- /deployment/images/training-option-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/amazon-rekognition-custom-ppe-detection-with-custom-labels/f779de549c9cf31081e24eb7fdf1f00286e456b0/deployment/images/training-option-2.gif -------------------------------------------------------------------------------- /deployment/samples/sample-input.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ParameterKey": "Email", 4 | "ParameterValue": "yourname@email.com" 5 | }, 6 | { 7 | "ParameterKey": "PriceClass", 8 | "ParameterValue": "Use Only U.S., Canada and Europe [PriceClass_100]" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /deployment/samples/training-dataset.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OUTDIR="dataset" 4 | APIKEY_PEXELS= 5 | APIKEY_UNSPLASH= 6 | 7 | function usage() { 8 | echo -e " 9 | ------------------------------------------------------------------------------ 10 | 11 | This script requires 'jq' command. Please make sure you have 'jq' command 12 | installed. You can download it from https://stedolan.github.io/jq/download/ 13 | 14 | It also requires a Pexels API Key and a Unsplash API Key as the dataset combines 15 | images from both pexels.com and unsplash.com. 16 | 17 | Create an account on https://www.pexels.com/api/ and copy and paste the 18 | API Key to PEXELS_APIKEY 19 | 20 | Create an account on https://unsplash.com/documentation and copy and paste 21 | the Access Key to UNSPLASH_APIKEY. 22 | 23 | The combined dataset will be downloaded to $OUTDIR 24 | 25 | ------------------------------------------------------------------------------ 26 | bash ./training-dataset.sh --pexels PEXELS_APIKEY --unsplash UNSPLASH_APIKEY 27 | 28 | where 29 | --pexels PEXELS_APIKEY specify Pexels API Key 30 | --unsplash UNSPLASH_APIKEY specify Unsplash Access Key 31 | " 32 | return 0 33 | } 34 | 35 | function download_pexels() { 36 | # Pexels Image IDs for training dataset 37 | local ids=(\ 38 | 1108101 \ 39 | 1216589 \ 40 | 159306 \ 41 | 159375 \ 42 | 209719 \ 43 | 2760243 \ 44 | 2965258 \ 45 | 3680957 \ 46 | 3680958 \ 47 | 3680959 \ 48 | 3681787 \ 49 | 3772616 \ 50 | 4481258 \ 51 | 4481262 \ 52 | 544966 \ 53 | 901941 \ 54 | ) 55 | local endpoint="https://api.pexels.com/v1/photos" 56 | for id in "${ids[@]}"; do 57 | url=$(curl --silent -H "Authorization: $APIKEY_PEXELS" "$endpoint/$id" | jq ".src.large" | tr -d '"') 58 | echo "Downloading pexels-${id}.jpg" 59 | (cd $OUTDIR; curl --silent "$url" --output "pexels-${id}.jpg") 60 | done; 61 | 62 | } 63 | 64 | function download_unsplash() { 65 | # Unsplash Image IDs for training dataset 66 | local ids=(\ 67 | -Ktik1A__KY \ 68 | 04rZ7R1fKhY \ 69 | 2DK-CP_WAuw \ 70 | 4zwozQxDbD4 \ 71 | CUTeHQGDaJ0 \ 72 | Esi7nknKxmw \ 73 | HbAEDtKQmmY \ 74 | K3bHI_saaMw \ 75 | MTMsK4cEF3M \ 76 | Pj4je7OjrME \ 77 | QCdRhVj7N8w \ 78 | Tlyteh1470o \ 79 | TtX79Vkm8gs \ 80 | UmN4sJZ7NJg \ 81 | VLPUm5wP5Z0 \ 82 | WJg2bynUWOk \ 83 | aBV8pVODWiM \ 84 | bKGpAV4gFnc \ 85 | fYD54gVXFGM \ 86 | jCKC5W8s-cc \ 87 | jYNvXKTUYvs \ 88 | qvBYnMuNJ9A \ 89 | s9XDWLJ_LyE \ 90 | sYK-jN0sKBY \ 91 | sgYamIzhAhg \ 92 | sp8EfimTH0g \ 93 | tUGLbI0fOfw \ 94 | w7nR9j326Hw \ 95 | x-ghf9LjrVg \ 96 | y7243Bohj8I \ 97 | zZza888FSKg \ 98 | zmHxLsWtfBU \ 99 | ) 100 | local endpoint="https://api.unsplash.com/photos" 101 | for id in "${ids[@]}"; do 102 | url=$(curl --silent -H "Authorization: Client-ID $APIKEY_UNSPLASH" "$endpoint/$id/download" | jq ".url" | tr -d '"') 103 | echo "Downloading unsplash-${id}.jpg" 104 | (cd $OUTDIR; curl --silent "$url?&w=1920" --output "unsplash-${id}.jpg") 105 | done; 106 | } 107 | 108 | # 109 | # Main Program 110 | # 111 | while [[ $# -gt 0 ]]; do 112 | key="$1" 113 | case $key in 114 | -p|--pexels) 115 | APIKEY_PEXELS="$2" 116 | shift # past key 117 | shift # past value 118 | ;; 119 | -u|--unsplash) 120 | APIKEY_UNSPLASH="$2" 121 | shift # past key 122 | shift # past value 123 | ;; 124 | *) 125 | shift 126 | ;; 127 | esac 128 | done 129 | 130 | [ -z "$(which jq)" ] && \ 131 | echo "error: jq is not installed" && \ 132 | usage && \ 133 | exit 1 134 | 135 | [ -z "$APIKEY_PEXELS" ] && \ 136 | echo "error: missing --pexels value" && \ 137 | usage && \ 138 | exit 1 139 | 140 | [ -z "$APIKEY_UNSPLASH" ] && \ 141 | echo "error: missing --unsplash value" && \ 142 | usage && \ 143 | exit 1 144 | 145 | mkdir -p $OUTDIR 146 | download_pexels 147 | download_unsplash 148 | -------------------------------------------------------------------------------- /source/.version: -------------------------------------------------------------------------------- 1 | 1.0.0 2 | -------------------------------------------------------------------------------- /source/api/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "strict": "error", 5 | "func-names": ["error", "never"], 6 | "comma-dangle": ["error", { 7 | "arrays": "always-multiline", 8 | "objects": "always-multiline", 9 | "imports": "always-multiline", 10 | "exports": "always-multiline", 11 | "functions": "never" 12 | }], 13 | "no-use-before-define": ["error", { 14 | "functions": false, 15 | "classes": true, 16 | "variables": true 17 | }], 18 | "prefer-arrow-callback": 0, 19 | "prefer-object-spread": 0, 20 | "implicit-arrow-linebreak": 0, 21 | "arrow-parens": 0, 22 | "operator-linebreak": 0, 23 | "class-methods-use-this": 0, 24 | "import/no-extraneous-dependencies": 0, 25 | "no-nested-ternary": 0, 26 | "import/no-unresolved": 0, 27 | "no-await-in-loop": 0, 28 | "max-classes-per-file": 0, 29 | "prefer-destructuring": 0, 30 | "no-plusplus": 0, 31 | "one-var": 0, 32 | "one-var-declaration-per-line": 0, 33 | "no-console": 0 34 | }, 35 | "env": { 36 | "es6": true, 37 | "browser": false, 38 | "node": true, 39 | "jquery": false, 40 | "mocha": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/api/index.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const { 4 | ApiRequest, 5 | } = require('./lib'); 6 | 7 | exports.handler = async (event, context) => { 8 | console.log(` 9 | event = ${JSON.stringify(event, null, 2)} 10 | context = ${JSON.stringify(context, null, 2)}`); 11 | 12 | const request = new ApiRequest(event, context); 13 | if (request.method === ApiRequest.Methods.OPTIONS) { 14 | return request.onOPTIONS().catch(e => 15 | request.onError(e)); 16 | } 17 | if (request.method === ApiRequest.Methods.GET) { 18 | return request.onGET().catch(e => 19 | request.onError(e)); 20 | } 21 | if (request.method === ApiRequest.Methods.POST) { 22 | return request.onPOST().catch(e => 23 | request.onError(e)); 24 | } 25 | throw new Error(`${request.method} not supported`); 26 | }; 27 | -------------------------------------------------------------------------------- /source/api/lib/mxValidation.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const mxValidation = Base => class extends Base { 4 | testBucket(val = '') { 5 | return !( 6 | (val.length < 3 || val.length > 63) 7 | || /[^a-z0-9-.]/.test(val) 8 | || /^[^a-z0-9]/.test(val) 9 | || /\.{2,}/.test(val) 10 | || /^\d+.\d+.\d+.\d+$/.test(val) 11 | ); 12 | } 13 | 14 | testUuid(val = '') { 15 | return /^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/.test(val); 16 | } 17 | 18 | testCognitoIdentityId(val = '') { 19 | return /^[a-z]{2,}-[a-z]{2,}-[0-9]{1}:[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$/.test(val); 20 | } 21 | 22 | testBase64JsonToken(val = '') { 23 | /* base64 token must be a JSON object */ 24 | try { 25 | JSON.parse(Buffer.from(val, 'base64').toString()); 26 | return true; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | testImageBlob(val = '') { 33 | return /^data:image\/(png|jpeg|jpg);base64,.{20,}/.test(val); 34 | } 35 | 36 | testS3Uri(val = '') { 37 | const { 38 | protocol, 39 | hostname: bkt, 40 | } = URL.parse(val); 41 | if (!bkt || !protocol || protocol.toLowerCase() !== 's3:') { 42 | return false; 43 | } 44 | return !( 45 | (bkt.length < 3 || bkt.length > 63) 46 | || /[^a-z0-9-.]/.test(bkt) 47 | || /^[^a-z0-9]/.test(bkt) 48 | || /\.{2,}/.test(bkt) 49 | || /^\d+.\d+.\d+.\d+$/.test(bkt) 50 | ); 51 | } 52 | 53 | testEmailAddress(val = '') { 54 | return /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(val); 55 | } 56 | 57 | testOperation(val = '') { 58 | return /^[a-zA-Z-]+$/.test(val); 59 | } 60 | 61 | testProjectArn(val = '') { 62 | return /^arn:aws:rekognition:[a-z\d-]+:\d{12}:project\/[a-zA-Z\d_-]+\/[a-zA-Z\d_-]+$/.test(val); 63 | } 64 | 65 | testProjectVersion(val = '') { 66 | return /^[a-zA-Z0-9_.-]+$/.test(val); 67 | } 68 | 69 | testProjectVersionArn(val = '') { 70 | return /^arn:aws:rekognition:[a-z\d-]+:\d{12}:project\/[a-zA-Z\d_-]+\/version\/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(val); 71 | } 72 | }; 73 | 74 | module.exports = { 75 | mxValidation, 76 | }; 77 | -------------------------------------------------------------------------------- /source/api/lib/s3utils.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const AWS = require('aws-sdk'); 4 | 5 | class S3Utils { 6 | static get Constants() { 7 | return { 8 | Expiration: 60 * 60 * 2, 9 | }; 10 | } 11 | 12 | static getInstance(params) { 13 | return new AWS.S3({ 14 | apiVersion: '2006-03-01', 15 | computeChecksums: true, 16 | signatureVersion: 'v4', 17 | s3DisableBodySigning: false, 18 | ...params, 19 | }); 20 | } 21 | 22 | static signUrl(bucket, key) { 23 | return S3Utils.getInstance().getSignedUrl('getObject', { 24 | Bucket: bucket, 25 | Key: key, 26 | Expires: S3Utils.Constants.Expiration, 27 | }); 28 | } 29 | 30 | static async getObject(bucket, key) { 31 | return S3Utils.getInstance().getObject({ 32 | Bucket: bucket, 33 | Key: key, 34 | }).promise(); 35 | } 36 | 37 | static async upload(bucket, key, body, options) { 38 | return S3Utils.getInstance().upload({ 39 | Bucket: bucket, 40 | Key: key, 41 | Body: body, 42 | ...options, 43 | }).promise(); 44 | } 45 | 46 | static async listObjects(bucket, prefix) { 47 | const collection = []; 48 | const s3 = S3Utils.getInstance(); 49 | let response; 50 | do { 51 | response = await s3.listObjectsV2({ 52 | Bucket: bucket, 53 | Prefix: prefix, 54 | MaxKeys: 100, 55 | ContinuationToken: (response || {}).NextContinuationToken, 56 | }).promise(); 57 | collection.splice(collection.length, 0, ...response.Contents); 58 | } while ((response || {}).NextContinuationToken); 59 | return collection; 60 | } 61 | 62 | static async selectS3Content(bucket, key, query) { 63 | return new Promise((resolve, reject) => { 64 | /* escape single quote character */ 65 | const escaped = query.replace(/'/g, '\'\''); 66 | const s3 = S3Utils.getInstance(); 67 | s3.selectObjectContent({ 68 | Bucket: bucket, 69 | Key: key, 70 | ExpressionType: 'SQL', 71 | Expression: escaped, 72 | InputSerialization: { 73 | JSON: { 74 | Type: 'DOCUMENT', 75 | }, 76 | }, 77 | OutputSerialization: { 78 | JSON: { 79 | RecordDelimiter: ';', 80 | }, 81 | }, 82 | }, (e, response) => { 83 | if (e) { 84 | reject(e); 85 | return; 86 | } 87 | 88 | const stream = response.Payload; 89 | let payload = ''; 90 | stream.on('error', e0 => 91 | reject(e0)); 92 | stream.on('end', () => 93 | resolve(payload.split(';').filter(x => x).map(x => JSON.parse(x)))); 94 | stream.on('data', (evt) => { 95 | if (evt.Records) { 96 | payload += evt.Records.Payload.toString(); 97 | } 98 | }); 99 | }); 100 | }); 101 | } 102 | } 103 | 104 | module.exports = { 105 | S3Utils, 106 | }; 107 | -------------------------------------------------------------------------------- /source/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-ppe-detection-api", 3 | "version": "1.0.0", 4 | "description": "(custom ppe detection demo) backend lambda for API Gateway", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": {}, 8 | "scripts": { 9 | "pretest": "npm install", 10 | "test": "mocha *.spec.js", 11 | "build:clean": "rm -rf dist && mkdir -p dist", 12 | "build:copy": "cp -rv index.js package.json lib dist/", 13 | "build:install": "cd dist && npm install --production", 14 | "build": "npm-run-all -s build:clean build:copy build:install", 15 | "zip": "cd dist && zip -rq" 16 | }, 17 | "author": "aws-specialist-sa-emea", 18 | "license": "MIT-0", 19 | "devDependencies": { 20 | "image-utils": "file:../layers/image-utils" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "build-sri", 3 | "version": "1.0.0", 4 | "description": "post build to inject SRI integrity to index.html", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "glob": "^7.1.6", 9 | "terser": "^4.7.0" 10 | }, 11 | "scripts": {}, 12 | "author": "aws-specialist-sa-emea", 13 | "license": "MIT-0", 14 | "devDependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /source/build/post-build.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | const FS = require('fs'); 4 | const PATH = require('path'); 5 | const CRYPTO = require('crypto'); 6 | const GLOB = require('glob'); 7 | const Terser = require('terser'); 8 | 9 | const COMMAND = { 10 | MINIFY: 'minify', 11 | INJECTSRI: 'inject-sri', 12 | }; 13 | 14 | function usage(message) { 15 | if (message) { 16 | console.error(`ERROR: ${message}`); 17 | } 18 | const script = PATH.parse(process.argv[1]).base; 19 | console.log(` 20 | Usage: 21 | node ${script} 22 | 23 | where: 24 | specify ${Object.values(COMMAND).join(', ')} 25 | 26 | ${COMMAND.MINIFY} 27 | --dir [mandatory] the top-level app name; ie., src/lib/js/ 28 | 29 | ${COMMAND.INJECTSRI} 30 | --html [mandatory] inject SRI code 31 | 32 | `); 33 | process.exit(1); 34 | } 35 | 36 | function parseCmdline() { 37 | const options = {}; 38 | const args = process.argv.slice(2); 39 | const command = args.shift(); 40 | while (args.length) { 41 | options[args.shift().slice(2)] = args.shift(); 42 | } 43 | if (command === COMMAND.MINIFY) { 44 | if (!options.dir) { 45 | return usage('\'--dir\' must be specified'); 46 | } 47 | } else if (command === COMMAND.INJECTSRI) { 48 | if (!options.html) { 49 | return usage('\'--html\' must be specified'); 50 | } 51 | } else { 52 | return usage(`command '${command}' not supported`); 53 | } 54 | options.command = command; 55 | return options; 56 | } 57 | 58 | async function injectSRICommand(options) { 59 | const original = PATH.resolve(options.html); 60 | const buffer = createBackupCopy(original); 61 | const output = []; 62 | const rootDir = PATH.parse(original).dir; 63 | const lines = buffer.toString().split('\n'); 64 | while (lines.length) { 65 | const line = lines.shift(); 66 | if (line.indexOf('%SRI%') < 0) { 67 | output.push(line); 68 | continue; 69 | } 70 | if (line.indexOf('= 0) { 71 | output.push(insertSRI(line, rootDir, /src="([^"]+)"/)); 72 | } else if (line.indexOf('= 0) { 73 | output.push(insertSRI(line, rootDir, /href="([^"]+)"/)); 74 | } else { 75 | throw new Error('only support 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /source/webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "custom-ppe-detection-webapp", 3 | "version": "1.0.0", 4 | "description": "web application of custom ppe detection demo", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "pretest": "npm install", 9 | "test": "echo \"no test\"", 10 | "build:clean": "rm -rf dist && mkdir -p dist/third_party", 11 | "build:copy": "cp -rv css images src favicon.ico index.html ./dist && cp -r third_party/*.js ./dist/third_party", 12 | "build": "npm-run-all -s build:clean build:copy", 13 | "zip": "cd dist && zip -rq" 14 | }, 15 | "author": "aws-specialist-sa-emea", 16 | "license": "MIT-0", 17 | "dependencies": {}, 18 | "devDependencies": {} 19 | } 20 | -------------------------------------------------------------------------------- /source/webapp/src/lib/js/app.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import AppUtils from './shared/appUtils.js'; 4 | import LocalCache from './shared/localCache.js'; 5 | import MainView from './mainView/mainView.js'; 6 | import SignInFlow from './signIn/signInFlow.js'; 7 | 8 | export default class DemoApp { 9 | constructor() { 10 | this.$ids = { 11 | container: `app-${AppUtils.randomHexstring()}`, 12 | }; 13 | const view = $('
').attr('id', this.ids.container); 14 | 15 | const mainView = new MainView(); 16 | mainView.appendTo(view); 17 | 18 | const signIn = new SignInFlow(); 19 | signIn.appendTo(view); 20 | 21 | signIn.view.on(SignInFlow.Events.View.Hidden, () => 22 | setTimeout(async () => 23 | mainView.show(), 10)); 24 | 25 | this.$signInFlow = signIn; 26 | this.$mainView = mainView; 27 | this.$view = view; 28 | } 29 | 30 | get ids() { 31 | return this.$ids; 32 | } 33 | 34 | get view() { 35 | return this.$view; 36 | } 37 | 38 | get mainView() { 39 | return this.$mainView; 40 | } 41 | 42 | get signInFlow() { 43 | return this.$signInFlow; 44 | } 45 | 46 | appendTo(parent) { 47 | parent.append(this.view); 48 | } 49 | 50 | async show() { 51 | this.hide(); 52 | await this.openIndexedDB(); 53 | return this.signInFlow.show(); 54 | } 55 | 56 | async hide() { 57 | return this.closeIndexedDB().catch(() => undefined); 58 | } 59 | 60 | async openIndexedDB() { 61 | return Promise.all([ 62 | LocalCache.getSingleton(), 63 | ].map(x => x.open().catch(() => undefined))); 64 | } 65 | 66 | async closeIndexedDB() { 67 | return Promise.all([ 68 | LocalCache.getSingleton(), 69 | ].map(x => x.close().catch(() => undefined))); 70 | } 71 | } 72 | 73 | $(document).ready(async () => { 74 | const demoApp = new DemoApp(); 75 | demoApp.appendTo($('#demo-app')); 76 | await demoApp.show(); 77 | console.log('app loaded'); 78 | 79 | $(window).on('unload', async () => { 80 | console.log('app unloading...'); 81 | await demoApp.hide(); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /source/webapp/src/lib/js/mainView/mainView.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | import SolutionManifest from '/solution-manifest.js'; 4 | import Localization from '../shared/localization.js'; 5 | import AppUtils from '../shared/appUtils.js'; 6 | import CognitoConnector from '../signIn/cognitoConnector.js'; 7 | import ProjectVersion from '../shared/projectVersion.js'; 8 | import TrainingTab from './tabControllers/trainingTab.js'; 9 | import AnalysisTab from './tabControllers/analysisTab.js'; 10 | import ProjectTab from './tabControllers/projectTab.js'; 11 | 12 | export default class MainView { 13 | constructor() { 14 | this.$ids = { 15 | container: `main-${AppUtils.randomHexstring()}`, 16 | toastlist: `main-${AppUtils.randomHexstring()}`, 17 | tablist: `main-${AppUtils.randomHexstring()}`, 18 | tabcontent: `main-${AppUtils.randomHexstring()}`, 19 | }; 20 | this.$view = $('
').attr('id', this.ids.container); 21 | this.$cognito = CognitoConnector.getSingleton(); 22 | this.$projectVersion = ProjectVersion.getSingleton(); 23 | 24 | this.$trainingTab = new TrainingTab(true); 25 | this.$analysisTab = new AnalysisTab(); 26 | this.$projectTab = new ProjectTab(); 27 | } 28 | 29 | static get Constants() { 30 | return { 31 | Solution: { 32 | Url: 'https://aws.amazon.com/rekognition/custom-labels-features/', 33 | }, 34 | }; 35 | } 36 | 37 | get ids() { 38 | return this.$ids; 39 | } 40 | 41 | get view() { 42 | return this.$view; 43 | } 44 | 45 | get cognito() { 46 | return this.$cognito; 47 | } 48 | 49 | get projectVersion() { 50 | return this.$projectVersion; 51 | } 52 | 53 | get analysisTab() { 54 | return this.$analysisTab; 55 | } 56 | 57 | get trainingTab() { 58 | return this.$trainingTab; 59 | } 60 | 61 | get projectTab() { 62 | return this.$projectTab; 63 | } 64 | 65 | appendTo(parent) { 66 | return parent.append(this.view); 67 | } 68 | 69 | async show() { 70 | await this.hide(); 71 | await this.projectVersion.startTimer(); 72 | const navbar = $('