43
43
DEFAULT_FRAME_NUM = 123456789
44
44
DEFAULT_HIGHPASS_FILTER_SIZE = 25
45
45
DEFAULT_NO_FILTER = 0
46
+ SHORT_BASE = "cam" # Use this as the short base for camera file naming
46
47
47
48
48
49
@@ -378,13 +379,48 @@ def py_correspondences_proc_c(exp):
378
379
else :
379
380
print ("Warning: No way to determine base names, skipping target writing" )
380
381
381
- print (
382
- "Frame "
383
- + str (frame )
384
- + " had "
385
- + repr ([s .shape [1 ] for s in sorted_pos ])
386
- + " correspondences."
387
- )
382
+ # Generate short_file_bases once per experiment
383
+ img_base_names = [exp .spar .get_img_base_name (i ) for i in range (exp .num_cams )]
384
+ exp .short_file_bases = generate_short_file_bases (img_base_names )
385
+
386
+ for frame in range (exp .spar .get_first (), exp .spar .get_last () + 1 ):
387
+ detections = []
388
+ corrected = []
389
+ for i_cam in range (exp .num_cams ):
390
+ targs = read_targets (exp .short_file_bases [i_cam ], frame )
391
+ targs .sort_y ()
392
+ detections .append (targs )
393
+ matched_coords = MatchedCoords (targs , exp .cpar , exp .cals [i_cam ])
394
+ pos , _ = matched_coords .as_arrays ()
395
+ corrected .append (matched_coords )
396
+ sorted_pos , sorted_corresp , _ = correspondences (
397
+ detections , corrected , exp .cals , exp .vpar , exp .cpar
398
+ )
399
+ print (
400
+ "Frame "
401
+ + str (frame )
402
+ + " had "
403
+ + repr ([s .shape [1 ] for s in sorted_pos ])
404
+ + " correspondences."
405
+ )
406
+ sorted_pos = np .concatenate (sorted_pos , axis = 1 )
407
+ sorted_corresp = np .concatenate (sorted_corresp , axis = 1 )
408
+ flat = np .array (
409
+ [corrected [i ].get_by_pnrs (sorted_corresp [i ]) for i in range (len (exp .cals ))]
410
+ )
411
+ pos , _ = point_positions (flat .transpose (1 , 0 , 2 ), exp .cpar , exp .cals , exp .vpar )
412
+ if len (exp .cals ) < 4 :
413
+ print_corresp = - 1 * np .ones ((4 , sorted_corresp .shape [1 ]))
414
+ print_corresp [: len (exp .cals ), :] = sorted_corresp
415
+ else :
416
+ print_corresp = sorted_corresp
417
+ rt_is_filename = default_naming ["corres" ].decode ()
418
+ rt_is_filename = f"{ rt_is_filename } .{ frame } "
419
+ with open (rt_is_filename , "w" , encoding = "utf8" ) as rt_is :
420
+ rt_is .write (str (pos .shape [0 ]) + "\n " )
421
+ for pix , pt in enumerate (pos ):
422
+ pt_args = (pix + 1 ,) + tuple (pt ) + tuple (print_corresp [:, pix ])
423
+ rt_is .write ("%4d %9.3f %9.3f %9.3f %4d %4d %4d %4d\n " % pt_args )
388
424
389
425
return sorted_pos , sorted_corresp , num_targs
390
426
@@ -532,31 +568,30 @@ def py_sequence_loop(exp) -> None:
532
568
533
569
first_frame = spar .get_first ()
534
570
last_frame = spar .get_last ()
535
- print (f" From { first_frame = } to { last_frame = } " )
571
+ # Generate short_file_bases once per experiment
572
+ img_base_names = [spar .get_img_base_name (i ) for i in range (num_cams )]
573
+ short_file_bases = generate_short_file_bases (img_base_names )
574
+ exp .short_file_bases = short_file_bases
536
575
537
576
for frame in range (first_frame , last_frame + 1 ):
538
577
detections = []
539
578
corrected = []
540
579
for i_cam in range (num_cams ):
541
- base_image_name = spar .get_img_base_name (i_cam )
542
580
if existing_target :
543
- targs = read_targets (base_image_name , frame )
581
+ targs = read_targets (short_file_bases [ i_cam ] , frame )
544
582
else :
545
- imname = Path (base_image_name % frame )
583
+ imname = Path (img_base_names [ i_cam ] % frame )
546
584
if not imname .exists ():
547
585
raise FileNotFoundError (f"{ imname } does not exist" )
548
586
else :
549
587
img = imread (imname )
550
588
if img .ndim > 2 :
551
589
img = rgb2gray (img )
552
-
553
590
if img .dtype != np .uint8 :
554
591
img = img_as_ubyte (img )
555
-
556
592
if parameter_manager .get_parameter ('ptv' , {}).get ('inverse' , False ):
557
593
print ("Invert image" )
558
594
img = negative (img )
559
-
560
595
masking_params = parameter_manager .get_parameter ('masking' )
561
596
if masking_params and masking_params .get ('mask_flag' , False ):
562
597
try :
@@ -566,85 +601,71 @@ def py_sequence_loop(exp) -> None:
566
601
)
567
602
background = imread (background_name )
568
603
img = np .clip (img - background , 0 , 255 ).astype (np .uint8 )
569
-
570
604
except (ValueError , FileNotFoundError ):
571
605
print ("failed to read the mask" )
572
-
573
606
high_pass = simple_highpass (img , cpar )
574
607
targs = target_recognition (high_pass , tpar , i_cam , cpar )
575
-
576
608
targs .sort_y ()
577
- # print(len(targs))
578
609
detections .append (targs )
579
610
matched_coords = MatchedCoords (targs , cpar , cals [i_cam ])
580
611
pos , _ = matched_coords .as_arrays ()
581
612
corrected .append (matched_coords )
582
-
583
613
sorted_pos , sorted_corresp , _ = correspondences (
584
614
detections , corrected , cals , vpar , cpar
585
615
)
586
-
587
616
for i_cam in range (num_cams ):
588
- base_name = spar .get_img_base_name (i_cam )
589
- write_targets (detections [i_cam ], base_name , frame )
590
-
617
+ write_targets (detections [i_cam ], short_file_bases [i_cam ], frame )
591
618
print (
592
619
"Frame "
593
620
+ str (frame )
594
621
+ " had "
595
622
+ repr ([s .shape [1 ] for s in sorted_pos ])
596
623
+ " correspondences."
597
624
)
598
-
599
625
sorted_pos = np .concatenate (sorted_pos , axis = 1 )
600
626
sorted_corresp = np .concatenate (sorted_corresp , axis = 1 )
601
-
602
627
flat = np .array (
603
628
[corrected [i ].get_by_pnrs (sorted_corresp [i ]) for i in range (len (exp .cals ))]
604
629
)
605
630
pos , _ = point_positions (flat .transpose (1 , 0 , 2 ), exp .cpar , exp .cals , exp .vpar )
606
-
607
631
if len (exp .cals ) < 4 :
608
632
print_corresp = - 1 * np .ones ((4 , sorted_corresp .shape [1 ]))
609
633
print_corresp [: len (exp .cals ), :] = sorted_corresp
610
634
else :
611
635
print_corresp = sorted_corresp
612
-
613
636
rt_is_filename = default_naming ["corres" ].decode ()
614
637
rt_is_filename = f"{ rt_is_filename } .{ frame } "
615
638
with open (rt_is_filename , "w" , encoding = "utf8" ) as rt_is :
616
639
rt_is .write (str (pos .shape [0 ]) + "\n " )
617
640
for pix , pt in enumerate (pos ):
618
641
pt_args = (pix + 1 ,) + tuple (pt ) + tuple (print_corresp [:, pix ])
619
642
rt_is .write ("%4d %9.3f %9.3f %9.3f %4d %4d %4d %4d\n " % pt_args )
643
+ for pix , pt in enumerate (pos ):
644
+ pt_args = (pix + 1 ,) + tuple (pt ) + tuple (print_corresp [:, pix ])
645
+ rt_is .write ("%4d %9.3f %9.3f %9.3f %4d %4d %4d %4d\n " % pt_args )
620
646
621
647
622
648
def py_trackcorr_init (exp ):
623
649
"""Reads all the necessary stuff into Tracker"""
624
650
625
- for cam_id in range (exp .cpar .get_num_cams ()):
626
- img_base_name = exp .spar .get_img_base_name (cam_id )
627
- short_name = Path (img_base_name ).parent / f'cam{ cam_id + 1 } .'
628
- print (f" Renaming { img_base_name } to { short_name } before C library tracker" )
629
- exp .spar .set_img_base_name (cam_id , str (short_name ))
651
+ img_base_names = [exp .spar .get_img_base_name (cam_id ) for cam_id in range (exp .cpar .get_num_cams ())]
652
+ short_file_bases = generate_short_file_bases (img_base_names )
653
+
654
+ # Generate short_file_bases once per experiment
655
+ img_base_names = [exp .spar .get_img_base_name (i ) for i in range (exp .cpar .get_num_cams ())]
656
+ short_file_bases = generate_short_file_bases (img_base_names )
657
+ exp .short_file_bases = short_file_bases
658
+
659
+ for cam_id , short_name in enumerate (short_file_bases ):
660
+ print (f"Setting tracker image base name for cam { cam_id + 1 } : { Path (short_name ).resolve ()} " )
661
+ exp .spar .set_img_base_name (cam_id , str (Path (short_name ).resolve ()))
630
662
631
663
tracker = Tracker (
632
664
exp .cpar , exp .vpar , exp .track_par , exp .spar , exp .cals , default_naming
633
665
)
634
666
635
667
return tracker
636
668
637
-
638
- def py_trackcorr_loop ():
639
- """Supposedly returns some lists of the linked targets at every step of a tracker"""
640
- pass
641
-
642
-
643
- def py_traject_loop ():
644
- """Used to plot trajectories after the full run
645
- """
646
-
647
-
648
669
# ------- Utilities ----------#
649
670
650
671
@@ -775,9 +796,33 @@ def py_calibration(selection, exp):
775
796
return targs_all , targ_ix_all , residuals_all
776
797
777
798
778
- def read_targets (file_base : str , frame : int = 123456789 ) -> TargetArray :
799
+ def write_targets (targets : TargetArray , short_file_base : str , frame : int ) -> bool :
800
+ """Write targets to a file."""
801
+ filename = f"{ short_file_base } .{ frame :04d} _targets"
802
+ num_targets = len (targets )
803
+ success = False
804
+ try :
805
+ target_arr = np .array (
806
+ [
807
+ ([t .pnr (), * t .pos (), * t .count_pixels (), t .sum_grey_value (), t .tnr ()])
808
+ for t in targets
809
+ ]
810
+ )
811
+ np .savetxt (
812
+ filename ,
813
+ target_arr ,
814
+ fmt = "%4d %9.4f %9.4f %5d %5d %5d %5d %5d" ,
815
+ header = f"{ num_targets } " ,
816
+ comments = "" ,
817
+ )
818
+ success = True
819
+ except IOError :
820
+ print (f"Can't open targets file: { filename } " )
821
+ return success
822
+
823
+ def read_targets (short_file_base : str , frame : int ) -> TargetArray :
779
824
"""Read targets from a file."""
780
- filename = file_base_to_filename ( file_base , frame )
825
+ filename = f" { short_file_base } . { frame :04d } _targets"
781
826
print (f" filename: { filename } " )
782
827
783
828
try :
@@ -805,44 +850,48 @@ def read_targets(file_base: str, frame: int = 123456789) -> TargetArray:
805
850
return targs
806
851
807
852
808
- def write_targets (targets : TargetArray , file_base : str , frame : int = 123456789 ) -> bool :
809
- """Write targets to a file."""
810
- success = False
811
- filename = file_base_to_filename (file_base , frame )
812
- num_targets = len (targets )
813
-
814
- try :
815
- target_arr = np .array (
816
- [
817
- ([t .pnr (), * t .pos (), * t .count_pixels (), t .sum_grey_value (), t .tnr ()])
818
- for t in targets
819
- ]
820
- )
821
- np .savetxt (
822
- filename ,
823
- target_arr ,
824
- fmt = "%4d %9.4f %9.4f %5d %5d %5d %5d %5d" ,
825
- header = f"{ num_targets } " ,
826
- comments = "" ,
827
- )
828
- success = True
829
- except IOError :
830
- print (f"Can't open targets file: { filename } " )
831
-
832
- return success
833
-
834
-
835
- def file_base_to_filename (file_base , frame ):
836
- """Convert file base name to a filename"""
837
- file_base = os .path .splitext (file_base )[0 ]
838
- file_base = re .sub (r"_\d*d" , "" , file_base )
839
- if re .search (r"%\d*d" , file_base ):
840
- _ = re .sub (r"%\d*d" , "%04d" , file_base )
841
- filename = Path (f"{ _ % frame } _targets" )
842
- else :
843
- filename = Path (f"{ file_base } .{ frame :04d} _targets" )
853
+ from typing import Union
844
854
845
- return filename
855
+ def extract_cam_id (file_base : str ) -> int :
856
+ """
857
+ Extracts the camera ID (number only) from a file base string.
858
+ Returns the camera id as a string (e.g., "1", "2"), or "0" if not found.
859
+ it's possible that our files are not named as cam0,1,2,3 but can be anything
860
+ like cam_1, cam-2, cam.3, etc. or any number that is not the order, e.g.
861
+ cam4,cam7 and cam12.
862
+ If no camera ID is found, returns 0.
863
+ """
864
+ base = os .path .splitext (file_base )[0 ]
865
+ base = re .sub (r'([_.-]?%+0?\d*d)$' , '' , base )
866
+ # Only strip trailing frame numbers (e.g., .0001 or _0001) or _targets, not camera IDs
867
+ # base = re.sub(r'([_.-]?\d{3,5})?(_targets)?$', '', base)
868
+ # base = re.sub(r'([_.-]?\d+(_targets)?)$', '', base)
869
+ # Look for 'cam' followed by digits anywhere in the string
870
+ match = re .search (r'cam[_ -]*0*(\d+)' , base , re .IGNORECASE )
871
+ if match :
872
+ return int (match .group (1 ))
873
+ # Otherwise, look for the last group of digits in the string
874
+ matches = list (re .finditer (r'(\d+)' , base ))
875
+ if matches :
876
+ return int (matches [- 1 ].group (1 ))
877
+ return 0
878
+
879
+ def generate_short_file_bases (img_base_names : List [str ]) -> List [str ]:
880
+ """
881
+ Given a list of image base names for all cameras, extract their camera IDs
882
+ and generate a list of short_file_base strings for targets.
883
+ This ensures target files are named with the correct camera ID, not just their order.
884
+ """
885
+ short_file_bases = []
886
+ for img_base_name in img_base_names :
887
+ file_cam_id = extract_cam_id (img_base_name )
888
+ parent = str (Path (img_base_name ).parent )
889
+ if parent == "." :
890
+ parent = ""
891
+ base = f"{ SHORT_BASE } { file_cam_id } "
892
+ short_file_base = str (Path (parent ) / base ) if parent else base
893
+ short_file_bases .append (short_file_base )
894
+ return short_file_bases
846
895
847
896
848
897
def read_rt_is_file (filename ) -> List [List [float ]]:
0 commit comments