Commit 5f577079 authored by Andy Regensky's avatar Andy Regensky
Browse files

Bugfix: Asymmetric optical centers were not handled correctly

parent 20131bd6
......@@ -117,8 +117,7 @@ class MainViewModel(QObject):
@property
def projection(self):
if self.projection_key == 'calibrated':
calib_coeffs = self.calibration_coeffs
projection = projections.CalibratedProjection(calib_coeffs,
projection = projections.CalibratedProjection(self.calibration_coeffs,
self.sensor_size_px[0]/self.sensor_height_mm,
self.focal_length_mm,
self.optical_center)
......@@ -128,7 +127,8 @@ class MainViewModel(QObject):
@property
def perspective(self):
return projections.PerspectiveProjection(self.focal_length_px, self.optical_center)
return projections.PerspectiveProjection(self.focal_length_px,
((self.sensor_size_px[1] - 1) / 2, (self.sensor_size_px[0] - 1) / 2))
@property
def fov_180_radius(self):
......
......@@ -38,21 +38,6 @@ class EquidistantProjection(RadialProjection):
Defines the equidistant projection model.
"""
@classmethod
def init_with(cls, sensor_size_mm: Tuple[float, float], sensor_size_px: Tuple[int, int], focal_length_mm: float):
"""
Specify new equidistant projection model with
:param sensor_size_mm: sensor size in mm [height, width]
:param sensor_size_px: sensor size in pixels [height, width]
:param focal_length_mm: focal length in mm
"""
if sensor_size_px[0]/sensor_size_mm[0] != sensor_size_px[1]/sensor_size_mm[1]:
raise Exception("Pixel shape must be square.")
focal_length_px = sensor_size_px[0]/sensor_size_mm[0] * focal_length_mm
optical_center = ((sensor_size_px[0] - 1) / 2, (sensor_size_px[1] - 1) / 2)
return cls(focal_length_px, optical_center)
def radius(self, theta: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
return self.focal_length_px * theta
......
......@@ -38,21 +38,6 @@ class EquisolidProjection(RadialProjection):
Defines the equisolid projection model.
"""
@classmethod
def init_with(cls, sensor_size_mm: Tuple[float, float], sensor_size_px: Tuple[int, int], focal_length_mm: float):
"""
Specify new equisolid projection model with
:param sensor_size_mm: sensor size in mm [height, width]
:param sensor_size_px: sensor size in pixels [height, width]
:param focal_length_mm: focal length in mm
"""
if sensor_size_px[0]/sensor_size_mm[0] != sensor_size_px[1]/sensor_size_mm[1]:
raise Exception("Pixel shape must be square.")
focal_length_px = sensor_size_px[0]/sensor_size_mm[0] * focal_length_mm
optical_center = ((sensor_size_px[0] - 1) / 2, (sensor_size_px[1] - 1) / 2)
return cls(focal_length_px, optical_center)
def radius(self, theta: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
return 2 * self.focal_length_px * np.sin(theta / 2)
......
......@@ -38,21 +38,6 @@ class OrthographicProjection(RadialProjection):
Defines the orthographic projection model.
"""
@classmethod
def init_with(cls, sensor_size_mm: Tuple[float, float], sensor_size_px: Tuple[int, int], focal_length_mm: float):
"""
Specify new orthographic projection model with
:param sensor_size_mm: sensor size in mm [height, width]
:param sensor_size_px: sensor size in pixels [height, width]
:param focal_length_mm: focal length in mm
"""
if sensor_size_px[0]/sensor_size_mm[0] != sensor_size_px[1]/sensor_size_mm[1]:
raise Exception("Pixel shape must be square.")
focal_length_px = sensor_size_px[0]/sensor_size_mm[0] * focal_length_mm
optical_center = ((sensor_size_px[0] - 1) / 2, (sensor_size_px[1] - 1) / 2)
return cls(focal_length_px, optical_center)
@property
def max_theta(self):
return 90
......
......@@ -39,21 +39,6 @@ class PerspectiveProjection(RadialProjection):
Defines the perspective projection model.
"""
@classmethod
def init_with(cls, sensor_size_mm: Tuple[float, float], sensor_size_px: Tuple[int, int], focal_length_mm: float):
"""
Specify new perspective projection model with
:param sensor_size_mm: sensor size in mm [height, width]
:param sensor_size_px: sensor size in pixels [height, width]
:param focal_length_mm: focal length in mm
"""
if sensor_size_px[0]/sensor_size_mm[0] != sensor_size_px[1]/sensor_size_mm[1]:
raise Exception("Pixel shape must be square.")
focal_length_px = sensor_size_px[0]/sensor_size_mm[0] * focal_length_mm
optical_center = ((sensor_size_px[0] - 1) / 2, (sensor_size_px[1] - 1) / 2)
return cls(focal_length_px, optical_center)
@property
def optical_center(self):
return self._optical_center
......@@ -70,8 +55,8 @@ class PerspectiveProjection(RadialProjection):
vip: Optional[Union[bool, np.ndarray]] = None,
*args, **kwargs) -> Union[Tuple[float, float, float],
Tuple[np.ndarray, np.ndarray, np.ndarray]]:
r, phi_s = coordinate_conversion.cartesian_to_polar(y - self._optical_center[0],
x - self._optical_center[1])
r, phi_s = coordinate_conversion.cartesian_to_polar(y - self._optical_center[1],
x - self._optical_center[0])
theta_s = self.theta(r)
# Virtual Image Plane Compensation (VIPC)
if vip is not None:
......@@ -91,4 +76,4 @@ class PerspectiveProjection(RadialProjection):
_, theta_s, phi_s = coordinate_conversion.cartesian_to_spherical(ys, -zs, -xs)
r = self.radius(theta_s)
y, x = coordinate_conversion.polar_to_cartesian(r, phi_s)
return y + self._optical_center[0], x + self._optical_center[1], r < 0
return y + self._optical_center[1], x + self._optical_center[0], r < 0
......@@ -131,8 +131,8 @@ class RadialProjection(Projection):
x: Union[float, np.ndarray],
*args, **kwargs) -> Union[Tuple[float, float, float],
Tuple[np.ndarray, np.ndarray, np.ndarray]]:
r, phi_s = coordinate_conversion.cartesian_to_polar(y - self._optical_center[0],
x - self._optical_center[1])
r, phi_s = coordinate_conversion.cartesian_to_polar(y - self._optical_center[1],
x - self._optical_center[0])
theta_s = self.theta(r)
xsr, ysr, zsr = coordinate_conversion.spherical_to_cartesian(1, theta_s, phi_s)
xs = -zsr
......@@ -148,4 +148,4 @@ class RadialProjection(Projection):
_, theta_s, phi_s = coordinate_conversion.cartesian_to_spherical(ys, -zs, -xs)
r = self.radius(theta_s)
y, x = coordinate_conversion.polar_to_cartesian(r, phi_s)
return y + self._optical_center[0], x + self._optical_center[1]
return y + self._optical_center[1], x + self._optical_center[0]
......@@ -38,21 +38,6 @@ class StereographicProjection(RadialProjection):
Defines the stereographic projection model.
"""
@classmethod
def init_with(cls, sensor_size_mm: Tuple[float, float], sensor_size_px: Tuple[int, int], focal_length_mm: float):
"""
Specify new stereographic projection model with
:param sensor_size_mm: sensor size in mm [height, width]
:param sensor_size_px: sensor size in pixels [height, width]
:param focal_length_mm: focal length in mm
"""
if sensor_size_px[0]/sensor_size_mm[0] != sensor_size_px[1]/sensor_size_mm[1]:
raise Exception("Pixel shape must be square.")
focal_length_px = sensor_size_px[0]/sensor_size_mm[0] * focal_length_mm
optical_center = ((sensor_size_px[0] - 1) / 2, (sensor_size_px[1] - 1) / 2)
return cls(focal_length_px, optical_center)
def radius(self, theta: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
return 2 * self.focal_length_px * np.tan(theta / 2)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment