This module simplifies and streamlines collecting frames from the cell’s 8 IP cameras. It also includes submodules for extra functionality for working with recorded images including creating videos, creating a grid of all 8 cameras, finding images that are closest to a specified date, and more.
This README is a word-for-word copy of my original User Guide which can be found here.
Actual documentation can be found here.
There are two ways to record frames using the ip-camera-feed module.
- record_with_cmd.py - a completely command-line focused application that runs once; this script starts recording immediately after it is run, waits for the user to click ENTER, and then stops recording
- record_with_gui.py - a GUI based application that can be used for recording multiple times without having to stop and restart the script; this script opens a GUI, waits for its record button to be pressed, starts recording, waits for the stop button to be pressed, stops recording, and then idles until the record button is clicked again (this script also displays how long it has been recording for)
To actually record, all you need to do is just run one of the above scripts in a Python environment.
Note: Neither is more efficient than the other; it is solely up to preference.
There are a few settings that can be edited before executing a script to allow for different recording situations.
...
NUM_CAMERAS = 8
CAPTURE_DELAY = 0.5 # in seconds
DT_OFFSET = 4 # in seconds
...
In both record_with_cmd.py and record_with_gui.py, there exist 3 global variables at the top:
NUM_CAMERAS
- how many cameras there areCAPTURE_DELAY
- how many seconds to wait in between capturing imagesDT_OFFSET
- how many seconds to add (or subtract if the number is negative) to the timestamp of the images to better align with the actual timestamps pasted on the images themselves
...
IMAGE_DIRS = ['images'] + [f'images\\ch{i + 1}' for i in range(NUM_CAMERAS)]
...
There is another global variable at the top of each script, IMAGE_DIRS
, but this should not be changed.
Two questions are also asked at runtime that don’t directly affect the recording but rather the user experience:
- Do you want to delete the old images? - deletes the images directory
- Enable verbose output? - logs to the console information about what is happening while the script is running
Previously recorded frames can be converted into videos using the video_creator module. Three public functions are available for use:
single_channel()
- combines all the images in a single channel's directory into one videoall_channels()
- creates a video made up of ImageCollection image grids organized using the ImageCollection.from_timestamp() class methodall_channels_basic()
- creates a video made up of ImageCollection image grids organized using the ImageCollection.from_index() class method
Creating a video of channel 3 is as simple as:
from video_creator import single_channel
single_channel(3, 'images')
And creating a video of all of the channels in a grid format is as simple as:
from video_creator import all_channels
all_channels('images')
Refer to the documentation for more information on the video_creator submodule.
The ImageCollection
class exists to aid in formatting recorded images for later use or presentation.
Creating an ImageCollection
object should only be done by using its class method constructors, like from_timestamp()
and from_index()
.
from_timestamp()
- creates anImageCollection
object with an image from each channel that is closest to the input timestamp (a datetime object)from_index()
- creates anImageCollection
object with the image from each channel directory at the specified index
For example, creating an ImageCollection
object with images from each channel that is close to 08/12/2022 at 09:45:00 can be done by:
from image_collection import ImageCollection
collection = ImageCollection.from_timestamp(datetime(2022, 8, 12, 9, 45))
To use recorded images in scripts, methods such as to_pil_images()
or to_cv2_images()
can be used. The default behavior of these methods is to create filler images for the channels that don’t have an image, but this can be overridden by setting the create_filler_images
parameter to False
.
collection.to_pil_images()
[<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=2592x1944 at
0x292AE2B5850>, <PIL.Image.Image [...]
Creating a grid of all of the channels in one image is as simple as:
collection.to_pil_image_grid()
<PIL.Image.Image image mode=RGB size=7776x5832 at 0x292AF6DE9D0>
You can also get a cv2-compatible image grid using:
collection.to_cv2_image_grid()
array([[[122, 130, 123], [113, 121, 114], [...]
For convenience, ImageCollection
even has static methods that facilitate conversion between PIL Images and cv2 images:
ImageCollection.pil_to_cv2()
ImageCollection.cv2_to_pil()
And another static method for grabbing a datetime object from a recorded image’s filename:
ImageCollection.datetime_from_image_name('images/ch8/2022-08-08 11_14_44.126554.jpg')
datetime.datetime(2022, 8, 8, 11, 14, 44, 126554)
Just like the record_with_cmd.py and record_with_gui.py scripts, there exists the global variable NUM_CAMERAS
at the top of image_collection.py that can be edited if necessary.
Refer to the documentation for more information on the image_collection submodule.
For use cases that are more involved than the two included scripts, record_with_cmd.py and record_with_gui.py, using the Recorder
class from the recorder module directly is necessary.
Its usage is very straightforward, as it only has two methods: start_recording()
and stop_recording()
. Additionally, its constructor parameters are almost identical to the explanation in the Configuring the Recording section of this User Guide.
The sole difference is the image_dirs
parameter. Its type must be a List[str]
and must contain the root directory of the images for its first index and the channel subdirectories (in order) for its remaining indices. Examples can be found at the top of record_with_cmd.py and record_with_gui.py.
Recorder objects are threaded, meaning a call to stop_recording()
will not block.
Below is an example of waiting for user input to start recording and then waiting for 200 images to be captured before stopping the recording. A different directory structure is used as well.
import os
from recorder import Recorder
IMAGE_DIRS = ['parent/images'] + [f'parent/images/camera{i + 1}' for i in range(8)]
if __name__ == '__main__':
recorder = Recorder(IMAGE_DIRS, 8, 4, 0.5, True, True)
input('Click ENTER to start recording')
recorder.start_recording()
while len(os.listdir('parent/images/camera1')) < 200:
pass # or do something productive
recorder.stop_recording()
Refer to the documentation for more information on the recorder submodule.
To record for a set amount of time rather than having to stop the recording manually, edit record_with_cmd.py as follows:
[...]
recorder = Recorder(IMAGE_DIRS, NUM_CAMERAS, DT_OFFSET, CAPTURE_DELAY, delete_old_images, verbose)
recorder.start_recording()
time.sleep(10)
recorder.stop_recording()