Skip to content

Commit

Permalink
Merge pull request #547 from mprib/535-workspace-quality-manager-to-t…
Browse files Browse the repository at this point in the history
…rack-stage-of-processing

Only partially complete, but I'm going to break goals into smaller target branches to stay focused.
  • Loading branch information
mprib authored Dec 1, 2023
2 parents 09f1bb3 + ef5c5b8 commit 45beedd
Show file tree
Hide file tree
Showing 14 changed files with 321 additions and 222 deletions.
1 change: 0 additions & 1 deletion dev/demo/demo_gui_prerecorded_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@
workspace_dir = Path(r"C:\Users\Mac Prible\OneDrive\pyxy3d\4_cam_prerecorded_practice_working")

window.launch_workspace(str(workspace_dir))
window.controller
window.show()
app.exec()
2 changes: 1 addition & 1 deletion pyxy3d/calibration/draw_charuco.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def grid_history(frame, ids, img_locs, connected_corners):
point_1 = observed_corners[pair[0]]
point_2 = observed_corners[pair[1]]

cv2.line(frame, point_1, point_2, (255, 165, 0), 1)
cv2.line(frame, point_1, point_2, (255, 165, 0), 3)

return frame

Expand Down
9 changes: 5 additions & 4 deletions pyxy3d/calibration/intrinsic_calibrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ def add_frame_packet(self, frame_packet: FramePacket):
"""
index = frame_packet.frame_index

self.all_ids[index] = frame_packet.points.point_id
self.all_img_loc[index] = frame_packet.points.img_loc
self.all_obj_loc[index] = frame_packet.points.obj_loc
if index != -1: # indicates end of stream
self.all_ids[index] = frame_packet.points.point_id
self.all_img_loc[index] = frame_packet.points.img_loc
self.all_obj_loc[index] = frame_packet.points.obj_loc

self.active_frame_index = index
self.active_frame_index = index

def add_calibration_frame_indices(self, frame_index: int):
self.calibration_frame_indices.append(frame_index)
Expand Down
4 changes: 2 additions & 2 deletions pyxy3d/calibration/stereocalibrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pathlib import Path

import pyxy3d.logger
logger = pyxy3d.logger.get(__name__)

import cv2
import pandas as pd
Expand All @@ -14,6 +13,7 @@
import numpy as np
import toml
from itertools import combinations
logger = pyxy3d.logger.get(__name__)

class StereoCalibrator:
def __init__(
Expand All @@ -27,7 +27,7 @@ def __init__(
self.ports = []
# set ports keeping in mind that something may be flagged for ignore
for key, value in self.config.items():
if key[0:3] == "cam":
if key[0:4] == "cam_":
#it's a camera so check if it should not be ignored
if not self.config[key]["ignore"]:
self.ports.append(int(key[4:]))
Expand Down
31 changes: 9 additions & 22 deletions pyxy3d/cameras/camera_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,41 +181,28 @@ def update_extrinsic_params(self, least_sq_result_x:np.array):
cam_vec = new_camera_params[index, :]
self.cameras[port].extrinsics_from_vector(cam_vec)

@property
def all_extrinsics_calibrated(self)->bool:

# assume extrinsics calibrated and provide otherwise
full_extrinsics = True
for port, cam in self.cameras.items():
if cam.rotation is None or cam.translation is None:
full_extrinsics = False

return full_extrinsics

def all_intrinsics_calibrated(self)->bool:
# assume true and prove false
full_intrinsics = True
for port, cam in self.cameras.items():
if cam.matrix is None or cam.distortions is None:
full_intrinsics = False
return full_intrinsics

@property
def projection_matrices(self) -> Dict:
logger.info(f"Creating camera array projection matrices")
logger.info("Creating camera array projection matrices")
proj_mat = Dict()
for port, cam in self.cameras.items():
proj_mat[port] = cam.projection_matrix

return proj_mat


class CalibrationStage(Enum):
"""
NOTE: this is not currently implemented. I was planning to set this up
in dev_post_process_eligible_check as a way to help manage the flow of the
GUI (it would be important to now the status of teh calibration in an easy way)
Unfortunately this begins to touch up on how/when the session loads the camera array,
and that's something that I'm not sure is fully relevant right now.
The camera array is not initialized until it is estimated via bundle adjustment.
Prior to that it is only a dictionary of cameras.
Consider removing...
"""
NO_INTRINSICS = auto()
PARTIAL_INTRINSICS = auto()
FULL_INTRINSICS = auto()
EXTRINSICS = auto()
140 changes: 53 additions & 87 deletions pyxy3d/configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(self, workspace_path: Path) -> None:

self.dict = toml.loads("")
self.dict["CreationDate"] = datetime.now()
self.dict["camera_count"] = 0
self.update_config_toml()

# default values enforced below
Expand All @@ -49,6 +50,14 @@ def __init__(self, workspace_path: Path) -> None:
if exists(self.point_estimates_toml_path):
self.refresh_point_estimates_from_toml()

def save_camera_count(self, count):
self.camera_count = count
self.dict["camera_count"] = count
self.update_config_toml()

def get_camera_count(self):
return self.dict["camera_count"]

def get_intrinsic_wait_time(self):
return self.dict["intrinsic_wait_time"]

Expand Down Expand Up @@ -99,75 +108,55 @@ def save_capture_volume(self, capture_volume: CaptureVolume):
] = capture_volume.origin_sync_index
self.update_config_toml()

def get_camera_from_source(self, port:int)->CameraData:
target_mp4_path = Path(self.workspace_path, "calibration", "intrinsic", f"port_{port}.mp4")
video_properties = read_video_properties(target_mp4_path)
size = video_properties["size"]
new_cam_data = CameraData(
port=port,
size=size,
)
return new_cam_data

def get_all_source_camera_ports(self)-> list:
target_mp4_dir = Path(self.workspace_path, "calibration", "intrinsic")
ports = []
for file in target_mp4_dir.iterdir():
if file.stem[0:5] == "port_":
port = file.stem.split("_")[1]
ports.append(int(port))

return ports

def get_configured_camera_data(self) -> dict[CameraData]:
all_camera_data = {}
for key, params in self.dict.items():
if key.startswith("cam"):
if not params["ignore"]:
port = params["port"]

if "error" in params.keys(): # intrinsics have been calculated
error = params["error"]
matrix = np.array(params["matrix"])
distortions = np.array(params["distortions"])
grid_count = params["grid_count"]
else:
error = None
matrix = None
distortions = None
grid_count = None

if (
"translation" in params.keys()
): # Extrinsics have been calculated
translation = np.array(params["translation"])
rotation = np.array(params["rotation"])

if rotation.shape == (
3,
): # camera rotation is stored as a matrix
rotation = cv2.Rodrigues(rotation)[0]

else:
translation = None
rotation = None

logger.info(f"Adding camera {port} to calibrated camera array...")
cam_data = CameraData(
port=port,
size=params["size"],
rotation_count=params["rotation_count"],
error=error,
matrix=matrix,
distortions=distortions,
grid_count=grid_count,
ignore=params["ignore"],
translation=translation,
rotation=rotation,
)

all_camera_data[port] = cam_data
logger.info(f"Camera successfully added at port {port}")
if key.startswith("cam_"):
port = params["port"]

if "error" in params.keys() and params["error"] is not None: # intrinsics have been calculated
error = params["error"]
matrix = np.array(params["matrix"])
distortions = np.array(params["distortions"])
grid_count = params["grid_count"]
else:
error = None
matrix = None
distortions = None
grid_count = None

if (
"translation" in params.keys() and params["translation"] is not None
): # Extrinsics have been calculated
translation = np.array(params["translation"])
rotation = np.array(params["rotation"])

if rotation.shape == (
3,
): # camera rotation is stored as a matrix
rotation = cv2.Rodrigues(rotation)[0]

else:
translation = None
rotation = None

logger.info(f"Adding camera {port} to calibrated camera array...")
cam_data = CameraData(
port=port,
size=params["size"],
rotation_count=params["rotation_count"],
error=error,
matrix=matrix,
distortions=distortions,
grid_count=grid_count,
translation=translation,
rotation=rotation,
)

all_camera_data[port] = cam_data
logger.info(f"Camera successfully added at port {port}")
logger.info("Camera data loaded and being passed back to caller")
return all_camera_data

Expand Down Expand Up @@ -327,29 +316,6 @@ def save_point_estimates(self, point_estimates: PointEstimates):
toml.dump(self.dict["point_estimates"], f)
# self.update_config_toml()

def read_video_properties(source_path: Path) -> dict:
# Dictionary to hold video properties
properties = {}

# Open the video file
video = cv2.VideoCapture(str(source_path))
logger.info(f"Attempting to open video file: {source_path}")

# Check if video opened successfully
if not video.isOpened():
raise ValueError(f"Could not open the video file: {source_path}")

# Extract video properties
properties["frame_count"] = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
properties["fps"] = video.get(cv2.CAP_PROP_FPS)
properties["width"] = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
properties["height"] = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
properties["size"] = (properties["width"], properties["height"])

# Release the video capture object
video.release()

return properties

if __name__ == "__main__":
import toml
Expand Down
Loading

0 comments on commit 45beedd

Please sign in to comment.