Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions config/fetch_and_alert.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ 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"]
detector_model_type: "<insert model type>"
#classifier_model - path to classifier model
classifier_model: /home/johnsmith/cougarvision/classifier_models/EfficientNetB5_456_Unfrozen_05_0.26_0.92.h5
checkpoint_frequency: -1
Expand Down
150 changes: 25 additions & 125 deletions cougarvision_env.yml
Original file line number Diff line number Diff line change
@@ -1,129 +1,29 @@
name: cougarvision
name: cougarvision_env
channels:
- conda-forge
- defaults
- nodefaults
dependencies:
- _libgcc_mutex=0.1=main
- _openmp_mutex=4.5=1_gnu
- _tflow_select=2.1.0=gpu
- absl-py=0.15.0=pyhd3eb1b0_0
- aiohttp=3.8.1=py38h7f8727e_0
- aiosignal=1.2.0=pyhd3eb1b0_0
- astor=0.8.1=py38h06a4308_0
- astunparse=1.6.3=py_0
- async-timeout=4.0.1=pyhd3eb1b0_0
- attrs=21.4.0=pyhd3eb1b0_0
- blas=1.0=mkl
- blinker=1.4=py38h06a4308_0
- brotlipy=0.7.0=py38h27cfd23_1003
- c-ares=1.18.1=h7f8727e_0
- ca-certificates=2022.3.29=h06a4308_0
- cachetools=4.2.2=pyhd3eb1b0_0
- certifi=2021.10.8=py38h06a4308_2
- cffi=1.15.0=py38hd667e15_1
- charset-normalizer=2.0.4=pyhd3eb1b0_0
- click=8.0.4=py38h06a4308_0
- cryptography=3.4.8=py38hd23ed53_0
- cudatoolkit=10.1.243=h6bb024c_0
- cudnn=7.6.5=cuda10.1_0
- cupti=10.1.168=0
- dataclasses=0.8=pyh6d0b6a4_7
- frozenlist=1.2.0=py38h7f8727e_0
- gast=0.4.0=pyhd3eb1b0_0
- google-pasta=0.2.0=pyhd3eb1b0_0
- hdf5=1.10.6=hb1b8bf9_0
- idna=3.3=pyhd3eb1b0_0
- importlib-metadata=4.8.2=py38h06a4308_0
- intel-openmp=2021.4.0=h06a4308_3561
- keras-preprocessing=1.1.2=pyhd8ed1ab_0
- ld_impl_linux-64=2.35.1=h7274673_9
- libffi=3.3=he6710b0_2
- libgcc-ng=9.3.0=h5101ec6_17
- libgfortran-ng=7.5.0=ha8ba4b0_17
- libgfortran4=7.5.0=ha8ba4b0_17
- libgomp=9.3.0=h5101ec6_17
- libprotobuf=3.19.1=h4ff587b_0
- libstdcxx-ng=9.3.0=hd4cf53a_17
- markdown=3.3.4=py38h06a4308_0
- mkl=2021.4.0=h06a4308_640
- mkl-service=2.4.0=py38h7f8727e_0
- mkl_fft=1.3.1=py38hd3c417c_0
- mkl_random=1.2.2=py38h51133e4_0
- multidict=5.2.0=py38h7f8727e_2
- ncurses=6.3=h7f8727e_2
- oauthlib=3.2.0=pyhd3eb1b0_0
- openssl=1.1.1n=h7f8727e_0
- opt_einsum=3.3.0=pyhd3eb1b0_1
- pip=21.2.4=py38h06a4308_0
- pyasn1=0.4.8=pyhd3eb1b0_0
- pyasn1-modules=0.2.8=py_0
- pycparser=2.21=pyhd3eb1b0_0
- pyjwt=2.1.0=py38h06a4308_0
- pyopenssl=21.0.0=pyhd3eb1b0_1
- pysocks=1.7.1=py38h06a4308_0
- python=3.8.12=h12debd9_0
- python_abi=3.8=2_cp38
- readline=8.1.2=h7f8727e_1
- requests=2.27.1=pyhd3eb1b0_0
- requests-oauthlib=1.3.0=py_0
- rsa=4.7.2=pyhd3eb1b0_1
- scipy=1.7.3=py38hc147768_0
- selenium=3.141.0=py38h27cfd23_1000
- setuptools=58.0.4=py38h06a4308_0
- sqlite=3.37.2=hc218d9a_0
- tensorboard-plugin-wit=1.6.0=py_0
- tensorflow-estimator=2.6.0=pyh7b7c402_0
- tensorflow-gpu=2.4.1=h30adc30_0
- termcolor=1.1.0=py38h06a4308_1
- tk=8.6.11=h1ccaba5_0
- urllib3=1.26.8=pyhd3eb1b0_0
- werkzeug=2.0.3=pyhd3eb1b0_0
- wheel=0.37.1=pyhd3eb1b0_0
- xz=5.2.5=h7b6447c_0
- yaml=0.2.5=h516909a_0
- yarl=1.6.3=py38h27cfd23_0
- zipp=3.7.0=pyhd3eb1b0_0
- zlib=1.2.11=h7f8727e_4
- python=3.12
- pip
- setuptools
- wheel
- numpy>=2.0.2
- pandas>=2.2.2
- scikit-learn>=1.5.2
- pillow>=11.0.0
- tqdm>=4.66.5
- dill>=0.4.0
- openssl
- sqlite
- zlib
- pip:
- chardet==4.0.0
- clang==5.0
- datetime==5.2
- flatbuffers==1.12
- fonttools==4.29.1
- google-auth==2.22.0
- google-auth-oauthlib==1.0.0
- grpcio==1.56.2
- h5py==3.1.0
- humanfriendly==10.0
- imbox==0.9.8
- jsonpickle==2.1.0
- keras==2.6.0
- keras-applications==1.0.2
- kiwisolver==1.3.2
- matplotlib==3.5.1
- mmdnn==0.3.1
- numpy==1.19.5
- opencv-python==4.8.0.74
- packaging==21.3
- pandas==1.2.4
- pillow==9.0.1
- protobuf==4.23.4
- pyparsing==3.0.7
- python-dateutil==2.8.2
- pytz==2023.3
- pyyaml==6.0
- sageranger==1.0.3
- schedule==1.1.0
- six==1.15.0
- tensorboard==2.13.0
- tensorboard-data-server==0.7.1
- tensorflow==2.6.0
- torch==1.10.2
- torchvision==0.11.3
- tqdm==4.63.0
- typing-extensions==3.7.4.3
- tzdata==2023.3
- webdriver-manager==3.5.4
- wrapt==1.12.1
- zope-interface==6.0
prefix: /home/katie/anaconda3/envs/cougarvision
- --extra-index-url https://download.pytorch.org/whl/cu124
- torch>=2.6.0
- torchvision>=0.21.0
- onnxruntime-gpu>=1.23.2
- opencv-python>=4.12.0.88
- timm>=1.0.9
- ultralytics>=8.3.95
- wget>=3.2
- schedule>=1.2.2
- sageranger>=1.0.3
76 changes: 40 additions & 36 deletions cougarvision_utils/detect_img.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
import sys
import yaml
from PIL import Image
from animl import parse_results, classify, split
from animl import classification, split
from sageranger import is_target, attach_image, post_event
from animl.detectMD import detect_MD_batch
from animl import detection

from cougarvision_utils.cropping import draw_bounding_box_on_image
from cougarvision_utils.alert import smtp_setup, send_alert
Expand All @@ -28,7 +28,7 @@
sys.path.append(CAM_CONFIG['camera_traps_path'])


def detect(images, config, c_model, d_model):
def detect(images, config, c_model, d_model, class_list):
'''
This function takes in a dataframe of images and runs a detector model,
classifies the species of interest, and sends alerts either to email or an
Expand All @@ -46,7 +46,6 @@ def detect(images, config, c_model, d_model):
log_dir = config['log_dir']
checkpoint_f = config['checkpoint_frequency']
confidence = config['confidence']
classes = config['classes']
targets = config['alert_targets']
username = config['username']
password = config['password']
Expand All @@ -55,61 +54,66 @@ def detect(images, config, c_model, d_model):
host = 'imap.gmail.com'
token = config['token']
authorization = config['authorization']

if len(images) > 0:
# extract paths from dataframe
image_paths = images[:, 2]
# detection.detect expects the image paths in a list
image_path_list = image_paths.tolist()
# Run Detection
results = detect_MD_batch(d_model,
image_paths,
checkpoint_path=None,
confidence_threshold=confidence,
checkpoint_frequency=checkpoint_f,
results=None,
quiet=False,
image_size=None)
results = detection.detect(d_model,
image_path_list,
resize_width=1280,
resize_height=1280,
confidence_threshold=confidence,
checkpoint_frequency=checkpoint_f,
batch_size=4
)
# Parse results
data_frame = parse_results.from_MD(results, None, None)
data_frame = detection.parse_detections(results)
# single classification function checks for the file extension so we add it
data_frame["extension"] = data_frame["filepath"].str.extract(r'(\.[^.]+)$', expand=False).str.lower()
# filter out all non animal detections
if not data_frame.empty:
animal_df = split.getAnimals(data_frame)
other_df = split.getEmpty(data_frame)
animal_df = split.get_animals(data_frame)
other_df = split.get_empty(data_frame)
# run classifier on animal detections if there are any
if not animal_df.empty:
# create generator for images
predictions = classify.predict_species(animal_df, c_model,
batch=4)
# Parse results
max_df = parse_results.from_classifier(animal_df,
predictions,
classes,
None)
# Creates a data frame with all relevant data
cougars = max_df[max_df['prediction'].isin(targets)]
predictions_raw = classification.classify(c_model,
animal_df,
batch_size=4)
# single classification expects a list
class_list_for_series = class_list["species"].tolist()
preds = classification.single_classification(animal_df,
None,
predictions_raw,
class_list_for_series
)
cougars = preds[preds['prediction'].isin(targets)]
# drops all detections with confidence less than threshold
cougars = cougars[cougars['conf'] >= confidence]
cougars = cougars[cougars['confidence'] >= confidence]
# reset dataframe index
cougars = cougars.reset_index(drop=True)
# create a row in the dataframe containing only the camera name
# flake8: disable-next
cougars['cam_name'] = cougars['file'].apply(lambda x: re.findall(r'[A-Z]\d+', x)[0]) # noqa: E501 # pylint: disable-msg=line-too-long
cougars['cam_name'] = cougars['filepath'].apply(lambda x: re.findall(r'[A-Z]\d+', x)[0]) # noqa: E501 # pylint: disable-msg=line-too-long
# Sends alert for each cougar detection
for idx in range(len(cougars.index)):
label = cougars.at[idx, 'prediction']
# uncomment this line to use conf value for dev email alert
prob = str(cougars.at[idx, 'conf'])
label = cougars.at[idx, 'class']
img = Image.open(cougars.at[idx, 'file'])
prob = str(cougars.at[idx, 'confidence'])
img = Image.open(cougars.at[idx, 'filepath'])
draw_bounding_box_on_image(img,
cougars.at[idx, 'bbox2'],
cougars.at[idx, 'bbox1'],
cougars.at[idx, 'bbox_y'],
cougars.at[idx, 'bbox_x'],
cougars.at[idx,
'bbox2'] +
'bbox_y'] +
cougars.at[idx,
'bbox4'],
'bbox_h'],
cougars.at[idx,
'bbox1'] +
'bbox_x'] +
cougars.at[idx,
'bbox3'],
'bbox_w'],
expansion=0,
use_normalized_coordinates=True)
image_bytes = BytesIO()
Expand Down
7 changes: 4 additions & 3 deletions cougarvision_utils/get_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,11 @@ def fetch_image_api(config):
continue
newname = config['save_dir'] + camera
newname += "_" + info['file_thumb_filename']
print(newname)
urllib.request.urlretrieve(info['file_thumb_url'], newname)
# 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([photos[i]['id'],
info['file_thumb_url'], newname])
info['file_thumb_url'], stripped_name])

new_photos = np.array(new_photos)
if len(new_photos) > 0: # update last image
Expand Down
14 changes: 7 additions & 7 deletions fetch_and_alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
from cougarvision_utils.alert import checkin
from cougarvision_utils.get_images import fetch_image_api
from sageranger.post_monthly import post_monthly_obs
from animl.classify import load_classifier
from animl import megadetector
from animl.classification import load_classifier
from animl.detection import load_detector


# Numpy FutureWarnings from tensorflow import
Expand All @@ -54,15 +54,15 @@
DETECTOR = CONFIG['detector_model']
DEV_EMAILS = CONFIG['dev_emails']
HOST = 'imap.gmail.com'

CLASSES = CONFIG['classes']
MODEL_TYPE = CONFIG['detector_model_type']

# Set interval for checking in
CHECKIN_INTERVAL = CONFIG['checkin_interval']

# load models once
CLASSIFIER_MODEL = load_classifier(CLASSIFIER)
DETECTOR_MODEL = megadetector.MegaDetector(DETECTOR)

CLASSIFIER_MODEL, CLASS_LIST = load_classifier(CLASSIFIER, CLASSES)
DETECTOR_MODEL = load_detector(DETECTOR, MODEL_TYPE)

def logger():
'''Function for creating log file'''
Expand All @@ -77,7 +77,7 @@ def fetch_detect_alert():
images = fetch_image_api(CONFIG)
print('Finished fetching images')
print('Starting Detection')
detect(images, CONFIG, CLASSIFIER_MODEL, DETECTOR_MODEL)
detect(images, CONFIG, CLASSIFIER_MODEL, DETECTOR_MODEL, CLASS_LIST)
print('Finished Detection')
print("Sleeping since: " + str(dt.now()))

Expand Down