From 22a041db6ccd061625f1b26db349e5a014d25358 Mon Sep 17 00:00:00 2001 From: "long.ao" Date: Tue, 17 Dec 2024 22:09:47 +0800 Subject: [PATCH] first commit --- README.md | 2 + odm_preprocess.py | 163 ++++++++++++++++++ .../__pycache__/gps_extractor.cpython-312.pyc | Bin 0 -> 3727 bytes .../__pycache__/gps_extractor.cpython-39.pyc | Bin 0 -> 1625 bytes .../__pycache__/gps_filter.cpython-312.pyc | Bin 0 -> 8844 bytes .../__pycache__/gps_filter.cpython-39.pyc | Bin 0 -> 4692 bytes .../__pycache__/grid_divider.cpython-312.pyc | Bin 0 -> 4662 bytes .../__pycache__/grid_divider.cpython-39.pyc | Bin 0 -> 1955 bytes preprocess/__pycache__/logger.cpython-312.pyc | Bin 0 -> 1586 bytes preprocess/gps_extractor.py | 55 ++++++ preprocess/gps_filter.py | 146 ++++++++++++++++ preprocess/grid_divider.py | 84 +++++++++ preprocess/logger.py | 36 ++++ show_GPS.py | 44 +++++ 14 files changed, 530 insertions(+) create mode 100644 README.md create mode 100644 odm_preprocess.py create mode 100644 preprocess/__pycache__/gps_extractor.cpython-312.pyc create mode 100644 preprocess/__pycache__/gps_extractor.cpython-39.pyc create mode 100644 preprocess/__pycache__/gps_filter.cpython-312.pyc create mode 100644 preprocess/__pycache__/gps_filter.cpython-39.pyc create mode 100644 preprocess/__pycache__/grid_divider.cpython-312.pyc create mode 100644 preprocess/__pycache__/grid_divider.cpython-39.pyc create mode 100644 preprocess/__pycache__/logger.cpython-312.pyc create mode 100644 preprocess/gps_extractor.py create mode 100644 preprocess/gps_filter.py create mode 100644 preprocess/grid_divider.py create mode 100644 preprocess/logger.py create mode 100644 show_GPS.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ed8f52 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# ODM_Pro +无人机三维重建 diff --git a/odm_preprocess.py b/odm_preprocess.py new file mode 100644 index 0000000..bdc56c7 --- /dev/null +++ b/odm_preprocess.py @@ -0,0 +1,163 @@ +from gps_extractor import GPSExtractor +from gps_filter import GPSFilter +from grid_divider import GridDivider +from logger import setup_logger +import os +import pandas as pd +import shutil +import matplotlib.pyplot as plt +from typing import List, Dict, Optional +from dataclasses import dataclass +from tqdm import tqdm + + +@dataclass +class PreprocessConfig: + """预处理配置类""" + image_dir: str + output_dir: str + filter_grid_size: float = 0.001 + filter_dense_distance_threshold: float = 10 + filter_distance_threshold: float = 0.001 + filter_min_neighbors: int = 6 + grid_overlap: float = 0.05 + enable_filter: bool = True + enable_grid_division: bool = True + enable_visualization: bool = True + + +class ImagePreprocessor: + def __init__(self, config: PreprocessConfig): + self.config = config + self.logger = setup_logger(config.output_dir) + self.gps_points = [] + + def extract_gps(self) -> List[Dict]: + """提取GPS数据""" + self.logger.info("开始提取GPS数据") + extractor = GPSExtractor(self.config.image_dir) + self.gps_points = extractor.extract_all_gps() + self.logger.info(f"成功提取 {len(self.gps_points)} 个GPS点") + return self.gps_points + + def filter_points(self) -> List[Dict]: + """过滤GPS点""" + if not self.config.enable_filter: + return self.gps_points + + self.logger.info("开始过滤GPS点") + filter = GPSFilter(self.config.output_dir) + + self.logger.info(f"开始过滤孤立点 (距离阈值: {self.config.filter_distance_threshold}, 最小邻居数: { + self.config.filter_min_neighbors})") + self.gps_points = filter.filter_isolated_points( + self.gps_points, + self.config.filter_distance_threshold, + self.config.filter_min_neighbors + ) + self.logger.info(f"孤立点过滤后剩余 {len(self.gps_points)} 个GPS点") + + self.logger.info(f"开始过滤密集点 (网格大小: {self.config.filter_grid_size}, 距离阈值: { + self.config.filter_dense_distance_threshold})") + self.gps_points = filter.filter_dense_points( + self.gps_points, + grid_size=self.config.filter_grid_size, + distance_threshold=self.config.filter_dense_distance_threshold + ) + self.logger.info(f"密集点过滤后剩余 {len(self.gps_points)} 个GPS点") + return self.gps_points + + def divide_grids(self) -> Dict[int, List[Dict]]: + """划分网格""" + if not self.config.enable_grid_division: + return {0: self.gps_points} # 不划分网格时,所有点放在一个网格中 + + self.logger.info(f"开始划分网格 (重叠率: {self.config.grid_overlap})") + grid_divider = GridDivider(overlap=self.config.grid_overlap) + grids = grid_divider.divide_grids(self.gps_points) + grid_points = grid_divider.assign_to_grids(self.gps_points, grids) + self.logger.info(f"成功划分为 {len(grid_points)} 个网格") + return grid_points + + def copy_images(self, grid_points: Dict[int, List[Dict]]): + """复制图像到目标文件夹""" + self.logger.info("开始复制图像文件") + os.makedirs(self.config.output_dir, exist_ok=True) + + for grid_idx, points in grid_points.items(): + if self.config.enable_grid_division: + output_dir = os.path.join(self.config.output_dir, f'grid_{ + grid_idx + 1}', 'images') + else: + output_dir = os.path.join(self.config.output_dir, 'images') + + os.makedirs(output_dir, exist_ok=True) + + for point in tqdm(points, desc=f"复制网格 {grid_idx + 1} 的图像"): + src = os.path.join(self.config.image_dir, point['file']) + dst = os.path.join(output_dir, point['file']) + shutil.copy(src, dst) + self.logger.info(f"网格 {grid_idx + 1} 包含 {len(points)} 张图像") + + def visualize_results(self): + """可视化处理结果""" + if not self.config.enable_visualization: + return + + self.logger.info("开始生成可视化结果") + extractor = GPSExtractor(self.config.image_dir) + original_points = extractor.extract_all_gps() + + with open(os.path.join(self.config.output_dir, 'del_imgs.txt'), "r", encoding="utf-8") as file: + filtered_file = [line.strip() for line in file] + + # 绘制散点图 + plt.figure(figsize=(10, 8)) + plt.scatter([p['lon'] for p in original_points], + [p['lat'] for p in original_points], + color='blue', label="Original Points", alpha=0.6) + plt.scatter([p['lon'] for p in original_points if p['file'] in filtered_file], + [p['lat'] + for p in original_points if p['file'] in filtered_file], + color="red", label="Filtered Points", alpha=0.6) + plt.title("GPS Coordinates of Images", fontsize=14) + plt.xlabel("Longitude", fontsize=12) + plt.ylabel("Latitude", fontsize=12) + plt.grid(True) + plt.legend() + plt.savefig(os.path.join(self.config.output_dir, 'filter_GPS.png')) + plt.close() + self.logger.info("预处理结果图已保存") + + def process(self): + """执行完整的预处理流程""" + try: + self.extract_gps() + self.filter_points() + grid_points = self.divide_grids() + self.copy_images(grid_points) + self.visualize_results() + self.logger.info("预处理任务完成") + except Exception as e: + self.logger.error(f"处理过程中发生错误: {str(e)}", exc_info=True) + raise + + +if __name__ == '__main__': + # 创建配置 + config = PreprocessConfig( + image_dir=r'C:\datasets\1815\output\grid_5\images', + output_dir=r'C:\datasets\1815\output\grid_5', + filter_grid_size=0.001, + filter_dense_distance_threshold=10, + filter_distance_threshold=0.001, + filter_min_neighbors=6, + grid_overlap=0.05, + enable_filter=False, + enable_grid_division=True, + enable_visualization=False + ) + + # 创建处理器并执行 + processor = ImagePreprocessor(config) + processor.process() diff --git a/preprocess/__pycache__/gps_extractor.cpython-312.pyc b/preprocess/__pycache__/gps_extractor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a9f3f8056d09938896b4937e08a58f62b42c544 GIT binary patch literal 3727 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 cti(~G0d6#NTjb^rhX literal 0 HcmV?d00001 diff --git a/preprocess/__pycache__/grid_divider.cpython-312.pyc b/preprocess/__pycache__/grid_divider.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6b87941eaf9cf621cddac3854755a8fa5843c72 GIT binary patch literal 4662 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 Gkn|VslUO$Z literal 0 HcmV?d00001 diff --git a/preprocess/gps_extractor.py b/preprocess/gps_extractor.py new file mode 100644 index 0000000..5b217db --- /dev/null +++ b/preprocess/gps_extractor.py @@ -0,0 +1,55 @@ +import os +from PIL import Image +import piexif +import logging + + +class GPSExtractor: + """从图像文件提取GPS坐标""" + + def __init__(self, image_dir): + self.image_dir = image_dir + self.logger = logging.getLogger('UAV_Preprocess.GPSExtractor') + + @staticmethod + def _dms_to_decimal(dms): + """将DMS格式转换为十进制度""" + return dms[0][0] / dms[0][1] + (dms[1][0] / dms[1][1]) / 60 + (dms[2][0] / dms[2][1]) / 3600 + + def get_gps(self, image_path): + """提取单张图片的GPS坐标""" + try: + image = Image.open(image_path) + exif_data = piexif.load(image.info['exif']) + gps_info = exif_data.get("GPS", {}) + if gps_info: + lat = self._dms_to_decimal(gps_info.get(2, [])) + lon = self._dms_to_decimal(gps_info.get(4, [])) + self.logger.debug(f"成功提取图片GPS坐标: {image_path} - 纬度: {lat}, 经度: {lon}") + return lat, lon + else: + self.logger.warning(f"图片无GPS信息: {image_path}") + return None, None + except Exception as e: + self.logger.error(f"提取GPS坐标时发生错误: {image_path} - {str(e)}") + return None, None + + def extract_all_gps(self): + """提取所有图片的GPS坐标""" + self.logger.info(f"开始从目录提取GPS坐标: {self.image_dir}") + gps_points = [] + total_images = 0 + successful_extractions = 0 + + for image_file in os.listdir(self.image_dir): + if image_file.lower().endswith('.jpg'): + total_images += 1 + image_path = os.path.join(self.image_dir, image_file) + lat, lon = self.get_gps(image_path) + if lat and lon: + successful_extractions += 1 + gps_points.append( + {'file': image_file, 'lat': lat, 'lon': lon}) + + self.logger.info(f"GPS坐标提取完成 - 总图片数: {total_images}, 成功提取: {successful_extractions}, 失败: {total_images - successful_extractions}") + return gps_points diff --git a/preprocess/gps_filter.py b/preprocess/gps_filter.py new file mode 100644 index 0000000..8c61088 --- /dev/null +++ b/preprocess/gps_filter.py @@ -0,0 +1,146 @@ +import os +import math +from itertools import combinations +import numpy as np +from scipy.spatial import KDTree +import logging + + +class GPSFilter: + """过滤密集点及孤立点""" + + def __init__(self, output_dir): + self.log_file = os.path.join(output_dir, 'del_imgs.txt') + self.logger = logging.getLogger('UAV_Preprocess.GPSFilter') + + @staticmethod + def _haversine(lat1, lon1, lat2, lon2): + """计算两点之间的地理距离(单位:米)""" + R = 6371000 # 地球平均半径,单位:米 + phi1, phi2 = math.radians(lat1), math.radians(lat2) + delta_phi = math.radians(lat2 - lat1) + delta_lambda = math.radians(lon2 - lon1) + + a = math.sin(delta_phi / 2) ** 2 + math.cos(phi1) * \ + math.cos(phi2) * math.sin(delta_lambda / 2) ** 2 + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) + return R * c + + @staticmethod + def _assign_to_grid(lat, lon, grid_size, min_lat, min_lon): + """根据经纬度和网格大小,将点分配到网格""" + grid_x = int((lat - min_lat) // grid_size) + grid_y = int((lon - min_lon) // grid_size) + return grid_x, grid_y + + def _get_distances(self, points, grid_size): + """读取图片 GPS 坐标,计算点对之间的距离并排序""" + # 确定经纬度范围 + coords = np.array([[p['lat'], p['lon']] for p in points]) + min_lat, min_lon = np.min(coords, axis=0) + max_lat, max_lon = np.max(coords, axis=0) + self.logger.info( + f"经纬度范围:纬度[{min_lat:.6f}, {max_lat:.6f}],纬度范围[{max_lat-min_lat:.6f}]," + f"经度[{min_lon:.6f}, {max_lon:.6f}],经度范围[{max_lon-min_lon:.6f}]") + + # 分配到网格 + grid_map = {} + for img_info_dict in points: + grid = self._assign_to_grid( + img_info_dict['lat'], img_info_dict['lon'], grid_size, min_lat, min_lon) + if grid not in grid_map: + grid_map[grid] = [] + grid_map[grid].append( + (img_info_dict['file'], img_info_dict['lat'], img_info_dict['lon'])) + + self.logger.info(f"图像点已分配到 {len(grid_map)} 个网格中") + + # 在每个网格中计算两两距离并排序 + sorted_distances = {} + for grid, images in grid_map.items(): + distances = [] + for (img1, lat1, lon1), (img2, lat2, lon2) in combinations(images, 2): + dist = self._haversine(lat1, lon1, lat2, lon2) + distances.append((img1, img2, dist)) + distances.sort(key=lambda x: x[2]) # 按距离升序排序 + sorted_distances[grid] = distances + self.logger.debug(f"网格 {grid} 中计算了 {len(distances)} 个距离对") + + return sorted_distances + + def filter_dense_points(self, points, grid_size=0.001, distance_threshold=13): + """过滤密集点,根据提供的距离阈值""" + self.logger.info(f"开始过滤密集点 (网格大小: {grid_size}, 距离阈值: {distance_threshold}米)") + + # 获取每个网格中的图片的两两距离信息 + sorted_distances = self._get_distances(points, grid_size) + + to_del_imgs = [] + """遍历每个网格,删除网格中距离小于阈值的点""" + for grid, distances in sorted_distances.items(): + grid_del_count = 0 + while distances: + candidate_img1, candidate_img2, dist = distances[0] + if dist < distance_threshold: + # 有小于阈值的距离,先删除最近的距离,这两个点作为候选点,要删掉一个 + distances.pop(0) + + # 获取候选图片1和图片2倒数第二短的距离 + candidate_img1_dist = None + candidate_img2_dist = None + for distance in distances: + if candidate_img1 in distance: + candidate_img1_dist = distance[2] + break + for distance in distances: + if candidate_img2 in distance: + candidate_img2_dist = distance[2] + break + + # 谁短删掉谁 + if candidate_img1_dist and candidate_img2_dist: + if candidate_img1_dist < candidate_img2_dist: + to_del_img = candidate_img1 + else: + to_del_img = candidate_img2 + to_del_imgs.append(to_del_img) + grid_del_count += 1 + self.logger.debug(f"在网格 {grid} 中删除密集点: {to_del_img} (距离: {dist:.2f}米)") + # 从距离列表中删除与被删除图片相关的记录 + distances = [ + distance for distance in distances if to_del_img not in distance] + else: + break + if grid_del_count > 0: + self.logger.info(f"网格 {grid} 中删除了 {grid_del_count} 个密集点") + + # 过滤掉删除的图片,写入日志 + with open(self.log_file, 'a', encoding='utf-8') as f: + for img in to_del_imgs: + f.write(img+'\n') + + filtered_points = [point for point in points if point['file'] not in to_del_imgs] + self.logger.info(f"密集点过滤完成,共删除 {len(to_del_imgs)} 个点,剩余 {len(filtered_points)} 个点") + return filtered_points + + def filter_isolated_points(self, points, threshold_distance=0.001, min_neighbors=6): + """过滤孤立点""" + self.logger.info(f"开始过滤孤立点 (距离阈值: {threshold_distance}, 最小邻居数: {min_neighbors})") + + coords = np.array([[p['lat'], p['lon']] for p in points]) + kdtree = KDTree(coords) + neighbors_count = [len(kdtree.query_ball_point( + coord, threshold_distance)) for coord in coords] + + isolated_points = [] + with open(self.log_file, 'a', encoding='utf-8') as f: + for i, p in enumerate(points): + if neighbors_count[i] < min_neighbors: + isolated_points.append(p['file']) + f.write(p['file']+'\n') + self.logger.debug(f"删除孤立点: {p['file']} (邻居数: {neighbors_count[i]})") + f.write('\n') + + filtered_points = [p for i, p in enumerate(points) if neighbors_count[i] >= min_neighbors] + self.logger.info(f"孤立点过滤完成,共删除 {len(isolated_points)} 个点,剩余 {len(filtered_points)} 个点") + return filtered_points diff --git a/preprocess/grid_divider.py b/preprocess/grid_divider.py new file mode 100644 index 0000000..733ce8c --- /dev/null +++ b/preprocess/grid_divider.py @@ -0,0 +1,84 @@ +import logging + +class GridDivider: + """划分九宫格,并将图片分配到对应网格""" + + def __init__(self, overlap=0.1): + self.overlap = overlap + self.logger = logging.getLogger('UAV_Preprocess.GridDivider') + self.logger.info(f"初始化网格划分器,重叠率: {overlap}") + + def divide_grids(self, points): + """计算边界框并划分九宫格""" + self.logger.info("开始划分九宫格") + + lats = [p['lat'] for p in points] + lons = [p['lon'] for p in points] + min_lat, max_lat = min(lats), max(lats) + min_lon, max_lon = min(lons), max(lons) + + self.logger.info( + f"区域边界: 纬度[{min_lat:.6f}, {max_lat:.6f}], " + f"经度[{min_lon:.6f}, {max_lon:.6f}]" + ) + + lat_step = (max_lat - min_lat) / 3 + lon_step = (max_lon - min_lon) / 3 + + self.logger.debug(f"网格步长: 纬度{lat_step:.6f}, 经度{lon_step:.6f}") + + grids = [] + for i in range(3): + for j in range(3): + grid_min_lat = min_lat + i * lat_step - self.overlap * lat_step + grid_max_lat = min_lat + \ + (i + 1) * lat_step + self.overlap * lat_step + grid_min_lon = min_lon + j * lon_step - self.overlap * lon_step + grid_max_lon = min_lon + \ + (j + 1) * lon_step + self.overlap * lon_step + grids.append((grid_min_lat, grid_max_lat, + grid_min_lon, grid_max_lon)) + + self.logger.debug( + f"网格[{i},{j}]: 纬度[{grid_min_lat:.6f}, {grid_max_lat:.6f}], " + f"经度[{grid_min_lon:.6f}, {grid_max_lon:.6f}]" + ) + + self.logger.info(f"成功划分为 {len(grids)} 个网格") + return grids + + def assign_to_grids(self, points, grids): + """将点分配到对应网格""" + self.logger.info(f"开始将 {len(points)} 个点分配到网格中") + + grid_points = {i: [] for i in range(len(grids))} + points_assigned = 0 + multiple_grid_points = 0 + + for point in points: + point_assigned = False + for i, (min_lat, max_lat, min_lon, max_lon) in enumerate(grids): + if min_lat <= point['lat'] <= max_lat and min_lon <= point['lon'] <= max_lon: + grid_points[i].append(point) + if point_assigned: + multiple_grid_points += 1 + else: + points_assigned += 1 + point_assigned = True + + self.logger.debug( + f"点 {point['file']} (纬度: {point['lat']:.6f}, 经度: {point['lon']:.6f}) " + f"被分配到网格" + ) + + # 记录每个网格的点数 + for grid_idx, points in grid_points.items(): + self.logger.info(f"网格 {grid_idx} 包含 {len(points)} 个点") + + self.logger.info( + f"点分配完成: 总点数 {len(points)}, " + f"成功分配 {points_assigned} 个点, " + f"{multiple_grid_points} 个点被分配到多个网格" + ) + + return grid_points diff --git a/preprocess/logger.py b/preprocess/logger.py new file mode 100644 index 0000000..c6e0448 --- /dev/null +++ b/preprocess/logger.py @@ -0,0 +1,36 @@ +import logging +import os +from datetime import datetime + +def setup_logger(output_dir): + # 创建logs目录 + log_dir = os.path.join(output_dir, 'logs') + os.makedirs(log_dir, exist_ok=True) + + # 创建日志文件名(包含时间戳) + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + log_file = os.path.join(log_dir, f'preprocess_{timestamp}.log') + + # 配置日志格式 + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' + ) + + # 配置文件处理器 + file_handler = logging.FileHandler(log_file, encoding='utf-8') + file_handler.setFormatter(formatter) + + # 配置控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + + # 获取根日志记录器 + logger = logging.getLogger('UAV_Preprocess') + logger.setLevel(logging.INFO) + + # 添加处理器 + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + return logger \ No newline at end of file diff --git a/show_GPS.py b/show_GPS.py new file mode 100644 index 0000000..955d712 --- /dev/null +++ b/show_GPS.py @@ -0,0 +1,44 @@ +from gps_extractor import GPSExtractor +from gps_filter import GPSFilter +from grid_divider import GridDivider +import os +import pandas as pd +import shutil +import matplotlib.pyplot as plt + +DATASET = r'C:\datasets\1815\output\grid_5\grid_5\images' +IS_ORIGIN = True + +if __name__ == '__main__': + if IS_ORIGIN: + extractor = GPSExtractor(DATASET) + gps_points = extractor.extract_all_gps() + else: + # 提取九宫格中的GPS数据 + gps_points = [] + for subdir in os.listdir(DATASET): + subdir_path = os.path.join(DATASET, subdir) + if os.path.isdir(subdir_path): + extractor = GPSExtractor(subdir_path) + sub_gps_points = extractor.extract_all_gps() + gps_points = gps_points + sub_gps_points + print(len(sub_gps_points)) + + longitudes = [p['lon'] for p in gps_points] + latitudes = [p['lat'] for p in gps_points] + # 绘制散点图 + plt.figure(figsize=(10, 8)) + plt.scatter(longitudes, latitudes, color='blue', marker='o') + + # 添加标记,显示每个点的图像文件名 + for i, image in enumerate(gps_points): + plt.text(longitudes[i], latitudes[i], image['file'], fontsize=9, ha='right') + + # 添加图表标签 + plt.title("GPS Coordinates of Images", fontsize=14) + plt.xlabel("Longitude", fontsize=12) + plt.ylabel("Latitude", fontsize=12) + + # 显示图形 + plt.grid(True) + plt.show()