Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/171-bug-project-numpy' into 171-…
Browse files Browse the repository at this point in the history
…bug-project-numpy
  • Loading branch information
hcwinsemius committed Jun 21, 2024
2 parents 37d6a70 + 994e89b commit 3077d82
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 38 deletions.
4 changes: 2 additions & 2 deletions pyorc/api/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def get_piv(self, **kwargs):
ds["x"] = x

# add all 2D-coordinates
ds = ds.velocimetry._add_xy_coords(
ds = ds.velocimetry.add_xy_coords(
[xp, yp, xs, ys, lons, lats],
coords,
{**const.PERSPECTIVE_ATTRS, **const.GEOGRAPHICAL_ATTRS}
Expand Down Expand Up @@ -240,7 +240,7 @@ def project(
# ensure no missing values are persisting
da_proj = da_proj.fillna(0.)
# assign coordinates
da_proj = da_proj.frames._add_xy_coords([xs, ys, lons, lats], coords, const.GEOGRAPHICAL_ATTRS)
da_proj = da_proj.frames.add_xy_coords([xs, ys, lons, lats], coords, const.GEOGRAPHICAL_ATTRS)
if "rgb" in da_proj.dims and len(da_proj.dims) == 4:
# ensure that "rgb" is the last dimension
da_proj = da_proj.transpose("time", "y", "x", "rgb")
Expand Down
2 changes: 1 addition & 1 deletion pyorc/api/orcbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def _set_camera_config(self):
self.camera_shape = self._obj.camera_shape


def _add_xy_coords(
def add_xy_coords(
self,
xy_coord_data,
coords,
Expand Down
59 changes: 30 additions & 29 deletions pyorc/api/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ def __init__(
"""
assert (isinstance(start_frame, (int, type(None)))), 'start_frame must be of type "int"'
assert (isinstance(end_frame, (int, type(None)))), 'end_frame must be of type "int"'
# assert (isinstance(stabilize, (list, type(None)))), f'stabilize must contain a list of points, but is {stabilize}'
self.feats_stats = None
self.feats_errs = None
self.ms = None
Expand Down Expand Up @@ -114,7 +113,7 @@ def __init__(
# set end and start frame
self.frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if start_frame is not None:
if (start_frame > self.frame_count and self.frame_count > 0):
if start_frame > self.frame_count > 0:
raise ValueError("Start frame is larger than total amount of frames")
else:
start_frame = 0
Expand Down Expand Up @@ -187,7 +186,6 @@ def lazy(self):
def lazy(self, lazy):
self._lazy = lazy


@property
def mask(self):
"""
Expand All @@ -200,7 +198,10 @@ def mask(self):
return self._mask

@mask.setter
def mask(self, mask):
def mask(
self,
mask: np.ndarray
):
if mask is None:
self._mask = None
else:
Expand All @@ -218,7 +219,10 @@ def camera_config(self):
return None

@camera_config.setter
def camera_config(self, camera_config_input):
def camera_config(
self,
camera_config_input: Union[str, dict]
):
"""
Set camera config as a serializable object from either a filename, json string or a dict
:param camera_config_input: str, dict, CameraConfig object, filename string, or json string containing camera
Expand Down Expand Up @@ -250,7 +254,10 @@ def end_frame(self):
return self._end_frame

@end_frame.setter
def end_frame(self, end_frame=None):
def end_frame(
self,
end_frame: Optional[int] = None
):
# sometimes last frames are not read by OpenCV, hence we skip the last frame always
if end_frame is None:
self._end_frame = self.frame_count - 1
Expand Down Expand Up @@ -304,7 +311,9 @@ def h_a(
assert (isinstance(h_a, float)), f"The actual water level must be a float, you supplied a {type(h_a)}"
if h_a < 0:
warnings.warn(
"Water level is negative. This can be correct, but may be unlikely, especially if you use a staff gauge.")
"Water level is negative. This can be correct, but may be unlikely, especially if you use a staff "
"gauge."
)
self._h_a = h_a

@property
Expand All @@ -325,7 +334,6 @@ def start_frame(
else:
self._start_frame = start_frame


@property
def frames(self):
return self._frames
Expand Down Expand Up @@ -369,7 +377,6 @@ def corners(
):
self._corners = corners


@property
def rotation(self):
if self._rotation is not None:
Expand All @@ -378,7 +385,6 @@ def rotation(self):
if hasattr(self.camera_config, "rotation"):
return helpers.get_rotation_code(self.camera_config.rotation)


@rotation.setter
def rotation(
self,
Expand All @@ -389,11 +395,10 @@ def rotation(
"""
self._rotation = helpers.get_rotation_code(rotation_code)


def get_frame(
self,
n: int,
method: Optional[Literal["grayscale", "rgb", "hsv"]] = "grayscale"
method: Optional[Literal["grayscale", "rgb", "hsv", "bgr"]] = "grayscale"
) -> np.ndarray:
"""
Retrieve one frame.
Expand All @@ -412,7 +417,8 @@ def get_frame(
"""
assert (n >= 0), "frame number cannot be negative"
assert (
n - self.start_frame <= self.end_frame - self.start_frame), "frame number is larger than the different between the start and end frame"
n - self.start_frame <= self.end_frame - self.start_frame
), "frame number is larger than the difference between the start and end frame "
# assert (method in ["grayscale", "rgb",
# "hsv"]), f'method must be "grayscale", "rgb" or "hsv", method is "{method}"'
cap = cv2.VideoCapture(self.fn)
Expand All @@ -430,7 +436,7 @@ def get_frame(

def get_frames(
self,
method: Optional[Literal["grayscale", "rgb", "hsv"]] = "grayscale"
method: Optional[Literal["grayscale", "rgb", "hsv", "bgr"]] = "grayscale"
) -> xr.DataArray:
"""
Get a xr.DataArray, containing a dask array of frames, from `start_frame` until `end_frame`, expected to be read
Expand All @@ -447,8 +453,9 @@ def get_frames(
frames : xr.DataArray
containing all requested frames
"""
assert (hasattr(self,
"_camera_config")), "No camera configuration is set, add it to the video using the .camera_config method"
assert (hasattr(
self,
"_camera_config")), "No camera configuration is set, add it to the video using the .camera_config method"
# camera_config may be altered for the frames object, so copy below
camera_config = copy.deepcopy(self.camera_config)

Expand All @@ -475,18 +482,9 @@ def get_frames(
da_stack = np.array([cv.color_scale(img, method) for img in da_stack])
sample = da_stack[0]

# undistort source control points
# if hasattr(camera_config, "gcps"):
# camera_config.gcps["src"] = cv.undistort_points(
# camera_config.gcps["src"],
# camera_config.camera_matrix,
# camera_config.dist_coeffs,
# )
time = np.array(
self.time) * 0.001 # measure in seconds to comply with CF conventions # np.arange(len(data_array))*1/self.fps
# y needs to be flipped up down to match the order of rows followed by coordinate systems (bottom to top)
# y = np.flipud(np.arange(data_array[0].shape[0]))
# x = np.arange(data_array[0].shape[1])
y = np.flipud(np.arange(sample.shape[0]))
x = np.arange(sample.shape[1])
# perspective column and row coordinate grids
Expand Down Expand Up @@ -516,13 +514,17 @@ def get_frames(
if len(sample.shape) == 3:
del coords["rgb"]
# add coordinate grids (i.e. without time)
frames = frames.frames._add_xy_coords([xp, yp], coords, const.PERSPECTIVE_ATTRS)
frames = frames.frames.add_xy_coords(
[xp, yp],
coords,
const.PERSPECTIVE_ATTRS
)
frames.name = "frames"
return frames

def set_mask_from_exterior(
self,
exterior
exterior: List[List]
):
"""
Prepare a mask grid with 255 outside of the stabilization polygon and 0 inside
Expand Down Expand Up @@ -551,11 +553,10 @@ def get_ms(
cap: cv2.VideoCapture,
split: Optional[int] = 2
):
self.ms = cv._get_ms_gftt(
self.ms = cv.get_ms_gftt(
cap,
start_frame=self.start_frame,
end_frame=self.end_frame,
split=split,
mask=self.mask,
)

2 changes: 1 addition & 1 deletion pyorc/cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _get_cam_mtx(height, width, c=2.0, focal_length=None):
return mtx


def _get_ms_gftt(cap, start_frame=0, end_frame=None, n_pts=None, split=2, mask=None, wdw=4):
def get_ms_gftt(cap, start_frame=0, end_frame=None, n_pts=None, split=2, mask=None, wdw=4):
# set end_frame to last if not defined
if end_frame is None:
end_frame = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
Expand Down
13 changes: 8 additions & 5 deletions pyorc/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np
import xarray as xr
from rasterio.features import rasterize
from typing import Literal
from typing import Optional
from flox.xarray import xarray_reduce

import pyorc
Expand Down Expand Up @@ -111,11 +111,10 @@ def project_numpy(
x: np.ndarray,
y: np.ndarray,
z: np.ndarray,
reducer: Literal["nearest", "average"] = "nearest"
reducer: Optional[str] = None
):
"""
Projection method that goes from pixels directly to target grid, including undistortion and projection
using a lookup method across the grid.
Project from FOV pixels directly to target grid, including undistortion and projection.
Parameters
----------
Expand All @@ -128,6 +127,10 @@ def project_numpy(
y-axis
z : float
vertical level value in real-world coordinates
reducer : str, optional
If set to a valid reducer (like mean, median, max) oversampled target pixels will be reduced by using the set
reducer. Oversampled target pixels are defined as pixels that have more than one pixels in the original
Field of View that fit within that pixel. All other pixels are defined with nearest-neighbour.
Returns
-------
Expand Down Expand Up @@ -193,7 +196,7 @@ def project_numpy(
vals = da.stack(points=("y", "x")).isel(points=idx_back)
da_new[..., idx_in] = vals

if reducer is not "nearest":
if reducer != "nearest":
# also fill in the parts that have valid averaged pixels
coli, rowi = np.meshgrid(
np.arange(len(da.x)),
Expand Down

0 comments on commit 3077d82

Please sign in to comment.