File size: 16,061 Bytes
9223079
 
1928ea3
9223079
8d7cbc7
8b9ccdd
 
 
8d7cbc7
 
 
 
 
 
 
 
9223079
 
f686028
0ef82b7
8b9ccdd
a3e4830
b3d624d
a3e4830
 
 
 
 
 
84f4ae6
a3e4830
 
d3c48b8
6c3005b
a3e4830
 
 
 
 
6c3005b
e3406b1
6c3005b
a3e4830
 
 
20c6b44
d3c48b8
 
 
 
 
8b53ab6
d3c48b8
 
 
5700ce2
d3c48b8
 
 
 
 
 
 
 
 
 
8b53ab6
d3c48b8
 
 
 
 
 
 
5700ce2
d3c48b8
 
 
 
 
 
8b9ccdd
9223079
 
a3e4830
9223079
8d7cbc7
 
 
 
 
 
 
 
 
 
 
 
 
 
9223079
 
8d7cbc7
 
 
 
 
 
 
 
9223079
8d7cbc7
 
 
 
 
 
 
 
 
5c1ffd8
 
 
8d7cbc7
 
 
 
 
 
 
 
 
9223079
 
 
8b9ccdd
9223079
8d7cbc7
 
 
 
 
 
 
 
 
8b9ccdd
 
9223079
 
 
 
 
 
5078caa
9223079
 
 
 
5078caa
9223079
 
 
 
 
 
 
 
8d7cbc7
 
9223079
 
 
 
 
8d7cbc7
 
9223079
 
 
ab830e5
8d7cbc7
9223079
8b9ccdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60ad158
8b9ccdd
5c1ffd8
8b9ccdd
 
8d7cbc7
8b9ccdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60ad158
8b9ccdd
 
 
 
 
8d7cbc7
8b9ccdd
9223079
8b9ccdd
9223079
 
8b9ccdd
 
9223079
 
 
 
8b9ccdd
 
 
 
 
9223079
 
 
 
 
 
8b9ccdd
9223079
 
 
8b9ccdd
 
 
 
 
 
 
 
 
 
 
 
9223079
 
 
5c1ffd8
 
 
 
8b9ccdd
5078caa
 
 
8b9ccdd
5c1ffd8
8b9ccdd
 
 
 
5078caa
 
 
 
 
5c1ffd8
8d7cbc7
9223079
 
8b9ccdd
 
 
9223079
 
8b9ccdd
 
 
9223079
 
 
 
5c1ffd8
 
 
9223079
 
8b9ccdd
 
9223079
 
 
 
 
 
8b9ccdd
 
9223079
 
 
 
 
 
 
5c1ffd8
 
 
9223079
 
8b9ccdd
 
 
 
 
 
 
9223079
8b9ccdd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0b88271
 
d3c48b8
9223079
 
 
 
 
8b9ccdd
 
 
 
9223079
 
 
 
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
import argparse
import gradio as gr
from common.utils import (
    matcher_zoo,
    ransac_zoo,
    change_estimate_geom,
    run_matching,
    gen_examples,
    DEFAULT_RANSAC_METHOD,
    DEFAULT_SETTING_GEOMETRY,
    DEFAULT_RANSAC_REPROJ_THRESHOLD,
    DEFAULT_RANSAC_CONFIDENCE,
    DEFAULT_RANSAC_MAX_ITER,
    DEFAULT_MATCHING_THRESHOLD,
    DEFAULT_SETTING_MAX_FEATURES,
    DEFAULT_DEFAULT_KEYPOINT_THRESHOLD,
)

model = "xuelunshen/gim"

DESCRIPTION = """
<div style="display: flex; justify-content: center; align-items: center;">
    <div style="width: 100%; text-align: center; font-size: 30px;">
        <strong>GIM: Learning Generalizable Image Matcher From Internet Videos</strong>
    </div>
</div> 

<div style="display: flex; justify-content: center; padding: 10px; align-items: center;">
    <div style="width: 75%; text-align: center; background-color: #FFFFE0;">
        LICENSE: This repository is under the MIT License. This content/model is provided here for research purposes only. Any use beyond this is your sole responsibility and subject to your securing the necessary rights for your purpose.
    </div>
</div>

<div style="display: flex; justify-content: center; align-items: flex-start; flex-wrap: wrap;">
    <div><a href="https://iclr.cc/Conferences/2024"><img src="https://img.shields.io/badge/%F0%9F%8C%9F_ICLR'2024_Spotlight-37414c" alt='ICLR 2024 Spotlight'></a></div>&nbsp;
    <div><a href="https://xuelunshen.com/gim"><img src="https://img.shields.io/badge/Project_Page-3A464E?logo=gumtree" alt='Project Page'></a></div>&nbsp;
    <div><a href="https://github.com/xuelunshen/gim"><img src="https://img.shields.io/badge/Source_Code-black?logo=Github" alt='Github Source Code'></a></div>&nbsp;
    <div><a href="https://arxiv.org/abs/2402.11095"><img src="https://img.shields.io/badge/arXiv-2402.11095-b31b1b?logo=arxiv" alt='arxiv'></a></div>&nbsp;
    <div><a href="https://www.youtube.com/watch?v=FU_MJLD8LeY"><img src="https://img.shields.io/badge/Overview_Video-E33122?logo=Youtube" alt='Overview Video'></a></div>
</div>
<p></p>
<div style="display: flex; justify-content: center; align-items: flex-start; flex-wrap: wrap;">
    <div><a href="https://en.xmu.edu.cn"><img src="https://img.shields.io/badge/Xiamen_University-183F9D?logo=Google%20Scholar&logoColor=white" alt='Intel'></a></div>&nbsp;
    <div><a href="https://www.intel.com"><img src="https://img.shields.io/badge/Labs-0071C5?logo=intel" alt='Intel'></a></div>&nbsp;
    <div><a href="https://www.dji.com"><img src="https://img.shields.io/badge/DJI-131313?logo=DJI" alt='Intel'></a></div>
</div>

<div style="display: flex; justify-content: center; align-items: flex-start;">
    <div style="width: 50%; padding: 10px;">
        <p align="left">
            <span style="color: black;">
                You can click on the example images below or upload a pair of images. Running a match takes about 3.5 minutes (because the code is deployed on free CPU). Please wait patiently and keep the window in the foreground during operation
            </span>
            <br>
            <span style="color: gray; font-size: smaller;">
                Thanks to <a href="https://huggingface.co/spaces/Realcat/image-matching-webui" style="color: gray;">https://huggingface.co/spaces/Realcat/image-matching-webui</a> for providing the UI framework.
            </span>
        </p>
    </div>
    <div style="width: 50%; padding: 10px;">
        <p align="left">
            <span style="color: black;">
                你可以点击下面的示例图片或者上传图片
            </span>
            <br>
            <span style="color: black;">
                运行一次匹配需要 3.5 分钟左右的时间 (因为代码部署在免费cpu上)
            </span>
            <br>
            <span style="color: black;">
                请你耐心等待, 运行期间保持窗口在最前面
            </span>
            <br>
            <span style="color: gray; font-size: smaller;">
                感谢 <a href="https://huggingface.co/spaces/Realcat/image-matching-webui" style="color: gray;">https://huggingface.co/spaces/Realcat/image-matching-webui</a> 提供的交互界面.
            </span>
        </p>
    </div>
</div>


"""



def ui_change_imagebox(choice):
    """
    Updates the image box with the given choice.

    Args:
        choice (list): The list of image sources to be displayed in the image box.

    Returns:
        dict: A dictionary containing the updated value, sources, and type for the image box.
    """
    return {
        "value": None,  # The updated value of the image box
        "sources": choice,  # The list of image sources to be displayed
        "__type__": "update",  # The type of update for the image box
    }


def ui_reset_state(*args):
    """
    Reset the state of the UI.

    Returns:
        tuple: A tuple containing the initial values for the UI state.
    """
    key = list(matcher_zoo.keys())[0]  # Get the first key from matcher_zoo
    return (
        None,  # image0
        None,  # image1
        DEFAULT_MATCHING_THRESHOLD,  # matching_threshold
        DEFAULT_SETTING_MAX_FEATURES,  # max_features
        DEFAULT_DEFAULT_KEYPOINT_THRESHOLD,  # keypoint_threshold
        key,  # matcher
        ui_change_imagebox("upload"),  # input image0
        ui_change_imagebox("upload"),  # input image1
        "upload",  # match_image_src
        None,  # keypoints
        None,  # raw matches
        None,  # ransac matches
        {},  # matches result info
        {},  # matcher config
        None,  # warped image
        {},  # geometry result
        DEFAULT_RANSAC_METHOD,  # ransac_method
        DEFAULT_RANSAC_REPROJ_THRESHOLD,  # ransac_reproj_threshold
        DEFAULT_RANSAC_CONFIDENCE,  # ransac_confidence
        DEFAULT_RANSAC_MAX_ITER,  # ransac_max_iter
        DEFAULT_SETTING_GEOMETRY,  # geometry
    )


# "footer {visibility: hidden}"
def run(config):
    """
    Runs the application.

    Args:
        config (dict): A dictionary containing configuration parameters for the application.

    Returns:
        None
    """
    with gr.Blocks(css="style.css") as app:
        gr.Markdown(DESCRIPTION)

        with gr.Row(equal_height=False):
            with gr.Column():
                with gr.Row():
                    matcher_list = gr.Dropdown(
                        choices=list(matcher_zoo.keys()),
                        value="gim",
                        label="Matching Model",
                        interactive=True,
                    )
                    match_image_src = gr.Radio(
                        ["upload", "webcam"],
                        label="Image Source",
                        value="upload",
                    )
                with gr.Row():
                    input_image0 = gr.Image(
                        label="Image 0",
                        type="numpy",
                        image_mode="RGB",
                        height=300,
                        interactive=True,
                    )
                    input_image1 = gr.Image(
                        label="Image 1",
                        type="numpy",
                        image_mode="RGB",
                        height=300,
                        interactive=True,
                    )

                with gr.Row():
                    button_reset = gr.Button(value="Reset")
                    button_run = gr.Button(value="Run Match", variant="primary")

                with gr.Accordion("Advanced Setting", open=False):
                    with gr.Accordion("Matching Setting", open=True):
                        with gr.Row():
                            match_setting_threshold = gr.Slider(
                                minimum=0.0,
                                maximum=1,
                                step=0.001,
                                label="Match thres.",
                                value=0.1,
                            )
                            match_setting_max_features = gr.Slider(
                                minimum=10,
                                maximum=10000,
                                step=10,
                                label="Max features",
                                value=1000,
                            )
                        # TODO: add line settings
                        with gr.Row():
                            detect_keypoints_threshold = gr.Slider(
                                minimum=0,
                                maximum=1,
                                step=0.001,
                                label="Keypoint thres.",
                                value=0.015,
                            )
                            detect_line_threshold = gr.Slider(
                                minimum=0.1,
                                maximum=1,
                                step=0.01,
                                label="Line thres.",
                                value=0.2,
                            )
                        # matcher_lists = gr.Radio(
                        #     ["NN-mutual", "Dual-Softmax"],
                        #     label="Matcher mode",
                        #     value="NN-mutual",
                        # )
                    with gr.Accordion("RANSAC Setting", open=True):
                        with gr.Row(equal_height=False):
                            # enable_ransac = gr.Checkbox(label="Enable RANSAC")
                            ransac_method = gr.Dropdown(
                                choices=ransac_zoo.keys(),
                                value=DEFAULT_RANSAC_METHOD,
                                label="RANSAC Method",
                                interactive=True,
                            )
                        ransac_reproj_threshold = gr.Slider(
                            minimum=0.0,
                            maximum=12,
                            step=0.01,
                            label="Ransac Reproj threshold",
                            value=8.0,
                        )
                        ransac_confidence = gr.Slider(
                            minimum=0.0,
                            maximum=1,
                            step=0.00001,
                            label="Ransac Confidence",
                            value=0.99999,
                        )
                        ransac_max_iter = gr.Slider(
                            minimum=0.0,
                            maximum=100000,
                            step=100,
                            label="Ransac Iterations",
                            value=10000,
                        )

                    with gr.Accordion("Geometry Setting", open=False):
                        with gr.Row(equal_height=False):
                            # show_geom = gr.Checkbox(label="Show Geometry")
                            choice_estimate_geom = gr.Radio(
                                ["Fundamental", "Homography"],
                                label="Reconstruct Geometry",
                                value=DEFAULT_SETTING_GEOMETRY,
                            )

                # with gr.Column():
                # collect inputs
                inputs = [
                    input_image0,
                    input_image1,
                    match_setting_threshold,
                    match_setting_max_features,
                    detect_keypoints_threshold,
                    matcher_list,
                    ransac_method,
                    ransac_reproj_threshold,
                    ransac_confidence,
                    ransac_max_iter,
                    choice_estimate_geom,
                ]

                # Add some examples
                with gr.Row():
                    # Example inputs
                    gr.Examples(
                        examples=gen_examples(),
                        inputs=inputs,
                        outputs=[],
                        fn=run_matching,
                        cache_examples=False,
                        label=(
                            "Examples (click one of the images below to Run"
                            " Match)"
                        ),
                    )
                with gr.Accordion("Open for More!", open=False):
                    gr.Markdown(
                        f"""
                        <h3>Supported Algorithms</h3>
                        {", ".join(matcher_zoo.keys())}
                        """
                    )

            with gr.Column():
                output_keypoints = gr.Image(label="Keypoints", type="numpy")
                output_matches_raw = gr.Image(label="Raw Matches", type="numpy")
                output_matches_ransac = gr.Image(
                    label="Ransac Matches", type="numpy"
                )
                output_wrapped = gr.Image(
                    label="Wrapped Pair", type="numpy"
                )
                with gr.Accordion(
                    "Open for More: Matches Statistics", open=False
                ):
                    matches_result_info = gr.JSON(label="Matches Statistics")
                    matcher_info = gr.JSON(label="Match info")

                with gr.Accordion(
                    "Open for More: Geometry info", open=False
                ):
                    geometry_result = gr.JSON(
                        label="Reconstructed Geometry"
                    )

            # callbacks
            match_image_src.change(
                fn=ui_change_imagebox,
                inputs=match_image_src,
                outputs=input_image0,
            )
            match_image_src.change(
                fn=ui_change_imagebox,
                inputs=match_image_src,
                outputs=input_image1,
            )

            # collect outputs
            outputs = [
                output_keypoints,
                output_matches_raw,
                output_matches_ransac,
                matches_result_info,
                matcher_info,
                geometry_result,
                output_wrapped,
            ]
            # button callbacks
            button_run.click(fn=run_matching, inputs=inputs, outputs=outputs)

            # Reset images
            reset_outputs = [
                input_image0,
                input_image1,
                match_setting_threshold,
                match_setting_max_features,
                detect_keypoints_threshold,
                matcher_list,
                input_image0,
                input_image1,
                match_image_src,
                output_keypoints,
                output_matches_raw,
                output_matches_ransac,
                matches_result_info,
                matcher_info,
                output_wrapped,
                geometry_result,
                ransac_method,
                ransac_reproj_threshold,
                ransac_confidence,
                ransac_max_iter,
                choice_estimate_geom,
            ]
            button_reset.click(
                fn=ui_reset_state, inputs=inputs, outputs=reset_outputs
            )

            # estimate geo
            choice_estimate_geom.change(
                fn=change_estimate_geom,
                inputs=[
                    input_image0,
                    input_image1,
                    geometry_result,
                    choice_estimate_geom,
                ],
                outputs=[output_wrapped, geometry_result],
            )

    import datetime
    print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'app.queue().launch start')
    app.queue().launch(share=False)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--config_path",
        type=str,
        default="config.yaml",
        help="configuration file path",
    )
    args = parser.parse_args()
    config = None
    run(config)