Source code for iris.nodes.normalization.common

from typing import Tuple, Union

import numpy as np
from pydantic import NonNegativeInt

from iris.io.dataclasses import GeometryPolygons
from iris.utils import common


[docs]def generate_iris_mask(extrapolated_contours: GeometryPolygons, noise_mask: np.ndarray) -> np.ndarray: """Generate iris mask by first finding the intersection region between extrapolated iris contours and eyeball contours. Then remove from the outputted mask those pixels for which noise_mask is equal to True. Args: extrapolated_contours (GeometryPolygons): Iris polygon vertices. noise_mask (np.ndarray): Noise mask. Returns: np.ndarray: Iris mask. """ img_h, img_w = noise_mask.shape[:2] iris_mask = common.contour_to_mask(extrapolated_contours.iris_array, (img_w, img_h)) eyeball_mask = common.contour_to_mask(extrapolated_contours.eyeball_array, (img_w, img_h)) iris_mask = iris_mask & eyeball_mask iris_mask = ~(iris_mask & noise_mask) & iris_mask return iris_mask
[docs]def correct_orientation( pupil_points: np.ndarray, iris_points: np.ndarray, eye_orientation: float ) -> Tuple[np.ndarray, np.ndarray]: """Correct orientation by changing the starting angle in pupil and iris points' arrays. Args: pupil_points (np.ndarray): Pupil boundary points' array. NumPy array of shape (num_points = 360, xy_coords = 2). iris_points (np.ndarray): Iris boundary points' array. NumPy array of shape (num_points = 360, xy_coords = 2). eye_orientation (float): Eye orientation angle in radians. Returns: Tuple[np.ndarray, np.ndarray]: Tuple with rotated based on eye_orientation angle boundary points (pupil_points, iris_points). """ orientation_angle = np.degrees(eye_orientation) num_rotations = -round(orientation_angle * len(pupil_points) / 360.0) pupil_points = np.roll(pupil_points, num_rotations, axis=0) iris_points = np.roll(iris_points, num_rotations, axis=0) return pupil_points, iris_points
[docs]def getgrids(res_in_r: NonNegativeInt, p2i_ratio: NonNegativeInt) -> np.ndarray: """Generate radius grids for nonlinear normalization based on p2i_ratio (pupil_to_iris ratio). Args: res_in_r (NonNegativeInt): Normalized image r resolution. p2i_ratio (NonNegativeInt): pupil_to_iris ratio, range in [0,100] Returns: np.ndarray: nonlinear sampling grids for normalization """ p = [np.square(x) for x in np.arange(28, max(74 - p2i_ratio, p2i_ratio - 14), 1)] q = p - p[0] q = q / q[-1] grids = np.interp(np.linspace(0, 1.0, res_in_r + 1), np.linspace(0, 1.0, len(q)), q) return grids[0:-1] + np.diff(grids) / 2
[docs]def get_pixel_or_default( image: np.ndarray, pixel_x: float, pixel_y: float, default: Union[bool, int] ) -> Union[bool, int]: """Get the value of a pixel in the image 2D array. Args: image (np.ndarray): 2D Array. pixel_x (float): Pixel x coordinate. pixel_y (float): Pixel y coordinate. default (Union[bool, int]): Default value to return when (pixel_x, pixel_y) is out-of-bounds Returns: Union[bool, int]: Pixel value. """ h, w = image.shape x, y = int(pixel_x), int(pixel_y) return image[y, x] if x >= 0 and x < w and y >= 0 and y < h else default
[docs]def interpolate_pixel_intensity(image: np.ndarray, pixel_coords: Tuple[float, float]) -> float: """Perform bilinear interpolation to estimate pixel intensity in a given location. Args: image (np.ndarray): Original, not normalized image. pixel_coords (Tuple[float, float]): Pixel coordinates. Returns: float: Interpolated pixel intensity. Reference: [1] https://en.wikipedia.org/wiki/Bilinear_interpolation """ def get_interpolation_points_coords( image: np.ndarray, pixel_x: float, pixel_y: float ) -> Tuple[float, float, float, float]: """Extract interpolation points coordinates. Args: image (np.ndarray): Original, not normalized image. pixel_x (float): Pixel x coordinate. pixel_y (float): Pixel y coordinate. Returns: Tuple[float, float, float, float]: Tuple with interpolation points coordinates in a format (xmin, ymin, xmax, ymax). """ xmin, ymin = np.floor(pixel_x), np.floor(pixel_y) xmax, ymax = np.ceil(pixel_x), np.ceil(pixel_y) img_h, img_w = image.shape[:2] if xmin == xmax and not xmax == img_w - 1: xmax += 1 if xmin == xmax and xmax == img_w - 1: xmin -= 1 if ymin == ymax and not ymax == img_h - 1: ymax += 1 if ymin == ymax and ymax == img_h - 1: ymin -= 1 return xmin, ymin, xmax, ymax pixel_x, pixel_y = pixel_coords xmin, ymin, xmax, ymax = get_interpolation_points_coords(image, pixel_x=pixel_x, pixel_y=pixel_y) lower_left_pixel_intensity = get_pixel_or_default(image, pixel_x=xmin, pixel_y=ymax, default=0.0) lower_right_pixel_intensity = get_pixel_or_default(image, pixel_x=xmax, pixel_y=ymax, default=0.0) upper_left_pixel_intensity = get_pixel_or_default(image, pixel_x=xmin, pixel_y=ymin, default=0.0) upper_right_pixel_intensity = get_pixel_or_default(image, pixel_x=xmax, pixel_y=ymin, default=0.0) xs_differences = np.array([xmax - pixel_x, pixel_x - xmin]) neighboring_pixel_intensities = np.array( [ [lower_left_pixel_intensity, upper_left_pixel_intensity], [lower_right_pixel_intensity, upper_right_pixel_intensity], ] ) ys_differences = np.array([[pixel_y - ymin], [ymax - pixel_y]]) pixel_intensity = np.matmul(np.matmul(xs_differences, neighboring_pixel_intensities), ys_differences) return pixel_intensity.item()
[docs]def normalize_all( image: np.ndarray, iris_mask: np.ndarray, src_points: np.ndarray, ) -> Tuple[np.ndarray, np.ndarray]: """Normalize all points of an image using nearest neighbor. Args: image (np.ndarray): Original, not normalized image. iris_mask (np.ndarray): Iris class segmentation mask. src_points (np.ndarray): original input image points. Returns: Tuple[np.ndarray, np.ndarray]: Tuple with normalized image and mask. """ src_shape = src_points.shape[0:2] src_points = np.vstack(src_points) image_size = image.shape src_points[src_points[:, 0] >= image_size[1], 0] = -1 src_points[src_points[:, 1] >= image_size[0], 1] = -1 normalized_image = np.array( [image[image_xy[1], image_xy[0]] if min(image_xy) >= 0 else 0 for image_xy in src_points] ) normalized_image = np.reshape(normalized_image, src_shape) normalized_mask = np.array( [iris_mask[image_xy[1], image_xy[0]] if min(image_xy) >= 0 else False for image_xy in src_points] ) normalized_mask = np.reshape(normalized_mask, src_shape) return normalized_image / 255.0, normalized_mask
[docs]def to_uint8(image: np.ndarray) -> np.ndarray: """Map normalized image values from [0, 1] range to [0, 255] and cast dtype to np.uint8. Args: image (np.ndarray): Normalized iris. Returns: np.ndarray: Normalized iris with modified values. """ out_image = np.round(image * 255) out_image = out_image.astype(np.uint8) return out_image