Out put the openMVG ,But can't find desc file.
A nice Work !.. but i meet some problem.
When I use OpenMVG on Windows, it generates matching results like the image above.
However, when I run:
bash python main.py --dir /root/buildBase --pipeline superpoint+lightglue --openmvg /root/deep-image-matching/config/openmvg_linux.yaml I can't find the .desc files and only get some .feat files.
I'm confused because I need the OpenMVG's location functionality for related tasks, but the absence of .desc files is causing issues.
I found the following code section:
python dim.io.export_to_openmvg( img_dir=imgs_dir, feature_path=feature_path, match_path=match_path, openmvg_out_path=openmvg_out_path, openmvg_sfm_bin=openmvg_sfm_bin, openmvg_database=openmvg_database, camera_config_path=config.general["camera_options"], ) Then I modified the code below (removed the # comments), but it still didn’t generate the .desc files:
python def add_keypoints(h5_path, image_path, matches_dir): keypoint_f = h5py.File(str(h5_path), "r") for filename in tqdm(list(keypoint_f.keys())): keypoints = keypoint_f[filename]["keypoints"].array() name = Path(filename).stem
path = os.path.join(image_path, filename)
if not os.path.isfile(path):
raise OSError(f"Invalid image path {path}")
if len(keypoints.shape) >= 2:
threading.Thread(
target=lambda: saveFeaturesOpenMVG(matches_dir, name, keypoints)
).start()
# threading.Thread(target=lambda: saveDescriptorsOpenMVG(matches_dir, filename, features.descriptors)).start()
return
I'm trying to modify the h5_to_openmvg.py code to generate .desc files, but I encountered an issue: when using SuperPoint (and later SIFT, after rerunning), the result shows no images could be found (i.e., 0 images detected). I wonder if anyone could help me troubleshoot this. Thank you for taking the time to look into it! Below are the relevant modified code snippets.
1. Modified Code in h5_to_openmvg.py
def saveDescriptorsOpenMVG(matches_folder, basename, descriptors):
with open(os.path.join(matches_folder, f"{basename}.desc"), "wb") as desc:
desc.write(len(descriptors).to_bytes(8, byteorder="little"))
desc.write(
((descriptors.numpy() + 1) * 0.5 * 255).round(0).astype(np.ubyte).tobytes()
)
def add_keypoints(h5_path, image_path, matches_dir):
keypoint_f = h5py.File(str(h5_path), "r")
processed_count = 0
for filename in tqdm(list(keypoint_f.keys()), desc="desc"):
try:
path = os.path.join(image_path, filename)
name = Path(filename).stem
keypoints = keypoint_f[filename]["keypoints"].__array__()
saveFeaturesOpenMVG(matches_dir, name, keypoints)
if "descriptors" in keypoint_f[filename]:
descriptors = keypoint_f[filename]["descriptors"].__array__().T
logger.debug(f"shape: {descriptors.shape}")
saveDescriptorsOpenMVG(matches_dir, name, descriptors)
logger.debug(f"file: {name}.desc")
else:
logger.warning(f"can't find {filename} desc")
processed_count += 1
except Exception as e:
logger.error(f"when {filename} mistake: {str(e)}")
continue
keypoint_f.close()
logger.info(f"sus! {processed_count} ")
return
2. New File: deep-image-matching/src/deep_image_matching/localize.py
Also, I created a new localize.py and placed an image_describer.json in the OpenMVG matches folder. Here's the full code for localize.py:
import os
from pathlib import Path
import yaml
import argparse
import subprocess
import h5py
import numpy as np
import shutil
import json
from tqdm import tqdm
import logging
import deep_image_matching as dim
from deep_image_matching.openmvg import openmvg_localization
from deep_image_matching.config import Config
import inspect
from PIL import Image
logger = logging.getLogger("dim")
def saveFeaturesOpenMVG(matches_folder, basename, keypoints):
with open(os.path.join(matches_folder, f"{basename}.feat"), "w") as feat:
for x, y in keypoints:
feat.write(f"{x} {y} 1.0 0.0\n")
def saveDescriptorsOpenMVG(matches_folder, basename, descriptors):
with open(os.path.join(matches_folder, f"{basename}.desc"), "wb") as desc:
num_descriptors = descriptors.shape[0]
desc.write(num_descriptors.to_bytes(8, byteorder="little"))
descriptors_uint8 = (descriptors * 512).clip(0, 255).round().astype(np.uint8)
desc.write(descriptors_uint8.tobytes())
def extract_query_features(query_path, matches_dir, config_dict):
matches_dir.mkdir(parents=True, exist_ok=True)
query_path = Path(query_path)
if query_path.is_dir():
query_images = list(query_path.glob("*.[jJ][pP][gG]")) + list(query_path.glob("*.[pP][nN][gG]"))
else:
query_images = [query_path]
fake_args = {
"images": str(query_path.parent),
"dir": str(query_path.parent),
"pipeline": config_dict["extractor"]["name"] + "+" + config_dict["matcher"]["name"],
"quality": config_dict["general"].get("quality", "high"),
"tiling": config_dict["general"].get("tile_selection", "none"),
"strategy": "bruteforce",
"force": True,
"verbose": False
}
if hasattr(fake_args["quality"], 'name'):
fake_args["quality"] = fake_args["quality"].name.lower()
if hasattr(fake_args["tiling"], 'name'):
fake_args["tiling"] = fake_args["tiling"].name.lower()
config = Config(fake_args)
config._cfg["extractor"].update(config_dict.get("extractor", {}))
config._cfg["matcher"].update(config_dict.get("matcher", {}))
if isinstance(config._cfg["extractor"], dict) and "name" in config._cfg["extractor"]:
config._cfg["extractor"]["name"] = str(config._cfg["extractor"]["name"])
if isinstance(config._cfg["matcher"], dict) and "name" in config._cfg["matcher"]:
config._cfg["matcher"]["name"] = str(config._cfg["matcher"]["name"])
try:
Extractor = dim.extractors.extractor_loader(dim.extractors, config.extractor["name"])
feature_extractor = Extractor(config)
except Exception as e:
logger.error(f"Failed to initialize feature extractor: {e}")
return
for img_path in tqdm(query_images, desc="Extracting query image features"):
try:
img_path_str = str(img_path)
if not os.path.exists(img_path_str):
logger.warning(f"Image file does not exist: {img_path_str}")
continue
feature_file_path = feature_extractor.extract(img_path_str)
if not feature_file_path.exists():
logger.warning(f"Feature file does not exist: {feature_file_path}")
continue
with h5py.File(feature_file_path, 'r') as f:
img_name = Path(img_path).name
if img_name not in f:
logger.warning(f"Could not find features for image {img_name} in feature file")
continue
group = f[img_name]
features = {
"keypoints": group['keypoints'][:],
"descriptors": group['descriptors'][:]
}
if features is None or len(features["keypoints"]) == 0:
logger.warning(f"Failed to extract features from image: {img_path}")
continue
basename = Path(img_path).stem
saveFeaturesOpenMVG(matches_dir, basename, features["keypoints"])
saveDescriptorsOpenMVG(matches_dir, basename, features["descriptors"].T)
logger.info(f"Generated feature and descriptor files for {Path(img_path).name}")
except Exception as e:
logger.error(f"Error processing image {img_path}: {str(e)}")
import traceback
logger.error(traceback.format_exc())
continue
def main():
parser = argparse.ArgumentParser(description='Image Localization Script')
parser.add_argument('--config', type=str, required=True, help='Path to OpenMVG config file')
parser.add_argument('--query', type=str, required=True, help='Path to query image or directory')
parser.add_argument('--output', type=str, required=True, help='Path to output directory')
parser.add_argument('--reconstruction_dir', type=str, required=True, help='Path to reconstruction directory')
parser.add_argument('--feature_config', type=str, required=True, help='Path to feature extraction config file')
args = parser.parse_args()
with open(args.config) as file:
openmvg_config = yaml.safe_load(file)
openmvg_sfm_bin = Path(openmvg_config["general"]["path_to_binaries"])
query_image_dir = Path(args.query)
output_dir = Path(args.output)
reconstruction_dir = Path(args.reconstruction_dir)
matches_dir = output_dir / "localization_matches"
matches_dir.mkdir(parents=True, exist_ok=True)
bin_file = reconstruction_dir / "sfm_data.bin"
json_file = reconstruction_dir / "sfm_data.json"
if bin_file.exists():
sfm_data_path = bin_file
elif json_file.exists():
sfm_data_path = json_file
else:
print(f"Error: No reconstruction data found in {reconstruction_dir}")
print(f"Available files: {list(reconstruction_dir.iterdir())}")
return
feature_config_path = Path(args.feature_config)
if not feature_config_path.exists():
print(f"Error: Could not find feature config file at {feature_config_path}")
return
with open(feature_config_path) as f:
feature_config = yaml.safe_load(f)
print(f"Using feature config from: {feature_config_path}")
logger.info("Starting query image feature extraction...")
extract_query_features(args.query, matches_dir, feature_config)
print(f"Using reconstruction data from: {sfm_data_path}")
try:
localization_results = openmvg_localization(
sfm_data_path=sfm_data_path,
openmvg_out_path=output_dir / "openmvg",
query_image_dir=query_image_dir,
skip_localization=False,
openmvg_sfm_bin=openmvg_sfm_bin,
matches_out_dir=matches_dir,
residual_error=4.0,
camera_model=3,
use_single_intrinsics=False,
export_structure=True,
reconstruction_matches_dir=reconstruction_dir.parent / "matches"
)
print(f"Localization results saved to: {localization_results}")
except Exception as e:
print(f"Error during localization: {e}")
try:
cmd = [
str(openmvg_sfm_bin / "openMVG_main_SfM_Localization"),
"-i", str(sfm_data_path),
"-m", str(output_dir / "openmvg" / "matches"),
"-o", str(output_dir / "openmvg" / "localization_results"),
"-u", str(matches_dir),
"-q", str(query_image_dir),
]
print(f"Running command: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
print(f"Return code: {result.returncode}")
print(f"Stdout: {result.stdout}")
print(f"Stderr: {result.stderr}")
except Exception as e2:
print(f"Failed to run OpenMVG directly: {e2}")
if __name__ == "__main__":
main()
Have a nice day!
Hi,
Sorry for the late reply, did you solve the problem?
In the meantime I checked, and there were some small issues with the new version of DIM, now you can find on master a working version of the code
Please be also sure to have ./config/cameras.yaml in this format:
If you run python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml", the code should work smoothly
Hi, 你好
Sorry for the late reply, did you solve the problem?对不起,回复晚了,你解决了问题吗?
In the meantime I checked, and there were some small issues with the new version of DIM, now you can find on master a working version of the code与此同时,我检查了一下,新版本的 DIM 存在一些小问题,现在您可以在 master 上找到代码的工作版本
Please be also sure to have
./config/cameras.yamlin this format:还请确保具有以下格式的./config/cameras.yaml:If you run `python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml"`, the code should work smoothly如果您运行 `python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml"`,代码应该可以顺利运行
Hi ! And good night? Thanks for the reply and the updated code! I'll give it a try for my issue — might take a while to get to it, but I'll definitely test it out.

If you run `python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml"`, the code should work smoothly如果您运行 `python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml"`,代码应该可以顺利运行