Coverage for src/rift_console/image_helper.py: 0%
93 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-08 09:36 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-08 09:36 +0000
1"""
3Provides helping functions that are used in image_processing.
5"""
7import re
8import os
9import datetime
10from loguru import logger
12import shared.constants as con
13from shared.models import CameraAngle
16def get_angle(image: str) -> CameraAngle:
17 if "narrow" in image:
18 return CameraAngle.Narrow
19 elif "normal" in image:
20 return CameraAngle.Normal
21 elif "wide" in image:
22 return CameraAngle.Wide
23 logger.warning(f"Unknown camera angle in {image}")
24 return CameraAngle.Unknown
27def get_date(image: str) -> str:
28 # pattern of year-month-dayThour-minute
29 pattern = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}"
31 found_matches = re.findall(pattern, image)
33 if len(found_matches) == 1:
34 # logger.debug(found_matches[0])
35 match: str = found_matches[0]
36 return match
37 else:
38 logger.warning("None or two dates in {image}")
39 return datetime.datetime.min.strftime("%Y-%m-%dT%H:%M:%S")
42def filter_by_date(
43 images: list[str], start: datetime.datetime, end: datetime.datetime
44) -> list[str]:
45 res = []
46 date_format = "%Y-%m-%dT%H:%M:%S"
47 for image in images:
48 date = datetime.datetime.strptime(get_date(image), date_format).replace(
49 tzinfo=datetime.timezone.utc
50 )
51 # logger.warning(f"{date} {start} {end}")
52 if date >= start and date <= end:
53 res.append(image)
54 return res
57def generate_spiral_walk(n: int) -> list[tuple[int, int]]:
58 """Create an spiraling offset pattern arround a central point, e.g. (0,0), (0,1), (1,0), (1,1), ...
59 sorted by Manhattan geometry
61 Args:
62 n (int): number of offsets to be generated
64 Returns:
65 list[tuple[int, int]]: list of offsets
66 """
68 # move right, up, left, down
69 directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
70 # start with going right
71 direction_index = 0
73 x, y = 0, 0
74 offsets = [(x, y)]
76 # Number of steps we take before changing direction
77 steps = 1
79 while len(offsets) < n:
80 for _ in range(2):
81 for _ in range(steps):
82 if len(offsets) < n:
83 # Move in the current direction
84 dx, dy = directions[direction_index]
85 x += dx
86 y += dy
87 # Add the new position to the spiral
88 offsets.append((x, y))
89 else:
90 break
91 # Change direction clockwise
92 direction_index = (direction_index + 1) % 4
93 # After moving two directions, we increase the number of steps
94 steps += 1
96 sorted_offset = sorted(offsets, key=lambda x: abs(x[0]) + abs(x[1]))
97 return sorted_offset
100def parse_image_name(name: str) -> tuple[int, int, int]:
101 """Parses an image name in the format generated by Melvonaut and extract the relevant properties for stitching
103 Args:
104 name (str): file name of the image
106 Returns:
107 tuple[int, int, int]: Used lenssize (and therefore if the image should be scaled to this later)
108 and approximated x/y coordinates on the stiched image
109 """
110 from shared.models import CameraAngle
112 # expected format: 'image_5344_wide_2024-12-11T17:31:27.507376_x_19936_y_4879'
113 # with 8 underscores
114 if len(name.split("_")) != con.IMAGE_NAME_UNDERSCORE_COUNT:
115 raise Exception("parse_image_name: filename has wrong format!")
117 # used CameraAngle is after second underscore
118 match name.split("_")[con.IMAGE_ANGLE_POSITION]:
119 case CameraAngle.Narrow:
120 lens_size = 600
121 case CameraAngle.Normal:
122 lens_size = 800
123 case CameraAngle.Wide:
124 lens_size = 1000
126 # find x and y in name
127 match = re.search(r"_x_(-?\d+)_y_(-?\d+)", name)
129 if match:
130 x = int(match.group(1))
131 y = int(match.group(2))
133 # old images position is not adjusted in melvonaut yet
134 if con.USE_LEGACY_IMAGE_NAMES:
135 x -= (int)(lens_size / 2)
136 y -= (int)(lens_size / 2)
137 else:
138 raise Exception("parse_image_name: could not match x/y coordinates!")
140 return lens_size, x, y
143# returns all images
144def find_image_names(directory: str) -> list[str]:
145 """Traverses the given directory and find + sorts all images in our filename format
147 Args:
148 directory (str): path to the folder, needs to include con.IMAGE_PATH
150 Returns:
151 list[str]: the name of all images in that folder, sorted by its timestamp from old to now
152 """
154 # find all names
155 image_names = []
156 for filename in os.listdir(directory):
157 if filename.startswith("image"):
158 image_names.append(filename)
160 # helper function used in sorting
161 def extract_timestamp(s: str) -> datetime.datetime:
162 timestamp_pattern = r"_(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6})"
164 match = re.search(timestamp_pattern, s)
165 if match:
166 return datetime.datetime.fromisoformat(match.group(1))
167 else:
168 raise Exception("find_image_names: did not found timestamp in image names")
170 def extract_pos(s: str) -> int:
171 pos_pattern = r"_x_(-?\d+)_y_(-?\d+)"
173 match = re.search(pos_pattern, s)
174 if match:
175 x = int(match.group(1))
176 y = int(match.group(2))
177 return x + y
178 else:
179 raise Exception("find_image_names: did not found position in image names")
181 # sort
182 if con.SORT_IMAGE_BY_POSITION:
183 image_names = sorted(image_names, key=extract_pos)
184 else:
185 image_names = sorted(image_names, key=extract_timestamp)
186 return image_names