Skip to content

Commit ebf51d9

Browse files
committed
preparing track additional tests, on the way changed half of the code :)
1 parent 7700ed1 commit ebf51d9

29 files changed

+556
-655
lines changed

pyptv/ptv.py

Lines changed: 131 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
DEFAULT_FRAME_NUM = 123456789
4444
DEFAULT_HIGHPASS_FILTER_SIZE = 25
4545
DEFAULT_NO_FILTER = 0
46+
SHORT_BASE = "cam" # Use this as the short base for camera file naming
4647

4748

4849

@@ -378,13 +379,48 @@ def py_correspondences_proc_c(exp):
378379
else:
379380
print("Warning: No way to determine base names, skipping target writing")
380381

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)
388424

389425
return sorted_pos, sorted_corresp, num_targs
390426

@@ -532,31 +568,30 @@ def py_sequence_loop(exp) -> None:
532568

533569
first_frame = spar.get_first()
534570
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
536575

537576
for frame in range(first_frame, last_frame + 1):
538577
detections = []
539578
corrected = []
540579
for i_cam in range(num_cams):
541-
base_image_name = spar.get_img_base_name(i_cam)
542580
if existing_target:
543-
targs = read_targets(base_image_name, frame)
581+
targs = read_targets(short_file_bases[i_cam], frame)
544582
else:
545-
imname = Path(base_image_name % frame)
583+
imname = Path(img_base_names[i_cam] % frame)
546584
if not imname.exists():
547585
raise FileNotFoundError(f"{imname} does not exist")
548586
else:
549587
img = imread(imname)
550588
if img.ndim > 2:
551589
img = rgb2gray(img)
552-
553590
if img.dtype != np.uint8:
554591
img = img_as_ubyte(img)
555-
556592
if parameter_manager.get_parameter('ptv', {}).get('inverse', False):
557593
print("Invert image")
558594
img = negative(img)
559-
560595
masking_params = parameter_manager.get_parameter('masking')
561596
if masking_params and masking_params.get('mask_flag', False):
562597
try:
@@ -566,85 +601,71 @@ def py_sequence_loop(exp) -> None:
566601
)
567602
background = imread(background_name)
568603
img = np.clip(img - background, 0, 255).astype(np.uint8)
569-
570604
except (ValueError, FileNotFoundError):
571605
print("failed to read the mask")
572-
573606
high_pass = simple_highpass(img, cpar)
574607
targs = target_recognition(high_pass, tpar, i_cam, cpar)
575-
576608
targs.sort_y()
577-
# print(len(targs))
578609
detections.append(targs)
579610
matched_coords = MatchedCoords(targs, cpar, cals[i_cam])
580611
pos, _ = matched_coords.as_arrays()
581612
corrected.append(matched_coords)
582-
583613
sorted_pos, sorted_corresp, _ = correspondences(
584614
detections, corrected, cals, vpar, cpar
585615
)
586-
587616
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)
591618
print(
592619
"Frame "
593620
+ str(frame)
594621
+ " had "
595622
+ repr([s.shape[1] for s in sorted_pos])
596623
+ " correspondences."
597624
)
598-
599625
sorted_pos = np.concatenate(sorted_pos, axis=1)
600626
sorted_corresp = np.concatenate(sorted_corresp, axis=1)
601-
602627
flat = np.array(
603628
[corrected[i].get_by_pnrs(sorted_corresp[i]) for i in range(len(exp.cals))]
604629
)
605630
pos, _ = point_positions(flat.transpose(1, 0, 2), exp.cpar, exp.cals, exp.vpar)
606-
607631
if len(exp.cals) < 4:
608632
print_corresp = -1 * np.ones((4, sorted_corresp.shape[1]))
609633
print_corresp[: len(exp.cals), :] = sorted_corresp
610634
else:
611635
print_corresp = sorted_corresp
612-
613636
rt_is_filename = default_naming["corres"].decode()
614637
rt_is_filename = f"{rt_is_filename}.{frame}"
615638
with open(rt_is_filename, "w", encoding="utf8") as rt_is:
616639
rt_is.write(str(pos.shape[0]) + "\n")
617640
for pix, pt in enumerate(pos):
618641
pt_args = (pix + 1,) + tuple(pt) + tuple(print_corresp[:, pix])
619642
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)
620646

621647

622648
def py_trackcorr_init(exp):
623649
"""Reads all the necessary stuff into Tracker"""
624650

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()))
630662

631663
tracker = Tracker(
632664
exp.cpar, exp.vpar, exp.track_par, exp.spar, exp.cals, default_naming
633665
)
634666

635667
return tracker
636668

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-
648669
# ------- Utilities ----------#
649670

650671

@@ -775,9 +796,33 @@ def py_calibration(selection, exp):
775796
return targs_all, targ_ix_all, residuals_all
776797

777798

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:
779824
"""Read targets from a file."""
780-
filename = file_base_to_filename(file_base, frame)
825+
filename = f"{short_file_base}.{frame:04d}_targets"
781826
print(f" filename: {filename}")
782827

783828
try:
@@ -805,44 +850,48 @@ def read_targets(file_base: str, frame: int = 123456789) -> TargetArray:
805850
return targs
806851

807852

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
844854

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
846895

847896

848897
def read_rt_is_file(filename) -> List[List[float]]:

0 commit comments

Comments
 (0)