deep-image-matching icon indicating copy to clipboard operation
deep-image-matching copied to clipboard

Out put the openMVG ,But can't find desc file.

Open Runasia opened this issue 3 months ago • 3 comments

Image

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.

Image

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

Runasia avatar Sep 07 '25 08:09 Runasia

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!

Runasia avatar Sep 17 '25 08:09 Runasia

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 Image

Please be also sure to have ./config/cameras.yaml in this format:

Image

If you run python .\demo.py -d ".\assets\pytest" -p superpoint+lightglue --force --openmvg ".\config\openmvg_win.yaml", the code should work smoothly

lcmrl avatar Sep 26 '25 14:09 lcmrl

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 上找到代码的工作版本 Image

Please be also sure to have ./config/cameras.yaml in this format:还请确保具有以下格式的 ./config/cameras.yaml

Image 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.

Runasia avatar Sep 26 '25 14:09 Runasia