You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+90-7Lines changed: 90 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,13 @@
2
2
3
3
## Overview
4
4
5
-
This repository contains an example application that demonstrates how to capture images from a Raspberry Pi using the camera module, store them in [Redis Hashes](https://redis.io/docs/data-types/hashes/) and render them in a web application.
5
+
This repository contains an example application that demonstrates how to capture images from a Raspberry Pi using the camera module, store them in [Redis Hashes](https://redis.io/docs/data-types/hashes/) and render them in a web application.
6
+
7
+
The project started by using an older Raspberry Pi camera module and the Redis `SCAN` command to retrieve all of the data and present it in the front end. [Release v0.1](https://github.com/simonprickett/redis-pi-camera/releases/tag/v0.0.1) has that code.
8
+
9
+
I enhanced the code to use the newer Raspberry Pi camera module with autofocus and also added configurable image expiry time in Redis and capture of Lux values from the camera. Check out [release v0.2](https://github.com/simonprickett/redis-pi-camera/releases/tag/v0.0.2) for that.
10
+
11
+
In my second live stream for this project, I replaced the `SCAN` command with the [Search capability of Redis Stack](https://redis.io/docs/stack/search/). [Release 0.3](https://github.com/simonprickett/redis-pi-camera/releases/tag/v0.0.3) contains the code from that stream.
6
12
7
13
Here's what the front end looks like when a few images have been captured by the Raspberry Pi...
8
14
@@ -12,9 +18,13 @@ And here's a Raspberry Pi with a camera attached:
12
18
13
19

14
20
15
-
## Watch the Video
21
+
## Watch the Videos
22
+
23
+
Watch the first video for a full walkthrough of this project. [Watch on YouTube](https://www.youtube.com/watch?v=OTDZIK55DX0).
16
24
17
-
Watch the video for a full walkthrough of this project. [Watch on YouTube](https://www.youtube.com/watch?v=OTDZIK55DX0).
25
+
In the second episode I started to use Redis Stack's Search capability. [Watch on YouTube](https://www.youtube.com/watch?v=mcGL6Lk2IXU).
26
+
27
+
I'm planning a third episode once I can figure out what to do in it :)
18
28
19
29
## Components of the Demo
20
30
@@ -27,9 +37,9 @@ Details of how each component works including how to configure and run it can be
27
37
28
38
## Redis
29
39
30
-
Both components need to be connected to the same Redis instance to talk to each other. Right now, this demo works on any Redis 5 or higher server. The enclosed Docker Compose file uses Redis Stack - this is to allow for future enhancements to the application to use Stack's Search capability.
40
+
Both components need to be connected to the same Redis Stack instance to talk to each other.
31
41
32
-
If you want to use Docker, start Redis like this:
42
+
If you want to use Docker, start Redis Stack like this:
33
43
34
44
```
35
45
docker-compose up -d
@@ -53,9 +63,11 @@ See the RedisInsight section of this document if you're interested in a graphica
53
63
54
64
You'll need to make sure that both components of the application can connect to your Redis instance. See details in each component's README.
55
65
56
-
This project will also work with a free Redis Stack cloud instance from Redis (the company). To use this, [sign up here](https://redis.com/try-free/) and make a note of your Redis host, port and password. You'll need those to configure each component.
66
+
This project will also work with a free Redis Stack cloud instance from Redis (the company). To use this, [sign up here](https://redis.com/try-free/) and make a note of your Redis host, port and password. You'll need those to configure each component. When using the free cloud instance, note that you get 30Mb space which will fill up with images quickly. You can manage this by setting a shorter image expiry time in the capture component's configuration.
67
+
68
+
## The Redis Data Model, Key Naming Strategy and Indexing
57
69
58
-
##The Redis Data Model and Key Naming Strategy
70
+
### Data Type and Key Naming Strategy
59
71
60
72
The application stores each image plus associated metadata in its own [Redis Hash](https://redis.io/docs/data-types/hashes/). A Hash in Redis can be thought of as a flat map of name/value properties. Each Hash is stored in its own key in Redis.
61
73
@@ -85,6 +97,77 @@ Here's a complete example, with the image data truncated for brevity:
85
97
```
86
98
With the camera that I used ([Raspberry Pi Camera Module 3](https://www.raspberrypi.com/products/camera-module-3/) capturing at 4608x2592 pixels - configurable in `capture.py`) you can expect each Hash to require around 1Mb of RAM in Redis.
87
99
100
+
### Indexing and Querying
101
+
102
+
As we saw in the initial live stream video, using `SCAN` allows us to retrieve all of the keys containing image data for display in the front end. This has a couple of problems:
103
+
104
+
* We can't do any meaningful filtering or searching on the server side in Redis.
105
+
*`SCAN` is effectively a O(n) time complexity command, so the bigger the dataset the longer it's going to take / the more load it will put on the Redis server.
106
+
107
+
The Search capability of Redis Stack addresses both of these and gives us a flexible way to index and query our data. To use it, we first have to create and index with the [`FT.CREATE` command](https://redis.io/commands/ft.create/). Once created, Redis Stack will monitor changes to keys that match the index criteria and update the index automatically. We can then write rich queries using the `FT.SEARCH` command.
108
+
109
+
Here's our index creation command - you'll need to paste this into Redis CLI or RedisInsight and execute it before running the front end:
110
+
111
+
```
112
+
FT.CREATE idx:images ON HASH PREFIX 1 image: SCHEMA mime_type TAG lux NUMERIC SORTABLE timestamp NUMERIC SORTABLE
113
+
```
114
+
115
+
This command creates an index on keys in Redis whose key names begin `image:` and which are of type `HASH`. Where found, the values of fields named `timestamp` and `lux` are indexed as numeric values and the values of fields named `mime_type` are indexed as tags (exact match string values).
116
+
117
+
Here are some example queries that we can run against this index.
118
+
119
+
Find the 9 most recent images (most recent first), returning their timestamp, MIME type and lux values:
*[RU203 - Querying, Indexing and Full-Text Search](https://university.redis.com/courses/ru203/): A free course at Redis University.
170
+
88
171
## (Optional, but Recommended): RedisInsight
89
172
90
173
RedisInsight is a free graphical management and database browsing tool for Redis. You don't need it to look at how the application stores data in Redis (you can use redis-cli if you prefer) but I'd recommend it as it's much easier to get an overall picture of the state of the database with a graphical tool. RedisInsight runs as a desktop application on your Mac, Windows or Linux machine.
First, we create the key name we're going to use when storing the Hash. It's `image:<timestamp>`.
85
85
86
-
`data_to_save` is a Python dictionary containing the name/value pairs to store in the Redis Hash. This needs to be a flat map of name/value pairs - nested structure isn't allowed in a Redis Hash. If you want more complex data structure, use the [Redis JSON data type](https://redis.io/docs/stack/json/) in Redis Stack.
86
+
`data_to_save` is a Python dictionary containing the name/value pairs to store in the Redis Hash. This needs to be a flat map of name/value pairs - nested structure isn't allowed in a Redis Hash. If you want to model a more complex data structure, use the [JSON data type](https://redis.io/docs/stack/json/) in Redis Stack.
87
87
88
88
Hashes in Redis are schemaless, so if you add extra fields there's no need to change any database schema (if you're looking for one, it doesn't exist!). You'll just need to modify any application code that reads the Hashes to use new fields.
89
89
90
90
We store the bytes of the image, the timestamp and the MIME or media type of the image... so that any front end knows what encoding the data in `image_data` is in.
91
91
92
-
Saving the Hash to Redis is then simply a matter of running the [`HSET` command](https://redis.io/commands/hset/), passing it the key name and dict of name/value pairs to store. When saving this fata, we also want to set an expiry time for it which we do with the Redis [`EXPIRE` command](https://redis.io/commands/expire/). The time to live for each hash is a configurable number of seconds, read from the `IMAGE_EXPIRY` environment variable (see later for details).
92
+
Saving the Hash to Redis is then simply a matter of running the [`HSET` command](https://redis.io/commands/hset/), passing it the key name and dict of name/value pairs to store. When saving this data, we also want to set an expiry time for it which we do with the Redis [`EXPIRE` command](https://redis.io/commands/expire/). The time to live for each Hash is a configurable number of seconds, read from the `IMAGE_EXPIRY` environment variable (see later for details).
93
93
94
94
This means that we want to send two commands to Redis. To save on network bandwidth, let's use a feature of the Redis protocol called a [pipeline](https://redis.io/docs/manual/pipelining/) and send both in the same network round trip:
This sets up the `HSET` and `EXPIRE` commands in a pipeline, which is then sent to Redis using the `execute` function.
103
+
This sets up the `HSET` and `EXPIRE` commands in a pipeline, which is then sent to Redis using the `execute` function. We don't need the results returned from Redis in this instance, but if we did then we can access them as a List returned by `execute`.
0 commit comments