diff --git a/docs/en/guides/security-alarm-system.md b/docs/en/guides/security-alarm-system.md index e8562485..9eb4b072 100644 --- a/docs/en/guides/security-alarm-system.md +++ b/docs/en/guides/security-alarm-system.md @@ -27,149 +27,54 @@ The Security Alarm System Project utilizing Ultralytics YOLO11 integrates advanc ### Code -#### Set up the parameters of the message - ???+ note App Password Generation is necessary -- Navigate to [App Password Generator](https://myaccount.google.com/apppasswords), designate an app name such as "security project," and obtain a 16-digit password. Copy this password and paste it into the designated password field as instructed. +- Navigate to [App Password Generator](https://myaccount.google.com/apppasswords), designate an app name such as "security project," and obtain a 16-digit password. Copy this password and paste it into the designated `password` field in the code below. -```python -password = "" -from_email = "" # must match the email used to generate the password -to_email = "" # receiver email -``` +!!! example "Security Alarm System using YOLO11 Example" -#### Server creation and authentication + === "Python" -```python -import smtplib + ```python + import cv2 -server = smtplib.SMTP("smtp.gmail.com: 587") -server.starttls() -server.login(from_email, password) -``` + from ultralytics import solutions -#### Email Send Function + cap = cv2.VideoCapture("Path/to/video/file.mp4") + assert cap.isOpened(), "Error reading video file" -```python -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText + # Video writer + w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS)) + video_writer = cv2.VideoWriter("security_alarm_output.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h)) + from_email = "abc@gmail.com" # The sender email address + password = "---- ---- ---- ----" # 16-digits password generated via: https://myaccount.google.com/apppasswords + to_email = "xyz@gmail.com" # The receiver email address -def send_email(to_email, from_email, object_detected=1): - """Sends an email notification indicating the number of objects detected; defaults to 1 object.""" - message = MIMEMultipart() - message["From"] = from_email - message["To"] = to_email - message["Subject"] = "Security Alert" - # Add in the message body - message_body = f"ALERT - {object_detected} objects has been detected!!" + # Init SecurityAlarm + security = solutions.SecurityAlarm( + show=True, # Display the output + model="yolo11n.pt", # i.e. YOLO11s.pt + records=1, # Total detections count to send an email about security + ) - message.attach(MIMEText(message_body, "plain")) - server.sendmail(from_email, to_email, message.as_string()) -``` + security.authenticate(from_email, password, to_email) # Authenticate the email server -#### Object Detection and Alert Sender + # Process video + while cap.isOpened(): + success, im0 = cap.read() + if not success: + print("Video frame is empty or video processing has been successfully completed.") + break + im0 = security.monitor(im0) + video_writer.write(im0) -```python -from time import time - -import cv2 -import torch - -from ultralytics import YOLO -from ultralytics.utils.plotting import Annotator, colors - - -class ObjectDetection: - def __init__(self, capture_index): - """Initializes an ObjectDetection instance with a given camera index.""" - self.capture_index = capture_index - self.email_sent = False - - # model information - self.model = YOLO("yolo11n.pt") - - # visual information - self.annotator = None - self.start_time = 0 - self.end_time = 0 - - # device information - self.device = "cuda" if torch.cuda.is_available() else "cpu" - - def predict(self, im0): - """Run prediction using a YOLO model for the input image `im0`.""" - results = self.model(im0) - return results - - def display_fps(self, im0): - """Displays the FPS on an image `im0` by calculating and overlaying as white text on a black rectangle.""" - self.end_time = time() - fps = 1 / round(self.end_time - self.start_time, 2) - text = f"FPS: {int(fps)}" - text_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 1.0, 2)[0] - gap = 10 - cv2.rectangle( - im0, - (20 - gap, 70 - text_size[1] - gap), - (20 + text_size[0] + gap, 70 + gap), - (255, 255, 255), - -1, - ) - cv2.putText(im0, text, (20, 70), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), 2) - - def plot_bboxes(self, results, im0): - """Plots bounding boxes on an image given detection results; returns annotated image and class IDs.""" - class_ids = [] - self.annotator = Annotator(im0, 3, results[0].names) - boxes = results[0].boxes.xyxy.cpu() - clss = results[0].boxes.cls.cpu().tolist() - names = results[0].names - for box, cls in zip(boxes, clss): - class_ids.append(cls) - self.annotator.box_label(box, label=names[int(cls)], color=colors(int(cls), True)) - return im0, class_ids - - def __call__(self): - """Run object detection on video frames from a camera stream, plotting and showing the results.""" - cap = cv2.VideoCapture(self.capture_index) - assert cap.isOpened() - cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) - cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) - frame_count = 0 - while True: - self.start_time = time() - ret, im0 = cap.read() - assert ret - results = self.predict(im0) - im0, class_ids = self.plot_bboxes(results, im0) - - if len(class_ids) > 0: # Only send email If not sent before - if not self.email_sent: - send_email(to_email, from_email, len(class_ids)) - self.email_sent = True - else: - self.email_sent = False - - self.display_fps(im0) - cv2.imshow("YOLO11 Detection", im0) - frame_count += 1 - if cv2.waitKey(5) & 0xFF == 27: - break - cap.release() - cv2.destroyAllWindows() - server.quit() -``` - -#### Call the Object Detection class and Run the Inference - -```python -detector = ObjectDetection(capture_index=0) -detector() -``` + cap.release() + video_writer.release() + cv2.destroyAllWindows() + ``` That's it! When you execute the code, you'll receive a single notification on your email if any object is detected. The notification is sent immediately, not repeatedly. However, feel free to customize the code to suit your project requirements. @@ -177,6 +82,21 @@ That's it! When you execute the code, you'll receive a single notification on yo Email Received Sample +### Arguments `SecurityAlarm` + +Here's a table with the `SecurityAlarm` arguments: + +| Name | Type | Default | Description | +| ------------ | ------ | ------- | ------------------------------------------------------- | +| `model` | `str` | `None` | Path to Ultralytics YOLO Model File | +| `line_width` | `int` | `2` | Line thickness for bounding boxes. | +| `show` | `bool` | `False` | Flag to control whether to display the video stream. | +| `records` | `int` | `5` | Total detections count to send an email about security. | + +### Arguments `model.track` + +{% include "macros/track-args.md" %} + ## FAQ ### How does Ultralytics YOLO11 improve the accuracy of a security alarm system? diff --git a/docs/en/macros/solutions-args.md b/docs/en/macros/solutions-args.md index 0ce5d52a..42ae58e6 100644 --- a/docs/en/macros/solutions-args.md +++ b/docs/en/macros/solutions-args.md @@ -9,3 +9,4 @@ | `kpts` | `list` | `[6, 8, 10]` | List of keypoints used for monitoring workouts. These keypoints correspond to body joints or parts, such as shoulders, elbows, and wrists, for exercises like push-ups, pull-ups, squats, ab-workouts. | | `analytics_type` | `str` | `line` | Specifies the type of analytics visualization to generate. Options include `"line"`, `"pie"`, `"bar"`, or `"area"`. The default is `"line"` for trend visualization. | | `json_file` | `str` | `None` | Path to the JSON file defining regions for parking systems or similar applications. Enables flexible configuration of analysis areas. | +| `records` | `int` | `5` | Total detections count that triggers an automated email notification about unusual activity. | diff --git a/docs/en/reference/solutions/security_alarm.md b/docs/en/reference/solutions/security_alarm.md new file mode 100644 index 00000000..a6f1b678 --- /dev/null +++ b/docs/en/reference/solutions/security_alarm.md @@ -0,0 +1,16 @@ +--- +description: Discover how Ultralytics' Security Alarm System enhances real-time surveillance with intelligent object detection and tracking. Learn about setup, monitoring, and threat detection. +keywords: Ultralytics, Security Alarm System, Real-time Surveillance, Object Detection, Video Monitoring, Python, Threat Detection +--- + +# Reference for `ultralytics/solutions/security_alarm.py` + +!!! note + + This file is available at [https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/security_alarm.py](https://github.com/ultralytics/ultralytics/blob/main/ultralytics/solutions/security_alarm.py). If you spot a problem please help fix it by [contributing](https://docs.ultralytics.com/help/contributing/) a [Pull Request](https://github.com/ultralytics/ultralytics/edit/main/ultralytics/solutions/security_alarm.py) 🛠️. Thank you 🙏! + +
+ +## ::: ultralytics.solutions.security_alarm.SecurityAlarm + +

diff --git a/docs/en/solutions/index.md b/docs/en/solutions/index.md index dd7b61d2..bd9f1692 100644 --- a/docs/en/solutions/index.md +++ b/docs/en/solutions/index.md @@ -49,6 +49,10 @@ Here's our curated list of Ultralytics solutions that can be used to create awes yolo solutions source="path/to/video/file.mp4" # specify video file path ``` +## Arguments + +{% include "macros/solutions-args.md" %} + ## Contribute to Our Solutions We welcome contributions from the community! If you've mastered a particular aspect of Ultralytics YOLO that's not yet covered in our solutions, we encourage you to share your expertise. Writing a guide is a great way to give back to the community and help us make our documentation more comprehensive and user-friendly. diff --git a/mkdocs.yml b/mkdocs.yml index 24e74716..92a387ba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -572,6 +572,7 @@ nav: - parking_management: reference/solutions/parking_management.md - queue_management: reference/solutions/queue_management.md - region_counter: reference/solutions/region_counter.md + - security_alarm: reference/solutions/security_alarm.md - solutions: reference/solutions/solutions.md - speed_estimation: reference/solutions/speed_estimation.md - streamlit_inference: reference/solutions/streamlit_inference.md diff --git a/ultralytics/cfg/solutions/default.yaml b/ultralytics/cfg/solutions/default.yaml index b50a2a32..3fd0110b 100644 --- a/ultralytics/cfg/solutions/default.yaml +++ b/ultralytics/cfg/solutions/default.yaml @@ -17,3 +17,6 @@ kpts: [6, 8, 10] # Keypoints for workouts monitoring, i.e. If you want to consid # Analytics settings analytics_type: "line" # Analytics type i.e "line", "pie", "bar" or "area" charts. By default, "line" analytics will be used for processing. json_file: # parking system regions file path. + +# Security alarm system +records: 5 # Total detections count to send an email about security diff --git a/ultralytics/solutions/__init__.py b/ultralytics/solutions/__init__.py index a2333129..3e36d167 100644 --- a/ultralytics/solutions/__init__.py +++ b/ultralytics/solutions/__init__.py @@ -8,6 +8,7 @@ from .object_counter import ObjectCounter from .parking_management import ParkingManagement, ParkingPtsSelection from .queue_management import QueueManager from .region_counter import RegionCounter +from .security_alarm import SecurityAlarm from .speed_estimation import SpeedEstimator from .streamlit_inference import inference from .trackzone import TrackZone @@ -25,4 +26,5 @@ __all__ = ( "inference", "RegionCounter", "TrackZone", + "SecurityAlarm", ) diff --git a/ultralytics/solutions/security_alarm.py b/ultralytics/solutions/security_alarm.py new file mode 100644 index 00000000..7e014e9f --- /dev/null +++ b/ultralytics/solutions/security_alarm.py @@ -0,0 +1,141 @@ +# Ultralytics YOLO 🚀, AGPL-3.0 license + +from ultralytics.solutions.solutions import LOGGER, BaseSolution +from ultralytics.utils.plotting import Annotator, colors + + +class SecurityAlarm(BaseSolution): + """ + A class to manage security alarm functionalities for real-time monitoring. + + This class extends the BaseSolution class and provides features to monitor + objects in a frame, send email notifications when specific thresholds are + exceeded for total detections, and annotate the output frame for visualization. + + Attributes: + email_sent (bool): Flag to track if an email has already been sent for the current event. + records (int): Threshold for the number of detected objects to trigger an alert. + + Methods: + authenticate: Sets up email server authentication for sending alerts. + send_email: Sends an email notification with details and an image attachment. + monitor: Monitors the frame, processes detections, and triggers alerts if thresholds are crossed. + + Examples: + >>> security = SecurityAlarm() + >>> security.authenticate("abc@gmail.com", "1111222233334444", "xyz@gmail.com") + >>> frame = cv2.imread("frame.jpg") + >>> processed_frame = security.monitor(frame) + """ + + def __init__(self, **kwargs): + """Initializes the SecurityAlarm class with parameters for real-time object monitoring.""" + super().__init__(**kwargs) + self.email_sent = False + self.records = self.CFG["records"] + + def authenticate(self, from_email, password, to_email): + """ + Authenticates the email server for sending alert notifications. + + Args: + from_email (str): Sender's email address. + password (str): Password for the sender's email account. + to_email (str): Recipient's email address. + + This method initializes a secure connection with the SMTP server + and logs in using the provided credentials. + + Examples: + >>> alarm = SecurityAlarm() + >>> alarm.authenticate("sender@example.com", "password123", "recipient@example.com") + """ + import smtplib + + self.server = smtplib.SMTP("smtp.gmail.com: 587") + self.server.starttls() + self.server.login(from_email, password) + self.to_email = to_email + self.from_email = from_email + + def send_email(self, im0, records=5): + """ + Sends an email notification with an image attachment indicating the number of objects detected. + + Args: + im0 (numpy.ndarray): The input image or frame to be attached to the email. + records (int): The number of detected objects to be included in the email message. + + This method encodes the input image, composes the email message with + details about the detection, and sends it to the specified recipient. + + Examples: + >>> alarm = SecurityAlarm() + >>> frame = cv2.imread("path/to/image.jpg") + >>> alarm.send_email(frame, records=10) + """ + from email.mime.image import MIMEImage + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + + import cv2 + + img_bytes = cv2.imencode(".jpg", im0)[1].tobytes() # Encode the image as JPEG + + # Create the email + message = MIMEMultipart() + message["From"] = self.from_email + message["To"] = self.to_email + message["Subject"] = "Security Alert" + + # Add the text message body + message_body = f"Ultralytics ALERT!!! " f"{records} objects have been detected!!" + message.attach(MIMEText(message_body, "plain")) + + # Attach the image + image_attachment = MIMEImage(img_bytes, name="ultralytics.jpg") + message.attach(image_attachment) + + # Send the email + try: + self.server.send_message(message) + LOGGER.info("✅ Email sent successfully!") + except Exception as e: + print(f"❌ Failed to send email: {e}") + + def monitor(self, im0): + """ + Monitors the frame, processes object detections, and triggers alerts if thresholds are exceeded. + + Args: + im0 (numpy.ndarray): The input image or frame to be processed and annotated. + + This method processes the input frame, extracts detections, annotates the frame + with bounding boxes, and sends an email notification if the number of detected objects + surpasses the specified threshold and an alert has not already been sent. + + Returns: + (numpy.ndarray): The processed frame with annotations. + + Examples: + >>> alarm = SecurityAlarm() + >>> frame = cv2.imread("path/to/image.jpg") + >>> processed_frame = alarm.monitor(frame) + """ + self.annotator = Annotator(im0, line_width=self.line_width) # Initialize annotator + self.extract_tracks(im0) # Extract tracks + + # Iterate over bounding boxes, track ids and classes index + for box, cls in zip(self.boxes, self.clss): + # Draw bounding box + self.annotator.box_label(box, label=self.names[cls], color=colors(cls, True)) + + total_det = len(self.clss) + if total_det > self.records: # Only send email If not sent before + if not self.email_sent: + self.send_email(im0, total_det) + self.email_sent = True + + self.display_output(im0) # display output with base class function + + return im0 # return output image for more usage