feat: configurations

This commit is contained in:
2025-12-29 16:08:12 +03:00
parent 0977a55cc2
commit a02c20a8ff
5 changed files with 310 additions and 33 deletions

View File

@@ -32,7 +32,6 @@ class Geolocation:
# Вычисляем угол поворота # Вычисляем угол поворота
rotation = -np.arctan2(mat[1, 0], mat[0, 0]) rotation = -np.arctan2(mat[1, 0], mat[0, 0])
scale = np.hypot(mat[0, 0], mat[0, 1]) scale = np.hypot(mat[0, 0], mat[0, 1])
print("Scale: ", scale)
# Координаты уже отцентрированы, поэтому используем их напрямую # Координаты уже отцентрированы, поэтому используем их напрямую
dx_meters = tx dx_meters = tx

View File

@@ -34,7 +34,7 @@ def main():
# points = get_trajectory_points('map.jpg') # points = get_trajectory_points('map.jpg')
# Trajectory #1 # Trajectory #1
points = [[np.float64(0.5384504359393909), np.float64(0.4084520767967683)], [np.float64(0.4451750568707629), np.float64(0.38213330305374654)], [np.float64(0.49266070439660997), np.float64(0.2789637099811013)], [np.float64(0.36377108968359656), np.float64(0.3263375027185404)], [np.float64(0.3535955937852008), np.float64(0.4337180995900692)]] # points = [[np.float64(0.5384504359393909), np.float64(0.4084520767967683)], [np.float64(0.4451750568707629), np.float64(0.38213330305374654)], [np.float64(0.49266070439660997), np.float64(0.2789637099811013)], [np.float64(0.36377108968359656), np.float64(0.3263375027185404)], [np.float64(0.3535955937852008), np.float64(0.4337180995900692)]]
# Trajectory #2 # Trajectory #2
# points = [[np.float64(0.29197731306713737), np.float64(0.3452870198135161)], [np.float64(0.33494051797147517), np.float64(0.2010601397017569)], [np.float64(0.39768940934491587), np.float64(0.25369768718780034)], [np.float64(0.4027771572941138), np.float64(0.4158213334448144)], [np.float64(0.2914120077394487), np.float64(0.5547844588079692)]] # points = [[np.float64(0.29197731306713737), np.float64(0.3452870198135161)], [np.float64(0.33494051797147517), np.float64(0.2010601397017569)], [np.float64(0.39768940934491587), np.float64(0.25369768718780034)], [np.float64(0.4027771572941138), np.float64(0.4158213334448144)], [np.float64(0.2914120077394487), np.float64(0.5547844588079692)]]
@@ -93,6 +93,7 @@ def main():
kp, des = chunk.compute_keypoints() kp, des = chunk.compute_keypoints()
plt.subplot(1, len(points), i+1) plt.subplot(1, len(points), i+1)
plt.imshow(chunk.image)
kp_coords = np.array([j.pt for j in kp]) kp_coords = np.array([j.pt for j in kp])
if len(kp_coords) > 0: if len(kp_coords) > 0:
plt.scatter(kp_coords[:, 0], kp_coords[:, 1], c='red', s=20, alpha=0.7, marker='o') plt.scatter(kp_coords[:, 0], kp_coords[:, 1], c='red', s=20, alpha=0.7, marker='o')
@@ -187,3 +188,5 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()
#TODO

File diff suppressed because one or more lines are too long

View File

@@ -18,3 +18,7 @@
| [+] Уменьшение погрешность за счёт удаления выбросов | [+] Уменьшение погрешность за счёт удаления выбросов
| [+] Исследовать причину погрешности при развороте | [+] Исследовать причину погрешности при развороте
[+] Устранение большой погрешности при повороте [+] Устранение большой погрешности при повороте
[ ] Переделать ключевые точки -> Optical Flow
[ ] Добавить перспективу
[ ] Эталоны на Google Maps, полёт тот же

View File

@@ -6,12 +6,13 @@ from pathlib import Path
from typing import Literal, Optional, Tuple from typing import Literal, Optional, Tuple
from PIL import Image from PIL import Image
FeatureMethod = Literal["orb", "sift", "surf", "akaze", "brisk"] FeatureMethod = Literal["orb", "sift", "akaze", "brisk"]
DEFAULT_METHOD = "akaze"
@dataclass @dataclass
class VisionChunk: class VisionChunk:
image: Image.Image image: Image.Image
feature_method: FeatureMethod = "orb" feature_method: FeatureMethod = DEFAULT_METHOD
keypoints: Optional[list] = field(default=None, init=False) keypoints: Optional[list] = field(default=None, init=False)
descriptors: Optional[np.ndarray] = field(default=None, init=False) descriptors: Optional[np.ndarray] = field(default=None, init=False)
@@ -26,7 +27,7 @@ class VisionChunk:
self._detector = cv2.ORB_create( self._detector = cv2.ORB_create(
nfeatures=1000, nfeatures=1000,
scaleFactor=1.2, scaleFactor=1.2,
nlevels=8, nlevels=32,
edgeThreshold=31, edgeThreshold=31,
firstLevel=0, firstLevel=0,
WTA_K=2, WTA_K=2,
@@ -35,18 +36,11 @@ class VisionChunk:
) )
elif self.feature_method == "sift": elif self.feature_method == "sift":
self._detector = cv2.SIFT_create( self._detector = cv2.SIFT_create(
nfeatures=1000, nfeatures=1500,
nOctaveLayers=3, nOctaveLayers=2,
contrastThreshold=0.04, contrastThreshold=0.01,
edgeThreshold=10, edgeThreshold=15,
sigma=1.6 sigma=3.3
)
elif self.feature_method == "surf":
self._detector = cv2.xfeatures2d.SURF_create(
hessianThreshold=400.0,
nOctaveLayers=3,
# nOctaveLayers=4,
upright=False
) )
elif self.feature_method == "akaze": elif self.feature_method == "akaze":
self._detector = cv2.AKAZE_create( self._detector = cv2.AKAZE_create(
@@ -59,8 +53,8 @@ class VisionChunk:
) )
elif self.feature_method == "brisk": elif self.feature_method == "brisk":
self._detector = cv2.BRISK_create( self._detector = cv2.BRISK_create(
thresh=30, thresh=70,
octaves=3, octaves=7,
patternScale=1.0 patternScale=1.0
) )
else: else:
@@ -69,15 +63,10 @@ class VisionChunk:
def _get_matcher(self) -> cv2.DescriptorMatcher: def _get_matcher(self) -> cv2.DescriptorMatcher:
if self._matcher is None: if self._matcher is None:
self._matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False) if self.feature_method == 'sift':
return self._matcher self._matcher = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)
if self.feature_method == 'orb': else:
self._matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False) self._matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)
elif self.feature_method == 'sift':
FLANN_INDEX_LSH = 6
index_params = dict(algorithm=FLANN_INDEX_LSH, table_number=6, key_size=12, multi_probe_level=1)
search_params = dict(checks=50)
self._matcher = cv2.FlannBasedMatcher(index_params, search_params)
return self._matcher return self._matcher
def _preprocess(self, img_np: np.ndarray) -> np.ndarray: def _preprocess(self, img_np: np.ndarray) -> np.ndarray:
@@ -90,7 +79,7 @@ class VisionChunk:
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8)) clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
return clahe.apply(gray) return clahe.apply(gray)
def compute_keypoints(self, force: bool = False) -> Tuple[list, Optional[np.ndarray]]: def compute_keypoints(self, force: bool = False) -> Tuple[list[cv2.KeyPoint], Optional[np.ndarray]]:
if self.keypoints is not None and self.descriptors is not None and not force: if self.keypoints is not None and self.descriptors is not None and not force:
return self.keypoints, self.descriptors return self.keypoints, self.descriptors
@@ -103,11 +92,21 @@ class VisionChunk:
# CLAHE предобработка # CLAHE предобработка
preprocessed = self._preprocess(img_np) preprocessed = self._preprocess(img_np)
kps, desc = detector.detectAndCompute(preprocessed, None) keypoints, descriptors = detector.detectAndCompute(preprocessed, None)
self.keypoints = kps # Получаем массив response для всех точек
self.descriptors = desc responses = np.array([kp.response for kp in keypoints])
return kps, desc
# Находим индексы топ-100
top_indices = np.argsort(responses)[-2500:][::-1]
# Отбираем keypoints и descriptors
best_keypoints = [keypoints[i] for i in top_indices]
best_descriptors = descriptors[top_indices]
self.keypoints = best_keypoints
self.descriptors = best_descriptors
return self.keypoints, self.descriptors
def detect_and_match_keypoints( def detect_and_match_keypoints(
self, self,
@@ -193,7 +192,7 @@ class VisionChunk:
return np.array(self.image) return np.array(self.image)
@classmethod @classmethod
def load_image(cls, path: Path | str, feature_method: FeatureMethod = "orb") -> "VisionChunk": def load_image(cls, path: Path | str, feature_method: FeatureMethod = DEFAULT_METHOD) -> "VisionChunk":
path = Path(path) path = Path(path)
image = Image.open(path) image = Image.open(path)
return cls(image=image, feature_method=feature_method) return cls(image=image, feature_method=feature_method)