diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc index 15624c7..2e266ff 100644 Binary files a/__pycache__/utils.cpython-311.pyc and b/__pycache__/utils.cpython-311.pyc differ diff --git a/hailort.log b/hailort.log index 4eb2e6a..30bacaa 100644 --- a/hailort.log +++ b/hailort.log @@ -4703,4 +4703,25 @@ [2025-10-31 15:59:20.277] [277301] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] EntryPushQEl0yolov11l_inat/input_layer1 | inputs: user | outputs: PreInferEl3yolov11l_inat/input_layer1(running in thread_id: 277320) [2025-10-31 15:59:20.277] [277301] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] PreInferEl3yolov11l_inat/input_layer1 | inputs: EntryPushQEl0yolov11l_inat/input_layer1[0] | outputs: PushQEl3yolov11l_inat/input_layer1 [2025-10-31 15:59:20.277] [277301] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] PushQEl3yolov11l_inat/input_layer1 | inputs: PreInferEl3yolov11l_inat/input_layer1[0] | outputs: AsyncHwEl(running in thread_id: 277321) -[2025-10-31 15:59:20.277] [277301] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] AsyncHwEl | inputs: PushQEl3yolov11l_inat/input_layer1[0] | outputs: MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Pro \ No newline at end of file +[2025-10-31 15:59:20.277] [277301] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] AsyncHwEl | inputs: PushQEl3yolov11l_inat/input_layer1[0] | outputs: MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Pro[2025-10-31 20:58:46.509] [278956] [HailoRT] [info] [vdevice.cpp:523] [create] Creating vdevice with params: device_count: 1, scheduling_algorithm: ROUND_ROBIN, multi_process_service: false +[2025-10-31 20:58:46.520] [278956] [HailoRT] [info] [device.cpp:49] [Device] OS Version: Linux 6.12.47+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.12.47-1+rpt1~bookworm (2025-09-16) aarch64 +[2025-10-31 20:58:46.521] [278956] [HailoRT] [info] [control.cpp:108] [control__parse_identify_results] firmware_version is: 4.20.0 +[2025-10-31 20:58:46.521] [278956] [HailoRT] [info] [vdevice.cpp:651] [create] VDevice Infos: 0001:01:00.0 +[2025-10-31 20:58:46.543] [278956] [HailoRT] [info] [hef.cpp:1929] [get_network_group_and_network_name] No name was given. Addressing all networks of default network_group: yolov11l_inat +[2025-10-31 20:58:46.543] [278956] [HailoRT] [info] [hef.cpp:1929] [get_network_group_and_network_name] No name was given. Addressing all networks of default network_group: yolov11l_inat +[2025-10-31 20:58:46.554] [278956] [HailoRT] [info] [internal_buffer_manager.cpp:204] [print_execution_results] Planned internal buffer memory: CMA memory 0, user memory 8041984. memory to edge layer usage factor is 0.8673109 +[2025-10-31 20:58:46.554] [278956] [HailoRT] [info] [internal_buffer_manager.cpp:212] [print_execution_results] Default Internal buffer planner executed successfully +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [device_internal.cpp:57] [configure] Configuring HEF took 46.568392 milliseconds +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [vdevice.cpp:749] [configure] Configuring HEF on VDevice took 46.890412 milliseconds +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [infer_model.cpp:436] [configure] Configuring network group 'yolov11l_inat' with params: batch size: 0, power mode: PERFORMANCE, latency: NONE +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [multi_io_elements.cpp:756] [create] Created (AsyncHwEl) +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [queue_elements.cpp:450] [create] Created (EntryPushQEl0yolov11l_inat/input_layer1 | timeout: 10s) +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [filter_elements.cpp:101] [create] Created (PreInferEl3yolov11l_inat/input_layer1 | Reorder - src_order: NHWC, src_shape: (640, 640, 3), dst_order: NHCW, dst_shape: (640, 640, 3)) +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [queue_elements.cpp:450] [create] Created (PushQEl3yolov11l_inat/input_layer1 | timeout: 10s) +[2025-10-31 20:58:46.590] [278956] [HailoRT] [info] [multi_io_elements.cpp:135] [create] Created (NmsPPMuxEl0YOLOV8-Post-Process | Op YOLOV8, Name: YOLOV8-Post-Process, Score threshold: 0.200, IoU threshold: 0.70, Classes: 198, Cross classes: false, NMS results order: BY_CLASS, Max bboxes per class: 100, Image height: 640, Image width: 640) +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [queue_elements.cpp:942] [create] Created (MultiPushQEl0YOLOV8-Post-Process | timeout: 10s) +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [edge_elements.cpp:187] [create] Created (LastAsyncEl0NmsPPMuxEl0YOLOV8-Post-Process) +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] EntryPushQEl0yolov11l_inat/input_layer1 | inputs: user | outputs: PreInferEl3yolov11l_inat/input_layer1(running in thread_id: 278975) +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] PreInferEl3yolov11l_inat/input_layer1 | inputs: EntryPushQEl0yolov11l_inat/input_layer1[0] | outputs: PushQEl3yolov11l_inat/input_layer1 +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] PushQEl3yolov11l_inat/input_layer1 | inputs: PreInferEl3yolov11l_inat/input_layer1[0] | outputs: AsyncHwEl(running in thread_id: 278976) +[2025-10-31 20:58:46.591] [278956] [HailoRT] [info] [pipeline.cpp:891] [print_deep_description] AsyncHwEl | inputs: PushQEl3yolov11l_inat/input_layer1[0] | outputs: MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Process MultiPushQEl0YOLOV8-Post-Pro \ No newline at end of file diff --git a/snap_version.py b/snap_version.py new file mode 100644 index 0000000..6d50fd0 --- /dev/null +++ b/snap_version.py @@ -0,0 +1,112 @@ +from common_code.settings import get_logger +from utils import run_model, start_snap_manager +import threading +import queue +import multiprocessing +import time + +logger = get_logger('live_inference', file_path='/var/log/live_inference.log', stdout=True) + +all_cameras_config = { + "camera_sidefeeder": { + 'ip': "192.168.1.157", + 'resolution': (360, 640, 3) + }, + "camera_driveway": { + 'ip': "192.168.1.152", + 'resolution': (480, 640, 3) + }, + "camera_railing": { + 'ip': "192.168.1.153", + 'resolution': (512, 896, 3) + }, + "camera_ptz": { + 'ip': "192.168.1.155", + 'resolution': (360, 640, 3) + }, + "camera_wrenwatch": { + 'ip': "192.168.1.158", + 'resolution': (360, 640, 3) + }, + "camera_backyard": { + 'ip': "192.168.1.162", + 'resolution': (432, 1536, 3), + 'split_into_two': True + }, +} + +for cam, details in all_cameras_config.items(): + details['url_rtsp'] = f"rtsp://admin:marybear@{details['ip']}:554/h264Preview_01_sub" + details['url_api'] = f"http://{details['ip']}/cgi-bin/api.cgi" + details['username'] = 'admin' + details['password'] = 'marybear' + details['camera_name'] = cam + +cameras_config = all_cameras_config + +def start_system(): + # Create shared multiprocessing queue for model + img_scoring_queue = multiprocessing.Queue(maxsize=len(cameras_config) * 2) + + # Create threading queues for each camera + for cam, details in cameras_config.items(): + details['msg_queue'] = queue.Queue(maxsize=1) + details['img_scoring_queue'] = img_scoring_queue + + # Start model process + model_process = multiprocessing.Process(target=run_model, args=(img_scoring_queue,)) + model_process.daemon = True + model_process.start() + logger.info('Started model process') + + # Start camera threads + camera_threads = [] + for cam_name, details in cameras_config.items(): + thread = threading.Thread(target=start_snap_manager, kwargs=details) + thread.daemon = True + thread.start() + camera_threads.append(thread) + logger.info(f'Started camera thread for {cam_name}') + + logger.info("System started. Available cameras:") + for cam_name in cameras_config.keys(): + logger.info(f" - {cam_name}") + + # Auto-capture loop - continuously send get messages when queues are empty + msg_counts = dict() + for cam_name in cameras_config: + msg_counts[cam_name] = 0 + + try: + while True: + for cam_name, details in cameras_config.items(): + try: + if details['msg_queue'].empty(): + details['msg_queue'].put('get', block=False) + msg_counts[cam_name]+=1 + logger.debug(f"Auto-sent 'get' to {cam_name}") + except queue.Full: + pass # Queue full, skip + except Exception as e: + logger.error(f"Error auto-sending to {cam_name}: {e}") + + time.sleep(0.1) # Small delay + except KeyboardInterrupt: + logger.info("Shutting down...") + # Send exit messages to all cameras + for details in cameras_config.values(): + try: + details['msg_queue'].put('exit', block=False) + except: + pass + + # Terminate model process + if model_process.is_alive(): + model_process.terminate() + model_process.join(timeout=5) + if model_process.is_alive(): + model_process.kill() + +if __name__ == "__main__": + multiprocessing.set_start_method('spawn', force=True) # Ensure compatibility + start_system() diff --git a/species_list b/species_list new file mode 100644 index 0000000..1617f45 --- /dev/null +++ b/species_list @@ -0,0 +1,198 @@ +Acanthis flammea +Accipiter cooperii +Accipiter striatus +Actitis macularius +Agelaius phoeniceus +Aix sponsa +Ambystoma laterale +Ambystoma maculatum +Ambystoma tigrinum +Anas crecca +Anas platyrhynchos +Anas platyrhynchos domesticus +Anas rubripes +Anaxyrus americanus +Anaxyrus americanus americanus +Anser anser +Anser anser domesticus +Antigone canadensis +Aramus guarauna +Archilochus colubris +Ardea alba +Ardea herodias +Asio flammeus +Aythya affinis +Aythya americana +Aythya collaris +Aythya marila +Aythya valisineria +Baeolophus bicolor +Blarina brevicauda +Bombycilla cedrorum +Branta canadensis +Branta hutchinsii +Bubo virginianus +Bucephala albeola +Bucephala clangula +Buteo jamaicensis +Buteo lagopus +Buteo platypterus +Butorides virescens +Calidris minutilla +Canis latrans +Cardellina canadensis +Cardellina pusilla +Cardinalis cardinalis +Castor canadensis +Cathartes aura +Catharus guttatus +Catharus minimus +Catharus ustulatus +Certhia americana +Chaetura pelagica +Charadrius vociferus +Coccyzus americanus +Coccyzus erythropthalmus +Colaptes auratus +Columba livia domestica +Contopus cooperi +Contopus virens +Corvus brachyrhynchos +Cyanocitta cristata +Cygnus buccinator +Cygnus olor +Didelphis virginiana +Dolichonyx oryzivorus +Dryocopus pileatus +Dumetella carolinensis +Empidonax minimus +Empidonax traillii +Eptesicus fuscus +Eremophila alpestris +Falco columbarius +Falco peregrinus +Felis catus +Fulica americana +Gavia immer +Gavia stellata +Geothlypis trichas +Glaucomys volans +Haemorhous mexicanus +Haliaeetus leucocephalus +Hirundo rustica +Histrionicus histrionicus +Hydroprogne caspia +Hyla versicolor +Hylocichla mustelina +Icterus cucullatus +Icterus galbula +Icterus spurius +Ictinia mississippiensis +Junco hyemalis +Larus delawarensis +Lithobates catesbeianus +Lithobates clamitans +Lithobates pipiens +Lithobates sylvaticus +Lophodytes cucullatus +Marmota monax +Megaceryle alcyon +Megascops asio +Melanerpes carolinus +Melanerpes erythrocephalus +Meleagris gallopavo +Meleagris gallopavo silvestris +Melospiza georgiana +Melospiza lincolnii +Melospiza melodia +Mephitis mephitis +Mergus merganser +Mniotilta varia +Molothrus ater +Myiarchus crinitus +Notophthalmus viridescens +Nyctanassa violacea +Nycticorax nycticorax +Odocoileus virginianus +Ondatra zibethicus +Oreothlypis peregrina +Oreothlypis ruficapilla +Oxyura jamaicensis +Pandion haliaetus +Passer domesticus +Passerculus sandwichensis +Passerina cyanea +Petrochelidon pyrrhonota +Phasianus colchicus +Pheucticus ludovicianus +Picoides pubescens +Picoides villosus +Pipilo erythrophthalmus +Piranga olivacea +Piranga rubra +Plethodon cinereus +Podiceps auritus +Podilymbus podiceps +Poecile atricapillus +Polioptila caerulea +Procyon lotor +Procyon lotor lotor +Pseudacris crucifer +Quiscalus quiscula +Regulus calendula +Regulus satrapa +Sayornis phoebe +Scalopus aquaticus +Sciurus carolinensis +Sciurus niger +Scolopax minor +Seiurus aurocapilla +Setophaga americana +Setophaga caerulescens +Setophaga castanea +Setophaga coronata +Setophaga coronata coronata +Setophaga fusca +Setophaga magnolia +Setophaga palmarum +Setophaga pensylvanica +Setophaga petechia +Setophaga pinus +Setophaga ruticilla +Setophaga striata +Setophaga tigrina +Setophaga virens +Sialia sialis +Sitta canadensis +Sitta carolinensis +Sphyrapicus varius +Spinus pinus +Spinus tristis +Spizella passerina +Spizella pusilla +Spizelloides arborea +Stelgidopteryx serripennis +Strix varia +Sturnella magna +Sturnus vulgaris +Sylvilagus floridanus +Tachycineta bicolor +Tamias striatus +Tamiasciurus hudsonicus +Thryothorus ludovicianus +Toxostoma rufum +Tringa solitaria +Troglodytes aedon +Troglodytes hiemalis +Turdus migratorius +Tyrannus tyrannus +Vermivora chrysoptera +Vermivora cyanoptera +Vireo gilvus +Vireo olivaceus +Vireo philadelphicus +Vireo solitarius +Vulpes vulpes +Zenaida macroura +Zonotrichia albicollis +Zonotrichia leucophrys diff --git a/test.py b/test.py new file mode 100644 index 0000000..e3c2b92 --- /dev/null +++ b/test.py @@ -0,0 +1,11 @@ +import time +import redis +r = redis.Redis('localhost',port=6379, db=14) +max_fail_age = 60*5, +proc_start_time = time.time() +# %% + + +redis_topic_ffmpeg_fail = 'camera_sidefeeder_sm_fail' +last_fails = [float(x) > max_fail_age for x in r.lrange(redis_topic_ffmpeg_fail, 0, -1) if float(x) > proc_start_time] + diff --git a/utils.py b/utils.py index b1362ae..4eba147 100644 --- a/utils.py +++ b/utils.py @@ -30,14 +30,45 @@ import json import redis import os pfm = LogColorize.score_obj_det_embed - +# %% with open('/home/thebears/source/infer/species_list','r') as sl: species_list = [x for x in sl.read().split('\n') if len(x) > 0] r = redis.Redis('localhost',port=6379, db=14) logger = logging.getLogger('live_inference') - + +def get_snap( url, username, password, proxies = None, timeout=5, save_image = None, camera_name = 'N/A'): + data = { + 'cmd': 'Snap', + 'channel': 0, + 'rs': ''.join(choices(string.ascii_uppercase + string.digits, k=10)), + 'snapType':'sub', + 'user': username, + 'password': password, + } + parms = parse.urlencode(data, safe="!").encode("utf-8") + + try: + response = requests.get(url, proxies=proxies, params=parms, timeout=timeout) + if response.status_code == 200: + rearr = np.frombuffer(bytearray(response.content), dtype=np.uint8) + img_bgr = cv2.imdecode(rearr,cv2.IMREAD_COLOR) + img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) + if save_image is not None: + os.makedirs(os.path.dirname(save_image), exist_ok=True) + cv2.imwrite( save_image, img_bgr) + logging.info(f'{camera_name}: Wrote image to {save_image}') + + logging.info(f'{camera_name}: Got image of {img_rgb.shape}') + return img_rgb + + except Exception as e: + logging.error(f'{camera_name} failure: {str(e)}') + raise + + +# %% def resize_image(img_in, reshape_to_final=False): if not isinstance(img_in, np.ndarray): img_in = np.asarray(img_in) @@ -153,7 +184,7 @@ class SnapManager(): self.password = password self.camera_name = camera_name self.split_into_two = split_into_two -n self.msg_queue = msg_queue + self.msg_queue = msg_queue self.img_scoring_queue = img_scoring_queue logger.info(f"{self.camera_name}: initialized") @@ -187,21 +218,18 @@ n self.msg_queue = msg_queue return msg def capture_and_prepare(self): - img = get_snap(self.username, self.password, self.url_api, self.camera_name) + img = get_snap(self.url_api,self.username, self.password, camera_name = self.camera_name) if img is not None: timestamp = time.time() return self.format_image_for_model(img, timestamp) return [] - def run_forever(self): while True: try: msg = self.msg_queue.get(timeout=0.1) if msg == 'exit': break - if msg == 'save_image': - if msg == 'get': logger.info(f'Processing capture for {self.camera_name}') model_msgs = self.capture_and_prepare()