Working With Media¶
You can use a variety of different media inputs and outputs with alwaysAI, as well as perform different media manipulations. This guide will outline using the different media options in your computer vision applications.
Capture Video From a Camera¶
The video stream class to use is based on the target you’re running on as well as the type of camera. Use this table to determine which class to use.
Target |
USB Webcam |
CSI (Ribbon) or Built-in Camera |
---|---|---|
Mac |
|
|
Windows |
|
|
Raspberry Pi |
|
|
NVIDIA Jetson |
|
|
Other devices |
|
|
Additionally, the following are supported across all targets:
Intel RealSense camera:
RealSense
IP video streams:
IPVideoStream
Head to the Supported Devices page to see the full list of supported boards and cameras.
General Usage¶
All the video stream classes follow the same operation. Using
WebcamVideoStream
, the video stream can be
started in the following way:
with edgeiq.WebcamVideoStream(cam=0) as video_stream:
The cam
parameter is the camera index of the webcam to use, where 0
is
often the default camera. The most recent frame can be read by using the
read()
function in the code block:
frame = video_stream.read()
Note that frames that become outdated are dropped to enable real-time analysis. The camera connection is automatically cleaned up upon exiting the code block.
Play Video From a File¶
The FileVideoStream
class enables loading
video from a local file. To generate a list of all video files in a directory,
the list_files()
function can be used.
video_paths = edgeiq.list_files(base_path="some/video/dir", valid_exts=(".avi", ".mpeg"))
The video stream can be started in the following way, using either a known path
to a video file, or a path returned by list_files()
:
with edgeiq.FileVideoStream("video.mpeg") as video_stream:
Within the following code block, the next frame can be read out using
the read()
function:
frame = video_stream.read()
Unlike WebcamVideoStream
, the frames are queued up
when the app read gets behind the video loading. This means there are no
dropped frames when using FileVideoStream
.
The file connection is automatically cleaned up upon exiting the code block.
To check if there are more frames left in the video, use the
more()
function. Alternatively, you
can catch the NoMoreFrames
exception when reading
the next frame:
try:
frame = video_stream.read()
except edgeiq.NoMoreFrames:
# Do something
If you’d like the video to loop endlessly, this is a good spot to restart the
video by calling the start()
function
again:
while True:
try:
frame = video_stream.read()
except edgeiq.NoMoreFrames:
video_stream.start()
continue
Simulate a Real-Time Video Stream¶
In the process of building your app, it can be useful to test against a video
but simulate a real-time video stream like you would get with a webcam. This
can be done by setting the play_realtime
argument to True
when
instantiating the FileVideoStream
object:
with edgeiq.FileVideoStream("video.mpeg", play_realtime=True) as video_stream:
This works by playing the video file back at the encoded FPS and dropping frames that aren’t read out in time.
Store Video Clips¶
The VideoWriter
class will save a video clip to a file. The input
parameters are the output path at which to save the video file, the frame rate
to save, whether to keep colors, and the codec to use. Start by instantiating
the VideoWriter
object, either by using a with
statement,
or by directly instantiating the object.
with edgeiq.VideoWriter(output_path="video_clip.avi") as video_writer:
or:
video_writer = edgeiq.VideoWriter(output_path="video_clip.avi")
For each frame that is ready to be added to the clip, use the
write_frame()
function.
video_writer.write_frame(frame)
For the context manager, the file will be written and closed when the code
block exits. Otherwise, the close()
function must be called.
Capture Video on an Event Trigger¶
The EventVideoWriter
makes it simple to capture specific events in
video clips. When instantiating the object, choose the frames-per-second you’d
like to encode the video at, and the amount of pre-roll and post-roll to record
for each event.
# Capture 5 seconds of pre-roll and post-roll at 30 FPS
event_video_writer = edgeiq.EventVideoWriter(pre_roll=150, post_roll=150, fps=30)
Then for each frame call the update()
function.
event_video_writer.update(frame)
When an event occurs, start recording the incoming frames, along with
any pre-roll frames with the
start_event()
function.
if event_occurred:
output_path="video-event{}.avi".format(event_index)
event_video_writer.start_event(output_path=output_path, callback_function=cb, callback_args=(arg1, arg2))
The callback function is called with the provided arguments when the video capture is complete. Finally, when the event completes, finish saving the video file.
if event_complete:
event_writer.finish_event()
Due to recording the post-roll, the video will not be saved immediately after
finish_event()
is called. Use the
callback function to perform actions once the video capture completes. For
example, to print out the path of the video that completed use the following
callback function and arguments.
def cb(output_path):
print("Video saved: {}".format(output_path))
...
if event_occurred:
output_path="video-event{}.avi".format(event_index)
event_video_writer.start_event(output_path=output_path, callback_function=cb, callback_args=(output_path))
The EventVideoWriter
class intelligently handles calls to
start_event()
while it is already
recording (during an event or post-roll). In those cases, the current video
clip will be extended to capture the new event as well.
Publish a Video Stream¶
The MjpgVideoWriter
class can be used to publish an MJPG video
stream that can be consumed by third-party apps such as VLC. The parameters
enable you to control the latency and likelihood of dropped frames, as well
as the output port and the image size and quality. Instantiating this class starts
an additional process that runs the server to host the stream.
Manipulate Images and Video Frames¶
Translate an Image¶
To translate an image by shifting it on the x and y axes, use the
translate()
function.
translated_image = edgeiq.translate(image, x=x_shift, y=y_shift)
Rotate an Image¶
To rotate an image about a specific point use the
rotate()
function. The center defaults to
the actual center of the image, and the scale factor defaults to 1.0
resulting in no scaling.
rotated_image = edgeiq.rotate(image, angle=90)
Resize an Image¶
To resize an image to be a specific width or height, use the
resize()
function; provide only the
desired dimension and leave keep_scale
set to True
. For example,
to resize an image so that its width is now new_width
:
resized_image = edgeiq.resize(image, width=new_width)
To resize an image so that it fits in a space no larger than max_width
and max_height
, do the following:
resized_image = edgeiq.resize(image, width=max_width, height=max_height)
Display Bounding Boxes¶
To display bounding boxes on an image, use the
markup_image()
function. The function will draw the
bounding boxes on the image, optionally along with labels and confidences.
Custom colors can be provided, and will be indexed according to the master
label list of the model. One method to define custom colors would be to first
create a dictionary of colors you’d like to set for specific labels.
We’ll set “person” to blue, “pottedplant” to green, and all other detections
to red. Note that the color values are in (B, G, R) format.
default_color = (0, 0, 255)
color_map = {
"person": (255, 0, 0),
"pottedplant": (0, 255, 0)
}
Now make the full colors list to pass into
markup_image()
.
colors = [color_map.get(label, default_color) for label in obj_detect.labels]
Finally, pass the color list into
markup_image()
and your detections will be displayed
with your custom colors.
new_image = edgeiq.markup_image(image, predictions, colors=colors)
Cut Out a Segment of an Image¶
A bounding box can also be used to cut out a segment of an image for further
processing. The cutout_image()
function takes a
bounding box and returns the segment of the image outlined by the box.
image_segment = edgeiq.cutout_image(image, prediction.box)
Related Tutorials