Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0b35fb2
Merge pull request #55 from conservationtechlab/master
kgarwoodsdzwa Nov 16, 2023
89effd9
Revert "updating wild demo to reflect master"
kgarwoodsdzwa Nov 16, 2023
7d15dc7
Merge pull request #56 from conservationtechlab/revert-55-master
kgarwoodsdzwa Nov 16, 2023
11906ef
Make the demo modular
kgarwoodsdzwa Nov 22, 2023
4c4243c
Change filename for cougarvision_visualize script
kgarwoodsdzwa Nov 28, 2023
bfddaa7
Condence function import and improve specificity on logging msg
kgarwoodsdzwa Nov 28, 2023
5a2f8a5
Fix logging duplicates
kgarwoodsdzwa Feb 6, 2024
9e7b8f9
Fix pylint and flake8 errors
kgarwoodsdzwa Feb 16, 2024
68e66d0
Handle .pt classifier models
kgarwoodsdzwa Mar 4, 2024
e5ba67c
Add some new config values/change names
kgarwoodsdzwa Mar 4, 2024
749b716
Remove dependency of old animl function
kgarwoodsdzwa Mar 26, 2024
ae22184
updated yml outline
kgarwoodsdzwa Mar 26, 2024
1410512
Merge branch 'animl_update' into wild_demo
kgarwoodsdzwa Mar 26, 2024
59966fc
Merge pull request #58 from conservationtechlab/wild_demo
kgarwoodsdzwa Mar 26, 2024
d4be3e6
Merge branch 'master' into animl_update
kgarwoodsdzwa Mar 26, 2024
cbfd01f
Make readme.md reflect the updated master README.md
kgarwoodsdzwa Mar 26, 2024
432ef5d
Merge branch 'animl_update' of github.com:conservationtechlab/cougarv…
kgarwoodsdzwa Mar 26, 2024
0b3f03b
get cameratraps path from main function
kgarwoodsdzwa May 26, 2025
dd8a68f
removed unecessary function to check and create new folder
kgarwoodsdzwa May 26, 2025
7b6e520
move everything to main rather than being free in the script
kgarwoodsdzwa May 26, 2025
2f35a3e
move the extra random comment to the docstring
kgarwoodsdzwa May 26, 2025
b0e2efd
remove our camera id and names from example config
kgarwoodsdzwa May 26, 2025
86aa246
fix variable passing issue when making functions
kgarwoodsdzwa Jun 16, 2025
dc09750
Merge branch 'animl_update' into wild_demo
kgarwoodsdzwa Jun 17, 2025
b0b69b6
Merge branch 'dev' into wild_demo
montse-jara Apr 9, 2026
3ca2ce2
Change visuzlize output to use configInfo class values.
montse-jara Apr 9, 2026
403e8bf
Change vizualize ouput portion to use ConfigInfo values.
montse-jara Apr 9, 2026
1d8f330
Add attributes to reflect updated yaml.
montse-jara Apr 9, 2026
20ebf92
Change visualize ouput portion to use values from an object of the Co…
montse-jara Apr 9, 2026
3669915
Add paths to labeled and unlabeled images.
montse-jara Apr 9, 2026
0a1f50b
Fix function docstrings to include Args and Returns.
montse-jara Apr 14, 2026
66effd1
Change main program logic to be in a main func called in actual main …
montse-jara Apr 14, 2026
ae8be73
Fix flake8 and pylint spacing and nameing errors.
montse-jara Apr 14, 2026
3987b7f
Change main logic to handle one monitor.
montse-jara Apr 14, 2026
5b658a2
Add setup_windows function for main readability and add parameters to…
montse-jara Apr 14, 2026
c4ad9a1
Fix pylint errors.
montse-jara Apr 14, 2026
635fbab
Fix flake8 errors.
montse-jara Apr 14, 2026
a920f41
Fix and update docstrings.
montse-jara Apr 14, 2026
c62b0d3
Delete unused display more images function and use save_dir path inst…
montse-jara Apr 14, 2026
1653dc8
Add new dataclass for display info
montse-jara Apr 14, 2026
ea37e13
Add imports for get_info and create a display_info dataclass object f…
montse-jara Apr 14, 2026
fe102d8
Delete unsued parse_args() function.
montse-jara Apr 14, 2026
00c4a25
Delete unused imports.
montse-jara Apr 14, 2026
7f8ea74
Add get_config_info function for adaptbility of parsing config yaml f…
montse-jara Apr 14, 2026
62074a6
Delete commented out code.
montse-jara Apr 14, 2026
b14daa0
Add fieldvalues for display_info class.
montse-jara Apr 16, 2026
e3d98d4
Add functionality to setup_windows() to handle number of screens from…
montse-jara Apr 16, 2026
c50dc7e
Add time stamp of image to image name saved in file Images.
montse-jara Apr 16, 2026
b5765b9
Change saved name of labeled photos to match names of photos in image…
montse-jara Apr 16, 2026
90437b1
Change function get recent image to compare and sort images by their …
montse-jara Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,3 @@ These scripts rely on the [CameraTraps Repo](https://github.com/microsoft/Camera
# Processing Batch Images
Run_batch_images.py can take in an input folder and apply the detector/classifier pipeline to render annotated images along with an output.json file in directory classifications. The output.json contains the final classifications on the cropped images and can be used to compare against a ground truth set. There is also an intermediate output.json which holds the crop detections and is used by the script to crop images, this one can be moved by configuration in the yml file.
These scripts rely on the [CameraTraps Repo](https://github.com/microsoft/CameraTraps). Download this repo and place it anywhere. Add that path to config. The CameraTraps repo will then be added to the python sys environment variables upon loading an individual script.

# Improvement Efforts
If you encounter any issues upon install or would like a feature added, create an issue in the issue tracker on this github and we will do our best to accomodate! Specifically we are looking to add new integrations for other types of cellular camera traps.
34 changes: 22 additions & 12 deletions config/fetch_and_alert.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#home_dir - path to local cougarvision repo
home_dir: /home/johnsmith/cougarvision
#True to send detections to consumer emails
email_alerts: True
er_alerts: True
#True to send detections to earthranger
# 1 = email alerts and 2 = earthranger alerts
email_alerts: True
er_alerts: True
#detector_model - path to detector model
detector_model: /home/johnsmith/cougarvision/detector_models/megadetector.pt
#type of detection model expected ["MDV5", "MDV6", "YOLO", "ONNX"]
Expand All @@ -13,29 +17,35 @@ checkpoint_frequency: -1
log_dir: /home/johnsmith/cougarvision/logs/
#classes - path to the class list for the classifier model
classes: /home/johnsmith/cougarvision/labels/sw_classes.txt
#Run version that allows for images cast to two screens? True/False
visualize_output: True
#for visualizing output, folder for cougarvision to put all images
path_to_unlabeled_output: /path/to/unlabeled/img/folder
#for visualizing output, folder for cougarvision detections with bounding boxes
path_to_labeled_output: /path/to/labeled/img/folder
#the emails that will receive email alerts, can be multiple emails
consumer_emails: [<insert emails>]
# the amiul (s) you with to receive the developmemt email alert- contains confidence value of detection
dev_emails: [<insert emails>]
consumer_emails: [your@email.com]
#emails to recieve daily check-in and confidence of detections
dev_emails: [your@email.com]
#threshold confidence score
confidence: 0.7
#cpu threads
threads: 8
# Web Scraping
#strike force api url
strikeforce_api: https://api.strikeforcewireless.com/api/v2/
#strikeforce wireless username
username_scraper: [yourusername.cam@gmail.com]
username_scraper: yourusername.cam@gmail.com
#strikeforce wireless password
password_scraper: [yourpassword]
#authorization token from Strike Force, can be obtained with login credentials and using strikeforceget.py in /cougarvision_utils folder
auth_token: ["<insert account token>"]
password_scraper: yourpassword
#authorization token from Strike Force
auth_token: <must get from web dev tools after strikeforcewireless login>
#save_dir - path to where the images get stored (must create folder)
save_dir: /home/johnsmith/images/
#dictionary containing the strikeforce unique ID with the actual 4 digit camera name, ID can be obtained using strikeforcegetcameras.py within the /cougarvision_utils folder
camera_names: {'<strikeforce id>': <camera name>, ...}
#dict of camera names and their respective strikeforce wireless ids, these must be retrieved from
#strikeforce wireless web dev tools in the code and currently the cam names must be 4 chars long
camera_names: {'<strikeforceid>': <camera name>, '<strikeforceid>': <camera name>...}
#animals that the detector will send alerts for if detected (from class list)
alert_targets: [cougar, bobcat, skunk, deer, dog, coyote]
alert_targets: [cougar, bobcat, skunk, deer, dog, coyote, rabbit, elephant]
#checkn_interval - time interval between still-alive emails to let us know no errors have crashed
#cougarvision if it's running constantly
checkin_interval: 24
Expand Down
17 changes: 17 additions & 0 deletions cougarvision_utils/detect_img.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
from io import BytesIO
from datetime import datetime as dt
import re
import os
from PIL import Image
from animl import classification, split
from animl import detection
from sageranger import is_target, attach_image, post_event

from cougarvision_utils.cropping import draw_bounding_box_on_image
from cougarvision_utils.alert import smtp_setup, send_alert
from cougarvision_visualize.visualize_helper import get_last_file_number


def detect(images, config): # pylint: disable=too-many-locals
Expand Down Expand Up @@ -110,6 +112,21 @@ def detect(images, config): # pylint: disable=too-many-locals
image_bytes = BytesIO()
img.save(image_bytes, format="JPEG")
img_byte = image_bytes.getvalue()

# not sure if labeled_img is meant to point at Images
labeled_img = config.path_to_labeled_output
if config.visualize_output is True:
os.makedirs(labeled_img, exist_ok=True)
last_file_number = get_last_file_number(labeled_img)
new_file_number = last_file_number + 1
name = images[idx][2]
name = name.split('/')[-1]
#new_file_name = f"{labeled_img + name}/image_{new_file_number}.jpg"
new_file_name = f"{labeled_img + "/" + name}"

with open(new_file_name, "wb") as folder:
folder.write(img_byte)

cam_name = cougars.at[idx, 'cam_name']
er_alerts = config.er_alerts
if label in config.alert_targets and er_alerts is True:
Expand Down
42 changes: 30 additions & 12 deletions cougarvision_utils/get_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import logging
import requests
import numpy as np
import os.path
import os
from cougarvision_visualize.visualize_helper import get_last_file_number

# pylint: disable=pointless-string-statement
"""
Expand All @@ -26,30 +29,29 @@
request <- "cameras"
parameters <- ""

#recent photo count
recent photo count
request <- "photos/recent/count"
parameters <- ""

#get recent photos across cameras
get recent photos across cameras
request <- "photos/recent"
parameters <- "limit=100"

#get photos from specific camera (will need to loop through pages)
get photos from specific camera (will need to loop through pages)
request <- "photos"
parameters <- "page=3&sort_date=desc&camera_id[]=59681"

#get photos from specific camera filtered by date (will need
# to loop through pages)
get photos from specific camera filtered by date (will need
to loop through pages)
request <- "photos"
parameters <- "page=1&sort_date=desc&camera_id[]=
#60272&date_start=2022-09-01&date_end=2022-10-07"
60272&date_start=2022-09-01&date_end=2022-10-07"

#get subscriptions
get subscriptions
request <- "subscriptions"
parameters <- ""
"""


def request_strikeforce(username, auth_token, base, request, parameters):
"""Strikeforce API call request.

Expand All @@ -69,7 +71,7 @@ def request_strikeforce(username, auth_token, base, request, parameters):
request fails.
"""
call = base + request + "?" + parameters

# if there is no internet connection try 5 times before raising exception
max_retries = 5
for attempt in range(max_retries):
Expand Down Expand Up @@ -148,21 +150,37 @@ def fetch_image_api(config): # pylint: disable=too-many-locals
print(info)

try:

camera = config.camera_names[photo['relationships']
['camera']['data']['id']]
except KeyError:
logging.warning('Cannot retrieve photo from camera\
as there is no asssociated ID in the config file')
logging.warning('skipped img: no associated cam ID')
continue
newname = config.save_dir + camera
newname += "_" + str(info['id'])
# 2023-10-29 16:17:20 -0700
date_time = (str(info['local_time'])).rsplit(' ',1)[0]
newname += "_" + date_time
newname += "_" + info['file_thumb_filename']

# native extension from strikeforce is .JPG.jpeg for some reason
stripped_name = newname.replace(".JPG.jpeg", ".jpg")
urllib.request.urlretrieve(info['file_thumb_url'], stripped_name)
new_photos.append([photo['id'],
info['file_thumb_url'], stripped_name])

# not sure what unlabeled output should be
unlabeled_img = config.path_to_unlabeled_output
if config.visualize_output is True:
os.makedirs(unlabeled_img, exist_ok=True)
newname = unlabeled_img + 'image'
new_file_num = get_last_file_number(unlabeled_img)
new_file_num = new_file_num + 1
# new_file_num = str(new_file_num)
newname = os.path.join(unlabeled_img, f"image_{new_file_num}.jpg")
# newname += "_" + new_file_num
urllib.request.urlretrieve(info['file_thumb_url'], newname)



new_photos = np.array(new_photos)
if len(new_photos) > 0: # update last image
Expand Down
11 changes: 11 additions & 0 deletions cougarvision_utils/get_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class ConfigInfo:
auth_token: str
id_path: str
password_scraper: str
visualize_output: str
path_to_unlabeled_output: str
path_to_labeled_output: str

# runtime fields not apart of inital constructor
classifier_model_load: Any = field(init=False)
Expand All @@ -70,3 +73,11 @@ def __post_init__(self):
self.detector_model_load = load_detector(
self.detector_model, self.detector_model_type
)


@dataclass
class display_info:
path_to_unlabeled_output: str
path_to_labeled_output: str
save_dir: str
display_num: int
19 changes: 0 additions & 19 deletions cougarvision_utils/strikeforceget.py

This file was deleted.

36 changes: 0 additions & 36 deletions cougarvision_utils/strikeforcegetcameras.py

This file was deleted.

Loading