File size: 5,224 Bytes
7a87926
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python3
"""
Quick test of smart pairing optimization.
Uses pre-computed poses to skip DA3 inference.
"""

import logging
import sys
from pathlib import Path
import numpy as np

# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

from ylff.services.ba_validator import BAValidator  # noqa: E402

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def test_smart_pairing():
    """Test smart pairing with a small set of images."""

    # Use existing images from previous run
    image_dir = project_root / "data" / "ba_validation_results_rickroll_v2" / "ba_work" / "images"

    if not image_dir.exists():
        logger.error(f"Image directory not found: {image_dir}")
        logger.info("Please run BA validation first to generate images")
        return

    image_paths = sorted(list(image_dir.glob("*.jpg")))[:10]  # Use first 10 images
    image_paths = [str(p) for p in image_paths]

    logger.info(f"Testing with {len(image_paths)} images")

    # Create dummy poses (simulate DA3 output)
    # For a video, poses should be relatively close together
    np.random.seed(42)
    base_pose = np.eye(4)[:3, :]
    poses = []
    for i in range(len(image_paths)):
        # Small random translation (simulating camera movement)
        pose = base_pose.copy()
        pose[:3, 3] = np.random.randn(3) * 0.1 * i  # Gradually move
        poses.append(pose)
    poses = np.array(poses)

    logger.info(f"Generated {len(poses)} poses")

    # Initialize validator
    validator = BAValidator(
        work_dir=project_root / "data" / "test_smart_pairing",
    )

    # Test pair generation
    logger.info("\n=== Testing Pair Generation ===")

    # Sequential
    pairs_seq = validator._generate_smart_pairs(image_paths, sequential_only=True)
    logger.info(f"Sequential pairs: {len(pairs_seq)}")

    # Spatial
    pairs_spatial = validator._generate_smart_pairs(
        image_paths,
        poses=poses,
        max_baseline=0.3,
        min_baseline=0.05,
        max_pairs_per_image=5,
    )
    logger.info(f"Spatial pairs: {len(pairs_spatial)}")

    # Exhaustive
    pairs_exhaustive = validator._generate_smart_pairs(image_paths)
    logger.info(f"Exhaustive pairs: {len(pairs_exhaustive)}")

    logger.info("\n=== Speedup Analysis ===")
    logger.info(f"Sequential: {len(pairs_exhaustive) / len(pairs_seq):.1f}x fewer pairs")
    logger.info(f"Spatial: {len(pairs_exhaustive) / len(pairs_spatial):.1f}x fewer pairs")

    # Test actual matching (if features exist)
    features_path = (
        project_root / "data" / "ba_validation_results_rickroll_v2" / "ba_work" / "features.h5"
    )

    if features_path.exists():
        logger.info("\n=== Testing Matching with Smart Pairs ===")

        import time
        from hloc import match_features

        # Test with sequential pairs
        pairs_file_seq = validator.work_dir / "pairs_seq.txt"
        with open(pairs_file_seq, "w") as f:
            for img1, img2 in pairs_seq:
                f.write(f"{Path(img1).name} {Path(img2).name}\n")

        matches_file_seq = validator.work_dir / "matches_seq.h5"

        logger.info(f"Matching {len(pairs_seq)} sequential pairs...")
        start = time.time()

        try:
            match_conf = match_features.confs["superpoint+lightglue"]
            match_features.main(
                conf=match_conf,
                pairs=pairs_file_seq,
                features=features_path,
                matches=matches_file_seq,
            )
            elapsed_seq = time.time() - start
            logger.info(f"✓ Sequential matching completed in {elapsed_seq:.2f}s")
        except Exception as e:
            logger.error(f"Matching failed: {e}")
            elapsed_seq = None

        # Test with exhaustive pairs (if we have time)
        if len(pairs_exhaustive) < 50:  # Only if reasonable
            pairs_file_exh = validator.work_dir / "pairs_exh.txt"
            with open(pairs_file_exh, "w") as f:
                for img1, img2 in pairs_exhaustive:
                    f.write(f"{Path(img1).name} {Path(img2).name}\n")

            matches_file_exh = validator.work_dir / "matches_exh.h5"

            logger.info(f"Matching {len(pairs_exhaustive)} exhaustive pairs...")
            start = time.time()

            try:
                match_features.main(
                    conf=match_conf,
                    pairs=pairs_file_exh,
                    features=features_path,
                    matches=matches_file_exh,
                )
                elapsed_exh = time.time() - start
                logger.info(f"✓ Exhaustive matching completed in {elapsed_exh:.2f}s")

                if elapsed_seq:
                    speedup = elapsed_exh / elapsed_seq
                    logger.info(f"\n=== Speedup: {speedup:.1f}x ===")
            except Exception as e:
                logger.error(f"Matching failed: {e}")
    else:
        logger.info(f"\nFeatures not found at {features_path}")
        logger.info("Skipping matching test. Run full BA validation first.")

    logger.info("\n=== Test Complete ===")


if __name__ == "__main__":
    test_smart_pairing()