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 and render them in a web application.
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.0.1 has that code.
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.0.2 for that.
In my second live stream for this project, I replaced the SCAN
command with the Search capability of Redis Stack. Release 0.0.3 contains the code from that stream.
Here's what the front end looks like when a few images have been captured by the Raspberry Pi...
And here's a Raspberry Pi with a camera attached:
Watch the first video for a full walkthrough of this project. Watch on YouTube.
In the second episode I started to use Redis Stack's Search capability. Watch on YouTube.
Not sure what I'll do yet, but I've scheduled a third episode for June 1st. Maybe add a trigger sensor and some more search functionality in the front end? Watch on YouTube.
This repository contains two components:
- Image capture component: This is written in Python and runs on a Raspberry Pi with a camera module attached. It takes a new picture every so often and stores it, plus some basic metadata, in Redis. Python was the obvious implementation choice here as Raspberry Pi provides an excellent Python library for their camera modules. See the
pi
folder for full details. - Front end component: This comprises a server written in Python using the Flask framework. It reads data from Redis and uses a HTML front end to display it in a grid view. It uses HTML, JavaScript and the Bulma framework. These were all arbitrary choices - this component could equally be built with Node.js and Express for example. See the
server
folder for full details.
Details of how each component works including how to configure and run it can be found in the README files in each of the above folders.
Both components need to be connected to the same Redis Stack instance to talk to each other.
If you want to use Docker, start Redis Stack like this:
docker-compose up -d
When you're done with the Docker container, stop it like this:
docker-compose down
Your data is saved in a Redis Append Only File in the redisdata
folder. Redis Stack will reload the dataset from this file when you restart the container.
With the container running, you can access the Redis CLI using this command:
docker exec -it redispiimages redis-cli
See the RedisInsight section of this document if you're interested in a graphical alternative to the Redis CLI interface.
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.
This project will also work with a free Redis Stack cloud instance from Redis (the company). To use this, sign up here 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.
The application stores each image plus associated metadata in its own Redis Hash. 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.
The key naming strategy is as follows. Each Hash's key has a fixed prefix image:
followed by the UNIX timestamp representing the time that the image was captured.
For example the key image:1681844748
would contain a Hash with data for an image taken on Tuesday 18th April 2023 at 7:05:48pm GMT.
Each key contains a hash with the following name/value pairs:
mime_type
: The MIME type for the captured image data. This will always beimage/jpeg
unless you change it and the image capture format in thecapture.py
script.timestamp
: The UNIX timestamp that the image was captured at, as recoded from the Raspberry Pi's clock. This will be the same value as the timestamp in the key name.lux
: The lux value captured by the camera when the image was taken.image_data
: A binary representation of the bytes for the image captured by the camera. This will be a JPEG image unless you change the capture format incapture.py
.
Here's a complete example, with the image data truncated for brevity:
> HGETALL image:1681843615
1) "mime_type"
2) "image/jpeg"
3) "timestamp"
4) "lux"
5) "268"
6) "1681843615"
7) "image_data"
8) "\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00...
With the camera that I used (Raspberry Pi Camera Module 3 capturing at 4608x2592 pixels - configurable in capture.py
) you can expect each Hash to require around 1Mb of RAM in Redis.
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:
- We can't do any meaningful filtering or searching on the server side in Redis.
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.
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. 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.
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:
FT.CREATE idx:images ON HASH PREFIX 1 image: SCHEMA mime_type TAG lux NUMERIC SORTABLE timestamp NUMERIC SORTABLE
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).
Here are some example queries that we can run against this index.
Find the 9 most recent images (most recent first), returning their timestamp, MIME type and lux values:
FT.SEARCH idx:images "*" RETURN 3 timestamp mime_type lux SORTBY timestamp DESC LIMIT 0 9
Truncated example response:
1) "12"
2) "image:1684427475"
3) 1) "timestamp"
2) "1684427475"
3) "mime_type"
4) "image/jpeg"
5) "lux"
6) "85"
4) "image:1684427251"
5) 1) "timestamp"
2) "1684427251"
3) "mime_type"
4) "image/jpeg"
5) "lux"
6) "104"
6) "image:1684427190"
7) 1) "timestamp"
2) "1684427190"
3) "mime_type"
4) "image/jpeg"
5) "lux"
6) "109"
8) "image:1684427130"
9) 1) "timestamp"
2) "1684427130"
3) "mime_type"
4) "image/jpeg"
5) "lux"
6) "93"
...
Find the 9 most recent images with lux value between 100 and 120, again with the most recent image first:
FT.SEARCH idx:images "@lux:[100 120]" RETURN 3 timestamp mime_type lux SORTBY timestamp DESC LIMIT 0 9
For more information on how to write search queries, check out:
- Search query syntax documentation.
- RU203 - Querying, Indexing and Full-Text Search: A free course at Redis University.
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.
If you're using the Docker compose file provided with this project to run Redis Stack, you can also access a web based version of RedisInsight with no additional software to install. With the Docker container running, navigate to http://localhost:8001
to use the web version.