123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- from math import floor, ceil
- class RegionOfInterest:
- """Abstract base class"""
- pass
- class CircularRegionOfInterest(RegionOfInterest):
- """Representation of a circular ROI
- *Usage:*
- >>> roi = CircularRegionOfInterest(20.0, 20.0, radius=5.0)
- >>> signal = image_sequence.signal_from_region(roi)
- *Required attributes/properties*:
- :x, y: (integers or floats)
- Pixel coordinates of the centre of the ROI
- :radius: (integer or float)
- Radius of the ROI in pixels
- """
- def __init__(self, x, y, radius):
- self.y = y
- self.x = x
- self.radius = radius
- @property
- def centre(self):
- return (self.x, self.y)
- @property
- def center(self):
- return self.centre
- def is_inside(self, x, y):
- if ((x - self.x) * (x - self.x) +
- (y - self.y) * (y - self.y) <= self.radius * self.radius):
- return True
- else:
- return False
- def pixels_in_region(self):
- """Returns a list of pixels whose *centres* are within the circle"""
- pixel_in_list = []
- for y in range(int(floor(self.y - self.radius)), int(ceil(self.y + self.radius))):
- for x in range(int(floor(self.x - self.radius)), int(ceil(self.x + self.radius))):
- if self.is_inside(x, y):
- pixel_in_list.append([x, y])
- return pixel_in_list
- class RectangularRegionOfInterest(RegionOfInterest):
- """Representation of a rectangular ROI
- *Usage:*
- >>> roi = RectangularRegionOfInterest(20.0, 20.0, width=5.0, height=5.0)
- >>> signal = image_sequence.signal_from_region(roi)
- *Required attributes/properties*:
- :x, y: (integers or floats)
- Pixel coordinates of the centre of the ROI
- :width: (integer or float)
- Width (x-direction) of the ROI in pixels
- :height: (integer or float)
- Height (y-direction) of the ROI in pixels
- """
- def __init__(self, x, y, width, height):
- self.x = x
- self.y = y
- self.width = width
- self.height = height
- def is_inside(self, x, y):
- if (self.x - self.width/2.0 <= x < self.x + self.width/2.0
- and self.y - self.height/2.0 <= y < self.y + self.height/2.0):
- return True
- else:
- return False
- def pixels_in_region(self):
- """Returns a list of pixels whose *centres* are within the rectangle"""
- pixel_list = []
- h = self.height
- w = self.width
- for y in range(int(floor(self.y - h / 2.0)), int(ceil(self.y + h / 2.0))):
- for x in range(int(floor(self.x - w / 2.0)), int(ceil(self.x + w / 2.0))):
- if self.is_inside(x, y):
- pixel_list.append([x, y])
- return pixel_list
- class PolygonRegionOfInterest(RegionOfInterest):
- """Representation of a polygonal ROI
- *Usage:*
- >>> roi = PolygonRegionOfInterest(
- ... (20.0, 20.0),
- ... (30.0, 20.0),
- ... (25.0, 25.0)
- ... )
- >>> signal = image_sequence.signal_from_region(roi)
- *Required attributes/properties*:
- :vertices:
- tuples containing the (x, y) coordinates, as integers or floats,
- of the vertices of the polygon
- """
- def __init__(self, *vertices):
- self.vertices = vertices
- def polygon_ray_casting(self, bounding_points, bounding_box_positions):
- # from https://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon
- # user Noresourses
- # Arrays containing the x- and y-coordinates of the polygon's vertices.
- vertx = [point[0] for point in bounding_points]
- verty = [point[1] for point in bounding_points]
- # Number of vertices in the polygon
- nvert = len(bounding_points)
- # Points that are inside
- points_inside = []
- # For every candidate position within the bounding box
- for idx, pos in enumerate(bounding_box_positions):
- testx, testy = (pos[0], pos[1])
- c = 0
- for i in range(0, nvert):
- j = i - 1 if i != 0 else nvert - 1
- if (((verty[i]*1.0 > testy*1.0) != (verty[j]*1.0 > testy*1.0)) and
- (testx*1.0 < (vertx[j]*1.0 - vertx[i]*1.0) * (testy*1.0 - verty[i]*1.0) /
- (verty[j]*1.0 - verty[i]*1.0) + vertx[i]*1.0)):
- c += 1
- # If odd, that means that we are inside the polygon
- if c % 2 == 1:
- points_inside.append(pos)
- return points_inside
- def pixels_in_region(self):
- min_x, max_x, min_y, max_y = (self.vertices[0][0], self.vertices[0][0],
- self.vertices[0][1], self.vertices[0][1])
- for i in self.vertices:
- if i[0] < min_x:
- min_x = i[0]
- if i[0] > max_x:
- max_x = i[0]
- if i[1] < min_y:
- min_y = i[1]
- if i[1] > max_y:
- max_y = i[1]
- list_coord = []
- for y in range(int(floor(min_y)), int(ceil(max_y))):
- for x in range(int(floor(min_x)), int(ceil(max_x))):
- list_coord.append((x, y))
- pixel_list = self.polygon_ray_casting(self.vertices, list_coord)
- return pixel_list
|