Skip to content

Commit

Permalink
Fixed side-car mode frame counting
Browse files Browse the repository at this point in the history
  • Loading branch information
connervieira committed Oct 30, 2024
1 parent 99421f1 commit b975e7b
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 11 deletions.
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ These are the features actively planned for Predator and are likely to be added
- [X] Convert plate corners to bounding box.
- [X] Create file viewer.
- [X] Implement revised process exiting for real-time mode.
- [ ] Finish alert documentation (ALERTS.md)
- [X] Finish alert documentation (ALERTS.md)
- [X] Fix frame counting for side-car mode.
- [X] Document `developer>frame_count_method` configuration value in `docs/CONFIGURE.md.


## Hypothetical

Expand Down
12 changes: 6 additions & 6 deletions alpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import os
import time
import json # Required to process JSON data
import cv2



Expand Down Expand Up @@ -288,15 +289,14 @@ def generate_dashcam_sidecar_files(working_directory, dashcam_files):
if (config["general"]["alpr"]["engine"] == "openalpr"): # Check to see if the configure ALPR engine is OpenALPR.
alpr_command = ["alpr", "-j", "-n", str(config["general"]["alpr"]["validation"]["guesses"]), working_directory + "/" + file] # Set up the OpenALPR command.

video_frame_count_command = "ffprobe -select_streams v -show_streams " + working_directory + "/" + file + " 2>/dev/null | grep nb_frames | sed -e 's/nb_frames=//'" # Define the commmand to count the frames in the video.
video_frame_count_process = subprocess.Popen(video_frame_count_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) # Execute the command to count the frames in the video.
video_frame_count, command_error = video_frame_count_process.communicate() # Fetch the results of the frame count command.
video_frame_count = int(video_frame_count) # Convert the frame count to an integer.
video_frame_count = utils.count_frames(os.path.join(working_directory, file), method="manual")

utils.debug_message("Running ALPR")
alpr_process = subprocess.Popen(alpr_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Execute the ALPR command.
command_output, command_error = alpr_process.communicate()
command_output = command_output.splitlines()
if (len(command_output) == video_frame_count): # Check to make sure the number of frames analyzed is the same as the frame count.
utils.debug_message("Processing results")
if (abs(len(command_output) - video_frame_count) < 10): # Check to make sure the number of frames analyzed is the same as the frame count.
analysis_results = {} # This will hold the analysis results for this video file.
for frame_number, frame_data in enumerate(command_output): # Iterate through each frame's analysis results from the commmand output.
frame_data = json.loads(frame_data)
Expand All @@ -319,5 +319,5 @@ def generate_dashcam_sidecar_files(working_directory, dashcam_files):
save_to_file(sidecar_filepath, json.dumps(analysis_results, indent=4)) # Save the analysis results for this file to the side-car file.
print(" Analysis complete")
else:
print(" The number of frames in the video does not match the number of frames analyzed.")
print(" The number of frames in the video (" + str(len(command_output)) + ") does not match the number of frames analyzed (" + str(video_frame_count) + ").")
print(" Skipping analysis")
3 changes: 2 additions & 1 deletion assets/support/configdefault.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
"dashcam_saving_queue_overflow": 2000,
"dashcam_shortterm_framerate_interval": 0.25,
"hard_crash_on_error": false,
"identify_to_remote_sources": true
"identify_to_remote_sources": true,
"frame_count_method": "manual"
}
}
3 changes: 2 additions & 1 deletion assets/support/configoutline.json
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@
"dashcam_saving_queue_overflow": "+int",
"dashcam_shortterm_framerate_interval": "+float",
"hard_crash_on_error": "bool",
"identify_to_remote_sources": "bool"
"identify_to_remote_sources": "bool",
"frame_count_method": ["opencv", "ffprobe", "manual"]
}
}
5 changes: 5 additions & 0 deletions docs/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,8 @@ This document describes the configuration values found `config.json`.
- Keep in mind that enabling this configuration option will cause Predator to boot-loop if the problem that caused the error isn't resolved between start-ups.
- `identify_to_remote_sources` determines whether Predator will attach its randomly generated identifier when fetching hot-lists from remote sources. This is helpful to allow administrators of remote hot-list sources to see how many clients are using their server.
- If you don't control the servers in your remote hot-list sources, you may consider disabling this feature to make it more difficult to identify you.
- `frame_count_method` determines the method by which Predator will count frames in video files during side-car file generation in pre-recorded mode.
- This value can only be set to one of the following strings:
- "opencv" uses the built in OpenCV method to count frames. This method is very efficient, but may produce unexpected results on certain systems.
- "ffprobe" uses the FFProbe command. This method is very efficient, but may not support all video formats, and may produce unexpected results on certain systems.
- "manual" will load the video with OpenCV, and manually count each valid frame. This method is extremely accurate, but takes significantly longer than the alternative methods.
5 changes: 3 additions & 2 deletions docs/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ This is the installation process for Predator and all of its dependencies. This
- `pytz`: Required to manage timezones.
- Highly recommended:
- `validators` and `requests`: Required for network functionality, like push notifications, status light interfacing, remote alert lists, and more.
- `opencv-python`, `cvlib`: Required for dash-cam recording.
- These packages are also required for object recognition and pre-recorded side-car file generation (but not basic license plate recognition).
- Unless you have a good reason not to (and you know exactly what you're doing) you should install these packages.
- Recommended:
- `gps`, `geopy`, and `gpsd-py3`: Required to enable GPS features.
- These packages are not required for reading GPX files, and are only necessary for interacting with live GPS devices.
- `opencv-python`, `cvlib`: Required for dash-cam recording.
- These packages are also required for object recognition (but not license plate recognition).
- Optional:
- `psutil`: Required to process disk usage information in management mode and dash-cam mode.
- `lxmf`, `rns`: Required to send offline parking notifications over the Reticulum stack.
Expand Down
30 changes: 30 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ def process_timing(action, identifier):
from gps import * # Required to access GPS information.
import gpsd
import signal # Required to time out functions.
if (config["developer"]["frame_count_method"] in ["manual" or "opencv"]):
try:
import cv2
except Exception as e:
print(e)
print("cv2 is required because the `developer>frame_count_method` value is set to 'manual' or 'opencv'")



Expand Down Expand Up @@ -794,3 +800,27 @@ def convert_corners_to_bounding_box(corners):
return bounding_box
else: # The number of corners is not the expected length.
return False


# This function counts the number of frames in a given video file.
def count_frames(video, method="manual"):
debug_message("Counting frames")
cap = cv2.VideoCapture(video)
if (method == "opencv"):
video_frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # Count the number of frames in the video.
elif (method == "ffprobe"):
video_frame_count_command = "ffprobe -select_streams v -show_streams \"" + video + "\" 2>/dev/null | grep nb_frames | sed -e 's/nb_frames=//'" # Define the commmand to count the frames in the video.
video_frame_count_process = subprocess.Popen(video_frame_count_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) # Execute the command to count the frames in the video.
video_frame_count, command_error = video_frame_count_process.communicate() # Fetch the results of the frame count command.
video_frame_count = int(video_frame_count) # Convert the frame count to an integer.
elif (method == "manual"):
video_frame_count = 0
while (cap.isOpened()):
ret, frame = cap.read() # Get the next frame.
if (ret == False):
break
video_frame_count += 1
else:
display_message("Invalid frame count method.", 3)
video_frame_count = 0
return video_frame_count

0 comments on commit b975e7b

Please sign in to comment.