File size: 11,139 Bytes
6c099d4
d342157
3d120f6
d342157
cdf98d2
 
 
f1a83d5
8ff8722
 
 
 
 
 
f1a83d5
 
8ff8722
f1a83d5
 
 
8ff8722
f1a83d5
 
 
8ff8722
d342157
6c099d4
 
 
 
cdf98d2
 
64582a1
cdf98d2
 
 
 
64582a1
 
 
cdf98d2
 
 
 
 
f1a83d5
 
 
 
 
 
2c4abaa
f1a83d5
40399e0
1f90301
94977c4
1f90301
 
94977c4
 
 
 
 
 
 
 
 
 
1f90301
edd96e4
 
 
 
1f90301
edd96e4
1f90301
edd96e4
 
1f90301
94977c4
4aaffff
94977c4
4aaffff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ff24bc
01a1bc2
94977c4
 
 
 
 
 
 
 
 
 
64582a1
 
 
 
 
 
 
 
 
 
 
 
 
94977c4
 
 
 
 
64582a1
 
 
 
 
94977c4
64582a1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94977c4
 
 
 
 
 
 
 
 
 
 
 
1f90301
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52f5401
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6c099d4
 
 
 
 
 
 
 
 
 
e7e26ca
6c099d4
 
 
 
 
1f90301
 
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
import os
import sys
import subprocess

# Disable SSR mode - it causes issues with Zero GPU
os.environ["GRADIO_SSR_MODE"] = "false"

# Check if we're running on HF Spaces with Zero GPU
# HF Spaces sets SPACE_ID environment variable
IS_HF_SPACE = bool(os.environ.get("SPACE_ID"))
IS_ZERO_GPU = IS_HF_SPACE or os.environ.get("ZERO_GPU_MODE", "0") == "1" or os.environ.get("SPACES_ZERO_GPU", "0") == "1"

if IS_HF_SPACE:
    print(f"πŸ€— Running on Hugging Face Space: {os.environ.get('SPACE_ID')}")

# Try to import spaces for Zero GPU support
# Note: spaces is provided by HF Spaces environment, not from pip
try:
    import spaces
    HAS_SPACES = True
    print("βœ… Spaces module available - Zero GPU support enabled")
except ImportError:
    HAS_SPACES = False
    spaces = None
    print("ℹ️ Spaces module not available - running without Zero GPU")

import gradio as gr


def _launch():
    # Add CropFormer and DeepLab to PYTHONPATH (needed when using prebuilt detectron2)
    # Prebuilt detectron2 doesn't include projects/, so we need to add them manually
    repo_root = os.path.dirname(os.path.abspath(__file__))
    projects_root = os.path.join(repo_root, "MaskClustering/third_party/detectron2/projects")

    # Add CropFormer (main project we use)
    cropformer_path = os.path.join(projects_root, "CropFormer")
    if cropformer_path not in sys.path:
        sys.path.insert(0, cropformer_path)

    # Add DeepLab (required by CropFormer for add_deeplab_config)
    deeplab_path = os.path.join(projects_root, "DeepLab")
    if deeplab_path not in sys.path:
        sys.path.insert(0, deeplab_path)

    # Set environment variables for Zero GPU optimization
    if IS_ZERO_GPU:
        print("πŸš€ Detected Zero GPU environment, optimizing settings...")
        os.environ["ZERO_GPU_MODE"] = "1"
        os.environ["MAX_GPU_MEMORY"] = "15GB"
        os.environ["BATCH_SIZE"] = "2"
        os.environ["MAX_IMAGES"] = "20"  # Limit max images for Zero GPU
        os.environ["MAX_RESOLUTION"] = "512"  # Lower resolution for memory optimization

    # Check if we need to install detectron2 and dependencies (first run only)
    # If using Dockerfile, DETECTRON2_INSTALLED is set and we skip compilation
    if not os.environ.get("DETECTRON2_INSTALLED"):
        print("πŸ”§ Installing CropFormer dependencies...")

        # Check if pytorch3d is already installed (from Dockerfile prebuild)
        pytorch3d_installed = False
        try:
            import pytorch3d
            pytorch3d_installed = True
            print(f"βœ… pytorch3d already installed (version: {getattr(pytorch3d, '__version__', 'unknown')})")
        except ImportError:
            pass

        try:
            # Install setuptools first (needed for mmcv build)
            subprocess.check_call([
                sys.executable, "-m", "pip", "install", "setuptools<81"
            ])
            # Install CropFormer/Mask2Former dependencies
            # Use --no-build-isolation to use system setuptools with pkg_resources
            subprocess.check_call([
                sys.executable, "-m", "pip", "install", "--no-build-isolation",
                "mmcv>=1.4.0,<2.0.0"
            ])

            # Only install pytorch3d if not already installed
            if not pytorch3d_installed:
                # Try prebuilt wheel first (much faster than compiling)
                # Using third-party wheels from https://github.com/MiroPsota/torch_packages_builder
                print("πŸ”§ Installing pytorch3d from prebuilt wheel...")
                try:
                    subprocess.check_call([
                        sys.executable, "-m", "pip", "install",
                        "--extra-index-url", "https://miropsota.github.io/torch_packages_builder",
                        "pytorch3d"
                    ])
                    print("βœ… pytorch3d installed from prebuilt wheel!")
                except subprocess.CalledProcessError:
                    # Fallback to compiling from source if prebuilt wheel not available
                    print("⚠️ Prebuilt wheel not found, compiling from source...")
                    cuda_env = {
                        **os.environ,
                        "FORCE_CUDA": "1",
                        "TORCH_CUDA_ARCH_LIST": "7.5;8.0;8.6;8.9;9.0",
                    }
                    subprocess.check_call([
                        sys.executable, "-m", "pip", "install", "--no-build-isolation",
                        "git+https://github.com/facebookresearch/pytorch3d.git"
                    ], env=cuda_env)
            print("βœ… CropFormer dependencies installed!")

            # Check if detectron2 is already installed (from Dockerfile prebuild)
            detectron2_installed = False
            try:
                import detectron2
                detectron2_installed = True
                print(f"βœ… detectron2 already installed (version: {getattr(detectron2, '__version__', 'unknown')})")
            except ImportError:
                pass

            if not detectron2_installed:
                # Use prebuilt wheels from https://miropsota.github.io/torch_packages_builder
                # This is MUCH faster than compiling from source (~5s vs ~60s)
                print("πŸ”§ Installing detectron2 from prebuilt wheel...")
                try:
                    subprocess.check_call([
                        sys.executable, "-m", "pip", "install",
                        "--extra-index-url", "https://miropsota.github.io/torch_packages_builder",
                        "detectron2"
                    ])
                    print("βœ… detectron2 installed from prebuilt wheel!")
                except subprocess.CalledProcessError:
                    # Fallback to compiling from vendored source
                    print("⚠️ Prebuilt wheel not found, compiling from vendored source...")
                    cuda_env = {
                        **os.environ,
                        "FORCE_CUDA": "1",
                        "TORCH_CUDA_ARCH_LIST": "7.5;8.0;8.6;8.9;9.0",
                    }
                    subprocess.check_call([
                        sys.executable, "-m", "pip", "install",
                        "--no-build-isolation", "-e", "MaskClustering/third_party/detectron2"
                    ], env=cuda_env)
                    print("βœ… detectron2 compiled and installed!")

                # Install MSDeformAttn from prebuilt wheel
                print("πŸ”§ Installing MultiScaleDeformableAttention from prebuilt wheel...")
                try:
                    subprocess.check_call([
                        sys.executable, "-m", "pip", "install",
                        "--extra-index-url", "https://miropsota.github.io/torch_packages_builder",
                        "MultiScaleDeformableAttention"
                    ])
                    print("βœ… MultiScaleDeformableAttention installed from prebuilt wheel!")
                except subprocess.CalledProcessError:
                    # Fallback to compiling from source
                    print("⚠️ Prebuilt wheel not found, compiling MSDeformAttn from source...")
                    repo_root = os.path.dirname(os.path.abspath(__file__))
                    cropformer_root = os.path.join(repo_root, "MaskClustering/third_party/detectron2/projects/CropFormer")
                    ops_dir = os.path.join(cropformer_root, "mask2former/modeling/pixel_decoder/ops")
                    if 'cuda_env' not in locals():
                        cuda_env = {
                            **os.environ,
                            "FORCE_CUDA": "1",
                            "TORCH_CUDA_ARCH_LIST": "7.5;8.0;8.6;8.9;9.0",
                        }
                    subprocess.check_call(
                        [sys.executable, "-m", "pip", "install", "-e", ".", "--no-build-isolation"],
                        cwd=ops_dir,
                        env=cuda_env
                    )
                    print("βœ… MSDeformAttn compiled and installed!")

                # Compile entity_api PythonAPI (still needed, but fast)
                repo_root = os.path.dirname(os.path.abspath(__file__))
                cropformer_root = os.path.join(repo_root, "MaskClustering/third_party/detectron2/projects/CropFormer")
                print("πŸ”§ Compiling entity_api PythonAPI...")
                entity_api_dir = os.path.join(cropformer_root, "entity_api/PythonAPI")
                if os.path.exists(entity_api_dir):
                    try:
                        subprocess.check_call(["make"], cwd=entity_api_dir, shell=True)
                        print("βœ… entity_api compiled successfully!")
                    except subprocess.CalledProcessError as e:
                        print(f"⚠️  entity_api compilation failed (non-critical): {e}")
                else:
                    print(f"⚠️  entity_api directory not found: {entity_api_dir}")
            
            print("πŸ”„ Restarting to load detectron2...")
            
            # Set environment variable to avoid reinstalling on restart
            os.environ["DETECTRON2_INSTALLED"] = "1"
            
            # Restart the script
            os.execv(sys.executable, [sys.executable] + sys.argv)
        except subprocess.CalledProcessError as e:
            print(f"❌ Failed to install dependencies: {e}")
            raise
    else:
        # Second run: verify imports work
        print("πŸ” Verifying detectron2 and mmcv imports...")
        try:
            import detectron2
            import mmcv
            print(f"βœ… detectron2 available (version: {getattr(detectron2, '__version__', 'unknown')})")
            print(f"βœ… mmcv available (version: {getattr(mmcv, '__version__', 'unknown')})")
        except ImportError as e:
            print(f"❌ Required module cannot be imported: {e}")
            print(f"   sys.path: {sys.path}")
            raise
    # HF Spaces/Gradio sometimes calls /api_info regardless of `show_api=False`.
    # Some gradio_client versions crash when JSON schema uses boolean `additionalProperties`.
    # Patch defensively to avoid bringing down the whole app.
    try:
        import gradio_client.utils as _gcu

        if hasattr(_gcu, "_json_schema_to_python_type"):
            _orig = _gcu._json_schema_to_python_type

            def _json_schema_to_python_type_patched(schema, defs=None):
                if isinstance(schema, bool):
                    return "Any"
                return _orig(schema, defs)

            _gcu._json_schema_to_python_type = _json_schema_to_python_type_patched
    except Exception:
        pass

    # HF Spaces expects the app to listen on 0.0.0.0:7860 (PORT may be provided).
    import mvp

    port = int(os.getenv("PORT", "7860"))
    # `mvp` defines `demo` (gr.Blocks). We launch it here instead of inside `mvp.py`.
    mvp.demo.queue(max_size=20).launch(
        server_name="0.0.0.0",
        server_port=port,
        show_error=True,
        share=False,
        show_api=False,
    )


if __name__ == "__main__":
    _launch()