├── _config.yml ├── docs ├── images │ ├── db-link.png │ ├── flow-tf.png │ ├── history.png │ ├── add-remote.png │ ├── db-first.png │ ├── db-group.png │ ├── db-reorder.png │ ├── db-sidebar.png │ ├── first-flow.png │ ├── flow-final.png │ ├── flow-table.png │ ├── flow-tidy.png │ ├── http-flow.png │ ├── booth-clear.png │ ├── change-expr.png │ ├── history-list.png │ ├── jsonata-test.png │ ├── table-config.png │ ├── tf-passthru.png │ ├── webcam-allow.png │ ├── webcam-debug.png │ ├── webcam-edit.png │ ├── webcam-file.png │ ├── booth-capture.png │ ├── clear-json-type.png │ ├── db-group-edit.png │ ├── history-commit.png │ ├── jsonata-editor.png │ ├── jsonata-expand.png │ ├── palette-install.png │ ├── webcam-capture.png │ ├── booth-display-all.png │ ├── flow-table-debug.png │ ├── flow-tf-to-webcam.png │ ├── projects-welcome.png │ ├── booth-display-table.png │ ├── change-img-payload.png │ ├── flow-annotate-link.png │ ├── flow-capture-debug.png │ ├── flow-webcam-change-tf.png │ └── flow-table-change-annotate.png ├── css │ ├── pdf-print.css │ ├── toolkit-code.css │ └── extra.css ├── part4 │ └── README.md ├── part1 │ ├── README.md │ ├── commit-changes.md │ ├── installing-nodes.md │ ├── projects.md │ ├── install.md │ └── create-flow.md ├── part2 │ ├── README.md │ ├── adding-controls.md │ └── create-dashboard.md ├── resources.md ├── part3 │ ├── adding-tf.md │ ├── README.md │ ├── display-objects.md │ └── select-objects.md ├── index.md └── flow-final.md ├── theme └── assets │ └── images │ └── favicon.png ├── .github ├── actions │ ├── entrypoint.sh │ ├── action.yml │ └── Dockerfile └── workflows │ └── build.yml ├── .gitignore ├── README.md ├── mkdocs.yml ├── overrides ├── main.html ├── assets │ └── stylesheets │ │ ├── home.css │ │ └── images │ │ └── catalyst.svg ├── partials │ └── nav.html └── home.html └── LICENSE.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /docs/images/db-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-link.png -------------------------------------------------------------------------------- /docs/images/flow-tf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-tf.png -------------------------------------------------------------------------------- /docs/images/history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/history.png -------------------------------------------------------------------------------- /docs/images/add-remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/add-remote.png -------------------------------------------------------------------------------- /docs/images/db-first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-first.png -------------------------------------------------------------------------------- /docs/images/db-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-group.png -------------------------------------------------------------------------------- /docs/images/db-reorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-reorder.png -------------------------------------------------------------------------------- /docs/images/db-sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-sidebar.png -------------------------------------------------------------------------------- /docs/images/first-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/first-flow.png -------------------------------------------------------------------------------- /docs/images/flow-final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-final.png -------------------------------------------------------------------------------- /docs/images/flow-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-table.png -------------------------------------------------------------------------------- /docs/images/flow-tidy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-tidy.png -------------------------------------------------------------------------------- /docs/images/http-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/http-flow.png -------------------------------------------------------------------------------- /docs/images/booth-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/booth-clear.png -------------------------------------------------------------------------------- /docs/images/change-expr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/change-expr.png -------------------------------------------------------------------------------- /docs/images/history-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/history-list.png -------------------------------------------------------------------------------- /docs/images/jsonata-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/jsonata-test.png -------------------------------------------------------------------------------- /docs/images/table-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/table-config.png -------------------------------------------------------------------------------- /docs/images/tf-passthru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/tf-passthru.png -------------------------------------------------------------------------------- /docs/images/webcam-allow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/webcam-allow.png -------------------------------------------------------------------------------- /docs/images/webcam-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/webcam-debug.png -------------------------------------------------------------------------------- /docs/images/webcam-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/webcam-edit.png -------------------------------------------------------------------------------- /docs/images/webcam-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/webcam-file.png -------------------------------------------------------------------------------- /docs/images/booth-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/booth-capture.png -------------------------------------------------------------------------------- /docs/images/clear-json-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/clear-json-type.png -------------------------------------------------------------------------------- /docs/images/db-group-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/db-group-edit.png -------------------------------------------------------------------------------- /docs/images/history-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/history-commit.png -------------------------------------------------------------------------------- /docs/images/jsonata-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/jsonata-editor.png -------------------------------------------------------------------------------- /docs/images/jsonata-expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/jsonata-expand.png -------------------------------------------------------------------------------- /docs/images/palette-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/palette-install.png -------------------------------------------------------------------------------- /docs/images/webcam-capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/webcam-capture.png -------------------------------------------------------------------------------- /theme/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/theme/assets/images/favicon.png -------------------------------------------------------------------------------- /.github/actions/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ENABLE_PDF_EXPORT=1 mkdocs gh-deploy --config-file "${GITHUB_WORKSPACE}/mkdocs.yml" --force -------------------------------------------------------------------------------- /docs/images/booth-display-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/booth-display-all.png -------------------------------------------------------------------------------- /docs/images/flow-table-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-table-debug.png -------------------------------------------------------------------------------- /docs/images/flow-tf-to-webcam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-tf-to-webcam.png -------------------------------------------------------------------------------- /docs/images/projects-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/projects-welcome.png -------------------------------------------------------------------------------- /docs/images/booth-display-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/booth-display-table.png -------------------------------------------------------------------------------- /docs/images/change-img-payload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/change-img-payload.png -------------------------------------------------------------------------------- /docs/images/flow-annotate-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-annotate-link.png -------------------------------------------------------------------------------- /docs/images/flow-capture-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-capture-debug.png -------------------------------------------------------------------------------- /docs/images/flow-webcam-change-tf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-webcam-change-tf.png -------------------------------------------------------------------------------- /docs/images/flow-table-change-annotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/node-red-workshop-photobooth/main/docs/images/flow-table-change-annotate.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.der 2 | *.pem 3 | *.csr 4 | *der.h 5 | .DS_Store 6 | *.code-workspace 7 | .vscode 8 | private 9 | .markdownlint.json 10 | site/ 11 | site.html 12 | -------------------------------------------------------------------------------- /.github/actions/action.yml: -------------------------------------------------------------------------------- 1 | # action.yml 2 | name: 'Deploy to GitHub Pages' 3 | description: 'Publish Markdown docs as GitHub Pages static site' 4 | runs: 5 | using: 'docker' 6 | image: 'Dockerfile' -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: GenerateSite 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | jobs: 9 | generate: 10 | name: 'Run mkdocs gh-deploy' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out repository 14 | uses: actions/checkout@v2 15 | 16 | - name: generate site 17 | uses: ./.github/actions/ 18 | -------------------------------------------------------------------------------- /docs/css/pdf-print.css: -------------------------------------------------------------------------------- 1 | /* 2 | ** for PDF Printing 3 | */ 4 | 5 | 6 | /* fixed for `base.css` of mkdocs v1.0.4 */ 7 | 8 | code, 9 | pre code { 10 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace !important; 11 | } 12 | 13 | @media print { 14 | hr { 15 | display: none; 16 | } 17 | p { 18 | font-size: inherit; 19 | } 20 | ol, 21 | ul, 22 | li { 23 | break-inside: avoid; 24 | break-before: avoid; 25 | break-after: avoid; 26 | } 27 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node-RED AI Photo Booth Workshop 2 | 3 | This is the source repository for the Node-RED AI Photo Booth Workshop. 4 | 5 | To view the workshop, visit this site: [https://johnwalicki.github.io/node-red-workshop-photobooth/](https://johnwalicki.github.io/node-red-workshop-photobooth/). 6 | 7 | A PDF version is available [here](https://johnwalicki.github.io/node-red-workshop-photobooth/pdf/node-red-workshop-photobooth.pdf) 8 | 9 | ## Updating content 10 | 11 | The workshop material is hosted on GitHub pages and is automatically generated 12 | using a GitHub Action from the content in the docs folder of this repository. 13 | -------------------------------------------------------------------------------- /.github/actions/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:alpine 2 | 3 | RUN apk add --no-cache \ 4 | build-base \ 5 | git \ 6 | git-fast-import \ 7 | openssh \ 8 | gcc \ 9 | musl-dev \ 10 | jpeg-dev \ 11 | zlib-dev \ 12 | libffi-dev \ 13 | cairo-dev \ 14 | pango-dev \ 15 | gdk-pixbuf-dev \ 16 | ttf-ubuntu-font-family \ 17 | msttcorefonts-installer \ 18 | fontconfig && \ 19 | update-ms-fonts && \ 20 | fc-cache -f 21 | 22 | RUN pip install --no-cache-dir mkdocs mkdocs-with-pdf mkdocs-material 23 | 24 | COPY entrypoint.sh /entrypoint.sh 25 | RUN chmod +x /entrypoint.sh 26 | 27 | ENTRYPOINT ["/entrypoint.sh"] -------------------------------------------------------------------------------- /docs/part4/README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | That's all folks. If you've reached this point of the workshop, you've covered 4 | a lot of ground. Whether you were already familiar with Node-RED, TensorFlow, 5 | or if it was all new to you, we hope you've found it interesting. 6 | 7 | The goal for this workshop was to show how you can quickly start building 8 | applications using low-coding tools. While the application we've 9 | built today is a bit of fun, it shows how much you can achieve with very little code 10 | being written. 11 | 12 | ## Next Steps 13 | 14 | Where you go next is very much up to you. 15 | 16 | If you're new to Node-RED, you can explore the [Flow Library](https://flows.nodered.org){:target="blank"} 17 | to see what other nodes are available and what example flows the community have shared 18 | 19 | If you're new to TensorFlow, you can explore the other TensorFlow nodes we mentioned and some of the 20 | other prebuilt models that are available. You could explore building your own model and getting that 21 | integrated into your Node-RED application. 22 | 23 | Check out the [resources](/resources.md) section for more useful links. 24 | -------------------------------------------------------------------------------- /docs/css/toolkit-code.css: -------------------------------------------------------------------------------- 1 | .md-typeset pre>code { 2 | --md-hue: 232; 3 | --md-code-fg-color: #f5f5f5; 4 | --md-code-bg-color: #030303; 5 | 6 | 7 | } 8 | code>* { 9 | --md-default-fg-color--light: hsl(66, 41%, 69%); 10 | --md-code-hl-number-color: hsla(6, 74%, 63%, 1); 11 | --md-code-hl-special-color: hsla(340, 83%, 66%, 1); 12 | --md-code-hl-function-color: hsla(291, 57%, 65%, 1); 13 | --md-code-hl-constant-color: hsla(250, 62%, 70%, 1); 14 | --md-code-hl-keyword-color: hsla(219, 66%, 64%, 1); 15 | --md-code-hl-string-color: hsla(150, 58%, 44%, 1); 16 | --md-code-hl-name-color: var(--md-code-fg-color); 17 | --md-code-hl-operator-color: var(--md-default-fg-color--light); 18 | --md-code-hl-punctuation-color: var(--md-default-fg-color--light); 19 | --md-code-hl-comment-color: var(--md-default-fg-color--light); 20 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 21 | --md-code-hl-variable-color: var(--md-default-fg-color--light); 22 | } 23 | .md-clipboard { 24 | color: white; 25 | } 26 | .highlight.no-copy .md-clipboard { 27 | display: none; 28 | } 29 | -------------------------------------------------------------------------------- /docs/part1/README.md: -------------------------------------------------------------------------------- 1 | # Introducing Node-RED 2 | 3 | Node-RED is a programming tool for building event-driven application. It takes 4 | a low-code approach - which means rather than write lots of code, you build 5 | applications by using its visual editor to create flows of nodes that describe 6 | what should happen when particular events happen. 7 | 8 | For example, the `HTTP In` node can be configured to listen on a particular path. 9 | When an HTTP request arrives on that path the node is triggered. It generates 10 | a message containing information about the request and passes it on to the nodes 11 | it is wired to. They in turn do whatever work they need to do using the message. 12 | 13 | Such as generating HTML content and adding it to the message before being 14 | passed on through the flow. In this example, the flow ends with an `HTTP Response` 15 | node which responds to the original HTTP request using the information in the 16 | message. 17 | 18 | ## Next Steps 19 | 20 | In this part of the workshop you will: 21 | 22 | - [Install Node-RED](install.md) 23 | - [Enable the Projects feature](projects.md) 24 | - [Install extra nodes into the palette](installing-nodes.md) 25 | - [Create a simple flow](create-flow.md) 26 | - [Commit your changes](commit-changes.md) 27 | 28 | -------------------------------------------------------------------------------- /docs/part2/README.md: -------------------------------------------------------------------------------- 1 | # Introducing Node-RED Dashboard 2 | 3 | 4 | Node-RED Dashboard is a set of nodes you can install into Node-RED that make 5 | is easy to create a web page that can interact with your flows. 6 | 7 | It comes with nodes to add buttons, text inputs, charts and other widgets. 8 | 9 | It does not provide the full flexibility of creating a page from scratch, but it 10 | is a good choice for getting something created quickly and easily. 11 | 12 | !!! info "Other dashboard options" 13 | There are other sets of nodes available from the community that can be 14 | used to create web pages connected to Node-RED flows. One such example is 15 | [UIbuilder](https://flows.nodered.org/node/node-red-contrib-uibuilder) that 16 | does not provide the pre-build widgets of Node-RED Dashboard, but does allow 17 | you to build the page from your own HTML/JavaScript/CSS. 18 | 19 | We use Node-RED Dashboard in this workshop for the convenience it provides 20 | in building the web page with a minimum of any custom coding. 21 | 22 | ## Installing Node-RED Dashboard 23 | 24 | Using the Manage Palette feature in the editor, install the following modules: 25 | 26 | - `node-red-dashboard` - be sure to install this one *first*. 27 | - `node-red-node-ui-table` 28 | - `node-red-node-ui-webcam` 29 | 30 | ## Next Steps 31 | 32 | Having installed the dashboard nodes, in this part of the workshop you will: 33 | 34 | - [Create your initial Photo Booth Dashboard](create-dashboard.md) 35 | - [Add more controls to the dashboard](adding-controls.md) 36 | 37 | -------------------------------------------------------------------------------- /docs/part1/commit-changes.md: -------------------------------------------------------------------------------- 1 | # Committing Changes 2 | 3 | Before we move on with the workshop, the next thing to learn about is how to commit 4 | changes you've made in your project. 5 | 6 | !!! note 7 | If you skipped over the previous step to create a flow, you won't have 8 | any changes to commit. Add a node to your workspace and click the Deploy button 9 | so you have a change to commit. 10 | 11 | 12 | 13 | 1. Open the History sidebar tab. This is where you can see the changes to your 14 | project that are ready to be staged and committed to the git repository. 15 | 2. You should see your flow file listed in the Local Files section. Clicking on 16 | it will open a dialog showing the changes to the file since it was last 17 | committed. 18 | 19 | ![](../images/history.png){: style="width: 200px"} 20 | 21 | 3. Click the `+` that appears when you hovered over the file name. The entry 22 | will move to the 'Changes to commit' section. 23 | 4. Do the same for any other files in the Local Files section. 24 | 5. Click the `commit` button at the top of the 'Changes to commit' section. 25 | 6. Enter a commit message and click the Commit button. 26 | 27 | ![](../images/history-commit.png){: style="width: 200px"} 28 | 29 | 30 | 7. Expand the 'Commit History' section of the sidebar. This lists the history 31 | of commits for the project. At this point there should be two commits - the 32 | original commit from when the project was created, and the one you've just 33 | created. 34 | 35 | ![](../images/history-list.png){: style="width: 200px"} 36 | 37 | ## Next Steps 38 | 39 | Your Node-RED environment is all setup now for the rest of this workshop. 40 | 41 | In the next part, we'll start looking at [Node-RED Dashboard](../part2/). 42 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Node-RED AI Photo Booth Workshop 2 | site_description: >- 3 | A workshop for creating an AI-infused photo booth application using Node-RED and TensorFlow.js 4 | site_url: https://johnwalicki.github.io/node-red-workshop-photobooth/ 5 | site_author: John Walicki 6 | repo_name: johnwalicki/node-red-workshop-photobooth 7 | repo_url: https://github.com/johnwalicki/node-red-workshop-photobooth 8 | edit_uri: edit/main/docs/ 9 | theme: 10 | name: material 11 | palette: 12 | scheme: default 13 | primary: black 14 | font: 15 | text: IBM Plex Sans 16 | code: IBM Plex Mono 17 | language: en 18 | custom_dir: overrides 19 | features: 20 | - navigation.instant 21 | - navigation.tracking 22 | use_directory_urls: true 23 | plugins: 24 | - search 25 | - with-pdf: 26 | cover_subtitle: OpenJS World 2021 27 | output_path: pdf/node-red-workshop-photobooth.pdf 28 | enabled_if_env: ENABLE_PDF_EXPORT 29 | markdown_extensions: 30 | - attr_list 31 | - admonition 32 | - toc: 33 | permalink: true 34 | toc_depth: 3 35 | extra_css: 36 | - css/extra.css 37 | - css/pdf-print.css 38 | nav: 39 | - Introduction: index.md 40 | - 1 - Node-RED: 41 | - Introduction: part1/README.md 42 | - Install Node-RED: part1/install.md 43 | - Enabling Projects: part1/projects.md 44 | - Installing Nodes: part1/installing-nodes.md 45 | - Creating a Flow: part1/create-flow.md 46 | - Committing Changes: part1/commit-changes.md 47 | - 2 - Node-RED Dashboard: 48 | - Introduction: part2/README.md 49 | - Create a dashboard: part2/create-dashboard.md 50 | - Adding controls: part2/adding-controls.md 51 | - 3 - TensorFlow: 52 | - Introduction: part3/README.md 53 | - TensorFlow in Node-RED: part3/adding-tf.md 54 | - Displaying detected objects: part3/display-objects.md 55 | - Selecting objects to display: part3/select-objects.md 56 | - 4 - Summary: part4/README.md 57 | - Resources: resources.md 58 | -------------------------------------------------------------------------------- /docs/part1/installing-nodes.md: -------------------------------------------------------------------------------- 1 | # Installing Nodes 2 | 3 | The building blocks of any Node-RED application are the nodes in its palette. 4 | 5 | Node-RED comes with a number of core nodes that provide the basic components, 6 | but the palette can be easily extended by installing additional nodes. 7 | 8 | Nodes are published as npm modules and the project provides an online catalogue 9 | of them at [https://flows.nodered.org](https://flows.nodered.org). 10 | 11 | 12 | There are two ways to install nodes - via the command-line or from within the 13 | Node-RED editor. 14 | 15 | ### Node-RED Palette Manager 16 | 17 | To install a node from within the editor, select the Manage Palette option from 18 | the main menu. 19 | 20 | This opens the Palette Manager which shows two tabs - a list of the modules you 21 | have installed and a searchable catalogue of modules available to install. 22 | 23 | Switch to the Install tab and search for `random` - you should see `node-red-node-random` in the list below. Click the `install` button next to it. 24 | 25 | ![](../images/palette-install.png){: style="width:350px"} 26 | 27 | After a short time the node will be installed and added to the palette. 28 | 29 | ### Command-line 30 | 31 | To install on the command-line, switch to the Node-RED user directory and run the appropriate `npm install` command. For example: 32 | 33 | ``` 34 | npm install node-red-node-random 35 | ``` 36 | 37 | !!! note "Node-RED User Directory" 38 | By default, Node-RED creates a directory called `.node-red` in the user's 39 | home directory. As it starts with a `.` it may be hidden from view by your file 40 | browser. 41 | 42 | As mentioned in the [Install](install.md) section, Node-RED logs the full 43 | path to the user directory when it starts up. If in doubt, check what it says. 44 | 45 | !!! note 46 | Some nodes will have external dependencies that cannot be automatically 47 | installed by Node-RED or npm. You should always check a module's readme 48 | for further information. This will be particularly true of some of the 49 | TensorFlow nodes we'll be using later in this workshop. 50 | 51 | ## Next Steps 52 | 53 | The next task is to [create you first flow in Node-RED](create-flow.md). 54 | 55 | -------------------------------------------------------------------------------- /docs/resources.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | Here are some useful links and information to help you to continue to explore Node-RED 4 | and TensorFlow.js. 5 | 6 | ## Node-RED Resources 7 | 8 | - You can find a complete copy of the Node-RED project this workshop creates 9 | [here](https://github.com/johnwalicki/node-red-workshop-photobooth-project). 10 | 11 | 12 | This includes the [flow file](https://github.com/johnwalicki/node-red-workshop-photobooth-project/blob/main/flows.json) 13 | you can import if you want to skip to the end. 14 | 15 | - Alternatively, you can access the final flow from this repository [here](flow-final.md). 16 | 17 | - For more information about using Node-RED, start with the [Node-RED Essentials playlist](https://www.youtube.com/playlist?list=PLyNBB9VCLmo1hyO-4fIZ08gqFcXBkHy-6) 18 | on YouTube. 19 | 20 | - The [Working with Messages](https://nodered.org/docs/user-guide/messages) section 21 | of the documentation provides a good introduction to understanding the structure 22 | of a message. 23 | 24 | - The [Flow Developer Guide](https://nodered.org/docs/developing-flows/) provides 25 | lots of useful information on best practices in creating flows. It covers how 26 | to layout your flows, how to structure your messages and also how to properly 27 | document your flows (something this workshop hasn't really touched on). 28 | 29 | - The [Node-RED community forum](https://discourse.nodered.org) is a great place to get help, 30 | as is the project's [Slack](https://nodered.org/slack). 31 | 32 | 33 | ## TensorFlow.js Resources 34 | 35 | - You can find out more about TensorFlow.js at its site [here](https://www.tensorflow.org/js) 36 | 37 | - We've used the Object Detection model in this workshop. For more information 38 | about other pre-trained models that are available, check out [this page](https://www.tensorflow.org/js/models). 39 | 40 | - To use a different model, you'll need to change over to the [node-red-contrib-tf-model](https://flows.nodered.org/node/node-red-contrib-tf-model) 41 | set of nodes. Make sure you read its documentation before installing it as you 42 | have to manually install the TensorFlow libraries first. 43 | 44 | You should not try installing it at the same time as the `node-red-contrib-tfjs-coco-ssd` module 45 | we've used in this workshop. 46 | -------------------------------------------------------------------------------- /docs/part1/projects.md: -------------------------------------------------------------------------------- 1 | # Enabling the Projects feature 2 | 3 | Node-RED comes with the Projects feature that allows you to version control your 4 | flows by creating a git repository around them. You can then commit changes 5 | from directly within the editor. You can also connect to a remote repository and 6 | push or pull changes to that remote. 7 | 8 | This feature needs to be enabled before it can be used. 9 | 10 | 1. Stop Node-RED by pressing `Ctrl-C` in the terminal window its running in. 11 | 12 | 2. Find your Node-RED `settings.js` file in your User directory. By default this will be `~/.node-red/settings.js`. Open it in a text editor. 13 | 14 | 3. Find the `editorTheme` section at the bottom of the file. Set the `enabled` 15 | property to `true`: 16 | 17 | editorTheme: { 18 | projects: { 19 | // To enable the Projects feature, set this value to true 20 | enabled: true 21 | } 22 | } 23 | 24 | 4. Save the file and restart Node-RED 25 | 26 | The log output should now include the line: 27 | 28 | ``` 29 | 23 Oct 10:49:09 - [warn] No active project : using default flows file 30 | ``` 31 | 32 | !!! attention "Troubleshooting" 33 | If you see a line saying `[warn] Projects disabled` then you are missing 34 | a prerequisite. The line should tell you what is missing. For example, if it says `git command not found` then you need to install the `git` command-line 35 | tool and ensure its on your path before running Node-RED. 36 | 37 | 38 | ## Creating a Node-RED Project 39 | 40 | Once you have enabled the Projects feature, the next time you load the editor 41 | in your browser, you will be greeted with a dialog inviting you to create your 42 | first project. 43 | 44 | ![](../images/projects-welcome.png){: style="width:350px"} 45 | 46 | 47 | Click the `Create Project` button and follow the steps it takes you through: 48 | 49 | - Setup your username/email used to create commits 50 | - Give your project a name and an optional description 51 | - Set the flow file name to `flows.json` 52 | - Configure the encryption of your credentials file. 53 | 54 | 55 | The project will then be created under `~/.node-red/projects/`. 56 | 57 | ## Next Steps 58 | 59 | The next task is to [install some extra nodes into the palette](installing-nodes.md). 60 | 61 | -------------------------------------------------------------------------------- /overrides/main.html: -------------------------------------------------------------------------------- 1 | 22 | 23 | {% extends "base.html" %} 24 | 25 | 26 | {% block extrahead %} 27 | 28 | 29 | {% set title = config.site_name %} 30 | {% if page and page.title and not page.is_homepage %} 31 | {% set title = config.site_name ~ " - " ~ page.title | striptags %} 32 | {% endif %} 33 | 34 | 35 | {% set image = config.site_url ~ 'assets/images/banner.png' %} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {% endblock %} 49 | 50 | 51 | 52 | 53 | {% block scripts %} 54 | {{ super() }} 55 | 56 | {% endblock %} 57 | -------------------------------------------------------------------------------- /docs/part3/adding-tf.md: -------------------------------------------------------------------------------- 1 | # TensorFlow in Node-RED 2 | 3 | 4 | ## Installing TensorFlow nodes 5 | 6 | For this workshop, we're going to use the `node-red-contrib-tfjs-coco-ssd` 7 | module that provides the `tf coco ssd` node. 8 | 9 | This module can be installed from the Manage Palette option in the editor, or by 10 | running the following command in `~/.node-red`: 11 | 12 | ``` 13 | npm install node-red-contrib-tfjs-coco-ssd 14 | ``` 15 | 16 | This will install the module and the TensorFlow library it depends on. 17 | 18 | 19 | ## Connecting TensorFlow to the WebCam 20 | 21 | In this part, we'll setup the TensorFlow node to receive images from the WebCam. 22 | 23 | 1. Add an instance of the `tf coco ssd` node from the "analysis" category of the 24 | palette into your workspace. 25 | 2. Wire the output of WebCam node to the input of the tf node. 26 | 3. Make sure the WebCam node is configured to capture `jpeg` images - we said we'd 27 | remind you about this. 28 | 3. Add a Debug node and connect it to the output of the tf node. 29 | 4. Click the Deploy button to save your changes. 30 | 31 | !!! note "Muting Debug nodes" 32 | In this screenshot you can see I have muted the Debug node attached to the 33 | webcam node by clicking its button in the workspace. This can be useful 34 | to turn off different bits of Debug without unwiring or removing the nodes 35 | entirely. 36 | 37 | ![](../images/flow-tf.png) 38 | 39 | On the dashboard, make sure your webcam can see you and click the capture button. 40 | Switch back to the Node-RED editor and open the Debug sidebar panel. You should see 41 | the message sent by the tf node. Its payload consists of a list of the objects 42 | it has detected. 43 | 44 | Each entry in the list has: 45 | 46 | - `class` - the type of object 47 | - `score` - the confidence level of the detection, from `0` to `1`. 48 | - `bbox` - an array giving the corners of the bounding box surrounding the detected object 49 | 50 | 51 | ![](../images/flow-capture-debug.png) 52 | 53 | !!! note "`msg.payload` format" 54 | Each of the TensorFlow nodes uses a slightly different object format. For 55 | example, the `node-red-contrib-tf-*` nodes set the `className` property 56 | rather than `class` as we have here. If you experiement with the other nodes 57 | make sure you read their documentation and use the Debug node to understand 58 | their message format. 59 | 60 | 61 | ## Next Steps 62 | 63 | With TensorFlow integrated into the dashboard, the next task is to 64 | [display the detected objects on the dashboard](display-objects.md). 65 | -------------------------------------------------------------------------------- /overrides/assets/stylesheets/home.css: -------------------------------------------------------------------------------- 1 | .home-hero { 2 | max-width: 100%; 3 | height: 45vh; 4 | max-height: 560px; 5 | width: 100%; 6 | position: relative; 7 | } 8 | 9 | .home-hero-image { 10 | height: 45vh; 11 | background-image: url(images/catalyst.svg); 12 | background-position: center; 13 | background-size: cover; 14 | } 15 | 16 | .home-hero-text { 17 | bottom: 0; 18 | position: absolute; 19 | } 20 | 21 | .home-hero-text h1 { 22 | color: #fff; 23 | background: none; 24 | bottom: 0; 25 | font-size: 3.6em; 26 | padding-left: 20px; 27 | margin-bottom: 10px 28 | } 29 | 30 | .home-description { 31 | max-width: 100%; 32 | width: 100%; 33 | background: #030303; 34 | } 35 | .home-purpose { 36 | max-width: 100%; 37 | width: 100%; 38 | background: #061f80; 39 | } 40 | 41 | .row { 42 | display: flex; 43 | flex-direction: row; 44 | flex-wrap: wrap; 45 | width: 100%; 46 | padding-left: 1rem; 47 | padding-right: 1rem; 48 | padding-top: 4rem; 49 | padding-bottom: 4rem; 50 | font-size: 1.6em; 51 | } 52 | 53 | .column { 54 | display: flex; 55 | flex-direction: column; 56 | flex-basis: 100%; 57 | flex: 1; 58 | height: 100%; 59 | font-family: IBM Plex Sans VF,Helvetica Neue,Arial,sans-serif; 60 | } 61 | 62 | .md-main__inner { 63 | margin: 0; 64 | max-width: 100%; 65 | } 66 | 67 | .md-main__inner .md-content__inner, 68 | .md-sidebar--primary:not([hidden]) ~ .md-content > .md-content__inner { 69 | margin: 0; 70 | padding: 0; 71 | } 72 | 73 | .md-typeset img, 74 | .md-typeset svg { 75 | max-width: initial; 76 | } 77 | 78 | :root >* { 79 | --md-default-bg-color: #161616; /* background */ 80 | --md-primary-bg-color: #fff; /* Title bar text */ 81 | --md-typeset-a-color: #aaa; /* Additional header text */ 82 | --md-typeset-color: #fff; /* nav text normal */ 83 | --md-accent-fg-color: #9f9; /* text hover + highlight*/ 84 | --md-default-fg-color--lighter: #33f; /* Nav scroll bar */ 85 | --md-primary-bg-color--light: #fff; /* Search bar text */ 86 | --md-default-fg-color: #fff; /* Search result box section header */ 87 | --md-default-fg-color--light: #eee; /* Search box result text */ 88 | 89 | } 90 | 91 | /* top banner home page */ 92 | [data-md-color-primary=black] .md-header { 93 | background-color: #161616; 94 | border-bottom: 1px solid #393939; 95 | } 96 | 97 | 98 | article.md-content__inner.md-typeset { 99 | background-color: #161616; 100 | border-left: 1px solid #393939; 101 | } -------------------------------------------------------------------------------- /overrides/partials/nav.html: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | {% set class = "md-nav md-nav--primary" %} 25 | {% if "navigation.tabs" in features %} 26 | {% set class = class ~ " md-nav--lifted" %} 27 | {% endif %} 28 | {% if "toc.integrate" in features %} 29 | {% set class = class ~ " md-nav--integrated" %} 30 | {% endif %} 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/part2/adding-controls.md: -------------------------------------------------------------------------------- 1 | # Adding controls 2 | 3 | ## Adding a capture button 4 | 5 | The `ui_webcam` node is fairly self-contained - providing both the live view 6 | of the camera as well as the button to trigger taking a photo. 7 | 8 | The node also supports being triggered by passing it a message with the `msg.capture` 9 | property set - something we'll make use of next. 10 | 11 | 1. Add a `ui_button` node. Configure it as follows: 12 | - Set its group to the existing `WebCam` group. 13 | - Set its size to `9x1` 14 | - Set the icon to `fa-camera fa-2x` 15 | - Set the name to `Capture` 16 | 2. Wire its output to a new Change node, configured to set `msg.capture` to 17 | the `boolean` value `true`. 18 | 3. Wire the output of the Change node to the input of the WebCam node. 19 | 4. Edit the WebCam node and select the 'Hide capture button' option. 20 | 21 | ![](../images/webcam-capture.png){: style="width: 500px"} 22 | 23 | 5. In the Dashboard sidebar, the `Capture` node should appear *below* the `WebCam` 24 | node. If it doesn't, you can drag the `Capture` node down into the right order. 25 | 26 | ![](../images/db-reorder.png){: style="width:150px"} 27 | 28 | 6. Deploy the changes. 29 | 30 | The Dashboard will now show the new button beneath the webcam widget - clicking 31 | it will trigger a photo to be taken. 32 | 33 | 34 | ![](../images/booth-capture.png) 35 | 36 | 37 | ## Adding a Clear button 38 | 39 | By default, the webcam node shows the captured image for two seconds before 40 | returning to the live feed. 41 | 42 | We're going to change that so it displays the captured image until the user 43 | either takes another photo or clicks another button to clear it. 44 | 45 | 1. Edit the WebCam node and untick the 'Clear image after...' option. 46 | 2. Add a new `ui_button` node. 47 | - Set its group to the existing `WebCam` group. 48 | - Set its size to `1x1` 49 | - Set the icon to `fa-trash fa-2x` 50 | - Clear the label field. 51 | - Set the Payload option to the `JSON` type and a value of `""`. 52 | 53 | ![](../images/clear-json-type.png){:style="width: 300px"} 54 | 55 | 3. Wire its output to the input of the WebCam node. 56 | 4. As before, check the new node appears below the `WebCam` and `Capture` nodes 57 | in the Dashboard sidebar. 58 | 5. Deploy the changes. 59 | 60 | ![](../images/booth-clear.png) 61 | 62 | Now when you click the camera button the captured image will be shown. Clicking 63 | the new button will clear the image and return to the live feed. 64 | 65 | 66 | ## Next Steps 67 | 68 | With the initial dashboard created, its now time to [add some TensorFlow infused AI](../part3/). 69 | -------------------------------------------------------------------------------- /docs/css/extra.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | --md-default-bg-color: #f4f4f4; 4 | } 5 | :root > * { 6 | --md-accent-fg-color: #030303; 7 | } 8 | .md-nav { 9 | font-size: .8rem; 10 | line-height: 1.4; 11 | } 12 | .md-grid { 13 | max-width: initial; 14 | } 15 | li .md-nav__item--active { 16 | background-color: #e0e0e0; 17 | } 18 | 19 | .md-nav__item .md-nav__link--active { 20 | color: #161616; 21 | font-weight: 600; 22 | } 23 | .md-clipboard { 24 | color: #161616; 25 | } 26 | 27 | .md-tabs__link { 28 | font-size: .9rem; 29 | } 30 | 31 | 32 | div.col-md-9 h1:first-of-type { 33 | text-align: center; 34 | font-size: 60px; 35 | font-weight: 300; 36 | } 37 | 38 | div.col-md-9 h1:first-of-type .headerlink { 39 | display: none; 40 | } 41 | 42 | code.no-highlight { 43 | color: black; 44 | } 45 | 46 | /* Definition List styles */ 47 | 48 | dd { 49 | padding-left: 20px; 50 | } 51 | 52 | /* Center images*/ 53 | 54 | img.center { 55 | display: block; 56 | margin: 0 auto; 57 | } 58 | 59 | .md-content, .md-sidebar__scrollwrap { 60 | padding-bottom: 6rem; 61 | } 62 | 63 | .md-header__inner { 64 | padding: .3rem .2rem; 65 | } 66 | 67 | span.md-ellipsis { 68 | font-weight: bold; 69 | } 70 | 71 | .homepage { 72 | font-size: 1.2rem; 73 | } 74 | 75 | .md-footer-meta__inner { 76 | justify-content: space-evenly; 77 | } 78 | 79 | .md-footer-column { 80 | /* We first create a flex layout context */ 81 | display: flex; 82 | 83 | /* Then we define the flow direction 84 | and if we allow the items to wrap 85 | * Remember this is the same as: 86 | * flex-direction: row; 87 | * flex-wrap: wrap; 88 | */ 89 | flex-flow: column wrap; 90 | 91 | /* Then we define how is distributed the remaining space */ 92 | justify-content: space-around; 93 | 94 | padding: 5px 5px; 95 | } 96 | 97 | .md-footer-text { 98 | font-size: .7rem; 99 | padding: 2px 0; 100 | } 101 | 102 | /* top git source icon */ 103 | .md-header__source { 104 | width: 2rem; 105 | } 106 | 107 | /* search box */ 108 | .md-search__inner { 109 | width: 8rem; 110 | } 111 | 112 | /* center colum white */ 113 | .md-content { 114 | background-color: #fff; 115 | } 116 | 117 | /* adjust center column */ 118 | .md-main__inner { 119 | margin-top: 0px; 120 | } 121 | 122 | /* top banner home page */ 123 | [data-md-color-primary=black] .md-header { 124 | background-color: #161616; 125 | } 126 | 127 | /* tabs border */ 128 | .md-typeset .tabbed-set>label { 129 | border-bottom: .1rem solid #4051b5; 130 | } 131 | 132 | /* headings */ 133 | .md-typeset h1, .md-typeset h2 { 134 | font-weight: 500; 135 | } 136 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Node-RED AI Photo Booth Workshop 2 | 3 | ## Welcome to the Node-RED AI Photo Booth Workshop 4 | 5 | In this workshop, participants will learn how to use Node-RED to create a photo 6 | booth web app infused with AI through the use of TensorFlow. The workshop will step 7 | through getting started with Node-RED, creating the web app and then containerizing 8 | it, ready to be deployed into the cloud or onto edge devices. 9 | 10 | ## Prerequisites 11 | 12 | This workshop requires: 13 | 14 | - A laptop/computer with a Web Cam attached 15 | - Node.js 12.x or 14.x 16 | - Git 17 | 18 | ## Navigation 19 | 20 | To move through the workshop you can use the side panels to select a specific 21 | section or use the navigation links at the bottom of each page to move to the 22 | next or previous section as required. 23 | 24 | ## Access to workshop material 25 | 26 | The source for this workshop is hosted on [GitHub](https://github.com/johnwalicki/node-red-workshop-photobooth/) 27 | and this site is automatically generated from it. The repository also contains 28 | examples and other content that can be used through the workshop. You may want 29 | to clone the repository to your local computer so you have them readily available. 30 | 31 | You can also download a PDF version of this workshop [here](https://johnwalicki.net/node-red-workshop-photobooth/pdf/node-red-workshop-photobooth.pdf). 32 | 33 | ## Getting help 34 | 35 | If you need help with the workshop, join the `#openjs_world-ibm_workshops` channel on the [OpenJS Foundation Community slack](https://openjs-foundation.slack.com). 36 | 37 | ## Workshop Outline 38 | 39 | ### [1 - Node-RED](part1/) 40 | 41 | The first part of the workshop introduces Node-RED - the low-code programming 42 | tool for event-driven applications. It will help you: 43 | 44 | - get Node-RED running on your local computer 45 | - enable the Projects feature 46 | - learn how to install additional nodes into its palette 47 | - create a simple application to learn how Node-RED works 48 | 49 | If you are already familiar with Node-RED, you can skip this part. 50 | 51 | ### [2 - Node-RED Dashboard](part2/) 52 | 53 | In this part you will install the Node-RED Dashboard set of nodes and learn how 54 | to quickly create a simple photo booth application using them. 55 | 56 | ### [3 - TensorFlow in Node-RED](part3/) 57 | 58 | This part brings TensorFlow into Node-RED. It will look at some of the different 59 | nodes for TensorFlow that are available from the community and compares their 60 | capabilities. 61 | 62 | You will then integrate the TensorFlow nodes into your photo booth application. 63 | 64 | ### [4 - Summary and next steps](part4/) 65 | 66 | Finally, we'll review what you have covered in this workshop and highlight a number 67 | of areas where the application you've created could be expanded as a follow-on 68 | activity. 69 | -------------------------------------------------------------------------------- /docs/part1/install.md: -------------------------------------------------------------------------------- 1 | # Installing Node-RED 2 | 3 | Node-RED is published as a node.js module available on npm, as well as a container 4 | available on Docker Hub. 5 | 6 | The full guide for installing and running Node-RED is available [here](https://nodered.org/docs/getting-started/local). 7 | 8 | !!! note "Linux" 9 | The following steps assume you are running on Windows or OSX. If you are running 10 | on a Linux OS, or a device like a Raspberry Pi, the project provides a set of 11 | install scripts that will get node, npm and Node-RED all installed at the latest 12 | stable versions. Refer to the docs linked above. 13 | 14 | You must have a supported version of Node.js installed. Node-RED supports 15 | the Active and LTS releases, 12.x and 14.x. 16 | 17 | You can then install Node-RED as a global module with the command: 18 | 19 | ``` 20 | npm install -g --unsafe-perm node-red 21 | ``` 22 | 23 | Depending on your Node.js installation, you may need to run this command using `sudo`. 24 | 25 | The install log output may contain some warnings - these can be ignored as long 26 | as the output ends with something like: 27 | 28 | 29 | ``` 30 | + node-red@1.2.2 31 | added 332 packages from 341 contributors in 18.494s 32 | ``` 33 | 34 | ## Running Node-RED 35 | 36 | Once installed, you should now have the `node-red` command available to run. 37 | 38 | !!! note "Command not found" 39 | If you do not have the `node-red` command available it may be a problem 40 | with your `PATH` configuration. 41 | 42 | Find where your global node modules are installed by running: 43 | 44 | npm get prefix 45 | 46 | Then ensure the `bin` subdirectory of that location is on your `PATH`. 47 | 48 | When you run `node-red`, the log output will appear 49 | 50 | ``` 51 | 23 Oct 00:12:01 - [info] 52 | 53 | Welcome to Node-RED 54 | =================== 55 | 56 | 23 Oct 00:12:01 - [info] Node-RED version: v1.2.2 57 | 23 Oct 00:12:01 - [info] Node.js version: v12.19.0 58 | 23 Oct 00:12:01 - [info] Darwin 18.7.0 x64 LE 59 | 23 Oct 00:12:01 - [info] Loading palette nodes 60 | 23 Oct 00:12:03 - [info] Settings file : /Users/nol/.node-red/settings.js 61 | 23 Oct 00:12:03 - [info] User directory : /Users/nol/.node-red 62 | 23 Oct 00:12:03 - [info] Server now running at http://127.0.0.1:1880/ 63 | 23 Oct 00:12:03 - [info] Flows file : /Users/nol/.node-red/flows.json 64 | 23 Oct 00:12:03 - [info] Starting flows 65 | 23 Oct 00:12:03 - [info] Started flows 66 | ``` 67 | 68 | This output contains an important piece of information you will need - 69 | the location of your `User directory`. 70 | 71 | 72 | ## Accessing the Node-RED editor 73 | 74 | Assuming you are running Node-RED on your local computer, open a browser and access 75 | the url [http://127.0.0.1:1880/](http://127.0.0.1:1880/){: target="blank"}. This will load the 76 | Node-RED editor - the tool used to build your applications. 77 | 78 | 79 | 80 | ## Next Steps 81 | 82 | The next task is to [enable the Projects feature](projects.md). -------------------------------------------------------------------------------- /docs/part1/create-flow.md: -------------------------------------------------------------------------------- 1 | # Creating a flow 2 | 3 | Before we start on the photo booth application, we're going to step through a 4 | quick example to help get you more familiar with Node-RED. 5 | 6 | 1. From the palette, drag an `Inject` node into workspace. This node can be 7 | used to manually inject messages into a flow, or to inject them at a regular 8 | interval. 9 | 10 | 2. Drag a `Debug` node into the workspace. This is a very useful node that can be 11 | used to examine messages in a flow by displaying them in the Debug sidebar. 12 | 13 | 3. Drag a wire from the output of the `Inject` node to the input of the `Debug` node. 14 | The wires determine where messages go when they pass from one node to another. 15 | 16 | 4. Click the red Deploy button to save your changes. At this point your flow is 17 | now running. 18 | 19 | 5. Click the button to the left of the Inject node - this triggers it to send 20 | a message. By default it will send a message with its `payload` set to the 21 | current time. 22 | 23 | 6. You should see the message get displayed in the Debug sidebar. 24 | 25 | 26 | ![](../images/first-flow.png) 27 | 28 | 29 | !!! note "Core Nodes" 30 | Node-RED comes with a number of core nodes that are the basic building blocks 31 | for any flow. It's worth spending a bit of time exploring what is available. 32 | 33 | The [documentation has more information](https://nodered.org/docs/user-guide/nodes){: target="blank"} 34 | about them. 35 | 36 | 37 | !!! note "Debug sidebar" 38 | The Debug node and sidebar are invaluable tools when creating flows. They help 39 | you understand the structure of the messages you are working. 40 | 41 | The ["Working with messages"](https://nodered.org/docs/user-guide/messages){: target="blank"} 42 | section of the documentation gives a good introduction in how to make the 43 | most of the Debug tools. 44 | 45 | ## Import/Exporting Flows 46 | 47 | Node-RED flows can be imported and exported from the editor using a JSON format. 48 | 49 | To import a flow, select the Import option from the menu (or hit `Ctrl-I`). This 50 | opens the import dialog. 51 | 52 | Copy the JSON below, paste it in and click `Import`. You will find a number of nodes 53 | attached to your mouse that you can click to place into the workspace. 54 | 55 | ``` 56 | [{"id":"10cd970c.dcde99","type":"http in","z":"ddeb3b89.ad9748","name":"", 57 | "url":"/hello","method":"get","upload":false,"swaggerDoc":"","x":140,"y":140, 58 | "wires":[["ec1f6830.222e38","43f8e071.77d4a"]]},{"id":"ec1f6830.222e38","type":"template", 59 | "z":"ddeb3b89.ad9748","name":"","field":"payload","fieldType":"msg","format":"handlebars", 60 | "syntax":"mustache","template":"{{# payload.name }}\n

Hello, {{{payload.name}}}!

\n{{/ payload.name }}\n{{^ payload.name }}\n
\n What is your name? \n \n
\n\n\n{{/ payload.name }}\n", 61 | "output":"str","x":340,"y":140,"wires":[["87f3adff.c4e43"]]},{"id":"87f3adff.c4e43", 62 | "type":"http response","z":"ddeb3b89.ad9748","name":"","statusCode":"","headers":{},"x":510, 63 | "y":140,"wires":[]},{"id":"43f8e071.77d4a","type":"debug","z":"ddeb3b89.ad9748", 64 | "name":"","active":true,"tosidebar":true,"console":false,"tostatus":false, 65 | "complete":"false","statusVal":"","statusType":"auto","x":350,"y":80,"wires":[]}] 66 | ``` 67 | 68 | This gives you a flow that starts with a `HTTP In`, configured to listen for 69 | requests on `/hello`. When a request arrives, it gets passed to a `Template` node 70 | that generates some simple HTML using the contents of the message. It then gets 71 | passed to a `HTTP Response` node to send back the response. 72 | 73 | You can see it in action by opening [http://localhost:1880/hello](http://localhost:1880/hello){: target="blank"}. 74 | 75 | ![](../images/http-flow.png) 76 | 77 | 78 | ## Next Steps 79 | 80 | The final task in this part of the workshop is to [commit your changes](commit-changes.md). 81 | 82 | -------------------------------------------------------------------------------- /overrides/home.html: -------------------------------------------------------------------------------- 1 | 22 | 23 | {% extends "main.html" %} 24 | 25 | 26 | 27 | 28 | {% block announce %} 29 | 30 | Previous version of site can be found here 31 | 32 | {% endblock %} 33 | 34 | 35 | {% block tabs %}{% endblock %} 36 | 37 | 38 | {% block content %} 39 | 40 | 41 | 42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |

Cloud Native Toolkit

50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Cloud-Native Toolkit 60 |
61 |
62 | The Cloud-Native Toolkit is an open-source collection of assets 63 | that enable application development and support teams to deliver business value 64 | quickly using Red Hat OpenShift or IBM Cloud-managed Kubernetes. 65 | This guide provides information to help Developers, Administrators, 66 | and Site Reliability Engineers use the Toolkit to support delivering business 67 | applications through the entire Software Development Life Cycle (SDLC). 68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Fit to purpose 77 |
78 |
79 | The Cloud-Native Toolkit environment has been built to support the principles 80 | of a robust SDLC while being flexible enough to fit into a wide range of 81 | development settings and toolchains. The Cloud-Native Toolkit supports different 82 | tool selections, from open source versions of tools like Artifactory and SonarQube to 83 | enterprise-class software like IBM Cloud Pak for Applications and IBM Multicloud Manager. 84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | {% endblock %} 92 | 93 | 94 | {% block footer %} 95 | {{ super() }} 96 | {% endblock %} 97 | 98 | -------------------------------------------------------------------------------- /docs/part3/README.md: -------------------------------------------------------------------------------- 1 | # Introducing TensorFlow 2 | 3 | TensorFlow is open source library for Machine Learning. It provides a platform 4 | that can be used to develop and train ML models and integrate them into applications. 5 | 6 | The project also provides TensorFlow.js - a JavaScript library that makes it 7 | easy to bring TensorFlow into browser and node applications. 8 | 9 | Once you have a trained model, you can pass it some input data, such as an image, 10 | and it will give you back the result of the model - such as identifying objects 11 | in the image. 12 | 13 | This is well suited to a Node-RED programming environment - where the underlying 14 | complexity of the TensorFlow model can be hidden behind a node. As an low-code 15 | developer, you just need to take care of passing in data to the model and then 16 | doing something with the resulting output. 17 | 18 | ### Models 19 | 20 | The key to creating a TensorFlow backed application is the model it uses. The model 21 | is what transforms the input to the output. TensorFlow provides the tools for 22 | developing and training custom models for specific applications. That's beyond the 23 | scope of this workshop. Thankfully, they provide a number of pre-trained models 24 | that can be used. 25 | 26 | The one we're interested in is the [Object Detection](https://github.com/tensorflow/models/blob/master/research/object_detection/README.md) model - commonly refered to as `coco-ssd`. 27 | Given an image in jpeg format, the model is capable of detecting 80 types of object, such as 28 | `cat`, `tennis racket`, `banana` amongst many others. 29 | 30 | 31 | ## TensorFlow nodes 32 | 33 | The Node-RED community have created a number of different nodes that wrap 34 | TensorFlow.js. In this section we'll briefly look at three of the modules that are 35 | available. 36 | 37 | !!! warning 38 | Each of the modules takes a slightly different approach in how it handles its 39 | dependency on the TensorFlow libraries. Do not install all of the modules 40 | listed here at the same time as that will cause conflicts. 41 | 42 | 43 | 44 | ### `node-red-contrib-tfjs-coco-ssd` 45 | 46 | The [node-red-contrib-tfjs-coco-ssd](https://flows.nodered.org/node/node-red-contrib-tfjs-coco-ssd) module provides a single node that wraps the Object Detection coco-ssd 47 | model and is based on TensorFlow 1.x. 48 | 49 | You pass the node an image as either a `Buffer` object, or a string containing the 50 | filename to load. It will then return an array of the detected objects and optionally 51 | the image with all of the detected objects highlighted and labelled. 52 | 53 | This is the module we'll be using in the workshop. 54 | 55 | 56 | ### `node-red-contrib-tf-model` 57 | 58 | The [node-red-contrib-tf-model](https://flows.nodered.org/node/node-red-contrib-tf-model) 59 | module provides a node based on TensorFlow 2.x. It does not come with any model 60 | built-in and you must provide the url of a model in JSON format. That makes it 61 | useful if you have a custom model, or want to quickly switch what model it is using. 62 | The downside is you either need to be online or host the model locally for the node 63 | to access it. 64 | 65 | This module has a [companion module](https://flows.nodered.org/node/node-red-contrib-tf-function) 66 | that provides a custom Function node with the TensorFlow.js APIs exposed. This 67 | has to be used to prepare the image data before it gets passed to the model node. 68 | 69 | Whilst that allows for more flexible use the nodes, they do have a steeper 70 | learning curve as you need to be familiar with the APIs. 71 | 72 | This module also requires you to manually install the `@tensorflow/tfjs-node` 73 | dependency before installing the node. 74 | 75 | ### `node-red-contrib-tensorflow` 76 | 77 | The [node-red-contrib-tensorflow](https://flows.nodered.org/node/node-red-contrib-tensorflow) 78 | module provides a set of nodes that each wraps a different pre-built model. 79 | This includes the CoCo-SSD Object Detection model, as well as human pose and hand 80 | pose detection. 81 | 82 | ## Next Steps 83 | 84 | Having introduced some of the TensorFlow nodes available in Node-RED, 85 | in this part of the workshop you will: 86 | 87 | - [Add TensorFlow to the Photo Booth dashboard](adding-tf.md) 88 | - [Display detected objects on the dashboard](display-objects.md) 89 | - [Allow the user to select what object to display](select-objects.md) 90 | -------------------------------------------------------------------------------- /docs/flow-final.md: -------------------------------------------------------------------------------- 1 | # The full flow 2 | 3 | 4 | This is flow represents the final application this workshop creates. You can import 5 | it into your Node-RED editor using the Import option from the main menu. 6 | 7 | You will need to have installed the extra node modules used by this flow: 8 | 9 | - `node-red-dashboard` 10 | - `node-red-node-ui-table` 11 | - `node-red-node-ui-webcam` 12 | - `node-red-contrib-tfjs-coco-ssd` 13 | - `node-red-node-annotate-image` 14 | 15 | ``` 16 | [{"id":"800f274b.6632d8","type":"ui_webcam","z":"ddeb3b89.ad9748","name":"","group":"5ecf1e5d.c16c4","order":1,"width":"10","height":"7","countdown":false,"autoStart":false,"hideCaptureButton":true,"showImage":0,"format":"jpeg","x":400,"y":100,"wires":[["f0e86e5b.c2eb5","deade4b6.926288","2be0395e.1879b6"]]},{"id":"f0e86e5b.c2eb5","type":"debug","z":"ddeb3b89.ad9748","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":60,"wires":[]},{"id":"deade4b6.926288","type":"change","z":"ddeb3b89.ad9748","name":"","rules":[{"t":"set","p":"filename","pt":"msg","to":"'/tmp/webcam_'& $moment().format('YYYY-MM-DD-hhmmss') & '.jpeg'","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":100,"wires":[["d062ebca.fa31e8"]]},{"id":"d062ebca.fa31e8","type":"file","z":"ddeb3b89.ad9748","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"false","encoding":"none","x":770,"y":100,"wires":[[]]},{"id":"bc8cf8a5.78e498","type":"ui_button","z":"ddeb3b89.ad9748","name":"","group":"5ecf1e5d.c16c4","order":2,"width":"9","height":"1","passthru":false,"label":"Capture","tooltip":"","color":"","bgcolor":"","icon":"fa-camera fa-2x","payload":"","payloadType":"str","topic":"","x":80,"y":60,"wires":[["1491b6df.c56d59"]]},{"id":"1491b6df.c56d59","type":"change","z":"ddeb3b89.ad9748","name":"msg.capture","rules":[{"t":"set","p":"capture","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":230,"y":60,"wires":[["800f274b.6632d8"]]},{"id":"1bfbdc10.aade94","type":"ui_button","z":"ddeb3b89.ad9748","name":"Clear","group":"5ecf1e5d.c16c4","order":2,"width":"1","height":"1","passthru":false,"label":"","tooltip":"","color":"","bgcolor":"","icon":"fa-trash fa-2x","payload":"\"\"","payloadType":"json","topic":"","x":70,"y":100,"wires":[["800f274b.6632d8"]]},{"id":"8cd39b6c.1e3608","type":"tensorflowCoco","z":"ddeb3b89.ad9748","name":"","modelUrl":"http://localhost:1880/coco/model.json","scoreThreshold":0.5,"passthru":"false","lineColour":"magenta","x":390,"y":240,"wires":[["75fbf592.1bde7c","3e1bfffc.64667"]]},{"id":"75fbf592.1bde7c","type":"debug","z":"ddeb3b89.ad9748","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":200,"wires":[]},{"id":"2936e203.f3245e","type":"ui_table","z":"ddeb3b89.ad9748","group":"cc44de93.d9496","name":"","order":0,"width":"6","height":"8","columns":[{"field":"class","title":"Object Type","width":"","align":"left","formatter":"plaintext","formatterParams":{"target":"_blank"}},{"field":"score","title":"Score","width":"","align":"left","formatter":"progress","formatterParams":{"target":"_blank"}}],"outputs":1,"cts":true,"x":390,"y":360,"wires":[["afc2661c.8a52a8","f1fc41c3.1b5d8"]]},{"id":"3e1bfffc.64667","type":"change","z":"ddeb3b89.ad9748","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$append([],payload.{\"class\":class,\"score\":score*100,\"bbox\":bbox})","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":360,"wires":[["2936e203.f3245e"]]},{"id":"847c5db6.44f4e","type":"link in","z":"ddeb3b89.ad9748","name":"to-webcam","links":["23c88371.85c4bc","70eca315.946a6c"],"x":275,"y":140,"wires":[["800f274b.6632d8"]]},{"id":"afc2661c.8a52a8","type":"debug","z":"ddeb3b89.ad9748","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":590,"y":360,"wires":[]},{"id":"90d9565a.3b5ea8","type":"annotate-image","z":"ddeb3b89.ad9748","name":"","fill":"","stroke":"#ffC000","lineWidth":5,"fontSize":24,"fontColor":"#ffC000","x":600,"y":420,"wires":[["70eca315.946a6c"]]},{"id":"70eca315.946a6c","type":"link out","z":"ddeb3b89.ad9748","name":"annotated-image-output","links":["847c5db6.44f4e"],"x":715,"y":420,"wires":[]},{"id":"2be0395e.1879b6","type":"change","z":"ddeb3b89.ad9748","name":"","rules":[{"t":"set","p":"image","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":200,"y":240,"wires":[["8cd39b6c.1e3608"]]},{"id":"f1fc41c3.1b5d8","type":"change","z":"ddeb3b89.ad9748","name":"","rules":[{"t":"set","p":"annotations","pt":"msg","to":"$append([],payload.{\"label\": class, \"bbox\": bbox})","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"image","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":420,"wires":[["90d9565a.3b5ea8"]]},{"id":"5ecf1e5d.c16c4","type":"ui_group","name":"WebCam","tab":"5967b7da.b44598","order":1,"disp":false,"width":"10","collapse":false},{"id":"cc44de93.d9496","type":"ui_group","name":"Objects","tab":"5967b7da.b44598","order":2,"disp":false,"width":"6","collapse":false},{"id":"5967b7da.b44598","type":"ui_tab","name":"AI Photo Booth","icon":"dashboard","order":1,"disabled":false,"hidden":false}] 17 | ``` -------------------------------------------------------------------------------- /docs/part3/display-objects.md: -------------------------------------------------------------------------------- 1 | # Displaying the detected objects 2 | 3 | In this part we're going to display the detected objects on the dashboard in two 4 | different ways. 5 | 6 | First we will display an annotated version of the captured image with all of the 7 | objects highlighted. We will then add a table to the dashboard that lists them out. 8 | 9 | ## Displaying an annotated image 10 | 11 | The `tf coco ssd` node has an option to output an annotated version of the image 12 | with all of the detected objects highlighted. The image is set on the `msg.image` 13 | message property. 14 | 15 | 1. Edit the `tf` node and configure the "Passthru" field to `Annotated Image` 16 | 17 | ![](../images/tf-passthru.png){: style="width:400px;"} 18 | 19 | 2. Add a Change node, wired to the output of the `tf` node and configure it to move 20 | `msg.image` to `msg.payload`. 21 | 22 | ![](../images/change-img-payload.png){:style="width:400px"} 23 | 24 | 3. Wire the Change node to the input of the WebCam node. 25 | 4. Click the Deploy button to save your changes. 26 | 27 | ![](../images/flow-tf-to-webcam.png) 28 | 29 | !!! note "Laying out flows" 30 | With this latest addition, you can see we now have wires crossing each other 31 | and looping back on themselves. As flows evolve, their wiring can become 32 | quite complex. It is always worth spending some time trying to find a layout 33 | that remains 'readable'. 34 | 35 | There is a [Flow Developer guide](https://nodered.org/docs/developing-flows/) 36 | in the Node-RED documenation that provides a number of tips on how to layout flows. 37 | 38 | Now when you take an image on the dashboard, you should see the annotated version 39 | of the image. 40 | 41 | ![](../images/booth-display-all.png) 42 | 43 | 44 | ## Adding a table of objects 45 | 46 | Install the module `node-red-node-ui-table` using the Manage Palette option in the 47 | editor, or run the following command in `~/.node-red`: 48 | 49 | ``` 50 | npm install node-red-node-ui-table 51 | ``` 52 | 53 | This adds the `ui_table` node to the palette which can be used to display tabular 54 | data. 55 | 56 | 1. In the Dashboard sidebar of the Node-RED editor, hover over the `AI Photo Booth` 57 | tab and click the `+ group` button. 58 | 2. Edit the new group and set its properties: 59 | - Set the name to 'Objects' 60 | - Set the width to `6` by clicking the button and dragging the box out to 6 61 | units wide. 62 | - Untick the 'Display group name' option. 63 | 3. Add a new `ui_table` node from the "dashboard" section of the palette into your 64 | workspace. Edit its properties as follows: 65 | - Add it to the 'Objects' group 66 | - Set its size to `6x8` 67 | - Add two columns by clicking the `+ add` button at the bottom. Configure them as: 68 | - Property: `class`, Title: `Object Type` 69 | - Property: `score`, Title: `Score`, Format: `Progress (0-100)` 70 | 71 | ![](../images/table-config.png){:style="width:400px"} 72 | 73 | 74 | 4. Add a Change node to the workspace. Configure it to set `msg.payload` to the 75 | expression 76 | ``` 77 | $append([],payload.{"class":class,"score":score*100,"bbox":bbox}) 78 | ``` 79 | 80 | !!! note 81 | Make sure you select the `expression` type for the `to` field of the Change node. 82 | This uses the [JSONata](https://jsonata.org){:target="blank"} expression language. 83 | 84 | 5. Create the following wires between nodes: 85 | - wire the output of the `tf` node to the Change node. 86 | - wire the output of the Change node to the Table node 87 | 6. Click the Deploy button to save your changes. 88 | 89 | Now when you capture an image on the dashboard, the table should list the detected 90 | objects. 91 | 92 | ![](../images/flow-table.png) 93 | 94 | ![](../images/booth-display-table.png) 95 | 96 | 97 | !!! idea "Side Quest - Star Ratings" 98 | The JSONata expression used in the Change node mapped the `score` property 99 | of each detected object from its original `0-1` range to the `0-100` range 100 | expected by the `ui_table` node's "Progress" column type. 101 | 102 | The table supports a number of other formats of displaying numeric values. 103 | For example, it can map a number in the 0-100 range to a traffic light colour. 104 | It can also display a value in the range 0-5 as a number of stars. 105 | 106 | Edit the table node to display the score using the star format. See if you can 107 | modify the expression in the Change node to map the original score to the required 108 | `0-5` range. 109 | 110 | 111 | !!! idea "Side Quest - Clear the table" 112 | With the current dashboard, when an image is captured it gets displayed in 113 | place of the live web cam view until the clear button is clicked. 114 | 115 | However clicking the button does not clear the table we've just added. 116 | 117 | Using what you've learnt so far, build a flow between the Clear button and 118 | the table node that will clear the table when the button is clicked. 119 | 120 | *Hint:* think about what payload must be passed to the table in order to clear it. 121 | 122 | 123 | 124 | ## Next Steps 125 | 126 | With the list of detected objects on the dashboard, the next task is to let the 127 | user [select which object to display](select-objects.md). 128 | 129 | -------------------------------------------------------------------------------- /docs/part2/create-dashboard.md: -------------------------------------------------------------------------------- 1 | # Create a dashboard 2 | 3 | In this part of the workshop you will begin to create your Photo Booth application. 4 | 5 | ## Dashboard Layouts 6 | 7 | The Dashboard uses a grid based layout. Widgets, such as buttons or text boxes, are 8 | given a size in grid-cells. They are packed into a group that has a defined width. 9 | The Groups are then placed on a Tab - laid out using a flow-based layout, filling 10 | the width of the page before wrapping. This arrangement provides some flexibility 11 | in how the page displays on different screen sizes. 12 | 13 | ## Dashboard Sidebar 14 | 15 | Within the editor, the Dashboard module provides a sidebar where you can customise 16 | its features as well as manage its tabs, groups and widgets. 17 | 18 | 1. Open the Dashboard sidebar 19 | 2. Click the `+ tab` button to create a new tab. 20 | 3. Hover over the newly added tab and click the `edit` button. 21 | 22 | ![](../images/db-sidebar.png){: style="width: 250px"} 23 | 24 | 4. In the edit dialog, give the tab a name of `AI Photo Booth` and click Update 25 | to close the dialog. 26 | 5. Hover over the tab again and click the `+ group` button. 27 | 6. Edit the new group and set its properties: 28 | 29 | ![](../images/db-group.png){: style="width: 250px"} 30 | 31 | - Set the name to `WebCam` 32 | - Set the width to `10` by clicking the button and dragging the box out to 10 33 | units wide. 34 | - Untick the 'Display group name' option. 35 | 36 | ![](../images/db-group-edit.png){: style="width: 300px"} 37 | 38 | This has created the initial layout components needed for the dashboard. You can 39 | now start to add content. 40 | 41 | 42 | ## Adding a `ui_webcam` node 43 | 44 | !!! note "Tidy up existing flows" 45 | If you followed the previous part of the workshop, you'll have some example 46 | flows in your workspace. You can delete those flows as you won't need them 47 | for the rest of the workshop. 48 | 49 | 1. Drag a `ui_webcam` node from the `dashboard` category of the palette into your 50 | workspace. This node can be used to capture images from the webcam on the 51 | device displaying the dashboard. 52 | 53 | ![](../images/webcam-edit.png){: style="width: 500px"} 54 | 55 | 2. Double click on the node to edit its properties. 56 | 3. Make sure the `WebCam` group you created earlier is selected in the select box. 57 | 5. Set the size to `10x7` - note that it will not let you make it wider than the 58 | group it is in. 59 | 5. Leave the rest of the options as their defaults for now and click Done. 60 | 61 | At this point you have created a dashboard with a single tab, containing a 62 | single group that contains a webcam widget. 63 | 64 | 6. Click the Deploy button to save your changes. 65 | 66 | To access the Dashboard, click the button in the top-right corner of the Dashboard 67 | sidebar. This will open the Dashboard in a new browser tab. 68 | 69 | ![](../images/db-link.png){: style="width: 250px"} 70 | 71 | 72 | Click on the camera button in the middle of the screen to turn on the web cam. 73 | Your browser will ask your permission for the page to access the web cam - make 74 | sure to allow access or you won't get much further. 75 | 76 | ![](../images/webcam-allow.png){: style="width: 250px"} 77 | 78 | You should then get a live feed of your web cam on the page. 79 | 80 | ![](../images/db-first.png) 81 | 82 | 83 | !!! attention "Troubleshooting" 84 | Due to standard browser security practices, you will only be able to use 85 | the web cam if you are accessing the page using `localhost` or `127.0.0.1` 86 | on the local device, or that you are using `https` if accessing it on another device. 87 | 88 | It's beyond the scope of this workshop to get `https` setup - so we 89 | assume you are running Node-RED on the same device you are using to access 90 | the dashboard. 91 | 92 | You can click the camera button to take a photo - the image should pause for a 93 | couple seconds before resuming the live feed. 94 | 95 | But at this point, nothing has happened with the photo you just took. The next 96 | task is to build a flow to do something with it. 97 | 98 | ## Capturing photos 99 | 100 | 1. Back in the Node-RED editor, edit the `ui_webcam` node as follows: 101 | - Enable the 'start webcam automatically' option - this removes the need 102 | to click the camera button each time the dashboard is refreshed. 103 | - Change the Image format to `jpeg` - this is the format needed by the 104 | TensorFlow nodes later on in this workshop. Make the change now so 105 | we don't forget - but we'll remind you later on. 106 | - Click Done to close the dialog. 107 | 2. Add a new Debug node into the workspace. Drag a wire from the output of the 108 | Webcam node to the input of the Debug node. 109 | 3. Click Deploy to save your changes. 110 | 111 | When you switch back to the Dashboard page the camera should already be running. 112 | Click the button to take a photo then switch back to the Node-RED editor. 113 | 114 | Open the Debug sidebar. You should see a message has been logged showing the 115 | received payload was a Buffer. This is the raw image data in `jpeg` format. 116 | 117 | 118 | ![](../images/webcam-debug.png) 119 | 120 | The next task is to start writing the photo to a file. 121 | 122 | ## Saving photos to a file 123 | 124 | 1. From the output of the webcam node, wire in a Change node followed by a File 125 | node. 126 | 2. Configure the Change node as follows: 127 | - Change the default rule to set `msg.filename` instead of `msg.payload`. 128 | - Change the type of the `to` field to `expression` (click the drop-down 129 | arrow on the left-hand edge of the field to select the type) 130 | - Set the value of the `to` field to: 131 | 132 | '/tmp/webcam_'& $moment().format('YYYY-MM-DD-hhmmss') & '.jpeg' 133 | 134 | This will generate a new filename containing the current date and time 135 | each time a message passes through the node. 136 | 137 | If you are running on Windows, be sure to modify the `/tmp/` part of the 138 | path to something suitable. 139 | 140 | ![](../images/change-expr.png){: style="width: 400px" } 141 | 142 | 3. Configure the File node as follows: 143 | - Set the Action to 'overwrite file' 144 | - Untick the 'Add newline to each payload' option 145 | 4. Deploy the updates. 146 | 147 | ![](../images/webcam-file.png){: style="width: 400px"} 148 | 149 | Now when you take photo from the dashboard it will get saved to a file. 150 | 151 | 152 | ## Next Steps 153 | 154 | The next task is to [add some different controls to the dashboard](adding-controls.md). 155 | 156 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 41 | 42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 44 | 45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 46 | 47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 48 | 49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 50 | 51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 52 | 53 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /docs/part3/select-objects.md: -------------------------------------------------------------------------------- 1 | # Selecting objects to display 2 | 3 | So far we have a flow that lets you take photo and then display all of the detected 4 | objects in it. 5 | 6 | In this part of the workshop we're going to change the flow to let the user select 7 | which object to highlight in the displayed image. 8 | 9 | Before we do that, we need to rearrange the nodes to make some space for what 10 | we'll be adding in this section. 11 | 12 | Here is what we have so far: 13 | 14 | ![](../images/flow-table.png) 15 | 16 | With a bit of moving around we can get to the following: 17 | 18 | ![](../images/flow-tidy.png) 19 | 20 | The main addition is the pair of Link nodes - a Link In node wired to the input of 21 | the webcam node, and a Link Out node wired to the output of the `move msg.image` 22 | Change node. Link nodes allow you to create virtual wires that only get shown 23 | when you select a link node at either end. This can help reduce some of the 24 | visual complexity of a flow. 25 | 26 | !!! note "Wiring Link nodes" 27 | You can wire Link nodes together in two ways. One way is to double click on 28 | a Link node to open its edit dialog. Then select which nodes it should be 29 | connected to from the list shown. The nodes are listed by their name or id. 30 | It is always worth naming your link nodes to help you identify them in the list. 31 | This method can be used to join link nodes that are on different tabs in the editor. 32 | 33 | The other way of connecting a pair of link nodes is to first select a node 34 | to reveal its 'virual port' and then drag a wire from that port to 35 | another link node just as you would wire regular nodes. 36 | 37 | 38 | ## Making the table selectable 39 | 40 | We need a way for the user to select the object to display and then 41 | regenerate the annotated image with just the selected object. 42 | 43 | The `ui_table` we added in the last step can be used to select the object. 44 | 45 | 1. Edit the table node and enable the 'Send data on click' option. 46 | 47 | The table node should now have an output port. 48 | 49 | 2. Add a Debug node and wire it to the table node output. 50 | 51 | ![](../images/flow-table-debug.png) 52 | 53 | Now when you select an object in the dashboard table you will see a message 54 | arrive in the Debug sidebar with the object's information. It will look something like: 55 | ``` 56 | { 57 | "class": "person", 58 | "score": 64.49049711227417, 59 | "bbox": [ 60 | 5.880241394042969, 61 | 49.79872226715088, 62 | 517.5165557861328, 63 | 429.3963575363159 64 | ], 65 | "id": 0 66 | } 67 | ``` 68 | 69 | ## Annotating the image 70 | 71 | The TensorFlow node we are using generates the image with all detected objects 72 | annotated. It doesn't provide a way to retrospectively generate the image 73 | with only a particular object highlighted. 74 | 75 | To do that we can use another module: [node-red-node-annotate-image](https://flows.nodered.org/node/node-red-node-annotate-image). 76 | 77 | This module can be installed from the Manage Palette option in the editor, or by 78 | running the following command in `~/.node-red`: 79 | 80 | ``` 81 | npm install node-red-node-annotate-image 82 | ``` 83 | 84 | Once installed, you will find a node called `annotate-image` in the Utility category 85 | of the palette. 86 | 87 | 1. Add a new `annotate-image` node into your workspace. 88 | 2. If you've added the Link nodes like we did at the start of this section: 89 | 1. Wire the output of the `annotate-image` node to the add a new Link Out node 90 | 2. Link the new Link Out node to the Link In node connected to the webcam node. 91 | 3. If you haven't added the Link nodes, wire the output of the `annotate-image` node 92 | directly to the input of the `ui_webcam` node. 93 | 94 | ![](../images/flow-annotate-link.png) 95 | 96 | !!! note 97 | In this screenshot, the virtual wire is show because the Link Out node is 98 | selected. 99 | 100 | The next task is to take the output of the `ui_table` node when an object is clicked 101 | and get it into the right format required by the `annotate-image` node. 102 | 103 | The annotate node expects two properties to be set on the message it is sent: 104 | - `msg.payload` should be a Buffer containing the image in JPEG format 105 | - `msg.annotations` should be an array of the annotations it should apply to the image. 106 | 107 | ### Storing the image 108 | 109 | In order to annotate the image, we need a copy of the image to work on. The flow 110 | we're working on is triggered when the user clicks on a row in the `ui_table`. This 111 | happens as a new event - it isn't tied to original event that captured the image. 112 | 113 | This means we need some way to get ahold of the original image in this new flow. We 114 | can use Context to do that. 115 | 116 | !!! information "Introducing "Context"" 117 | "Context" is a way to store information in Node-RED that is not tied to individual 118 | messages passing through the flow. For example, it can be used to store global state 119 | that needs to be shared amongst flows. 120 | 121 | For more information about context, read the [documentation](https://nodered.org/docs/user-guide/context){:target="blank"}. 122 | 123 | The first task is to store the image in context whenever a photo is taken. 124 | 125 | 1. Add a Change node in between the `ui_webcam` and `tf coco ssd` nodes. 126 | 2. Configure the node to set `flow.image` to the value of `msg.payload`. 127 | 128 | ![](../images/flow-webcam-change-tf.png){:style="width: 300px"} 129 | 130 | 131 | The next task is to retrieve that image and add it to the messages coming from 132 | the `ui_table` node. 133 | 134 | 1. Add a Change node and wire in between the output of the `ui_table` node and the `annotate-image` node. 135 | 2. Configure the node to set `msg.payload` to the value of `flow.image` - this is essentially 136 | the reverse of the operation done in the previous task. 137 | 138 | ![](../images/flow-table-change-annotate.png){:style="width: 400px"} 139 | 140 | 141 | ### Creating the annotations 142 | 143 | As shown earlier, the payload coming from the `ui_table` node looks like this: 144 | 145 | ``` 146 | { 147 | "class": "person", 148 | "score": 64.49049711227417, 149 | "bbox": [ 150 | 5.880241394042969, 151 | 49.79872226715088, 152 | 517.5165557861328, 153 | 429.3963575363159 154 | ], 155 | "id": 0 156 | } 157 | ``` 158 | 159 | In order draw that as an annotation on the image, the annotation node expects 160 | `msg.annotations` to be a array containing an object like this: 161 | ``` 162 | [ 163 | { 164 | "label": "person", 165 | "bbox": [ 166 | 5.880241394042969, 167 | 49.79872226715088, 168 | 517.5165557861328, 169 | 429.3963575363159 170 | ] 171 | } 172 | ] 173 | ``` 174 | 175 | You can see they are very similar, but some work is needed to map between them. 176 | 177 | 1. Edit the Change node added in the previous step (between the `ui_table` and `annotate-image` nodes) 178 | 2. Add a new rule, and then **drag it to the top of the list of rules**. This is 179 | important because the new rule will use the information in `msg.payload` 180 | so needs to be applied *before* the rule that overwrites `msg.payload` with the image data. 181 | 3. Configure the new rule to set `msg.annotations` to the *expression* type and set 182 | the expression to: 183 | 184 | $append([],payload.{"label": class, "bbox": bbox}) 185 | This will create an array of annotations with just the `label` and `bbox` properties 186 | set from the original object. 187 | 188 | !!! note "Testing JSONata Expressions" 189 | Node-RED provides an editor for JSONata expressions with the ability to 190 | test the expression whilst working on it. 191 | 192 | 1. Click on the expand button next to the Expression: 193 | 194 | ![](../images/jsonata-expand.png){:style="width: 250px"} 195 | 196 | 2. This opens the JSONata editor. You can edit the expression in the top 197 | pane. In the lower pane is a Function Reference and a Test tab. Click 198 | on the Test tab. This shows an example message on the left and the result 199 | of the expression on the right. 200 | 201 | ![](../images/jsonata-editor.png){:style="width: 400px"} 202 | 203 | 3. Copy the example output of the annotate node from above and replace 204 | the `"hello world"` example payload. You should see the result update 205 | with shown here. 206 | 207 | ![](../images/jsonata-test.png){:style="width: 400px"} 208 | 209 | 210 | 211 | You should now have a flow that starts with the `ui_table` node, passes through 212 | a `Change` node to format the message, then to an `annotate image` node and finally 213 | to the `ui_webcam` node, possibly via a `Link` node. 214 | 215 | Before deploying these changes there are couple more changes to the flow needed. 216 | We aren't going to use the image produced by the `tf coco ssd` node anymore. 217 | 218 | 1. Delete the `move msg.image` Change node that is connected to the output of 219 | the `tf coco ssd` node. If you added the Link Out node, delete that as well. 220 | 2. Edit the `tf coco ssd` node to set the Passthru option to `nothing` 221 | 3. Click the Deploy button to save your changes. 222 | 223 | Now when you capture an image on the webcam, you'll see the captured image on the 224 | dashboard without any annotations. The table below the image will list the 225 | detected objects. Clicking on an object will update the image to highlight the 226 | selected image. 227 | 228 | This is what the final flow should look like: 229 | 230 | ![](../images/flow-final.png) 231 | 232 | 233 | ## Next Steps 234 | 235 | With the application complete, the next task is to [wrap it in a container](../part4/). 236 | -------------------------------------------------------------------------------- /overrides/assets/stylesheets/images/catalyst.svg: -------------------------------------------------------------------------------- 1 | catalyst-tools-dashboard --------------------------------------------------------------------------------