retuve.classes.draw
Class for creating visual overlays on images.
1# Copyright 2024 Adam McArthur 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Class for creating visual overlays on images. 17""" 18 19from enum import Enum 20from typing import List, Literal, Tuple 21 22import numpy as np 23from PIL import Image, ImageDraw 24from radstract.data.colors import LabelColours 25 26from retuve.classes.seg import SegFrameObjects 27from retuve.keyphrases.config import Config 28 29 30class DrawTypes(Enum): 31 """ 32 Enum for the different types of overlays that can be drawn. 33 34 """ 35 36 LINES = "lines" 37 SEGS = "segs" 38 TEXT = "text" 39 POINTS = "points" 40 CIRCLE = "circle" 41 RECTANGLE = "rectangle" 42 43 @classmethod 44 def ALL(cls) -> List["DrawTypes"]: 45 """ 46 Specified in order of drawing. 47 """ 48 return [ 49 cls.SEGS, 50 cls.LINES, 51 cls.CIRCLE, 52 cls.RECTANGLE, 53 cls.POINTS, 54 cls.TEXT, 55 ] 56 57 @classmethod 58 def type_to_func(cls, draw, dtype): 59 """ 60 Returns the function to use for a given overlay type. 61 62 :param draw: ImageDraw object. 63 :param dtype: Type of overlay to draw. 64 65 :return: Function to use for a given overlay type. 66 """ 67 if dtype == cls.LINES: 68 return draw.line 69 elif dtype == cls.SEGS: 70 return draw.polygon 71 elif dtype == cls.TEXT: 72 return draw.text 73 elif dtype == cls.POINTS: 74 return draw.point 75 elif dtype == cls.CIRCLE: 76 return draw.ellipse 77 elif dtype == cls.RECTANGLE: 78 return draw.rectangle 79 80 81class Overlay: 82 """ 83 Class for creating visual overlays on images. 84 """ 85 86 def __init__(self, shape: Tuple[int, int], config: Config): 87 """ 88 :param shape: Shape of the image to overlay on. 89 :param config: Config object. 90 91 :attr shape: Shape of the image to overlay on. 92 :attr overlays: Dictionary of overlays. 93 :attr config: Config object. 94 """ 95 self.shape = (shape[0], shape[1], 4) 96 self.config = config 97 self.operations = {} 98 99 for drtype in DrawTypes.ALL(): 100 self.operations[drtype] = [] 101 102 def add_operation(self, optype, *args, **kwargs): 103 # Store the drawing operation in the operations list 104 self.operations[optype].append(((args, kwargs))) 105 106 def apply_to_image(self, image): 107 108 if not any(self.operations.values()): 109 return image 110 111 image = Image.fromarray(image) 112 draw = ImageDraw.Draw(image, "RGBA") 113 114 # Execute all stored operations 115 # go in order of segs, lines, points, text 116 for optype in DrawTypes.ALL(): 117 for args, kwargs in self.operations[optype]: 118 func = DrawTypes.type_to_func(draw, optype) 119 func(*args, **kwargs) 120 121 final_image = np.array(image, dtype=np.uint8) 122 123 # remove alpha channel 124 final_image = final_image[:, :, :3] 125 126 return final_image 127 128 def get_nifti_frame( 129 self, seg_frame_objs: SegFrameObjects, shape: list 130 ) -> Image.Image: 131 """ 132 Create an image with segmentation overlay ready for NIFTI storage. 133 134 :param seg_frame_objs: Segmentation frame objects. 135 :param shape: Shape of the image. 136 137 :return: Image with segmentation overlay. 138 """ 139 140 # Create empty frame of write shape 141 seg_overlay = Image.new("RGB", shape[:2], (0, 0, 0)) 142 draw = ImageDraw.Draw(seg_overlay) 143 144 for seg_obj in seg_frame_objs: 145 if not seg_obj.empty: 146 draw.polygon( 147 seg_obj.points, 148 fill=LabelColours.get_color_from_index( 149 seg_obj.cls.value + 1 150 ), 151 ) 152 153 return seg_overlay 154 155 def draw_cross(self, point: Tuple[int, int]): 156 """ 157 Draws a cross of a given radius at a specified point on the overlay. 158 159 :param point: Point to draw the cross at. 160 """ 161 x, y = point 162 163 radius = self.config.visuals.points_radius 164 color = self.config.visuals.points_color.rgba() 165 166 self.add_operation( 167 DrawTypes.LINES, 168 (x - radius, y, x + radius, y), 169 fill=color, 170 width=self.config.visuals.line_thickness, 171 ) 172 self.add_operation( 173 DrawTypes.LINES, 174 (x, y - radius, x, y + radius), 175 fill=color, 176 width=self.config.visuals.line_thickness, 177 ) 178 179 def draw_segmentation(self, points: List[Tuple[int, int]]): 180 """ 181 Draws a polygon on the overlay. 182 183 :param points: List of points to draw the polygon. 184 """ 185 186 self.add_operation( 187 DrawTypes.SEGS, 188 points, 189 fill=self.config.visuals.seg_color.rgba( 190 self.config.visuals.seg_alpha 191 ), 192 ) 193 194 def draw_box(self, box: Tuple[int, int, int, int], grafs: bool = False): 195 """ 196 Draws a bounding box on the overlay. 197 198 :param box: Bounding box to draw. 199 :param grafs: Whether to draw with the graf frame colours or not. 200 201 """ 202 x1, y1, x2, y2 = box 203 start_point = (int(x1), int(y1)) 204 end_point = (int(x2), int(y2)) # bottom right corner 205 206 if grafs: 207 color = self.config.visuals.graf_color.rgba() 208 width = 10 209 else: 210 color = self.config.visuals.bounding_box_color.rgba() 211 width = self.config.visuals.bounding_box_thickness 212 213 self.add_operation( 214 DrawTypes.RECTANGLE, 215 [start_point, end_point], 216 outline=color, 217 width=width, 218 ) 219 220 def draw_skeleton(self, skel: List[Tuple[int, int]]): 221 """ 222 Draws a skeleton on the overlay. 223 224 :param skel: List of points to draw the skeleton. 225 """ 226 227 for point in skel: 228 y, x = point 229 self.add_operation( 230 DrawTypes.POINTS, 231 (x, y), 232 fill=self.config.hip.midline_color.rgba(), 233 ) 234 235 def draw_lines( 236 self, line_points: List[Tuple[Tuple[int, int], Tuple[int, int]]] 237 ): 238 """ 239 Draws lines on the overlay. 240 241 :param line_points: List of tuples of points to draw lines between. 242 243 """ 244 245 for point1, point2 in line_points: 246 self.add_operation( 247 DrawTypes.LINES, 248 (tuple(point1), tuple(point2)), 249 fill=self.config.visuals.line_color.rgba(), 250 width=self.config.visuals.line_thickness, 251 ) 252 253 def draw_text( 254 self, 255 label_text: str, 256 x1: int, 257 y1: int, 258 header: Literal["h1", "h2"] = "h1", 259 grafs: bool = False, 260 ): 261 """ 262 Draws text on the overlay. 263 264 :param label_text: Text to draw. 265 :param x1: x coordinate to draw text. 266 :param y1: y coordinate to draw text. 267 :param header: Header type to use. 268 :param grafs: Whether to draw with the graf frame colours or not. 269 270 """ 271 if header == "h1": 272 font = self.config.visuals.font_h1 273 elif header == "h2": 274 font = self.config.visuals.font_h2 275 276 temp_draw = ImageDraw.Draw(Image.new("RGBA", self.shape[:2])) 277 278 bbox = temp_draw.textbbox((x1, y1), label_text, font=font) 279 280 if grafs: 281 color = self.config.visuals.graf_color.rgba() 282 else: 283 color = self.config.visuals.background_text_color.rgba() 284 285 self.add_operation( 286 DrawTypes.RECTANGLE, 287 bbox, 288 fill=color, 289 ) 290 291 self.add_operation( 292 DrawTypes.TEXT, 293 (x1, y1), 294 label_text, 295 font=font, 296 fill=self.config.visuals.text_color.rgba(), 297 ) 298 299 def draw_circle(self, point: Tuple[int, int], radius: int): 300 """ 301 Draws a circle on the overlay. 302 303 :param point: Center of the circle. 304 :param radius: Radius of the circle. 305 """ 306 307 x, y = point 308 309 self.add_operation( 310 DrawTypes.CIRCLE, 311 (x - radius, y - radius, x + radius, y + radius), 312 outline=self.config.visuals.points_color.rgba(), 313 width=self.config.visuals.line_thickness, 314 )
class
DrawTypes(enum.Enum):
31class DrawTypes(Enum): 32 """ 33 Enum for the different types of overlays that can be drawn. 34 35 """ 36 37 LINES = "lines" 38 SEGS = "segs" 39 TEXT = "text" 40 POINTS = "points" 41 CIRCLE = "circle" 42 RECTANGLE = "rectangle" 43 44 @classmethod 45 def ALL(cls) -> List["DrawTypes"]: 46 """ 47 Specified in order of drawing. 48 """ 49 return [ 50 cls.SEGS, 51 cls.LINES, 52 cls.CIRCLE, 53 cls.RECTANGLE, 54 cls.POINTS, 55 cls.TEXT, 56 ] 57 58 @classmethod 59 def type_to_func(cls, draw, dtype): 60 """ 61 Returns the function to use for a given overlay type. 62 63 :param draw: ImageDraw object. 64 :param dtype: Type of overlay to draw. 65 66 :return: Function to use for a given overlay type. 67 """ 68 if dtype == cls.LINES: 69 return draw.line 70 elif dtype == cls.SEGS: 71 return draw.polygon 72 elif dtype == cls.TEXT: 73 return draw.text 74 elif dtype == cls.POINTS: 75 return draw.point 76 elif dtype == cls.CIRCLE: 77 return draw.ellipse 78 elif dtype == cls.RECTANGLE: 79 return draw.rectangle
Enum for the different types of overlays that can be drawn.
LINES =
<DrawTypes.LINES: 'lines'>
SEGS =
<DrawTypes.SEGS: 'segs'>
TEXT =
<DrawTypes.TEXT: 'text'>
POINTS =
<DrawTypes.POINTS: 'points'>
CIRCLE =
<DrawTypes.CIRCLE: 'circle'>
RECTANGLE =
<DrawTypes.RECTANGLE: 'rectangle'>
44 @classmethod 45 def ALL(cls) -> List["DrawTypes"]: 46 """ 47 Specified in order of drawing. 48 """ 49 return [ 50 cls.SEGS, 51 cls.LINES, 52 cls.CIRCLE, 53 cls.RECTANGLE, 54 cls.POINTS, 55 cls.TEXT, 56 ]
Specified in order of drawing.
@classmethod
def
type_to_func(cls, draw, dtype):
58 @classmethod 59 def type_to_func(cls, draw, dtype): 60 """ 61 Returns the function to use for a given overlay type. 62 63 :param draw: ImageDraw object. 64 :param dtype: Type of overlay to draw. 65 66 :return: Function to use for a given overlay type. 67 """ 68 if dtype == cls.LINES: 69 return draw.line 70 elif dtype == cls.SEGS: 71 return draw.polygon 72 elif dtype == cls.TEXT: 73 return draw.text 74 elif dtype == cls.POINTS: 75 return draw.point 76 elif dtype == cls.CIRCLE: 77 return draw.ellipse 78 elif dtype == cls.RECTANGLE: 79 return draw.rectangle
Returns the function to use for a given overlay type.
Parameters
- draw: ImageDraw object.
- dtype: Type of overlay to draw.
Returns
Function to use for a given overlay type.
Inherited Members
- enum.Enum
- name
- value
class
Overlay:
82class Overlay: 83 """ 84 Class for creating visual overlays on images. 85 """ 86 87 def __init__(self, shape: Tuple[int, int], config: Config): 88 """ 89 :param shape: Shape of the image to overlay on. 90 :param config: Config object. 91 92 :attr shape: Shape of the image to overlay on. 93 :attr overlays: Dictionary of overlays. 94 :attr config: Config object. 95 """ 96 self.shape = (shape[0], shape[1], 4) 97 self.config = config 98 self.operations = {} 99 100 for drtype in DrawTypes.ALL(): 101 self.operations[drtype] = [] 102 103 def add_operation(self, optype, *args, **kwargs): 104 # Store the drawing operation in the operations list 105 self.operations[optype].append(((args, kwargs))) 106 107 def apply_to_image(self, image): 108 109 if not any(self.operations.values()): 110 return image 111 112 image = Image.fromarray(image) 113 draw = ImageDraw.Draw(image, "RGBA") 114 115 # Execute all stored operations 116 # go in order of segs, lines, points, text 117 for optype in DrawTypes.ALL(): 118 for args, kwargs in self.operations[optype]: 119 func = DrawTypes.type_to_func(draw, optype) 120 func(*args, **kwargs) 121 122 final_image = np.array(image, dtype=np.uint8) 123 124 # remove alpha channel 125 final_image = final_image[:, :, :3] 126 127 return final_image 128 129 def get_nifti_frame( 130 self, seg_frame_objs: SegFrameObjects, shape: list 131 ) -> Image.Image: 132 """ 133 Create an image with segmentation overlay ready for NIFTI storage. 134 135 :param seg_frame_objs: Segmentation frame objects. 136 :param shape: Shape of the image. 137 138 :return: Image with segmentation overlay. 139 """ 140 141 # Create empty frame of write shape 142 seg_overlay = Image.new("RGB", shape[:2], (0, 0, 0)) 143 draw = ImageDraw.Draw(seg_overlay) 144 145 for seg_obj in seg_frame_objs: 146 if not seg_obj.empty: 147 draw.polygon( 148 seg_obj.points, 149 fill=LabelColours.get_color_from_index( 150 seg_obj.cls.value + 1 151 ), 152 ) 153 154 return seg_overlay 155 156 def draw_cross(self, point: Tuple[int, int]): 157 """ 158 Draws a cross of a given radius at a specified point on the overlay. 159 160 :param point: Point to draw the cross at. 161 """ 162 x, y = point 163 164 radius = self.config.visuals.points_radius 165 color = self.config.visuals.points_color.rgba() 166 167 self.add_operation( 168 DrawTypes.LINES, 169 (x - radius, y, x + radius, y), 170 fill=color, 171 width=self.config.visuals.line_thickness, 172 ) 173 self.add_operation( 174 DrawTypes.LINES, 175 (x, y - radius, x, y + radius), 176 fill=color, 177 width=self.config.visuals.line_thickness, 178 ) 179 180 def draw_segmentation(self, points: List[Tuple[int, int]]): 181 """ 182 Draws a polygon on the overlay. 183 184 :param points: List of points to draw the polygon. 185 """ 186 187 self.add_operation( 188 DrawTypes.SEGS, 189 points, 190 fill=self.config.visuals.seg_color.rgba( 191 self.config.visuals.seg_alpha 192 ), 193 ) 194 195 def draw_box(self, box: Tuple[int, int, int, int], grafs: bool = False): 196 """ 197 Draws a bounding box on the overlay. 198 199 :param box: Bounding box to draw. 200 :param grafs: Whether to draw with the graf frame colours or not. 201 202 """ 203 x1, y1, x2, y2 = box 204 start_point = (int(x1), int(y1)) 205 end_point = (int(x2), int(y2)) # bottom right corner 206 207 if grafs: 208 color = self.config.visuals.graf_color.rgba() 209 width = 10 210 else: 211 color = self.config.visuals.bounding_box_color.rgba() 212 width = self.config.visuals.bounding_box_thickness 213 214 self.add_operation( 215 DrawTypes.RECTANGLE, 216 [start_point, end_point], 217 outline=color, 218 width=width, 219 ) 220 221 def draw_skeleton(self, skel: List[Tuple[int, int]]): 222 """ 223 Draws a skeleton on the overlay. 224 225 :param skel: List of points to draw the skeleton. 226 """ 227 228 for point in skel: 229 y, x = point 230 self.add_operation( 231 DrawTypes.POINTS, 232 (x, y), 233 fill=self.config.hip.midline_color.rgba(), 234 ) 235 236 def draw_lines( 237 self, line_points: List[Tuple[Tuple[int, int], Tuple[int, int]]] 238 ): 239 """ 240 Draws lines on the overlay. 241 242 :param line_points: List of tuples of points to draw lines between. 243 244 """ 245 246 for point1, point2 in line_points: 247 self.add_operation( 248 DrawTypes.LINES, 249 (tuple(point1), tuple(point2)), 250 fill=self.config.visuals.line_color.rgba(), 251 width=self.config.visuals.line_thickness, 252 ) 253 254 def draw_text( 255 self, 256 label_text: str, 257 x1: int, 258 y1: int, 259 header: Literal["h1", "h2"] = "h1", 260 grafs: bool = False, 261 ): 262 """ 263 Draws text on the overlay. 264 265 :param label_text: Text to draw. 266 :param x1: x coordinate to draw text. 267 :param y1: y coordinate to draw text. 268 :param header: Header type to use. 269 :param grafs: Whether to draw with the graf frame colours or not. 270 271 """ 272 if header == "h1": 273 font = self.config.visuals.font_h1 274 elif header == "h2": 275 font = self.config.visuals.font_h2 276 277 temp_draw = ImageDraw.Draw(Image.new("RGBA", self.shape[:2])) 278 279 bbox = temp_draw.textbbox((x1, y1), label_text, font=font) 280 281 if grafs: 282 color = self.config.visuals.graf_color.rgba() 283 else: 284 color = self.config.visuals.background_text_color.rgba() 285 286 self.add_operation( 287 DrawTypes.RECTANGLE, 288 bbox, 289 fill=color, 290 ) 291 292 self.add_operation( 293 DrawTypes.TEXT, 294 (x1, y1), 295 label_text, 296 font=font, 297 fill=self.config.visuals.text_color.rgba(), 298 ) 299 300 def draw_circle(self, point: Tuple[int, int], radius: int): 301 """ 302 Draws a circle on the overlay. 303 304 :param point: Center of the circle. 305 :param radius: Radius of the circle. 306 """ 307 308 x, y = point 309 310 self.add_operation( 311 DrawTypes.CIRCLE, 312 (x - radius, y - radius, x + radius, y + radius), 313 outline=self.config.visuals.points_color.rgba(), 314 width=self.config.visuals.line_thickness, 315 )
Class for creating visual overlays on images.
Overlay(shape: Tuple[int, int], config: retuve.keyphrases.config.Config)
87 def __init__(self, shape: Tuple[int, int], config: Config): 88 """ 89 :param shape: Shape of the image to overlay on. 90 :param config: Config object. 91 92 :attr shape: Shape of the image to overlay on. 93 :attr overlays: Dictionary of overlays. 94 :attr config: Config object. 95 """ 96 self.shape = (shape[0], shape[1], 4) 97 self.config = config 98 self.operations = {} 99 100 for drtype in DrawTypes.ALL(): 101 self.operations[drtype] = []
Parameters
- shape: Shape of the image to overlay on.
- config: Config object.
:attr shape: Shape of the image to overlay on. :attr overlays: Dictionary of overlays. :attr config: Config object.
def
apply_to_image(self, image):
107 def apply_to_image(self, image): 108 109 if not any(self.operations.values()): 110 return image 111 112 image = Image.fromarray(image) 113 draw = ImageDraw.Draw(image, "RGBA") 114 115 # Execute all stored operations 116 # go in order of segs, lines, points, text 117 for optype in DrawTypes.ALL(): 118 for args, kwargs in self.operations[optype]: 119 func = DrawTypes.type_to_func(draw, optype) 120 func(*args, **kwargs) 121 122 final_image = np.array(image, dtype=np.uint8) 123 124 # remove alpha channel 125 final_image = final_image[:, :, :3] 126 127 return final_image
def
get_nifti_frame( self, seg_frame_objs: retuve.classes.seg.SegFrameObjects, shape: list) -> PIL.Image.Image:
129 def get_nifti_frame( 130 self, seg_frame_objs: SegFrameObjects, shape: list 131 ) -> Image.Image: 132 """ 133 Create an image with segmentation overlay ready for NIFTI storage. 134 135 :param seg_frame_objs: Segmentation frame objects. 136 :param shape: Shape of the image. 137 138 :return: Image with segmentation overlay. 139 """ 140 141 # Create empty frame of write shape 142 seg_overlay = Image.new("RGB", shape[:2], (0, 0, 0)) 143 draw = ImageDraw.Draw(seg_overlay) 144 145 for seg_obj in seg_frame_objs: 146 if not seg_obj.empty: 147 draw.polygon( 148 seg_obj.points, 149 fill=LabelColours.get_color_from_index( 150 seg_obj.cls.value + 1 151 ), 152 ) 153 154 return seg_overlay
Create an image with segmentation overlay ready for NIFTI storage.
Parameters
- seg_frame_objs: Segmentation frame objects.
- shape: Shape of the image.
Returns
Image with segmentation overlay.
def
draw_cross(self, point: Tuple[int, int]):
156 def draw_cross(self, point: Tuple[int, int]): 157 """ 158 Draws a cross of a given radius at a specified point on the overlay. 159 160 :param point: Point to draw the cross at. 161 """ 162 x, y = point 163 164 radius = self.config.visuals.points_radius 165 color = self.config.visuals.points_color.rgba() 166 167 self.add_operation( 168 DrawTypes.LINES, 169 (x - radius, y, x + radius, y), 170 fill=color, 171 width=self.config.visuals.line_thickness, 172 ) 173 self.add_operation( 174 DrawTypes.LINES, 175 (x, y - radius, x, y + radius), 176 fill=color, 177 width=self.config.visuals.line_thickness, 178 )
Draws a cross of a given radius at a specified point on the overlay.
Parameters
- point: Point to draw the cross at.
def
draw_segmentation(self, points: List[Tuple[int, int]]):
180 def draw_segmentation(self, points: List[Tuple[int, int]]): 181 """ 182 Draws a polygon on the overlay. 183 184 :param points: List of points to draw the polygon. 185 """ 186 187 self.add_operation( 188 DrawTypes.SEGS, 189 points, 190 fill=self.config.visuals.seg_color.rgba( 191 self.config.visuals.seg_alpha 192 ), 193 )
Draws a polygon on the overlay.
Parameters
- points: List of points to draw the polygon.
def
draw_box(self, box: Tuple[int, int, int, int], grafs: bool = False):
195 def draw_box(self, box: Tuple[int, int, int, int], grafs: bool = False): 196 """ 197 Draws a bounding box on the overlay. 198 199 :param box: Bounding box to draw. 200 :param grafs: Whether to draw with the graf frame colours or not. 201 202 """ 203 x1, y1, x2, y2 = box 204 start_point = (int(x1), int(y1)) 205 end_point = (int(x2), int(y2)) # bottom right corner 206 207 if grafs: 208 color = self.config.visuals.graf_color.rgba() 209 width = 10 210 else: 211 color = self.config.visuals.bounding_box_color.rgba() 212 width = self.config.visuals.bounding_box_thickness 213 214 self.add_operation( 215 DrawTypes.RECTANGLE, 216 [start_point, end_point], 217 outline=color, 218 width=width, 219 )
Draws a bounding box on the overlay.
Parameters
- box: Bounding box to draw.
- grafs: Whether to draw with the graf frame colours or not.
def
draw_skeleton(self, skel: List[Tuple[int, int]]):
221 def draw_skeleton(self, skel: List[Tuple[int, int]]): 222 """ 223 Draws a skeleton on the overlay. 224 225 :param skel: List of points to draw the skeleton. 226 """ 227 228 for point in skel: 229 y, x = point 230 self.add_operation( 231 DrawTypes.POINTS, 232 (x, y), 233 fill=self.config.hip.midline_color.rgba(), 234 )
Draws a skeleton on the overlay.
Parameters
- skel: List of points to draw the skeleton.
def
draw_lines(self, line_points: List[Tuple[Tuple[int, int], Tuple[int, int]]]):
236 def draw_lines( 237 self, line_points: List[Tuple[Tuple[int, int], Tuple[int, int]]] 238 ): 239 """ 240 Draws lines on the overlay. 241 242 :param line_points: List of tuples of points to draw lines between. 243 244 """ 245 246 for point1, point2 in line_points: 247 self.add_operation( 248 DrawTypes.LINES, 249 (tuple(point1), tuple(point2)), 250 fill=self.config.visuals.line_color.rgba(), 251 width=self.config.visuals.line_thickness, 252 )
Draws lines on the overlay.
Parameters
- line_points: List of tuples of points to draw lines between.
def
draw_text( self, label_text: str, x1: int, y1: int, header: Literal['h1', 'h2'] = 'h1', grafs: bool = False):
254 def draw_text( 255 self, 256 label_text: str, 257 x1: int, 258 y1: int, 259 header: Literal["h1", "h2"] = "h1", 260 grafs: bool = False, 261 ): 262 """ 263 Draws text on the overlay. 264 265 :param label_text: Text to draw. 266 :param x1: x coordinate to draw text. 267 :param y1: y coordinate to draw text. 268 :param header: Header type to use. 269 :param grafs: Whether to draw with the graf frame colours or not. 270 271 """ 272 if header == "h1": 273 font = self.config.visuals.font_h1 274 elif header == "h2": 275 font = self.config.visuals.font_h2 276 277 temp_draw = ImageDraw.Draw(Image.new("RGBA", self.shape[:2])) 278 279 bbox = temp_draw.textbbox((x1, y1), label_text, font=font) 280 281 if grafs: 282 color = self.config.visuals.graf_color.rgba() 283 else: 284 color = self.config.visuals.background_text_color.rgba() 285 286 self.add_operation( 287 DrawTypes.RECTANGLE, 288 bbox, 289 fill=color, 290 ) 291 292 self.add_operation( 293 DrawTypes.TEXT, 294 (x1, y1), 295 label_text, 296 font=font, 297 fill=self.config.visuals.text_color.rgba(), 298 )
Draws text on the overlay.
Parameters
- label_text: Text to draw.
- x1: x coordinate to draw text.
- y1: y coordinate to draw text.
- header: Header type to use.
- grafs: Whether to draw with the graf frame colours or not.
def
draw_circle(self, point: Tuple[int, int], radius: int):
300 def draw_circle(self, point: Tuple[int, int], radius: int): 301 """ 302 Draws a circle on the overlay. 303 304 :param point: Center of the circle. 305 :param radius: Radius of the circle. 306 """ 307 308 x, y = point 309 310 self.add_operation( 311 DrawTypes.CIRCLE, 312 (x - radius, y - radius, x + radius, y + radius), 313 outline=self.config.visuals.points_color.rgba(), 314 width=self.config.visuals.line_thickness, 315 )
Draws a circle on the overlay.
Parameters
- point: Center of the circle.
- radius: Radius of the circle.