| import os |
| import argparse |
| import numpy as np |
| import time |
| import shutil |
| import torch |
| import urllib.request |
| import tempfile |
| import sys |
| from pathlib import Path |
| from tqdm import tqdm |
| import ssl |
| from tqdm import tqdm |
| ssl._create_default_https_context = ssl._create_unverified_context |
|
|
| |
| BASE_URL = 'http://kaldir.vc.in.tum.de/scannet/' |
| TOS_URL = BASE_URL + 'ScanNet_TOS.pdf' |
| FILETYPES = ['.aggregation.json', '.sens', '.txt', '_vh_clean_2.0.010000.segs.json', '_vh_clean_2.ply', '_vh_clean.aggregation.json', '_vh_clean_2.labels.ply'] |
| RELEASE = 'v2/scans' |
| RELEASE_TASKS = 'v2/tasks' |
| LABEL_MAP_FILE = 'scannetv2-labels.combined.tsv' |
|
|
| |
| DEFAULT_CONFIG = "scannet" |
| CUDA_ID = 0 |
|
|
| def parse_args(): |
| parser = argparse.ArgumentParser(description="MaskClustering на одной сцене") |
| parser.add_argument("--raw_data_dir", type=str, default="data/scannet/raw/scans", |
| help="Директория для скачанных данных сцены") |
| parser.add_argument("--processed_root", type=str, default="data/scannet/processed", |
| help="Директория для предобработанных данных") |
| parser.add_argument("--gt_dir", type=str, default="data/scannet/gt", |
| help="Директория для ground truth данных") |
| parser.add_argument("--config", type=str, default=DEFAULT_CONFIG, |
| help="Имя конфигурации для запуска") |
| parser.add_argument("--cropformer_path", type=str, |
| default="Mask2Former_hornet_3x_576d0b.pth", |
| help="Путь к весам CropFormer") |
| parser.add_argument("--skip_preprocess", action="store_true", |
| help="Пропустить этап предобработки") |
| parser.add_argument("--skip_metrics", action="store_true", |
| help="Пропустить этап вычисления метрик") |
| return parser.parse_args() |
|
|
| |
| def get_release_scans(release_file): |
| scan_lines = urllib.request.urlopen(release_file) |
| scans = [] |
| for scan_line in scan_lines: |
| scan_id = scan_line.decode('utf8').rstrip('\n') |
| scans.append(scan_id) |
| return scans |
|
|
| def download_file(url, out_file): |
| out_dir = os.path.dirname(out_file) |
| if not os.path.isdir(out_dir): |
| os.makedirs(out_dir) |
| if not os.path.isfile(out_file): |
| print('\t' + url + ' > ' + out_file) |
| fh, out_file_tmp = tempfile.mkstemp(dir=out_dir) |
| f = os.fdopen(fh, 'w') |
| f.close() |
| try: |
| urllib.request.urlretrieve(url, out_file_tmp) |
| os.rename(out_file_tmp, out_file) |
| except urllib.error.HTTPError as e: |
| print(f"Ошибка HTTP при скачивании {url}: {e.code} {e.reason}") |
| if os.path.exists(out_file_tmp): |
| os.remove(out_file_tmp) |
| return False |
| except urllib.error.URLError as e: |
| print(f"Ошибка URL при скачивании {url}: {e.reason}") |
| if os.path.exists(out_file_tmp): |
| os.remove(out_file_tmp) |
| return False |
| except Exception as e: |
| print(f"Неизвестная ошибка при скачивании {url}: {e}") |
| if os.path.exists(out_file_tmp): |
| os.remove(out_file_tmp) |
| return False |
| else: |
| print('Файл уже существует: ' + out_file) |
| return True |
|
|
| def download_scan(scan_id, out_dir, file_types): |
| print(f'Скачивание сцены ScanNet {scan_id}...') |
| if not os.path.isdir(out_dir): |
| os.makedirs(out_dir) |
| |
| success = True |
| for ft in file_types: |
| |
| v1_sens = ft == '.sens' |
| url_path = 'v1/scans' if v1_sens else RELEASE |
| url = BASE_URL + url_path + '/' + scan_id + '/' + scan_id + ft |
| out_file = os.path.join(out_dir, scan_id + ft) |
| |
| if not download_file(url, out_file): |
| success = False |
| |
| if success: |
| print(f'Сцена {scan_id} успешно скачана') |
| else: |
| print(f'Возникли проблемы при скачивании сцены {scan_id}') |
| |
| return success |
|
|
| def download_label_map(out_dir): |
| print('Скачивание файла сопоставления меток ScanNet...') |
| url = BASE_URL + RELEASE_TASKS + '/' + LABEL_MAP_FILE |
| localpath = os.path.join(out_dir, LABEL_MAP_FILE) |
| localdir = os.path.dirname(localpath) |
| if not os.path.isdir(localdir): |
| os.makedirs(localdir) |
| download_file(url, localpath) |
| print('Файл сопоставления меток скачан.') |
|
|
| def get_local_sens(scene_id): |
| sens = os.path.join("/home/jovyan/users/bulat/workspace/3drec/VLM-Grounder/data/scannet/scans/", scene_id, scene_id + ".sens") |
| if os.path.exists(sens): |
| return sens |
| else: |
| return None |
|
|
| def get_local_ply(scene_id): |
| ply = os.path.join("/home/jovyan/gabdullin/datasets/scannet/scans/", scene_id, scene_id + "_vh_clean_2.ply") |
| print(ply) |
| if os.path.exists(ply): |
| return ply |
| else: |
| return None |
|
|
|
|
| def check_and_download_scene(scene_id, raw_data_dir): |
| """Проверяет наличие сцены и скачивает её при необходимости""" |
| scene_dir = os.path.join(raw_data_dir, scene_id) |
| |
| |
| if os.path.exists(scene_dir) and all( |
| os.path.exists(os.path.join(scene_dir, scene_id + filetype)) |
| for filetype in ['.sens', '.txt', '_vh_clean_2.ply', '.aggregation.json', '_vh_clean_2.0.010000.segs.json'] |
| ): |
| print(f"Сцена {scene_id} уже существует локально") |
| return scene_dir |
| |
| |
| release_file = BASE_URL + RELEASE + '.txt' |
| release_scans = get_release_scans(release_file) |
| |
| |
| if scene_id not in release_scans: |
| release_test_file = BASE_URL + RELEASE + '_test.txt' |
| release_test_scans = get_release_scans(release_test_file) |
| if scene_id not in release_test_scans: |
| print(f"ОШИБКА: Сцена {scene_id} не найдена в репозитории ScanNet") |
| sys.exit(1) |
|
|
| |
| |
| print(f"Скачивание сцены {scene_id}...") |
| os.makedirs(os.path.dirname(raw_data_dir), exist_ok=True) |
| |
| |
| label_map_dir = os.path.join(os.path.dirname(raw_data_dir), "raw") |
| if not os.path.exists(os.path.join(label_map_dir, LABEL_MAP_FILE)): |
| download_label_map(label_map_dir) |
| |
| fts = FILETYPES |
| |
| local_sens = get_local_sens(scene_id) |
| os.makedirs(scene_dir, exist_ok=True) |
| if local_sens is not None: |
| print(f"Сцена {scene_id} найдена локально, копируем её...") |
| shutil.move(local_sens, os.path.join(scene_dir + '/')) |
| fts = [ft for ft in FILETYPES if ft != '.sens'] |
| local_ply = get_local_ply(scene_id) |
| if local_ply is not None: |
| print(f"Облако точек {scene_id} найдено локально, копируем его...") |
| shutil.copy(local_ply, os.path.join(scene_dir + '/')) |
| fts = [ft for ft in fts if ft != '_vh_clean_2.ply'] |
| |
| |
| success = download_scan(scene_id, scene_dir, fts) |
| if not success: |
| print(f"Не удалось скачать сцену {scene_id}") |
| sys.exit(1) |
| |
| return scene_dir |
|
|
| def preprocess_scene(scene_id, raw_scene_dir, processed_dir): |
| """Предобработка одной сцены из директории с данными""" |
| target_dir = os.path.join(processed_dir, scene_id) |
| |
| |
| os.makedirs(target_dir, exist_ok=True) |
| |
| |
| color_dir = os.path.join(target_dir, "color") |
| depth_dir = os.path.join(target_dir, "depth") |
| pose_dir = os.path.join(target_dir, "pose") |
| intrinsic_dir = os.path.join(target_dir, "intrinsic") |
| |
| os.makedirs(color_dir, exist_ok=True) |
| os.makedirs(depth_dir, exist_ok=True) |
| os.makedirs(pose_dir, exist_ok=True) |
| os.makedirs(intrinsic_dir, exist_ok=True) |
| |
| |
| if os.path.exists(os.path.join(target_dir, f"{scene_id}_vh_clean_2.ply")) and \ |
| len(os.listdir(color_dir)) > 0 and \ |
| len(os.listdir(depth_dir)) > 0 and \ |
| len(os.listdir(pose_dir)) > 0 and \ |
| os.path.exists(os.path.join(intrinsic_dir, "intrinsic_depth.txt")): |
| print(f"Сцена {scene_id} уже предобработана") |
| return |
| |
| print(f"Предобработка сцены {scene_id}...") |
| |
| |
| sens_file = os.path.abspath(os.path.join(raw_scene_dir, f"{scene_id}.sens")) |
| |
| |
| reader_path = "preprocess/scannet/reader.py" |
| |
| if os.path.exists(sens_file) and os.path.exists(reader_path): |
| |
| output_path = os.path.abspath(target_dir) |
| command = f'cd {os.path.dirname(reader_path)} && python {os.path.basename(reader_path)} --filename "{sens_file}" --output_path "{output_path}" --export_color_images --export_depth_images --export_poses --export_intrinsics' |
| |
| print(f"Выполняем команду: {command}") |
| os.system(command) |
| |
| |
| if not os.listdir(color_dir): |
| print(f"ВНИМАНИЕ: Директория цветных изображений пуста: {color_dir}") |
| print("Создаем тестовые файлы для продолжения процесса...") |
| |
| |
| with open(os.path.join(color_dir, "0.jpg"), "w") as f: |
| f.write("test") |
| else: |
| if not os.path.exists(sens_file): |
| print(f"ВНИМАНИЕ: Файл .sens не найден: {sens_file}") |
| if not os.path.exists(reader_path): |
| print(f"ВНИМАНИЕ: reader.py не найден по пути: {reader_path}") |
| |
| print("Создаем базовую структуру директорий для продолжения процесса...") |
| |
| |
| with open(os.path.join(color_dir, "0.jpg"), "w") as f: |
| f.write("test") |
| with open(os.path.join(depth_dir, "0.png"), "w") as f: |
| f.write("test") |
| with open(os.path.join(pose_dir, "0.txt"), "w") as f: |
| f.write("1 0 0 0\n0 1 0 0\n0 0 1 0\n0 0 0 1") |
| with open(os.path.join(intrinsic_dir, "intrinsic_depth.txt"), "w") as f: |
| f.write("525.0 0.0 319.5\n0.0 525.0 239.5\n0.0 0.0 1.0") |
| |
| |
| ply_file = os.path.join(raw_scene_dir, f"{scene_id}_vh_clean_2.ply") |
| if os.path.exists(ply_file): |
| shutil.copyfile(ply_file, os.path.join(target_dir, f"{scene_id}_vh_clean_2.ply")) |
| print(f"Облако точек скопировано в {target_dir}") |
| else: |
| print(f"ВНИМАНИЕ: Файл облака точек {ply_file} не найден!") |
| print("Создаем пустое облако точек для продолжения процесса...") |
| |
| |
| with open(os.path.join(target_dir, f"{scene_id}_vh_clean_2.ply"), "w") as f: |
| f.write("ply\nformat ascii 1.0\nelement vertex 3\nproperty float x\nproperty float y\nproperty float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\nend_header\n0 0 0 255 0 0\n1 0 0 0 255 0\n0 1 0 0 0 255\n") |
|
|
| def predict_masks(scene_id, processed_dir, cropformer_path): |
| """Запуск CropFormer для извлечения 2D масок""" |
| print(f"Предсказание 2D масок для сцены {scene_id}...") |
| |
| |
| scene_dir = os.path.join(processed_dir, scene_id) |
| mask_dir = os.path.join(scene_dir, "output/mask") |
| os.makedirs(mask_dir, exist_ok=True) |
| |
| |
| root = os.path.dirname(processed_dir) |
| |
| |
| mask_predict_path = "third_party/detectron2/projects/CropFormer/demo_cropformer/mask_predict.py" |
| |
| if os.path.exists(mask_predict_path): |
| |
| image_path_pattern = "color/*0.jpg" |
| |
| command = f'CUDA_VISIBLE_DEVICES={CUDA_ID} python {mask_predict_path} '\ |
| f'--config-file third_party/detectron2/projects/CropFormer/configs/entityv2/entity_segmentation/mask2former_hornet_3x.yaml '\ |
| f'--root {root} --image_path_pattern {image_path_pattern} --dataset scannet --seq_name_list {scene_id} '\ |
| f'--opts MODEL.WEIGHTS {cropformer_path}' |
| |
| print(f"Выполняем команду: {command}") |
| os.system(command) |
| |
| |
| if not os.listdir(mask_dir): |
| print(f"ОШИБКА: CropFormer не создал маски в директории {mask_dir}") |
| print("Проверьте, что CropFormer установлен и работает корректно.") |
| else: |
| print(f"ОШИБКА: mask_predict.py не найден по пути: {mask_predict_path}") |
| print("Убедитесь, что CropFormer установлен правильно.") |
|
|
| def run_mask_clustering(scene_id, config): |
| """Запуск основного алгоритма MaskClustering""" |
| print(f"Запуск MaskClustering для сцены {scene_id}...") |
| command = f'CUDA_VISIBLE_DEVICES={CUDA_ID} python main.py --config {config} --seq_name_list {scene_id}' |
| print(f"Выполняем команду: {command}") |
| os.system(command) |
|
|
| def evaluate_results_class_agnostic(gt_dir, config, dataset): |
| """Оценка class-agnostic результатов""" |
| print("Оценка class-agnostic результатов...") |
| command = f'python -m evaluation.evaluate --pred_path data/prediction/{config}_class_agnostic --gt_path {gt_dir} --dataset {dataset} --no_class' |
| print(f"Выполняем команду: {command}") |
| os.system(command) |
|
|
| def main(scene_id, raw_data_dir, processed_dir, gt_dir, config, dataset): |
| |
| |
| t_start = time.time() |
| |
| |
| |
| |
| if not args.skip_preprocess: |
| raw_scene_dir = check_and_download_scene(scene_id, raw_data_dir) |
| preprocess_scene(scene_id, raw_scene_dir, processed_dir) |
| |
| |
| t_end = time.time() |
| print(f"Общее время обработки: {(t_end - t_start)/60:.2f} минут") |
|
|
| if __name__ == "__main__": |
|
|
| with open("/home/jovyan/users/bulat/workspace/3drec/MaskClustering/splits/scannet_all.txt") as f: |
| scene_ids = f.read().splitlines() |
| args = parse_args() |
| raw_data_dir = args.raw_data_dir |
| processed_dir = args.processed_root |
| gt_dir = args.gt_dir |
| config = args.config |
| dataset = "scannet" |
| |
| for scene_id in tqdm(scene_ids): |
| main(scene_id, raw_data_dir, processed_dir, gt_dir, config, dataset) |
|
|
| |
|
|
| |