checkschoolbus.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env -S uv run --script
  2. # /// script
  3. # requires-python = ">=3.11"
  4. # dependencies = [
  5. # "requests",
  6. # "opencv-python",
  7. # "numpy",
  8. # "ntfy",
  9. # ]
  10. # ///
  11. import requests
  12. import cv2
  13. import numpy as np
  14. import time
  15. # URL for the video stream
  16. stream_url = 'http://loge.local:8083/stream'
  17. public_url = "https://mail.see.unbl.ink/stream"
  18. # Load a template image of a school bus (you need to have this template)
  19. template = cv2.imread('/home/powellc/var/inbox/school_bus_template.jpg', 0)
  20. w, h = template.shape[::-1]
  21. # Open the stream using OpenCV
  22. cap = cv2.VideoCapture(stream_url)
  23. # Check if the stream opened successfully
  24. if not cap.isOpened():
  25. print("Error: Could not open video stream.")
  26. exit()
  27. # Configure ntfy server and topic
  28. ntfy_server = "https://ntfy.unbl.ink" # Replace with your ntfy server
  29. topic = "school-bus-alert" # Replace with your desired topic
  30. # Send notification function with custom server and topic
  31. def send_ntfy_notification(title, message):
  32. # Construct the full URL with topic (this is how ntfy expects the URL)
  33. url = f"{ntfy_server}"
  34. # Payload for the notification
  35. headers = {"Title": title}
  36. payload = {
  37. "topic": topic,
  38. "message": message,
  39. }
  40. # Send the POST request to the ntfy server
  41. response = requests.post(url, json=payload, headers=headers)
  42. if response.status_code == 200:
  43. print("Notification sent successfully.")
  44. else:
  45. print(f"Failed to send notification: {response.status_code} - {response.text}")
  46. while True:
  47. # Read a frame from the stream
  48. ret, frame = cap.read()
  49. if not ret:
  50. print("Failed to capture frame.")
  51. continue
  52. # Convert the frame to grayscale for template matching
  53. gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  54. # Perform template matching on the current frame
  55. res = cv2.matchTemplate(gray_frame, template, cv2.TM_CCOEFF_NORMED)
  56. threshold = 0.80 # Set your threshold for matching
  57. loc = np.where(res >= threshold)
  58. # If a match is found, send a notification
  59. if len(loc[0]) > 0:
  60. print("Bus detected!")
  61. # Save the frame with bounding boxes to a file in /tmp
  62. timestamp = time.strftime("%Y%m%d_%H%M")
  63. filename = f"school_bus_detection_{timestamp}.jpg"
  64. output_path = f"/media/photos/misc/school_bus/{filename}"
  65. cv2.imwrite(output_path, frame)
  66. print(f"Frame with match locations saved to {output_path}")
  67. # Send notification using ntfy (with both title and message)
  68. title = "School Bus Detected!"
  69. message = f"Check the video feed at: {public_url}\nMatched image at https://files.lab.unbl.ink/school_bus/{filename}"
  70. # Send the notification with both title and message
  71. send_ntfy_notification(title, message)
  72. # Draw bounding boxes on the detected matches
  73. for pt in zip(*loc[::-1]): # Switch x and y for rectangle drawing
  74. cv2.rectangle(frame, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
  75. print("Pausing for 3 minutes to let the bus leave...")
  76. time.sleep(180) # Sleep for 3 minutes (180 seconds)
  77. else:
  78. print("No bus found, sleeping for 2 seconds")
  79. # Optional: Display the frame with the match location (for debugging)
  80. #for pt in zip(*loc[::-1]):
  81. # cv2.rectangle(frame, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
  82. ## Display the frame (for debugging)
  83. #cv2.imshow('Frame', frame)
  84. time.sleep(2) # two seconds so we don't spam the camera
  85. # Break the loop on key press (e.g., 'q' to quit)
  86. if cv2.waitKey(1) & 0xFF == ord('q'):
  87. break