# LongBench v2 / 66ebd3ba5a08c7b9b35e0446

task_id: a6429116-9271-5977-bef8-52b58c5c8b37
task_key: train--66ebd3ba5a08c7b9b35e0446
task_revision_id: 3

{"choice_A":"In the repo, OpenLRM’s reliance on large datasets like Objaverse and MVImgNet introduces challenges in balancing reconstruction fidelity with computational efficiency, particularly when scaling to more complex scenes compared to the more efficient architecture of Instant3D, which is optimized for real-time applications and lower-latency tasks, but this leads to a tradeoff in latency and scalability.","choice_B":"From the python files, we can see that  while Instant3D focuses heavily on computational efficiency for rapid object generation in real-time, OpenLRM’s large-scale reconstruction model prioritizes generalization across various 3D environments. However, the hybrid datasets, Objaverse and MVImgNet,  introduce issues with model overfitting to certain object types, leading to difficulties in scaling and maintaining real-time generation accuracy, unlike Instant3D’s streamlined approach.","choice_C":"In the core openlrm package, we see that OpenLRM’s approach focuses on generating high-fidelity 3D reconstructions using hybrid datasets, which can be computationally demanding. However, unlike Instant3D’s optimization for speed and lower resource usage, OpenLRM faces challenges in optimizing its memory consumption and real-time performance when scaling to larger datasets or more detailed reconstructions.","choice_D":"We can note from the codebase that the architectural difference between OpenLRM and Instant3D lies in their treatment of real-time constraints and scalability. OpenLRM is designed to reconstruct larger scenes and complex objects, which Instant3D resolves by focusing more narrowly on specific object categories and reducing the training dataset size to improve real-time performance.","context":"# OpenLRM: Open-Source Large Reconstruction Models\n\n[![Code License](https://img.shields.io/badge/Code%20License-Apache_2.0-yellow.svg)](LICENSE)\n[![Weight License](https://img.shields.io/badge/Weight%20License-CC%20By%20NC%204.0-red)](LICENSE_WEIGHT)\n[![LRM](https://img.shields.io/badge/LRM-Arxiv%20Link-green)](https://arxiv.org/abs/2311.04400)\n\n[![HF Models](https://img.shields.io/badge/Models-Huggingface%20Models-bron)](https://huggingface.co/zxhezexin)\n[![HF Demo](https://img.shields.io/badge/Demo-Huggingface%20Demo-blue)](https://huggingface.co/spaces/zxhezexin/OpenLRM)\n\n<img src=\"assets/rendered_video/teaser.gif\" width=\"75%\" height=\"auto\"/>\n\n<div style=\"text-align: left\">\n    <img src=\"assets/mesh_snapshot/crop.owl.ply00.png\" width=\"12%\" height=\"auto\"/>\n    <img src=\"assets/mesh_snapshot/crop.owl.ply01.png\" width=\"12%\" height=\"auto\"/>\n    <img src=\"assets/mesh_snapshot/crop.building.ply00.png\" width=\"12%\" height=\"auto\"/>\n    <img src=\"assets/mesh_snapshot/crop.building.ply01.png\" width=\"12%\" height=\"auto\"/>\n    <img src=\"assets/mesh_snapshot/crop.rose.ply00.png\" width=\"12%\" height=\"auto\"/>\n    <img src=\"assets/mesh_snapshot/crop.rose.ply01.png\" width=\"12%\" height=\"auto\"/>\n</div>\n\n## News\n\n- [2024.03.13] Update [training code](openlrm/runners/train) and release [OpenLRM v1.1.1](https://github.com/3DTopia/OpenLRM/releases/tag/v1.1.1).\n- [2024.03.08] We have released the core [blender script](scripts/data/objaverse/blender_script.py) used to render Objaverse images.\n- [2024.03.05] The [Huggingface demo](https://huggingface.co/spaces/zxhezexin/OpenLRM) now uses `openlrm-mix-base-1.1` model by default. Please refer to the [model card](model_card.md) for details on the updated model architecture and training settings.\n- [2024.03.04] Version update v1.1. Release model weights trained on both Objaverse and MVImgNet. Codebase is majorly refactored for better usability and extensibility. Please refer to [v1.1.0](https://github.com/3DTopia/OpenLRM/releases/tag/v1.1.0) for details.\n- [2024.01.09] Updated all v1.0 models trained on Objaverse. Please refer to [HF Models](https://huggingface.co/zxhezexin) and overwrite previous model weights.\n- [2023.12.21] [Hugging Face Demo](https://huggingface.co/spaces/zxhezexin/OpenLRM) is online. Have a try!\n- [2023.12.20] Release weights of the base and large models trained on Objaverse.\n- [2023.12.20] We release this project OpenLRM, which is an open-source implementation of the paper [LRM](https://arxiv.org/abs/2311.04400).\n\n## Setup\n\n### Installation\n```\ngit clone https://github.com/3DTopia/OpenLRM.git\ncd OpenLRM\n```\n\n### Environment\n- Install requirements for OpenLRM first.\n  ```\n  pip install -r requirements.txt\n  ```\n- Please then follow the [xFormers installation guide](https://github.com/facebookresearch/xformers?tab=readme-ov-file#installing-xformers) to enable memory efficient attention inside [DINOv2 encoder](openlrm/models/encoders/dinov2/layers/attention.py).\n\n## Quick Start\n\n### Pretrained Models\n\n- Model weights are released on [Hugging Face](https://huggingface.co/zxhezexin).\n- Weights will be downloaded automatically when you run the inference script for the first time.\n- Please be aware of the [license](LICENSE_WEIGHT) before using the weights.\n\n| Model | Training Data | Layers | Feat. Dim | Trip. Dim. | In. Res. | Link |\n| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n| openlrm-obj-small-1.1 | Objaverse | 12 | 512 | 32 | 224 | [HF](https://huggingface.co/zxhezexin/openlrm-obj-small-1.1) |\n| openlrm-obj-base-1.1 | Objaverse | 12 | 768 | 48 | 336 | [HF](https://huggingface.co/zxhezexin/openlrm-obj-base-1.1) |\n| openlrm-obj-large-1.1 | Objaverse | 16 | 1024 | 80 | 448 | [HF](https://huggingface.co/zxhezexin/openlrm-obj-large-1.1) |\n| openlrm-mix-small-1.1 | Objaverse + MVImgNet | 12 | 512 | 32 | 224 | [HF](https://huggingface.co/zxhezexin/openlrm-mix-small-1.1) |\n| openlrm-mix-base-1.1 | Objaverse + MVImgNet | 12 | 768 | 48 | 336 | [HF](https://huggingface.co/zxhezexin/openlrm-mix-base-1.1) |\n| openlrm-mix-large-1.1 | Objaverse + MVImgNet | 16 | 1024 | 80 | 448 | [HF](https://huggingface.co/zxhezexin/openlrm-mix-large-1.1) |\n\nModel cards with additional details can be found in [model_card.md](model_card.md).\n\n### Prepare Images\n- We put some sample inputs under `assets/sample_input`, and you can quickly try them.\n- Prepare RGBA images or RGB images with white background (with some background removal tools, e.g., [Rembg](https://github.com/danielgatis/rembg), [Clipdrop](https://clipdrop.co)).\n\n### Inference\n- Run the inference script to get 3D assets.\n- You may specify which form of output to generate by setting the flags `EXPORT_VIDEO=true` and `EXPORT_MESH=true`.\n- Please set default `INFER_CONFIG` according to the model you want to use. E.g., `infer-b.yaml` for base models and `infer-s.yaml` for small models.\n- An example usage is as follows:\n\n  ```\n  # Example usage\n  EXPORT_VIDEO=true\n  EXPORT_MESH=true\n  INFER_CONFIG=\"./configs/infer-b.yaml\"\n  MODEL_NAME=\"zxhezexin/openlrm-mix-base-1.1\"\n  IMAGE_INPUT=\"./assets/sample_input/owl.png\"\n\n  python -m openlrm.launch infer.lrm --infer $INFER_CONFIG model_name=$MODEL_NAME image_input=$IMAGE_INPUT export_video=$EXPORT_VIDEO export_mesh=$EXPORT_MESH\n  ```\n\n### Tips\n- The recommended PyTorch version is `>=2.1`. Code is developed and tested under PyTorch `2.1.2`.\n- If you encounter CUDA OOM issues, please try to reduce the `frame_size` in the inference configs.\n- You should be able to see `UserWarning: xFormers is available` if `xFormers` is actually working.\n\n## Training\n\n### Configuration\n- We provide a sample accelerate config file under `configs/accelerate-train.yaml`, which defaults to use 8 GPUs with `bf16` mixed precision.\n- You may modify the configuration file to fit your own environment.\n\n### Data Preparation\n- We provide the core [Blender script](scripts/data/objaverse/blender_script.py) used to render Objaverse images.\n- Please refer to [Objaverse Rendering](https://github.com/allenai/objaverse-rendering) for other scripts including distributed rendering.\n\n### Run Training\n- A sample training config file is provided under `configs/train-sample.yaml`.\n- Please replace data related paths in the config file with your own paths and customize the training settings.\n- An example training usage is as follows:\n\n  ```\n  # Example usage\n  ACC_CONFIG=\"./configs/accelerate-train.yaml\"\n  TRAIN_CONFIG=\"./configs/train-sample.yaml\"\n\n  accelerate launch --config_file $ACC_CONFIG -m openlrm.launch train.lrm --config $TRAIN_CONFIG\n  ```\n\n### Inference on Trained Models\n- The inference pipeline is compatible with huggingface utilities for better convenience.\n- You need to convert the training checkpoint to inference models by running the following script.\n\n  ```\n  python scripts/convert_hf.py --config <YOUR_EXACT_TRAINING_CONFIG> convert.global_step=null\n  ```\n\n- The converted model will be saved under `exps/releases` by default and can be used for inference following the [inference guide](https://github.com/3DTopia/OpenLRM?tab=readme-ov-file#inference).\n\n## Acknowledgement\n\n- We thank the authors of the [original paper](https://arxiv.org/abs/2311.04400) for their great work! Special thanks to Kai Zhang and Yicong Hong for assistance during the reproduction.\n- This project is supported by Shanghai AI Lab by providing the computing resources.\n- This project is advised by Ziwei Liu and Jiaya Jia.\n\n## Citation\n\nIf you find this work useful for your research, please consider citing:\n```\n@article{hong2023lrm,\n  title={Lrm: Large reconstruction model for single image to 3d},\n  author={Hong, Yicong and Zhang, Kai and Gu, Jiuxiang and Bi, Sai and Zhou, Yang and Liu, Difan and Liu, Feng and Sunkavalli, Kalyan and Bui, Trung and Tan, Hao},\n  journal={arXiv preprint arXiv:2311.04400},\n  year={2023}\n}\n```\n\n```\n@misc{openlrm,\n  title = {OpenLRM: Open-Source Large Reconstruction Models},\n  author = {Zexin He and Tengfei Wang},\n  year = {2023},\n  howpublished = {\\url{https://github.com/3DTopia/OpenLRM}},\n}\n```\n\n## License\n\n- OpenLRM as a whole is licensed under the [Apache License, Version 2.0](LICENSE), while certain components are covered by [NVIDIA's proprietary license](LICENSE_NVIDIA). Users are responsible for complying with the respective licensing terms of each component.\n- Model weights are licensed under the [Creative Commons Attribution-NonCommercial 4.0 International License](LICENSE_WEIGHT). They are provided for research purposes only, and CANNOT be used commercially.\n\n\n# Model Card for OpenLRM V1.1\n\n## Overview\n\n- This model card is for the [OpenLRM](https://github.com/3DTopia/OpenLRM) project, which is an open-source implementation of the paper [LRM](https://arxiv.org/abs/2311.04400).\n- Information contained in this model card corresponds to [Version 1.1](https://github.com/3DTopia/OpenLRM/releases).\n\n## Model Details\n\n- Training data\n\n    | Model | Training Data |\n    | :---: | :---: |\n    | [openlrm-obj-small-1.1](https://huggingface.co/zxhezexin/openlrm-obj-small-1.1) | Objaverse |\n    | [openlrm-obj-base-1.1](https://huggingface.co/zxhezexin/openlrm-obj-base-1.1) | Objaverse |\n    | [openlrm-obj-large-1.1](https://huggingface.co/zxhezexin/openlrm-obj-large-1.1) | Objaverse |\n    | [openlrm-mix-small-1.1](https://huggingface.co/zxhezexin/openlrm-mix-small-1.1) | Objaverse + MVImgNet |\n    | [openlrm-mix-base-1.1](https://huggingface.co/zxhezexin/openlrm-mix-base-1.1) | Objaverse + MVImgNet |\n    | [openlrm-mix-large-1.1](https://huggingface.co/zxhezexin/openlrm-mix-large-1.1) | Objaverse + MVImgNet |\n\n- Model architecture (version==1.1)\n\n    | Type  | Layers | Feat. Dim | Attn. Heads | Triplane Dim. | Input Res. | Image Encoder     | Encoder Dim. | Size  |\n    | :---: | :----: | :-------: | :---------: | :-----------: | :--------: | :---------------: | :----------: | :---: |\n    | small |   12   |    512    |      8      |      32       |    224     | dinov2_vits14_reg |      384     | 446M  |\n    | base  |   12   |    768    |     12      |      48       |    336     | dinov2_vitb14_reg |      768     | 1.04G |\n    | large |   16   |   1024    |     16      |      80       |    448     | dinov2_vitb14_reg |      768     | 1.81G |\n\n- Training settings\n\n    | Type  | Rend. Res. | Rend. Patch | Ray Samples |\n    | :---: | :--------: | :---------: | :---------: |\n    | small |    192     |     64      |     96      |\n    | base  |    288     |     96      |     96      |\n    | large |    384     |    128      |    128      |\n\n## Notable Differences from the Original Paper\n\n- We do not use the deferred back-propagation technique in the original paper.\n- We used random background colors during training.\n- The image encoder is based on the [DINOv2](https://github.com/facebookresearch/dinov2) model with register tokens.\n- The triplane decoder contains 4 layers in our implementation.\n\n## License\n\n- The model weights are released under the [Creative Commons Attribution-NonCommercial 4.0 International License](LICENSE_WEIGHT).\n- They are provided for research purposes only, and CANNOT be used commercially.\n\n## Disclaimer\n\nThis model is an open-source implementation and is NOT the official release of the original research paper. While it aims to reproduce the original results as faithfully as possible, there may be variations due to model implementation, training data, and other factors.\n\n### Ethical Considerations\n\n- This model should be used responsibly and ethically, and should not be used for malicious purposes.\n- Users should be aware of potential biases in the training data.\n- The model should not be used under the circumstances that could lead to harm or unfair treatment of individuals or groups.\n\n### Usage Considerations\n\n- The model is provided \"as is\" without warranty of any kind.\n- Users are responsible for ensuring that their use complies with all relevant laws and regulations.\n- The developers and contributors of this model are not liable for any damages or losses arising from the use of this model.\n\n---\n\n*This model card is subject to updates and modifications. Users are advised to check for the latest version regularly.*\n\n\ntorch>=2.1.2\nlpips\nomegaconf\ntransformers\nsafetensors\naccelerate\nimageio[ffmpeg]\nPyMCubes\ntrimesh\nmegfile\nopencv-python\noptimum[onnxruntime-gpu]\nrembg[gpu,cli]\nhttpx[socks]\ntensorboard\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nfrom PIL import Image\nimport numpy as np\nimport gradio as gr\n\n\ndef assert_input_image(input_image):\n    if input_image is None:\n        raise gr.Error(\"No image selected or uploaded!\")\n\ndef prepare_working_dir():\n    import tempfile\n    working_dir = tempfile.TemporaryDirectory()\n    return working_dir\n\ndef init_preprocessor():\n    from openlrm.utils.preprocess import Preprocessor\n    global preprocessor\n    preprocessor = Preprocessor()\n\ndef preprocess_fn(image_in: np.ndarray, remove_bg: bool, recenter: bool, working_dir):\n    image_raw = os.path.join(working_dir.name, \"raw.png\")\n    with Image.fromarray(image_in) as img:\n        img.save(image_raw)\n    image_out = os.path.join(working_dir.name, \"rembg.png\")\n    success = preprocessor.preprocess(image_path=image_raw, save_path=image_out, rmbg=remove_bg, recenter=recenter)\n    assert success, f\"Failed under preprocess_fn!\"\n    return image_out\n\n\ndef demo_openlrm(infer_impl):\n\n    def core_fn(image: str, source_cam_dist: float, working_dir):\n        dump_video_path = os.path.join(working_dir.name, \"output.mp4\")\n        dump_mesh_path = os.path.join(working_dir.name, \"output.ply\")\n        infer_impl(\n            image_path=image,\n            source_cam_dist=source_cam_dist,\n            export_video=True,\n            export_mesh=False,\n            dump_video_path=dump_video_path,\n            dump_mesh_path=dump_mesh_path,\n        )\n        return dump_video_path\n\n    def example_fn(image: np.ndarray):\n        from gradio.utils import get_cache_folder\n        working_dir = get_cache_folder()\n        image = preprocess_fn(\n            image_in=image,\n            remove_bg=True,\n            recenter=True,\n            working_dir=working_dir,\n        )\n        video = core_fn(\n            image=image,\n            source_cam_dist=2.0,\n            working_dir=working_dir,\n        )\n        return image, video\n\n\n    _TITLE = '''OpenLRM: Open-Source Large Reconstruction Models'''\n\n    _DESCRIPTION = '''\n        <div>\n            <a style=\"display:inline-block\" href='https://github.com/3DTopia/OpenLRM'><img src='https://img.shields.io/github/stars/3DTopia/OpenLRM?style=social'/></a>\n            <a style=\"display:inline-block; margin-left: .5em\" href=\"https://huggingface.co/zxhezexin\"><img src='https://img.shields.io/badge/Model-Weights-blue'/></a>\n        </div>\n        OpenLRM is an open-source implementation of Large Reconstruction Models.\n\n        <strong>Image-to-3D in 10 seconds with A100!</strong>\n\n        <strong>Disclaimer:</strong> This demo uses `openlrm-mix-base-1.1` model with 288x288 rendering resolution here for a quick demonstration.\n    '''\n\n    with gr.Blocks(analytics_enabled=False) as demo:\n\n        # HEADERS\n        with gr.Row():\n            with gr.Column(scale=1):\n                gr.Markdown('# ' + _TITLE)\n        with gr.Row():\n            gr.Markdown(_DESCRIPTION)\n\n        # DISPLAY\n        with gr.Row():\n\n            with gr.Column(variant='panel', scale=1):\n                with gr.Tabs(elem_id=\"openlrm_input_image\"):\n                    with gr.TabItem('Input Image'):\n                        with gr.Row():\n                            input_image = gr.Image(label=\"Input Image\", image_mode=\"RGBA\", width=\"auto\", sources=\"upload\", type=\"numpy\", elem_id=\"content_image\")\n\n            with gr.Column(variant='panel', scale=1):\n                with gr.Tabs(elem_id=\"openlrm_processed_image\"):\n                    with gr.TabItem('Processed Image'):\n                        with gr.Row():\n                            processed_image = gr.Image(label=\"Processed Image\", image_mode=\"RGBA\", type=\"filepath\", elem_id=\"processed_image\", width=\"auto\", interactive=False)\n\n            with gr.Column(variant='panel', scale=1):\n                with gr.Tabs(elem_id=\"openlrm_render_video\"):\n                    with gr.TabItem('Rendered Video'):\n                        with gr.Row():\n                            output_video = gr.Video(label=\"Rendered Video\", format=\"mp4\", width=\"auto\", autoplay=True)\n\n        # SETTING\n        with gr.Row():\n            with gr.Column(variant='panel', scale=1):\n                with gr.Tabs(elem_id=\"openlrm_attrs\"):\n                    with gr.TabItem('Settings'):\n                        with gr.Column(variant='panel'):\n                            gr.Markdown(\n                                \"\"\"\n                                <strong>Best Practice</strong>:\n                                    Centered objects in reasonable sizes. Try adjusting source camera distances.\n                                \"\"\"\n                            )\n                            checkbox_rembg = gr.Checkbox(True, label='Remove background')\n                            checkbox_recenter = gr.Checkbox(True, label='Recenter the object')\n                            slider_cam_dist = gr.Slider(1.0, 3.5, value=2.0, step=0.1, label=\"Source Camera Distance\")\n                            submit = gr.Button('Generate', elem_id=\"openlrm_generate\", variant='primary')\n\n        # EXAMPLES\n        with gr.Row():\n            examples = [\n                ['assets/sample_input/owl.png'],\n                ['assets/sample_input/building.png'],\n                ['assets/sample_input/mailbox.png'],\n                ['assets/sample_input/fire.png'],\n                ['assets/sample_input/girl.png'],\n                ['assets/sample_input/lamp.png'],\n                ['assets/sample_input/hydrant.png'],\n                ['assets/sample_input/hotdogs.png'],\n                ['assets/sample_input/traffic.png'],\n                ['assets/sample_input/ceramic.png'],\n            ]\n            gr.Examples(\n                examples=examples,\n                inputs=[input_image], \n                outputs=[processed_image, output_video],\n                fn=example_fn,\n                cache_examples=bool(os.getenv('SPACE_ID')),\n                examples_per_page=20,\n            )\n\n        working_dir = gr.State()\n        submit.click(\n            fn=assert_input_image,\n            inputs=[input_image],\n            queue=False,\n        ).success(\n            fn=prepare_working_dir,\n            outputs=[working_dir],\n            queue=False,\n        ).success(\n            fn=preprocess_fn,\n            inputs=[input_image, checkbox_rembg, checkbox_recenter, working_dir],\n            outputs=[processed_image],\n        ).success(\n            fn=core_fn,\n            inputs=[processed_image, slider_cam_dist, working_dir],\n            outputs=[output_video],\n        )\n\n        demo.queue()\n        demo.launch()\n\n\ndef launch_gradio_app():\n\n    os.environ.update({\n        \"APP_ENABLED\": \"1\",\n        \"APP_MODEL_NAME\": \"zxhezexin/openlrm-mix-base-1.1\",\n        \"APP_INFER\": \"./configs/infer-gradio.yaml\",\n        \"APP_TYPE\": \"infer.lrm\",\n        \"NUMBA_THREADING_LAYER\": 'omp',\n    })\n\n    from openlrm.runners import REGISTRY_RUNNERS\n    from openlrm.runners.infer.base_inferrer import Inferrer\n    InferrerClass : Inferrer = REGISTRY_RUNNERS[os.getenv(\"APP_TYPE\")]\n    with InferrerClass() as inferrer:\n        init_preprocessor()\n        if not bool(os.getenv('SPACE_ID')):\n            from openlrm.utils.proxy import no_proxy\n            demo = no_proxy(demo_openlrm)\n        else:\n            demo = demo_openlrm\n        demo(infer_impl=inferrer.infer_single)\n\n\nif __name__ == '__main__':\n\n    launch_gradio_app()\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport argparse\n\nfrom openlrm.runners import REGISTRY_RUNNERS\n\n\ndef main():\n\n    parser = argparse.ArgumentParser(description='OpenLRM launcher')\n    parser.add_argument('runner', type=str, help='Runner to launch')\n    args, unknown = parser.parse_known_args()\n\n    if args.runner not in REGISTRY_RUNNERS:\n        raise ValueError('Runner {} not found'.format(args.runner))\n\n    RunnerClass = REGISTRY_RUNNERS[args.runner]\n    with RunnerClass() as runner:\n        runner.run()\n\n\nif __name__ == '__main__':\n    main()\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Empty\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom abc import ABC, abstractmethod\n\n\nclass Runner(ABC):\n    \"\"\"Abstract runner class\"\"\"\n\n    def __init__(self):\n        pass\n\n    @abstractmethod\n    def run(self):\n        pass\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom openlrm.utils.registry import Registry\n\nREGISTRY_RUNNERS = Registry()\n\nfrom .train import *\nfrom .infer import *\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nimport math\nfrom tqdm.auto import tqdm\nimport torch\nimport torch.nn as nn\nfrom torchvision.utils import make_grid\nfrom accelerate.logging import get_logger\n\nfrom .base_trainer import Trainer\nfrom openlrm.utils.profiler import DummyProfiler\nfrom openlrm.runners import REGISTRY_RUNNERS\n\n\nlogger = get_logger(__name__)\n\n\n@REGISTRY_RUNNERS.register('train.lrm')\nclass LRMTrainer(Trainer):\n    def __init__(self):\n        super().__init__()\n\n        self.model = self._build_model(self.cfg)\n        self.optimizer = self._build_optimizer(self.model, self.cfg)\n        self.train_loader, self.val_loader = self._build_dataloader(self.cfg)\n        self.scheduler = self._build_scheduler(self.optimizer, self.cfg)\n        self.pixel_loss_fn, self.perceptual_loss_fn, self.tv_loss_fn = self._build_loss_fn(self.cfg)\n\n    def _build_model(self, cfg):\n        assert cfg.experiment.type == 'lrm', \\\n            f\"Config type {cfg.experiment.type} does not match with runner {self.__class__.__name__}\"\n        from openlrm.models import ModelLRM\n        model = ModelLRM(**cfg.model)\n        return model\n\n    def _build_optimizer(self, model: nn.Module, cfg):\n        decay_params, no_decay_params = [], []\n\n        # add all bias and LayerNorm params to no_decay_params\n        for name, module in model.named_modules():\n            if isinstance(module, nn.LayerNorm):\n                no_decay_params.extend([p for p in module.parameters()])\n            elif hasattr(module, 'bias') and module.bias is not None:\n                no_decay_params.append(module.bias)\n\n        # add remaining parameters to decay_params\n        _no_decay_ids = set(map(id, no_decay_params))\n        decay_params = [p for p in model.parameters() if id(p) not in _no_decay_ids]\n\n        # filter out parameters with no grad\n        decay_params = list(filter(lambda p: p.requires_grad, decay_params))\n        no_decay_params = list(filter(lambda p: p.requires_grad, no_decay_params))\n\n        # monitor this to make sure we don't miss any parameters\n        logger.info(\"======== Weight Decay Parameters ========\")\n        logger.info(f\"Total: {len(decay_params)}\")\n        logger.info(\"======== No Weight Decay Parameters ========\")\n        logger.info(f\"Total: {len(no_decay_params)}\")\n\n        # Optimizer\n        opt_groups = [\n            {'params': decay_params, 'weight_decay': cfg.train.optim.weight_decay},\n            {'params': no_decay_params, 'weight_decay': 0.0},\n        ]\n        optimizer = torch.optim.AdamW(\n            opt_groups,\n            lr=cfg.train.optim.lr,\n            betas=(cfg.train.optim.beta1, cfg.train.optim.beta2),\n        )\n\n        return optimizer\n\n    def _build_scheduler(self, optimizer, cfg):\n        local_batches_per_epoch = math.floor(len(self.train_loader) / self.accelerator.num_processes)\n        total_global_batches = cfg.train.epochs * math.ceil(local_batches_per_epoch / self.cfg.train.accum_steps)\n        effective_warmup_iters = cfg.train.scheduler.warmup_real_iters\n        logger.debug(f\"======== Scheduler effective max iters: {total_global_batches} ========\")\n        logger.debug(f\"======== Scheduler effective warmup iters: {effective_warmup_iters} ========\")\n        if cfg.train.scheduler.type == 'cosine':\n            from openlrm.utils.scheduler import CosineWarmupScheduler\n            scheduler = CosineWarmupScheduler(\n                optimizer=optimizer,\n                warmup_iters=effective_warmup_iters,\n                max_iters=total_global_batches,\n            )\n        else:\n            raise NotImplementedError(f\"Scheduler type {cfg.train.scheduler.type} not implemented\")\n        return scheduler\n\n    def _build_dataloader(self, cfg):\n        # dataset class\n        from openlrm.datasets import MixerDataset\n\n        # build dataset\n        train_dataset = MixerDataset(\n            split=\"train\",\n            subsets=cfg.dataset.subsets,\n            sample_side_views=cfg.dataset.sample_side_views,\n            render_image_res_low=cfg.dataset.render_image.low,\n            render_image_res_high=cfg.dataset.render_image.high,\n            render_region_size=cfg.dataset.render_image.region,\n            source_image_res=cfg.dataset.source_image_res,\n            normalize_camera=cfg.dataset.normalize_camera,\n            normed_dist_to_center=cfg.dataset.normed_dist_to_center,\n        )\n        val_dataset = MixerDataset(\n            split=\"val\",\n            subsets=cfg.dataset.subsets,\n            sample_side_views=cfg.dataset.sample_side_views,\n            render_image_res_low=cfg.dataset.render_image.low,\n            render_image_res_high=cfg.dataset.render_image.high,\n            render_region_size=cfg.dataset.render_image.region,\n            source_image_res=cfg.dataset.source_image_res,\n            normalize_camera=cfg.dataset.normalize_camera,\n            normed_dist_to_center=cfg.dataset.normed_dist_to_center,\n        )\n\n        # build data loader\n        train_loader = torch.utils.data.DataLoader(\n            train_dataset,\n            batch_size=cfg.train.batch_size,\n            shuffle=True,\n            drop_last=True,\n            num_workers=cfg.dataset.num_train_workers,\n            pin_memory=cfg.dataset.pin_mem,\n            persistent_workers=True,\n        )\n        val_loader = torch.utils.data.DataLoader(\n            val_dataset,\n            batch_size=cfg.val.batch_size,\n            shuffle=False,\n            drop_last=False,\n            num_workers=cfg.dataset.num_val_workers,\n            pin_memory=cfg.dataset.pin_mem,\n            persistent_workers=False,\n        )\n\n        return train_loader, val_loader\n\n    def _build_loss_fn(self, cfg):\n        from openlrm.losses import PixelLoss, LPIPSLoss, TVLoss\n        pixel_loss_fn = PixelLoss()\n        with self.accelerator.main_process_first():\n            perceptual_loss_fn = LPIPSLoss(device=self.device, prefech=True)\n        tv_loss_fn = TVLoss()\n        return pixel_loss_fn, perceptual_loss_fn, tv_loss_fn\n\n    def register_hooks(self):\n        pass\n\n    def forward_loss_local_step(self, data):\n\n        source_camera = data['source_camera']\n        render_camera = data['render_camera']\n        source_image = data['source_image']\n        render_image = data['render_image']\n        render_anchors = data['render_anchors']\n        render_full_resolutions = data['render_full_resolutions']\n        render_bg_colors = data['render_bg_colors']\n\n        N, M, C, H, W = render_image.shape\n\n        # forward\n        outputs = self.model(\n            image=source_image,\n            source_camera=source_camera,\n            render_cameras=render_camera,\n            render_anchors=render_anchors,\n            render_resolutions=render_full_resolutions,\n            render_bg_colors=render_bg_colors,\n            render_region_size=self.cfg.dataset.render_image.region,\n        )\n\n        # loss calculation\n        loss = 0.\n        loss_pixel = None\n        loss_perceptual = None\n        loss_tv = None\n\n        if self.cfg.train.loss.pixel_weight > 0.:\n            loss_pixel = self.pixel_loss_fn(outputs['images_rgb'], render_image)\n            loss += loss_pixel * self.cfg.train.loss.pixel_weight\n        if self.cfg.train.loss.perceptual_weight > 0.:\n            loss_perceptual = self.perceptual_loss_fn(outputs['images_rgb'], render_image)\n            loss += loss_perceptual * self.cfg.train.loss.perceptual_weight\n        if self.cfg.train.loss.tv_weight > 0.: \n            loss_tv = self.tv_loss_fn(outputs['planes'])\n            loss += loss_tv * self.cfg.train.loss.tv_weight\n\n        return outputs, loss, loss_pixel, loss_perceptual, loss_tv\n\n    def train_epoch(self, pbar: tqdm, loader: torch.utils.data.DataLoader, profiler: torch.profiler.profile):\n        self.model.train()\n\n        local_step_losses = []\n        global_step_losses = []\n\n        logger.debug(f\"======== Starting epoch {self.current_epoch} ========\")\n        for data in loader:\n\n            logger.debug(f\"======== Starting global step {self.global_step} ========\")\n            with self.accelerator.accumulate(self.model):\n\n                # forward to loss\n                outs, loss, loss_pixel, loss_perceptual, loss_tv = self.forward_loss_local_step(data)\n                \n                # backward\n                self.accelerator.backward(loss)\n                if self.accelerator.sync_gradients and self.cfg.train.optim.clip_grad_norm > 0.:\n                    self.accelerator.clip_grad_norm_(self.model.parameters(), self.cfg.train.optim.clip_grad_norm)\n                self.optimizer.step()\n                self.optimizer.zero_grad()\n\n                # track local losses\n                local_step_losses.append(torch.stack([\n                    _loss.detach() if _loss is not None else torch.tensor(float('nan'), device=self.device)\n                    for _loss in [loss, loss_pixel, loss_perceptual, loss_tv]\n                ]))\n\n            # track global step\n            if self.accelerator.sync_gradients:\n                profiler.step()\n                self.scheduler.step()\n                logger.debug(f\"======== Scheduler step ========\")\n                self.global_step += 1\n                global_step_loss = self.accelerator.gather(torch.stack(local_step_losses)).mean(dim=0).cpu()\n                loss, loss_pixel, loss_perceptual, loss_tv = global_step_loss.unbind()\n                loss_kwargs = {\n                    'loss': loss.item(),\n                    'loss_pixel': loss_pixel.item(),\n                    'loss_perceptual': loss_perceptual.item(),\n                    'loss_tv': loss_tv.item(),\n                }\n                self.log_scalar_kwargs(\n                    step=self.global_step, split='train',\n                    **loss_kwargs\n                )\n                self.log_optimizer(step=self.global_step, attrs=['lr'], group_ids=[0, 1])\n                local_step_losses = []\n                global_step_losses.append(global_step_loss)\n\n                # manage display\n                pbar.update(1)\n                description = {\n                    **loss_kwargs,\n                    'lr': self.optimizer.param_groups[0]['lr'],\n                }\n                description = '[TRAIN STEP]' + \\\n                    ', '.join(f'{k}={tqdm.format_num(v)}' for k, v in description.items() if not math.isnan(v))\n                pbar.set_description(description)\n\n                # periodic actions\n                if self.global_step % self.cfg.saver.checkpoint_global_steps == 0:\n                    self.save_checkpoint()\n                if self.global_step % self.cfg.val.global_step_period == 0:\n                    self.evaluate()\n                    self.model.train()\n                if self.global_step % self.cfg.logger.image_monitor.train_global_steps == 0:\n                    self.log_image_monitor(\n                        step=self.global_step, split='train',\n                        renders=outs['images_rgb'].detach()[:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n                        gts=data['render_image'][:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n                    )\n\n                # progress control\n                if self.global_step >= self.N_max_global_steps:\n                    self.accelerator.set_trigger()\n                    break\n\n        # track epoch\n        self.current_epoch += 1\n        epoch_losses = torch.stack(global_step_losses).mean(dim=0)\n        epoch_loss, epoch_loss_pixel, epoch_loss_perceptual, epoch_loss_tv = epoch_losses.unbind()\n        epoch_loss_dict = {\n            'loss': epoch_loss.item(),\n            'loss_pixel': epoch_loss_pixel.item(),\n            'loss_perceptual': epoch_loss_perceptual.item(),\n            'loss_tv': epoch_loss_tv.item(),\n        }\n        self.log_scalar_kwargs(\n            epoch=self.current_epoch, split='train',\n            **epoch_loss_dict,\n        )\n        logger.info(\n            f'[TRAIN EPOCH] {self.current_epoch}/{self.cfg.train.epochs}: ' + \\\n                ', '.join(f'{k}={tqdm.format_num(v)}' for k, v in epoch_loss_dict.items() if not math.isnan(v))\n        )\n\n    def train(self):\n        \n        starting_local_step_in_epoch = self.global_step_in_epoch * self.cfg.train.accum_steps\n        skipped_loader = self.accelerator.skip_first_batches(self.train_loader, starting_local_step_in_epoch)\n        logger.info(f\"======== Skipped {starting_local_step_in_epoch} local batches ========\")\n\n        with tqdm(\n            range(0, self.N_max_global_steps),\n            initial=self.global_step,\n            disable=(not self.accelerator.is_main_process),\n        ) as pbar:\n\n            profiler = torch.profiler.profile(\n                activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA],\n                schedule=torch.profiler.schedule(\n                    wait=10, warmup=10, active=100,\n                ),\n                on_trace_ready=torch.profiler.tensorboard_trace_handler(os.path.join(\n                    self.cfg.logger.tracker_root,\n                    self.cfg.experiment.parent, self.cfg.experiment.child,\n                )),\n                record_shapes=True,\n                profile_memory=True,\n                with_stack=True,\n            ) if self.cfg.logger.enable_profiler else DummyProfiler()\n            \n            with profiler:\n\n                self.optimizer.zero_grad()\n                for _ in range(self.current_epoch, self.cfg.train.epochs):\n\n                    loader = skipped_loader or self.train_loader\n                    skipped_loader = None\n                    self.train_epoch(pbar=pbar, loader=loader, profiler=profiler)\n                    if self.accelerator.check_trigger():\n                        break\n\n            logger.info(f\"======== Training finished at global step {self.global_step} ========\")\n\n            # final checkpoint and evaluation\n            self.save_checkpoint()\n            self.evaluate()\n\n    @torch.no_grad()\n    @torch.compiler.disable\n    def evaluate(self, epoch: int = None):\n        self.model.eval()\n\n        max_val_batches = self.cfg.val.debug_batches or len(self.val_loader)\n        running_losses = []\n        sample_data, sample_outs = None, None\n\n        for data in tqdm(self.val_loader, disable=(not self.accelerator.is_main_process), total=max_val_batches):\n\n            if len(running_losses) >= max_val_batches:\n                logger.info(f\"======== Early stop validation at {len(running_losses)} batches ========\")\n                break\n\n            outs, loss, loss_pixel, loss_perceptual, loss_tv = self.forward_loss_local_step(data)\n            sample_data, sample_outs = data, outs\n\n            running_losses.append(torch.stack([\n                _loss if _loss is not None else torch.tensor(float('nan'), device=self.device)\n                for _loss in [loss, loss_pixel, loss_perceptual, loss_tv]\n            ]))\n\n        total_losses = self.accelerator.gather(torch.stack(running_losses)).mean(dim=0).cpu()\n        total_loss, total_loss_pixel, total_loss_perceptual, total_loss_tv = total_losses.unbind()\n        total_loss_dict = {\n            'loss': total_loss.item(),\n            'loss_pixel': total_loss_pixel.item(),\n            'loss_perceptual': total_loss_perceptual.item(),\n            'loss_tv': total_loss_tv.item(),\n        }\n\n        if epoch is not None:\n            self.log_scalar_kwargs(\n                epoch=epoch, split='val',\n                **total_loss_dict,\n            )\n            logger.info(\n                f'[VAL EPOCH] {epoch}/{self.cfg.train.epochs}: ' + \\\n                    ', '.join(f'{k}={tqdm.format_num(v)}' for k, v in total_loss_dict.items() if not math.isnan(v))\n            )\n            self.log_image_monitor(\n                epoch=epoch, split='val',\n                renders=sample_outs['images_rgb'][:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n                gts=sample_data['render_image'][:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n            )\n        else:\n            self.log_scalar_kwargs(\n                step=self.global_step, split='val',\n                **total_loss_dict,\n            )\n            logger.info(\n                f'[VAL STEP] {self.global_step}/{self.N_max_global_steps}: ' + \\\n                    ', '.join(f'{k}={tqdm.format_num(v)}' for k, v in total_loss_dict.items() if not math.isnan(v))\n            )\n            self.log_image_monitor(\n                step=self.global_step, split='val',\n                renders=sample_outs['images_rgb'][:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n                gts=sample_data['render_image'][:self.cfg.logger.image_monitor.samples_per_log].cpu(),\n            )\n\n    @Trainer.control('on_main_process')\n    def log_image_monitor(\n        self, epoch: int = None, step: int = None, split: str = None,\n        renders: torch.Tensor = None, gts: torch.Tensor = None,\n        ):\n        M = renders.shape[1]\n        merged = torch.stack([renders, gts], dim=1)[0].view(-1, *renders.shape[2:])\n        renders, gts = renders.view(-1, *renders.shape[2:]), gts.view(-1, *gts.shape[2:])\n        renders, gts, merged = make_grid(renders, nrow=M), make_grid(gts, nrow=M), make_grid(merged, nrow=M)\n        log_type, log_progress = self._get_str_progress(epoch, step)\n        split = f'/{split}' if split else ''\n        self.log_images({\n            f'Images_split{split}/rendered': renders.unsqueeze(0),\n            f'Images_split{split}/gt': gts.unsqueeze(0),\n            f'Images_merged{split}': merged.unsqueeze(0),\n        }, log_progress)\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nimport time\nimport math\nimport argparse\nimport shutil\nimport torch\nimport safetensors\nfrom omegaconf import OmegaConf\nfrom abc import abstractmethod\nfrom contextlib import contextmanager\nfrom accelerate import Accelerator\nfrom accelerate.logging import get_logger\nfrom accelerate.utils import DistributedDataParallelKwargs, ProjectConfiguration, set_seed\n\nfrom openlrm.utils.logging import configure_logger\nfrom openlrm.utils.compile import configure_dynamo\nfrom openlrm.runners.abstract import Runner\n\n\nlogger = get_logger(__name__)\n\n\ndef parse_configs():\n    # Define argparse arguments\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--config', type=str, default='./assets/config.yaml')\n    args, unknown = parser.parse_known_args()\n\n    # Load configuration file\n    cfg = OmegaConf.load(args.config)\n\n    # Override with command-line arguments\n    cli_cfg = OmegaConf.from_cli(unknown)\n    cfg = OmegaConf.merge(cfg, cli_cfg)\n\n    return cfg\n\n\nclass Trainer(Runner):\n\n    def __init__(self):\n        super().__init__()\n\n        self.cfg = parse_configs()\n        self.timestamp = time.strftime(\"%Y%m%d-%H%M%S\")\n\n        self.accelerator = Accelerator(\n            mixed_precision=self.cfg.train.mixed_precision,\n            gradient_accumulation_steps=self.cfg.train.accum_steps,\n            log_with=tuple(self.cfg.logger.trackers),\n            project_config=ProjectConfiguration(\n                logging_dir=self.cfg.logger.tracker_root,\n            ),\n            use_seedable_sampler=True,\n            kwargs_handlers=[\n                DistributedDataParallelKwargs(\n                    find_unused_parameters=self.cfg.train.find_unused_parameters,\n                ),\n            ],\n        )\n        set_seed(self.cfg.experiment.seed, device_specific=True)\n        with self.accelerator.main_process_first():\n            configure_logger(\n                stream_level=self.cfg.logger.stream_level,\n                log_level=self.cfg.logger.log_level,\n                file_path=os.path.join(\n                    self.cfg.logger.log_root,\n                    self.cfg.experiment.parent, self.cfg.experiment.child,\n                    f\"{self.timestamp}.log\",\n                ) if self.accelerator.is_main_process else None,\n            )\n        logger.info(self.accelerator.state, main_process_only=False, in_order=True)\n        configure_dynamo(dict(self.cfg.compile))\n\n        # attributes with defaults\n        self.model : torch.nn.Module = None\n        self.optimizer: torch.optim.Optimizer = None\n        self.scheduler: torch.optim.lr_scheduler.LRScheduler = None\n        self.train_loader: torch.utils.data.DataLoader = None\n        self.val_loader: torch.utils.data.DataLoader = None\n        self.N_max_global_steps: int = None\n        self.N_global_steps_per_epoch: int = None\n        self.global_step: int = 0\n        self.current_epoch: int = 0\n\n    def __enter__(self):\n        self.accelerator.init_trackers(\n            project_name=f\"{self.cfg.experiment.parent}/{self.cfg.experiment.child}\",\n        )\n        self.prepare_everything()\n        self.log_inital_info()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.accelerator.end_training()\n\n    @staticmethod\n    def control(option: str = None, synchronized: bool = False):\n        def decorator(func):\n            def wrapper(self, *args, **kwargs):\n                if option is None or hasattr(self.accelerator, option):\n                    accelerated_func = getattr(self.accelerator, option)(func) if option is not None else func\n                    result = accelerated_func(self, *args, **kwargs)\n                    if synchronized:\n                        self.accelerator.wait_for_everyone()\n                    return result\n                else:\n                    raise AttributeError(f\"Accelerator has no attribute {option}\")\n            return wrapper\n        return decorator\n\n    @contextmanager\n    def exec_in_order(self):\n        for rank in range(self.accelerator.num_processes):\n            try:\n                if self.accelerator.process_index == rank:\n                    yield\n            finally:\n                self.accelerator.wait_for_everyone()\n\n    @property\n    def device(self):\n        return self.accelerator.device\n\n    @property\n    def is_distributed(self) -> bool:\n        return self.accelerator.num_processes > 1\n\n    def prepare_everything(self, is_dist_validation: bool = True):\n        # prepare with accelerator\n        if is_dist_validation:\n            self.model, self.optimizer, self.train_loader, self.val_loader = \\\n                self.accelerator.prepare(\n                    self.model, self.optimizer, self.train_loader, self.val_loader,\n                )\n        else:\n            self.model, self.optimizer, self.train_loader = \\\n                self.accelerator.prepare(\n                    self.model, self.optimizer, self.train_loader,\n                )\n        self.accelerator.register_for_checkpointing(self.scheduler)\n        # prepare stats\n        N_total_batch_size = self.cfg.train.batch_size * self.accelerator.num_processes * self.cfg.train.accum_steps\n        self.N_global_steps_per_epoch = math.ceil(len(self.train_loader) / self.cfg.train.accum_steps)\n        self.N_max_global_steps = self.N_global_steps_per_epoch * self.cfg.train.epochs\n        if self.cfg.train.debug_global_steps is not None:\n            logger.warning(f\"Overriding max global steps from {self.N_max_global_steps} to {self.cfg.train.debug_global_steps}\")\n            self.N_max_global_steps = self.cfg.train.debug_global_steps\n        logger.info(f\"======== Statistics ========\")\n        logger.info(f\"** N_max_global_steps: {self.N_max_global_steps}\")\n        logger.info(f\"** N_total_batch_size: {N_total_batch_size}\")\n        logger.info(f\"** N_epochs: {self.cfg.train.epochs}\")\n        logger.info(f\"** N_global_steps_per_epoch: {self.N_global_steps_per_epoch}\")\n        logger.debug(f\"** Prepared loader length: {len(self.train_loader)}\")\n        logger.info(f\"** Distributed validation: {is_dist_validation}\")\n        logger.info(f\"============================\")\n        logger.info(f\"======== Trainable parameters ========\")\n        logger.info(f\"** Total: {sum(p.numel() for p in self.model.parameters() if p.requires_grad)}\")\n        for sub_name, sub_module in self.accelerator.unwrap_model(self.model).named_children():\n            logger.info(f\"** {sub_name}: {sum(p.numel() for p in sub_module.parameters() if p.requires_grad)}\")\n        logger.info(f\"=====================================\")\n        self.accelerator.wait_for_everyone()\n        # load checkpoint or model\n        self.load_ckpt_or_auto_resume_(self.cfg)\n        # register hooks\n        self.register_hooks()\n\n    @abstractmethod\n    def register_hooks(self):\n        pass\n\n    def auto_resume_(self, cfg) -> bool:\n        ckpt_root = os.path.join(\n            cfg.saver.checkpoint_root,\n            cfg.experiment.parent, cfg.experiment.child,\n        )\n        if not os.path.exists(ckpt_root):\n            return False\n        ckpt_dirs = os.listdir(ckpt_root)\n        if len(ckpt_dirs) == 0:\n            return False\n        ckpt_dirs.sort()\n        latest_ckpt = ckpt_dirs[-1]\n        latest_ckpt_dir = os.path.join(ckpt_root, latest_ckpt)\n        logger.info(f\"======== Auto-resume from {latest_ckpt_dir} ========\")\n        self.accelerator.load_state(latest_ckpt_dir)\n        self.global_step = int(latest_ckpt)\n        self.current_epoch = self.global_step // self.N_global_steps_per_epoch\n        return True\n\n    def load_model_(self, cfg):\n        logger.info(f\"======== Loading model from {cfg.saver.load_model} ========\")\n        safetensors.torch.load_model(\n            self.accelerator.unwrap_model(self.model),\n            cfg.saver.load_model,\n            strict=True,\n        )\n        logger.info(f\"======== Model loaded ========\")\n\n    @control(synchronized=True)\n    def load_ckpt_or_auto_resume_(self, cfg):\n        # auto resume has higher priority, load model from path if auto resume is not available\n        # cfg.saver.auto_resume and cfg.saver.load_model\n        if cfg.saver.auto_resume:\n            successful_resume = self.auto_resume_(cfg)\n            if successful_resume:\n                return\n        if cfg.saver.load_model:\n            successful_load = self.load_model_(cfg)\n            if successful_load:\n                return\n        logger.debug(f\"======== No checkpoint or model is loaded ========\")\n\n    @control('on_main_process', synchronized=True)\n    def save_checkpoint(self):\n        ckpt_dir = os.path.join(\n            self.cfg.saver.checkpoint_root,\n            self.cfg.experiment.parent, self.cfg.experiment.child,\n            f\"{self.global_step:06d}\",\n        )\n        self.accelerator.save_state(output_dir=ckpt_dir, safe_serialization=True)\n        logger.info(f\"======== Saved checkpoint at global step {self.global_step} ========\")\n        # manage stratified checkpoints\n        ckpt_dirs = os.listdir(os.path.dirname(ckpt_dir))\n        ckpt_dirs.sort()\n        max_ckpt = int(ckpt_dirs[-1])\n        ckpt_base = int(self.cfg.saver.checkpoint_keep_level)\n        ckpt_period = self.cfg.saver.checkpoint_global_steps\n        logger.debug(f\"Checkpoint base: {ckpt_base}\")\n        logger.debug(f\"Checkpoint period: {ckpt_period}\")\n        cur_order = ckpt_base ** math.floor(math.log(max_ckpt // ckpt_period, ckpt_base))\n        cur_idx = 0\n        while cur_order > 0:\n            cur_digit = max_ckpt // ckpt_period // cur_order % ckpt_base\n            while cur_idx < len(ckpt_dirs) and int(ckpt_dirs[cur_idx]) // ckpt_period // cur_order % ckpt_base < cur_digit:\n                if int(ckpt_dirs[cur_idx]) // ckpt_period % cur_order != 0:\n                    shutil.rmtree(os.path.join(os.path.dirname(ckpt_dir), ckpt_dirs[cur_idx]))\n                    logger.info(f\"Removed checkpoint {ckpt_dirs[cur_idx]}\")\n                cur_idx += 1\n            cur_order //= ckpt_base\n\n    @property\n    def global_step_in_epoch(self):\n        return self.global_step % self.N_global_steps_per_epoch\n\n    @abstractmethod\n    def _build_model(self):\n        pass\n\n    @abstractmethod\n    def _build_optimizer(self):\n        pass\n\n    @abstractmethod\n    def _build_scheduler(self):\n        pass\n\n    @abstractmethod\n    def _build_dataloader(self):\n        pass\n\n    @abstractmethod\n    def _build_loss_fn(self):\n        pass\n\n    @abstractmethod\n    def train(self):\n        pass\n\n    @abstractmethod\n    def evaluate(self):\n        pass\n\n    @staticmethod\n    def _get_str_progress(epoch: int = None, step: int = None):\n        if epoch is not None:\n            log_type = 'epoch'\n            log_progress = epoch\n        elif step is not None:\n            log_type = 'step'\n            log_progress = step\n        else:\n            raise ValueError('Either epoch or step must be provided')\n        return log_type, log_progress\n\n    @control('on_main_process')\n    def log_scalar_kwargs(self, epoch: int = None, step: int = None, split: str = None, **scalar_kwargs):\n        log_type, log_progress = self._get_str_progress(epoch, step)\n        split = f'/{split}' if split else ''\n        for key, value in scalar_kwargs.items():\n            self.accelerator.log({f'{key}{split}/{log_type}': value}, log_progress)\n\n    @control('on_main_process')\n    def log_images(self, values: dict, step: int | None = None, log_kwargs: dict | None = {}):\n        for tracker in self.accelerator.trackers:\n            if hasattr(tracker, 'log_images'):\n                tracker.log_images(values, step=step, **log_kwargs.get(tracker.name, {}))\n\n    @control('on_main_process')\n    def log_optimizer(self, epoch: int = None, step: int = None, attrs: list[str] = [], group_ids: list[int] = []):\n        log_type, log_progress = self._get_str_progress(epoch, step)\n        assert self.optimizer is not None, 'Optimizer is not initialized'\n        if not attrs:\n            logger.warning('No optimizer attributes are provided, nothing will be logged')\n        if not group_ids:\n            logger.warning('No optimizer group ids are provided, nothing will be logged')\n        for attr in attrs:\n            assert attr in ['lr', 'momentum', 'weight_decay'], f'Invalid optimizer attribute {attr}'\n            for group_id in group_ids:\n                self.accelerator.log({f'opt/{attr}/{group_id}': self.optimizer.param_groups[group_id][attr]}, log_progress)\n\n    @control('on_main_process')\n    def log_inital_info(self):\n        assert self.model is not None, 'Model is not initialized'\n        assert self.optimizer is not None, 'Optimizer is not initialized'\n        assert self.scheduler is not None, 'Scheduler is not initialized'\n        self.accelerator.log({'Config': \"```\\n\" + OmegaConf.to_yaml(self.cfg) + \"\\n```\"})\n        self.accelerator.log({'Model': \"```\\n\" + str(self.model) + \"\\n```\"})\n        self.accelerator.log({'Optimizer': \"```\\n\" + str(self.optimizer) + \"\\n```\"})\n        self.accelerator.log({'Scheduler': \"```\\n\" + str(self.scheduler) + \"\\n```\"})\n\n    def run(self):\n        self.train()\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom .lrm import LRMTrainer\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nfrom abc import abstractmethod\nfrom accelerate import Accelerator\nfrom accelerate.logging import get_logger\n\nfrom openlrm.runners.abstract import Runner\n\n\nlogger = get_logger(__name__)\n\n\nclass Inferrer(Runner):\n\n    EXP_TYPE: str = None\n\n    def __init__(self):\n        super().__init__()\n\n        torch._dynamo.config.disable = True\n        self.accelerator = Accelerator()\n\n        self.model : torch.nn.Module = None\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        pass\n\n    @property\n    def device(self):\n        return self.accelerator.device\n\n    @abstractmethod\n    def _build_model(self, cfg):\n        pass\n\n    @abstractmethod\n    def infer_single(self, *args, **kwargs):\n        pass\n\n    @abstractmethod\n    def infer(self):\n        pass\n\n    def run(self):\n        self.infer()\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport os\nimport argparse\nimport mcubes\nimport trimesh\nimport numpy as np\nfrom PIL import Image\nfrom omegaconf import OmegaConf\nfrom tqdm.auto import tqdm\nfrom accelerate.logging import get_logger\n\nfrom .base_inferrer import Inferrer\nfrom openlrm.datasets.cam_utils import build_camera_principle, build_camera_standard, surrounding_views_linspace, create_intrinsics\nfrom openlrm.utils.logging import configure_logger\nfrom openlrm.runners import REGISTRY_RUNNERS\nfrom openlrm.utils.video import images_to_video\nfrom openlrm.utils.hf_hub import wrap_model_hub\n\n\nlogger = get_logger(__name__)\n\n\ndef parse_configs():\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--config', type=str)\n    parser.add_argument('--infer', type=str)\n    args, unknown = parser.parse_known_args()\n\n    cfg = OmegaConf.create()\n    cli_cfg = OmegaConf.from_cli(unknown)\n\n    # parse from ENV\n    if os.environ.get('APP_INFER') is not None:\n        args.infer = os.environ.get('APP_INFER')\n    if os.environ.get('APP_MODEL_NAME') is not None:\n        cli_cfg.model_name = os.environ.get('APP_MODEL_NAME')\n\n    if args.config is not None:\n        cfg_train = OmegaConf.load(args.config)\n        cfg.source_size = cfg_train.dataset.source_image_res\n        cfg.render_size = cfg_train.dataset.render_image.high\n        _relative_path = os.path.join(cfg_train.experiment.parent, cfg_train.experiment.child, os.path.basename(cli_cfg.model_name).split('_')[-1])\n        cfg.video_dump = os.path.join(\"exps\", 'videos', _relative_path)\n        cfg.mesh_dump = os.path.join(\"exps\", 'meshes', _relative_path)\n\n    if args.infer is not None:\n        cfg_infer = OmegaConf.load(args.infer)\n        cfg.merge_with(cfg_infer)\n        cfg.setdefault('video_dump', os.path.join(\"dumps\", cli_cfg.model_name, 'videos'))\n        cfg.setdefault('mesh_dump', os.path.join(\"dumps\", cli_cfg.model_name, 'meshes'))\n\n    cfg.merge_with(cli_cfg)\n\n    \"\"\"\n    [required]\n    model_name: str\n    image_input: str\n    export_video: bool\n    export_mesh: bool\n\n    [special]\n    source_size: int\n    render_size: int\n    video_dump: str\n    mesh_dump: str\n\n    [default]\n    render_views: int\n    render_fps: int\n    mesh_size: int\n    mesh_thres: float\n    frame_size: int\n    logger: str\n    \"\"\"\n\n    cfg.setdefault('logger', 'INFO')\n\n    # assert not (args.config is not None and args.infer is not None), \"Only one of config and infer should be provided\"\n    assert cfg.model_name is not None, \"model_name is required\"\n    if not os.environ.get('APP_ENABLED', None):\n        assert cfg.image_input is not None, \"image_input is required\"\n        assert cfg.export_video or cfg.export_mesh, \\\n            \"At least one of export_video or export_mesh should be True\"\n        cfg.app_enabled = False\n    else:\n        cfg.app_enabled = True\n\n    return cfg\n\n\n@REGISTRY_RUNNERS.register('infer.lrm')\nclass LRMInferrer(Inferrer):\n\n    EXP_TYPE: str = 'lrm'\n\n    def __init__(self):\n        super().__init__()\n\n        self.cfg = parse_configs()\n        configure_logger(\n            stream_level=self.cfg.logger,\n            log_level=self.cfg.logger,\n        )\n\n        self.model = self._build_model(self.cfg).to(self.device)\n\n    def _build_model(self, cfg):\n        from openlrm.models import model_dict\n        hf_model_cls = wrap_model_hub(model_dict[self.EXP_TYPE])\n        model = hf_model_cls.from_pretrained(cfg.model_name)\n        return model\n\n    def _default_source_camera(self, dist_to_center: float = 2.0, batch_size: int = 1, device: torch.device = torch.device('cpu')):\n        # return: (N, D_cam_raw)\n        canonical_camera_extrinsics = torch.tensor([[\n            [1, 0, 0, 0],\n            [0, 0, -1, -dist_to_center],\n            [0, 1, 0, 0],\n        ]], dtype=torch.float32, device=device)\n        canonical_camera_intrinsics = create_intrinsics(\n            f=0.75,\n            c=0.5,\n            device=device,\n        ).unsqueeze(0)\n        source_camera = build_camera_principle(canonical_camera_extrinsics, canonical_camera_intrinsics)\n        return source_camera.repeat(batch_size, 1)\n\n    def _default_render_cameras(self, n_views: int, batch_size: int = 1, device: torch.device = torch.device('cpu')):\n        # return: (N, M, D_cam_render)\n        render_camera_extrinsics = surrounding_views_linspace(n_views=n_views, device=device)\n        render_camera_intrinsics = create_intrinsics(\n            f=0.75,\n            c=0.5,\n            device=device,\n        ).unsqueeze(0).repeat(render_camera_extrinsics.shape[0], 1, 1)\n        render_cameras = build_camera_standard(render_camera_extrinsics, render_camera_intrinsics)\n        return render_cameras.unsqueeze(0).repeat(batch_size, 1, 1)\n\n    def infer_planes(self, image: torch.Tensor, source_cam_dist: float):\n        N = image.shape[0]\n        source_camera = self._default_source_camera(dist_to_center=source_cam_dist, batch_size=N, device=self.device)\n        planes = self.model.forward_planes(image, source_camera)\n        assert N == planes.shape[0]\n        return planes\n\n    def infer_video(self, planes: torch.Tensor, frame_size: int, render_size: int, render_views: int, render_fps: int, dump_video_path: str):\n        N = planes.shape[0]\n        render_cameras = self._default_render_cameras(n_views=render_views, batch_size=N, device=self.device)\n        render_anchors = torch.zeros(N, render_cameras.shape[1], 2, device=self.device)\n        render_resolutions = torch.ones(N, render_cameras.shape[1], 1, device=self.device) * render_size\n        render_bg_colors = torch.ones(N, render_cameras.shape[1], 1, device=self.device, dtype=torch.float32) * 1.\n\n        frames = []\n        for i in range(0, render_cameras.shape[1], frame_size):\n            frames.append(\n                self.model.synthesizer(\n                    planes=planes,\n                    cameras=render_cameras[:, i:i+frame_size],\n                    anchors=render_anchors[:, i:i+frame_size],\n                    resolutions=render_resolutions[:, i:i+frame_size],\n                    bg_colors=render_bg_colors[:, i:i+frame_size],\n                    region_size=render_size,\n                )\n            )\n        # merge frames\n        frames = {\n            k: torch.cat([r[k] for r in frames], dim=1)\n            for k in frames[0].keys()\n        }\n        # dump\n        os.makedirs(os.path.dirname(dump_video_path), exist_ok=True)\n        for k, v in frames.items():\n            if k == 'images_rgb':\n                images_to_video(\n                    images=v[0],\n                    output_path=dump_video_path,\n                    fps=render_fps,\n                    gradio_codec=self.cfg.app_enabled,\n                )\n\n    def infer_mesh(self, planes: torch.Tensor, mesh_size: int, mesh_thres: float, dump_mesh_path: str):\n        grid_out = self.model.synthesizer.forward_grid(\n            planes=planes,\n            grid_size=mesh_size,\n        )\n        \n        vtx, faces = mcubes.marching_cubes(grid_out['sigma'].squeeze(0).squeeze(-1).cpu().numpy(), mesh_thres)\n        vtx = vtx / (mesh_size - 1) * 2 - 1\n\n        vtx_tensor = torch.tensor(vtx, dtype=torch.float32, device=self.device).unsqueeze(0)\n        vtx_colors = self.model.synthesizer.forward_points(planes, vtx_tensor)['rgb'].squeeze(0).cpu().numpy()  # (0, 1)\n        vtx_colors = (vtx_colors * 255).astype(np.uint8)\n        \n        mesh = trimesh.Trimesh(vertices=vtx, faces=faces, vertex_colors=vtx_colors)\n\n        # dump\n        os.makedirs(os.path.dirname(dump_mesh_path), exist_ok=True)\n        mesh.export(dump_mesh_path)\n\n    def infer_single(self, image_path: str, source_cam_dist: float, export_video: bool, export_mesh: bool, dump_video_path: str, dump_mesh_path: str):\n        source_size = self.cfg.source_size\n        render_size = self.cfg.render_size\n        render_views = self.cfg.render_views\n        render_fps = self.cfg.render_fps\n        mesh_size = self.cfg.mesh_size\n        mesh_thres = self.cfg.mesh_thres\n        frame_size = self.cfg.frame_size\n        source_cam_dist = self.cfg.source_cam_dist if source_cam_dist is None else source_cam_dist\n\n        # prepare image: [1, C_img, H_img, W_img], 0-1 scale\n        image = torch.from_numpy(np.array(Image.open(image_path))).to(self.device)\n        image = image.permute(2, 0, 1).unsqueeze(0) / 255.0\n        if image.shape[1] == 4:  # RGBA\n            image = image[:, :3, ...] * image[:, 3:, ...] + (1 - image[:, 3:, ...])\n        image = torch.nn.functional.interpolate(image, size=(source_size, source_size), mode='bicubic', align_corners=True)\n        image = torch.clamp(image, 0, 1)\n\n        with torch.no_grad():\n            planes = self.infer_planes(image, source_cam_dist=source_cam_dist)\n\n            results = {}\n            if export_video:\n                frames = self.infer_video(planes, frame_size=frame_size, render_size=render_size, render_views=render_views, render_fps=render_fps, dump_video_path=dump_video_path)\n                results.update({\n                    'frames': frames,\n                })\n            if export_mesh:\n                mesh = self.infer_mesh(planes, mesh_size=mesh_size, mesh_thres=mesh_thres, dump_mesh_path=dump_mesh_path)\n                results.update({\n                    'mesh': mesh,\n                })\n\n    def infer(self):\n\n        image_paths = []\n        if os.path.isfile(self.cfg.image_input):\n            omit_prefix = os.path.dirname(self.cfg.image_input)\n            image_paths.append(self.cfg.image_input)\n        else:\n            omit_prefix = self.cfg.image_input\n            for root, dirs, files in os.walk(self.cfg.image_input):\n                for file in files:\n                    if file.endswith('.png'):\n                        image_paths.append(os.path.join(root, file))\n            image_paths.sort()\n\n        # alloc to each DDP worker\n        image_paths = image_paths[self.accelerator.process_index::self.accelerator.num_processes]\n\n        for image_path in tqdm(image_paths, disable=not self.accelerator.is_local_main_process):\n\n            # prepare dump paths\n            image_name = os.path.basename(image_path)\n            uid = image_name.split('.')[0]\n            subdir_path = os.path.dirname(image_path).replace(omit_prefix, '')\n            subdir_path = subdir_path[1:] if subdir_path.startswith('/') else subdir_path\n            dump_video_path = os.path.join(\n                self.cfg.video_dump,\n                subdir_path,\n                f'{uid}.mov',\n            )\n            dump_mesh_path = os.path.join(\n                self.cfg.mesh_dump,\n                subdir_path,\n                f'{uid}.ply',\n            )\n\n            self.infer_single(\n                image_path,\n                source_cam_dist=None,\n                export_video=self.cfg.export_video,\n                export_mesh=self.cfg.export_mesh,\n                dump_video_path=dump_video_path,\n                dump_mesh_path=dump_mesh_path,\n            )\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom .lrm import LRMInferrer\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\n\n__all__ = ['TVLoss']\n\n\nclass TVLoss(nn.Module):\n    \"\"\"\n    Total variance loss.\n    \"\"\"\n\n    def __init__(self):\n        super().__init__()\n\n    def numel_excluding_first_dim(self, x):\n        return x.numel() // x.shape[0]\n\n    @torch.compile\n    def forward(self, x):\n        \"\"\"\n        Assume batched and channel first with inner sizes.\n\n        Args:\n            x: [N, M, C, H, W]\n\n        Returns:\n            Mean-reduced TV loss with element-level scaling.\n        \"\"\"\n        N, M, C, H, W = x.shape\n        x = x.reshape(N*M, C, H, W)\n        diff_i = x[..., 1:, :] - x[..., :-1, :]\n        diff_j = x[..., :, 1:] - x[..., :, :-1]\n        div_i = self.numel_excluding_first_dim(diff_i)\n        div_j = self.numel_excluding_first_dim(diff_j)\n        tv_i = diff_i.pow(2).sum(dim=[1,2,3]) / div_i\n        tv_j = diff_j.pow(2).sum(dim=[1,2,3]) / div_j\n        tv = tv_i + tv_j\n        batch_tv = tv.reshape(N, M).mean(dim=1)\n        all_tv = batch_tv.mean()\n        return all_tv\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\n\n__all__ = ['LPIPSLoss']\n\n\nclass LPIPSLoss(nn.Module):\n    \"\"\"\n    Compute LPIPS loss between two images.\n    \"\"\"\n\n    def __init__(self, device, prefech: bool = False):\n        super().__init__()\n        self.device = device\n        self.cached_models = {}\n        if prefech:\n            self.prefetch_models()\n\n    def _get_model(self, model_name: str):\n        if model_name not in self.cached_models:\n            import warnings\n            with warnings.catch_warnings():\n                warnings.filterwarnings('ignore', category=UserWarning)\n                import lpips\n                _model = lpips.LPIPS(net=model_name, eval_mode=True, verbose=False).to(self.device)\n            _model = torch.compile(_model)\n            self.cached_models[model_name] = _model\n        return self.cached_models[model_name]\n\n    def prefetch_models(self):\n        _model_names = ['alex', 'vgg']\n        for model_name in _model_names:\n            self._get_model(model_name)\n\n    def forward(self, x, y, is_training: bool = True):\n        \"\"\"\n        Assume images are 0-1 scaled and channel first.\n        \n        Args:\n            x: [N, M, C, H, W]\n            y: [N, M, C, H, W]\n            is_training: whether to use VGG or AlexNet.\n        \n        Returns:\n            Mean-reduced LPIPS loss across batch.\n        \"\"\"\n        model_name = 'vgg' if is_training else 'alex'\n        loss_fn = self._get_model(model_name)\n        N, M, C, H, W = x.shape\n        x = x.reshape(N*M, C, H, W)\n        y = y.reshape(N*M, C, H, W)\n        image_loss = loss_fn(x, y, normalize=True).mean(dim=[1, 2, 3])\n        batch_loss = image_loss.reshape(N, M).mean(dim=1)\n        all_loss = batch_loss.mean()\n        return all_loss\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\n\n__all__ = ['PixelLoss']\n\n\nclass PixelLoss(nn.Module):\n    \"\"\"\n    Pixel-wise loss between two images.\n    \"\"\"\n\n    def __init__(self, option: str = 'mse'):\n        super().__init__()\n        self.loss_fn = self._build_from_option(option)\n\n    @staticmethod\n    def _build_from_option(option: str, reduction: str = 'none'):\n        if option == 'mse':\n            return nn.MSELoss(reduction=reduction)\n        elif option == 'l1':\n            return nn.L1Loss(reduction=reduction)\n        else:\n            raise NotImplementedError(f'Unknown pixel loss option: {option}')\n\n    @torch.compile\n    def forward(self, x, y):\n        \"\"\"\n        Assume images are channel first.\n        \n        Args:\n            x: [N, M, C, H, W]\n            y: [N, M, C, H, W]\n        \n        Returns:\n            Mean-reduced pixel loss across batch.\n        \"\"\"\n        N, M, C, H, W = x.shape\n        x = x.reshape(N*M, C, H, W)\n        y = y.reshape(N*M, C, H, W)\n        image_loss = self.loss_fn(x, y).mean(dim=[1, 2, 3])\n        batch_loss = image_loss.reshape(N, M).mean(dim=1)\n        all_loss = batch_loss.mean()\n        return all_loss\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom .pixelwise import *\nfrom .perceptual import *\nfrom .tvloss import *\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\nfrom accelerate.logging import get_logger\n\nfrom .embedder import CameraEmbedder\nfrom .transformer import TransformerDecoder\nfrom .rendering.synthesizer import TriplaneSynthesizer\n\n\nlogger = get_logger(__name__)\n\n\nclass ModelLRM(nn.Module):\n    \"\"\"\n    Full model of the basic single-view large reconstruction model.\n    \"\"\"\n    def __init__(self, camera_embed_dim: int, rendering_samples_per_ray: int,\n                 transformer_dim: int, transformer_layers: int, transformer_heads: int,\n                 triplane_low_res: int, triplane_high_res: int, triplane_dim: int,\n                 encoder_freeze: bool = True, encoder_type: str = 'dino',\n                 encoder_model_name: str = 'facebook/dino-vitb16', encoder_feat_dim: int = 768):\n        super().__init__()\n        \n        # attributes\n        self.encoder_feat_dim = encoder_feat_dim\n        self.camera_embed_dim = camera_embed_dim\n        self.triplane_low_res = triplane_low_res\n        self.triplane_high_res = triplane_high_res\n        self.triplane_dim = triplane_dim\n\n        # modules\n        self.encoder = self._encoder_fn(encoder_type)(\n            model_name=encoder_model_name,\n            freeze=encoder_freeze,\n        )\n        self.camera_embedder = CameraEmbedder(\n            raw_dim=12+4, embed_dim=camera_embed_dim,\n        )\n        # initialize pos_embed with 1/sqrt(dim) * N(0, 1)\n        self.pos_embed = nn.Parameter(torch.randn(1, 3*triplane_low_res**2, transformer_dim) * (1. / transformer_dim) ** 0.5)\n        self.transformer = TransformerDecoder(\n            block_type='cond_mod',\n            num_layers=transformer_layers, num_heads=transformer_heads,\n            inner_dim=transformer_dim, cond_dim=encoder_feat_dim, mod_dim=camera_embed_dim,\n        )\n        self.upsampler = nn.ConvTranspose2d(transformer_dim, triplane_dim, kernel_size=2, stride=2, padding=0)\n        self.synthesizer = TriplaneSynthesizer(\n            triplane_dim=triplane_dim, samples_per_ray=rendering_samples_per_ray,\n        )\n\n    @staticmethod\n    def _encoder_fn(encoder_type: str):\n        encoder_type = encoder_type.lower()\n        assert encoder_type in ['dino', 'dinov2'], \"Unsupported encoder type\"\n        if encoder_type == 'dino':\n            from .encoders.dino_wrapper import DinoWrapper\n            logger.info(\"Using DINO as the encoder\")\n            return DinoWrapper\n        elif encoder_type == 'dinov2':\n            from .encoders.dinov2_wrapper import Dinov2Wrapper\n            logger.info(\"Using DINOv2 as the encoder\")\n            return Dinov2Wrapper\n\n    def forward_transformer(self, image_feats, camera_embeddings):\n        assert image_feats.shape[0] == camera_embeddings.shape[0], \\\n            \"Batch size mismatch for image_feats and camera_embeddings!\"\n        N = image_feats.shape[0]\n        x = self.pos_embed.repeat(N, 1, 1)  # [N, L, D]\n        x = self.transformer(\n            x,\n            cond=image_feats,\n            mod=camera_embeddings,\n        )\n        return x\n\n    def reshape_upsample(self, tokens):\n        N = tokens.shape[0]\n        H = W = self.triplane_low_res\n        x = tokens.view(N, 3, H, W, -1)\n        x = torch.einsum('nihwd->indhw', x)  # [3, N, D, H, W]\n        x = x.contiguous().view(3*N, -1, H, W)  # [3*N, D, H, W]\n        x = self.upsampler(x)  # [3*N, D', H', W']\n        x = x.view(3, N, *x.shape[-3:])  # [3, N, D', H', W']\n        x = torch.einsum('indhw->nidhw', x)  # [N, 3, D', H', W']\n        x = x.contiguous()\n        return x\n\n    @torch.compile\n    def forward_planes(self, image, camera):\n        # image: [N, C_img, H_img, W_img]\n        # camera: [N, D_cam_raw]\n        N = image.shape[0]\n\n        # encode image\n        image_feats = self.encoder(image)\n        assert image_feats.shape[-1] == self.encoder_feat_dim, \\\n            f\"Feature dimension mismatch: {image_feats.shape[-1]} vs {self.encoder_feat_dim}\"\n\n        # embed camera\n        camera_embeddings = self.camera_embedder(camera)\n        assert camera_embeddings.shape[-1] == self.camera_embed_dim, \\\n            f\"Feature dimension mismatch: {camera_embeddings.shape[-1]} vs {self.camera_embed_dim}\"\n\n        # transformer generating planes\n        tokens = self.forward_transformer(image_feats, camera_embeddings)\n        planes = self.reshape_upsample(tokens)\n        assert planes.shape[0] == N, \"Batch size mismatch for planes\"\n        assert planes.shape[1] == 3, \"Planes should have 3 channels\"\n\n        return planes\n\n    def forward(self, image, source_camera, render_cameras, render_anchors, render_resolutions, render_bg_colors, render_region_size: int):\n        # image: [N, C_img, H_img, W_img]\n        # source_camera: [N, D_cam_raw]\n        # render_cameras: [N, M, D_cam_render]\n        # render_anchors: [N, M, 2]\n        # render_resolutions: [N, M, 1]\n        # render_bg_colors: [N, M, 1]\n        # render_region_size: int\n        assert image.shape[0] == source_camera.shape[0], \"Batch size mismatch for image and source_camera\"\n        assert image.shape[0] == render_cameras.shape[0], \"Batch size mismatch for image and render_cameras\"\n        assert image.shape[0] == render_anchors.shape[0], \"Batch size mismatch for image and render_anchors\"\n        assert image.shape[0] == render_bg_colors.shape[0], \"Batch size mismatch for image and render_bg_colors\"\n        N, M = render_cameras.shape[:2]\n\n        planes = self.forward_planes(image, source_camera)\n\n        # render target views\n        render_results = self.synthesizer(planes, render_cameras, render_anchors, render_resolutions, render_bg_colors, render_region_size)\n        assert render_results['images_rgb'].shape[0] == N, \"Batch size mismatch for render_results\"\n        assert render_results['images_rgb'].shape[1] == M, \"Number of rendered views should be consistent with render_cameras\"\n\n        return {\n            'planes': planes,\n            **render_results,\n        }\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\n\n\nclass CameraEmbedder(nn.Module):\n    \"\"\"\n    Embed camera features to a high-dimensional vector.\n    \n    Reference:\n    DiT: https://github.com/facebookresearch/DiT/blob/main/models.py#L27\n    \"\"\"\n    def __init__(self, raw_dim: int, embed_dim: int):\n        super().__init__()\n        self.mlp = nn.Sequential(\n            nn.Linear(raw_dim, embed_dim),\n            nn.SiLU(),\n            nn.Linear(embed_dim, embed_dim),\n        )\n\n    @torch.compile\n    def forward(self, x):\n        return self.mlp(x)\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom functools import partial\nimport torch\nimport torch.nn as nn\nfrom accelerate.logging import get_logger\n\n\nlogger = get_logger(__name__)\n\n\nclass TransformerDecoder(nn.Module):\n\n    \"\"\"\n    Transformer blocks that process the input and optionally use condition and modulation.\n    \"\"\"\n\n    def __init__(self, block_type: str,\n                 num_layers: int, num_heads: int,\n                 inner_dim: int, cond_dim: int = None, mod_dim: int = None,\n                 eps: float = 1e-6):\n        super().__init__()\n        self.block_type = block_type\n        self.layers = nn.ModuleList([\n            self._block_fn(inner_dim, cond_dim, mod_dim)(\n                num_heads=num_heads,\n                eps=eps,\n            )\n            for _ in range(num_layers)\n        ])\n        self.norm = nn.LayerNorm(inner_dim, eps=eps)\n\n    @property\n    def block_type(self):\n        return self._block_type\n\n    @block_type.setter\n    def block_type(self, block_type):\n        assert block_type in ['basic', 'cond', 'mod', 'cond_mod'], \\\n            f\"Unsupported block type: {block_type}\"\n        self._block_type = block_type\n\n    def _block_fn(self, inner_dim, cond_dim, mod_dim):\n        assert inner_dim is not None, f\"inner_dim must always be specified\"\n        if self.block_type == 'basic':\n            assert cond_dim is None and mod_dim is None, \\\n                f\"Condition and modulation are not supported for BasicBlock\"\n            from .block import BasicBlock\n            logger.debug(f\"Using BasicBlock\")\n            return partial(BasicBlock, inner_dim=inner_dim)\n        elif self.block_type == 'cond':\n            assert cond_dim is not None, f\"Condition dimension must be specified for ConditionBlock\"\n            assert mod_dim is None, f\"Modulation dimension is not supported for ConditionBlock\"\n            from .block import ConditionBlock\n            logger.debug(f\"Using ConditionBlock\")\n            return partial(ConditionBlock, inner_dim=inner_dim, cond_dim=cond_dim)\n        elif self.block_type == 'mod':\n            logger.error(f\"modulation without condition is not implemented\")\n            raise NotImplementedError(f\"modulation without condition is not implemented\")\n        elif self.block_type == 'cond_mod':\n            assert cond_dim is not None and mod_dim is not None, \\\n                f\"Condition and modulation dimensions must be specified for ConditionModulationBlock\"\n            from .block import ConditionModulationBlock\n            logger.debug(f\"Using ConditionModulationBlock\")\n            return partial(ConditionModulationBlock, inner_dim=inner_dim, cond_dim=cond_dim, mod_dim=mod_dim)\n        else:\n            raise ValueError(f\"Unsupported block type during runtime: {self.block_type}\")\n\n    def assert_runtime_integrity(self, x: torch.Tensor, cond: torch.Tensor, mod: torch.Tensor):\n        assert x is not None, f\"Input tensor must be specified\"\n        if self.block_type == 'basic':\n            assert cond is None and mod is None, \\\n                f\"Condition and modulation are not supported for BasicBlock\"\n        elif self.block_type == 'cond':\n            assert cond is not None and mod is None, \\\n                f\"Condition must be specified and modulation is not supported for ConditionBlock\"\n        elif self.block_type == 'mod':\n            raise NotImplementedError(f\"modulation without condition is not implemented\")\n        else:\n            assert cond is not None and mod is not None, \\\n                f\"Condition and modulation must be specified for ConditionModulationBlock\"\n\n    def forward_layer(self, layer: nn.Module, x: torch.Tensor, cond: torch.Tensor, mod: torch.Tensor):\n        if self.block_type == 'basic':\n            return layer(x)\n        elif self.block_type == 'cond':\n            return layer(x, cond)\n        elif self.block_type == 'mod':\n            return layer(x, mod)\n        else:\n            return layer(x, cond, mod)\n\n    def forward(self, x: torch.Tensor, cond: torch.Tensor = None, mod: torch.Tensor = None):\n        # x: [N, L, D]\n        # cond: [N, L_cond, D_cond] or None\n        # mod: [N, D_mod] or None\n        self.assert_runtime_integrity(x, cond, mod)\n        for layer in self.layers:\n            x = self.forward_layer(layer, x, cond, mod)\n        x = self.norm(x)\n        return x\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch.nn as nn\n\nfrom .modulate import ModLN\n\n\nclass BasicBlock(nn.Module):\n    \"\"\"\n    Transformer block that is in its simplest form.\n    Designed for PF-LRM architecture.\n    \"\"\"\n    # Block contains a self-attention layer and an MLP\n    def __init__(self, inner_dim: int, num_heads: int, eps: float,\n                 attn_drop: float = 0., attn_bias: bool = False,\n                 mlp_ratio: float = 4., mlp_drop: float = 0.):\n        super().__init__()\n        self.norm1 = nn.LayerNorm(inner_dim, eps=eps)\n        self.self_attn = nn.MultiheadAttention(\n            embed_dim=inner_dim, num_heads=num_heads,\n            dropout=attn_drop, bias=attn_bias, batch_first=True)\n        self.norm2 = nn.LayerNorm(inner_dim, eps=eps)\n        self.mlp = nn.Sequential(\n            nn.Linear(inner_dim, int(inner_dim * mlp_ratio)),\n            nn.GELU(),\n            nn.Dropout(mlp_drop),\n            nn.Linear(int(inner_dim * mlp_ratio), inner_dim),\n            nn.Dropout(mlp_drop),\n        )\n\n    def forward(self, x):\n        # x: [N, L, D]\n        before_sa = self.norm1(x)\n        x = x + self.self_attn(before_sa, before_sa, before_sa, need_weights=False)[0]\n        x = x + self.mlp(self.norm2(x))\n        return x\n\n\nclass ConditionBlock(nn.Module):\n    \"\"\"\n    Transformer block that takes in a cross-attention condition.\n    Designed for SparseLRM architecture.\n    \"\"\"\n    # Block contains a cross-attention layer, a self-attention layer, and an MLP\n    def __init__(self, inner_dim: int, cond_dim: int, num_heads: int, eps: float,\n                 attn_drop: float = 0., attn_bias: bool = False,\n                 mlp_ratio: float = 4., mlp_drop: float = 0.):\n        super().__init__()\n        self.norm1 = nn.LayerNorm(inner_dim, eps=eps)\n        self.cross_attn = nn.MultiheadAttention(\n            embed_dim=inner_dim, num_heads=num_heads, kdim=cond_dim, vdim=cond_dim,\n            dropout=attn_drop, bias=attn_bias, batch_first=True)\n        self.norm2 = nn.LayerNorm(inner_dim, eps=eps)\n        self.self_attn = nn.MultiheadAttention(\n            embed_dim=inner_dim, num_heads=num_heads,\n            dropout=attn_drop, bias=attn_bias, batch_first=True)\n        self.norm3 = nn.LayerNorm(inner_dim, eps=eps)\n        self.mlp = nn.Sequential(\n            nn.Linear(inner_dim, int(inner_dim * mlp_ratio)),\n            nn.GELU(),\n            nn.Dropout(mlp_drop),\n            nn.Linear(int(inner_dim * mlp_ratio), inner_dim),\n            nn.Dropout(mlp_drop),\n        )\n\n    def forward(self, x, cond):\n        # x: [N, L, D]\n        # cond: [N, L_cond, D_cond]\n        x = x + self.cross_attn(self.norm1(x), cond, cond, need_weights=False)[0]\n        before_sa = self.norm2(x)\n        x = x + self.self_attn(before_sa, before_sa, before_sa, need_weights=False)[0]\n        x = x + self.mlp(self.norm3(x))\n        return x\n\n\nclass ConditionModulationBlock(nn.Module):\n    \"\"\"\n    Transformer block that takes in a cross-attention condition and another modulation vector applied to sub-blocks.\n    Designed for raw LRM architecture.\n    \"\"\"\n    # Block contains a cross-attention layer, a self-attention layer, and an MLP\n    def __init__(self, inner_dim: int, cond_dim: int, mod_dim: int, num_heads: int, eps: float,\n                 attn_drop: float = 0., attn_bias: bool = False,\n                 mlp_ratio: float = 4., mlp_drop: float = 0.):\n        super().__init__()\n        self.norm1 = ModLN(inner_dim, mod_dim, eps)\n        self.cross_attn = nn.MultiheadAttention(\n            embed_dim=inner_dim, num_heads=num_heads, kdim=cond_dim, vdim=cond_dim,\n            dropout=attn_drop, bias=attn_bias, batch_first=True)\n        self.norm2 = ModLN(inner_dim, mod_dim, eps)\n        self.self_attn = nn.MultiheadAttention(\n            embed_dim=inner_dim, num_heads=num_heads,\n            dropout=attn_drop, bias=attn_bias, batch_first=True)\n        self.norm3 = ModLN(inner_dim, mod_dim, eps)\n        self.mlp = nn.Sequential(\n            nn.Linear(inner_dim, int(inner_dim * mlp_ratio)),\n            nn.GELU(),\n            nn.Dropout(mlp_drop),\n            nn.Linear(int(inner_dim * mlp_ratio), inner_dim),\n            nn.Dropout(mlp_drop),\n        )\n\n    def forward(self, x, cond, mod):\n        # x: [N, L, D]\n        # cond: [N, L_cond, D_cond]\n        # mod: [N, D_mod]\n        x = x + self.cross_attn(self.norm1(x, mod), cond, cond, need_weights=False)[0]\n        before_sa = self.norm2(x, mod)\n        x = x + self.self_attn(before_sa, before_sa, before_sa, need_weights=False)[0]\n        x = x + self.mlp(self.norm3(x, mod))\n        return x\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\n\n\nclass ModLN(nn.Module):\n    \"\"\"\n    Modulation with adaLN.\n    \n    References:\n    DiT: https://github.com/facebookresearch/DiT/blob/main/models.py#L101\n    \"\"\"\n    def __init__(self, inner_dim: int, mod_dim: int, eps: float):\n        super().__init__()\n        self.norm = nn.LayerNorm(inner_dim, eps=eps)\n        self.mlp = nn.Sequential(\n            nn.SiLU(),\n            nn.Linear(mod_dim, inner_dim * 2),\n        )\n\n    @staticmethod\n    def modulate(x, shift, scale):\n        # x: [N, L, D]\n        # shift, scale: [N, D]\n        return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)\n\n    def forward(self, x: torch.Tensor, mod: torch.Tensor) -> torch.Tensor:\n        shift, scale = self.mlp(mod).chunk(2, dim=-1)  # [N, D]\n        return self.modulate(self.norm(x), shift, scale)  # [N, L, D]\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom .modeling_lrm import ModelLRM\n\n\nmodel_dict = {\n    'lrm': ModelLRM,\n}\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\nfrom accelerate.logging import get_logger\n\n\nlogger = get_logger(__name__)\n\n\nclass Dinov2Wrapper(nn.Module):\n    \"\"\"\n    Dino v2 wrapper using original implementation, hacked with modulation.\n    \"\"\"\n    def __init__(self, model_name: str, modulation_dim: int = None, freeze: bool = True):\n        super().__init__()\n        self.modulation_dim = modulation_dim\n        self.model = self._build_dinov2(model_name, modulation_dim=modulation_dim)\n        if freeze:\n            if modulation_dim is not None:\n                raise ValueError(\"Modulated Dinov2 requires training, freezing is not allowed.\")\n            self._freeze()\n\n    def _freeze(self):\n        logger.warning(f\"======== Freezing Dinov2Wrapper ========\")\n        self.model.eval()\n        for name, param in self.model.named_parameters():\n            param.requires_grad = False\n\n    @staticmethod\n    def _build_dinov2(model_name: str, modulation_dim: int = None, pretrained: bool = True):\n        from importlib import import_module\n        dinov2_hub = import_module(\".dinov2.hub.backbones\", package=__package__)\n        model_fn = getattr(dinov2_hub, model_name)\n        logger.debug(f\"Modulation dim for Dinov2 is {modulation_dim}.\")\n        model = model_fn(modulation_dim=modulation_dim, pretrained=pretrained)\n        return model\n\n    @torch.compile\n    def forward(self, image: torch.Tensor, mod: torch.Tensor = None):\n        # image: [N, C, H, W]\n        # mod: [N, D] or None\n        # RGB image with [0,1] scale and properly sized\n        if self.modulation_dim is None:\n            assert mod is None, \"Unexpected modulation input in dinov2 forward.\"\n            outs = self.model(image, is_training=True)\n        else:\n            assert mod is not None, \"Modulation input is required in modulated dinov2 forward.\"\n            outs = self.model(image, mod=mod, is_training=True)\n        ret = torch.cat([\n            outs[\"x_norm_clstoken\"].unsqueeze(dim=1),\n            outs[\"x_norm_patchtokens\"],\n        ], dim=1)\n        return ret\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Empty\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch\nimport torch.nn as nn\nfrom transformers import ViTImageProcessor, ViTModel\nfrom accelerate.logging import get_logger\n\n\nlogger = get_logger(__name__)\n\n\nclass DinoWrapper(nn.Module):\n    \"\"\"\n    Dino v1 wrapper using huggingface transformer implementation.\n    \"\"\"\n    def __init__(self, model_name: str, freeze: bool = True):\n        super().__init__()\n        self.model, self.processor = self._build_dino(model_name)\n        if freeze:\n            self._freeze()\n\n    @torch.compile\n    def forward_model(self, inputs):\n        return self.model(**inputs, interpolate_pos_encoding=True)\n\n    def forward(self, image):\n        # image: [N, C, H, W], on cpu\n        # RGB image with [0,1] scale and properly sized\n        inputs = self.processor(images=image, return_tensors=\"pt\", do_rescale=False, do_resize=False).to(self.model.device)\n        # This resampling of positional embedding uses bicubic interpolation\n        outputs = self.forward_model(inputs)\n        last_hidden_states = outputs.last_hidden_state\n        return last_hidden_states\n\n    def _freeze(self):\n        logger.warning(f\"======== Freezing DinoWrapper ========\")\n        self.model.eval()\n        for name, param in self.model.named_parameters():\n            param.requires_grad = False\n\n    @staticmethod\n    def _build_dino(model_name: str, proxy_error_retries: int = 3, proxy_error_cooldown: int = 5):\n        import requests\n        try:\n            model = ViTModel.from_pretrained(model_name, add_pooling_layer=False)\n            processor = ViTImageProcessor.from_pretrained(model_name)\n            return model, processor\n        except requests.exceptions.ProxyError as err:\n            if proxy_error_retries > 0:\n                print(f\"Huggingface ProxyError: Retrying ({proxy_error_retries}) in {proxy_error_cooldown} seconds...\")\n                import time\n                time.sleep(proxy_error_cooldown)\n                return DinoWrapper._build_dino(model_name, proxy_error_retries - 1, proxy_error_cooldown)\n            else:\n                raise err\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Empty\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/main/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py\n\n# ******************************************************************************\n#   Code modified by Zexin He in 2023-2024.\n#   Modifications are marked with clearly visible comments\n#   licensed under the Apache License, Version 2.0.\n# ******************************************************************************\n\nfrom functools import partial\nimport math\nimport logging\nfrom typing import Sequence, Tuple, Union, Callable\n\nimport torch\nimport torch.nn as nn\nimport torch.utils.checkpoint\nfrom torch.nn.init import trunc_normal_\n\n# ********** Modified by Zexin He in 2023-2024 **********\n# Avoid using nested tensor for now, deprecating usage of NestedTensorBlock\nfrom ..layers import Mlp, PatchEmbed, SwiGLUFFNFused, MemEffAttention, Block, BlockWithModulation\n# ********************************************************\n\n\nlogger = logging.getLogger(\"dinov2\")\n\n\ndef named_apply(fn: Callable, module: nn.Module, name=\"\", depth_first=True, include_root=False) -> nn.Module:\n    if not depth_first and include_root:\n        fn(module=module, name=name)\n    for child_name, child_module in module.named_children():\n        child_name = \".\".join((name, child_name)) if name else child_name\n        named_apply(fn=fn, module=child_module, name=child_name, depth_first=depth_first, include_root=True)\n    if depth_first and include_root:\n        fn(module=module, name=name)\n    return module\n\n\nclass BlockChunk(nn.ModuleList):\n    def forward(self, x):\n        for b in self:\n            x = b(x)\n        return x\n\n\nclass DinoVisionTransformer(nn.Module):\n    def __init__(\n        self,\n        img_size=224,\n        patch_size=16,\n        in_chans=3,\n        embed_dim=768,\n        depth=12,\n        num_heads=12,\n        mlp_ratio=4.0,\n        qkv_bias=True,\n        ffn_bias=True,\n        proj_bias=True,\n        drop_path_rate=0.0,\n        drop_path_uniform=False,\n        init_values=None,  # for layerscale: None or 0 => no layerscale\n        embed_layer=PatchEmbed,\n        act_layer=nn.GELU,\n        block_fn=Block,\n        # ********** Modified by Zexin He in 2023-2024 **********\n        modulation_dim: int = None,\n        # ********************************************************\n        ffn_layer=\"mlp\",\n        block_chunks=1,\n        num_register_tokens=0,\n        interpolate_antialias=False,\n        interpolate_offset=0.1,\n    ):\n        \"\"\"\n        Args:\n            img_size (int, tuple): input image size\n            patch_size (int, tuple): patch size\n            in_chans (int): number of input channels\n            embed_dim (int): embedding dimension\n            depth (int): depth of transformer\n            num_heads (int): number of attention heads\n            mlp_ratio (int): ratio of mlp hidden dim to embedding dim\n            qkv_bias (bool): enable bias for qkv if True\n            proj_bias (bool): enable bias for proj in attn if True\n            ffn_bias (bool): enable bias for ffn if True\n            drop_path_rate (float): stochastic depth rate\n            drop_path_uniform (bool): apply uniform drop rate across blocks\n            weight_init (str): weight init scheme\n            init_values (float): layer-scale init values\n            embed_layer (nn.Module): patch embedding layer\n            act_layer (nn.Module): MLP activation layer\n            block_fn (nn.Module): transformer block class\n            ffn_layer (str): \"mlp\", \"swiglu\", \"swiglufused\" or \"identity\"\n            block_chunks: (int) split block sequence into block_chunks units for FSDP wrap\n            num_register_tokens: (int) number of extra cls tokens (so-called \"registers\")\n            interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings\n            interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings\n        \"\"\"\n        super().__init__()\n\n        # ********** Modified by Zexin He in 2023-2024 **********\n        block_norm_layer = None\n        if modulation_dim is not None:\n            from ....modulate import ModLN\n            block_norm_layer = partial(ModLN, mod_dim=modulation_dim)\n        else:\n            block_norm_layer = nn.LayerNorm\n        block_norm_layer = partial(block_norm_layer, eps=1e-6)\n        # ********************************************************\n        norm_layer = partial(nn.LayerNorm, eps=1e-6)\n\n        self.num_features = self.embed_dim = embed_dim  # num_features for consistency with other models\n        self.num_tokens = 1\n        self.n_blocks = depth\n        self.num_heads = num_heads\n        self.patch_size = patch_size\n        self.num_register_tokens = num_register_tokens\n        self.interpolate_antialias = interpolate_antialias\n        self.interpolate_offset = interpolate_offset\n\n        self.patch_embed = embed_layer(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)\n        num_patches = self.patch_embed.num_patches\n\n        self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))\n        self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))\n        assert num_register_tokens >= 0\n        self.register_tokens = (\n            nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) if num_register_tokens else None\n        )\n\n        if drop_path_uniform is True:\n            dpr = [drop_path_rate] * depth\n        else:\n            dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)]  # stochastic depth decay rule\n\n        if ffn_layer == \"mlp\":\n            logger.info(\"using MLP layer as FFN\")\n            ffn_layer = Mlp\n        elif ffn_layer == \"swiglufused\" or ffn_layer == \"swiglu\":\n            logger.info(\"using SwiGLU layer as FFN\")\n            ffn_layer = SwiGLUFFNFused\n        elif ffn_layer == \"identity\":\n            logger.info(\"using Identity layer as FFN\")\n\n            def f(*args, **kwargs):\n                return nn.Identity()\n\n            ffn_layer = f\n        else:\n            raise NotImplementedError\n\n        blocks_list = [\n            block_fn(\n                dim=embed_dim,\n                num_heads=num_heads,\n                mlp_ratio=mlp_ratio,\n                qkv_bias=qkv_bias,\n                proj_bias=proj_bias,\n                ffn_bias=ffn_bias,\n                drop_path=dpr[i],\n                # ********** Modified by Zexin He in 2023-2024 **********\n                norm_layer=block_norm_layer,\n                # ********************************************************\n                act_layer=act_layer,\n                ffn_layer=ffn_layer,\n                init_values=init_values,\n            )\n            for i in range(depth)\n        ]\n        if block_chunks > 0:\n            self.chunked_blocks = True\n            chunked_blocks = []\n            chunksize = depth // block_chunks\n            for i in range(0, depth, chunksize):\n                # this is to keep the block index consistent if we chunk the block list\n                chunked_blocks.append([nn.Identity()] * i + blocks_list[i : i + chunksize])\n            self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])\n        else:\n            self.chunked_blocks = False\n            self.blocks = nn.ModuleList(blocks_list)\n\n        self.norm = norm_layer(embed_dim)\n        self.head = nn.Identity()\n\n        # ********** Modified by Zexin He in 2023-2024 **********\n        # hacking unused mask_token for better DDP\n        # self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))\n        # ********************************************************\n\n        self.init_weights()\n\n    def init_weights(self):\n        trunc_normal_(self.pos_embed, std=0.02)\n        nn.init.normal_(self.cls_token, std=1e-6)\n        if self.register_tokens is not None:\n            nn.init.normal_(self.register_tokens, std=1e-6)\n        named_apply(init_weights_vit_timm, self)\n\n    def interpolate_pos_encoding(self, x, w, h):\n        previous_dtype = x.dtype\n        npatch = x.shape[1] - 1\n        N = self.pos_embed.shape[1] - 1\n        if npatch == N and w == h:\n            return self.pos_embed\n        pos_embed = self.pos_embed.float()\n        class_pos_embed = pos_embed[:, 0]\n        patch_pos_embed = pos_embed[:, 1:]\n        dim = x.shape[-1]\n        w0 = w // self.patch_size\n        h0 = h // self.patch_size\n        # we add a small number to avoid floating point error in the interpolation\n        # see discussion at https://github.com/facebookresearch/dino/issues/8\n        w0, h0 = w0 + self.interpolate_offset, h0 + self.interpolate_offset\n\n        sqrt_N = math.sqrt(N)\n        sx, sy = float(w0) / sqrt_N, float(h0) / sqrt_N\n        patch_pos_embed = nn.functional.interpolate(\n            patch_pos_embed.reshape(1, int(sqrt_N), int(sqrt_N), dim).permute(0, 3, 1, 2),\n            scale_factor=(sx, sy),\n            mode=\"bicubic\",\n            antialias=self.interpolate_antialias,\n        )\n\n        assert int(w0) == patch_pos_embed.shape[-2]\n        assert int(h0) == patch_pos_embed.shape[-1]\n        patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)\n        return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(previous_dtype)\n\n    def prepare_tokens_with_masks(self, x, masks=None):\n        B, nc, w, h = x.shape\n        x = self.patch_embed(x)\n        if masks is not None:\n            # ********** Modified by Zexin He in 2023-2024 **********\n            raise NotImplementedError(\"Masking is not supported in hacked DINOv2\")\n            # x = torch.where(masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x)\n            # ********************************************************\n\n        x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)\n        x = x + self.interpolate_pos_encoding(x, w, h)\n\n        if self.register_tokens is not None:\n            x = torch.cat(\n                (\n                    x[:, :1],\n                    self.register_tokens.expand(x.shape[0], -1, -1),\n                    x[:, 1:],\n                ),\n                dim=1,\n            )\n\n        return x\n\n    def forward_features_list(self, x_list, masks_list):\n        x = [self.prepare_tokens_with_masks(x, masks) for x, masks in zip(x_list, masks_list)]\n        for blk in self.blocks:\n            x = blk(x)\n\n        all_x = x\n        output = []\n        for x, masks in zip(all_x, masks_list):\n            x_norm = self.norm(x)\n            output.append(\n                {\n                    \"x_norm_clstoken\": x_norm[:, 0],\n                    \"x_norm_regtokens\": x_norm[:, 1 : self.num_register_tokens + 1],\n                    \"x_norm_patchtokens\": x_norm[:, self.num_register_tokens + 1 :],\n                    \"x_prenorm\": x,\n                    \"masks\": masks,\n                }\n            )\n        return output\n\n    # ********** Modified by Zexin He in 2023-2024 **********\n    def forward_features(self, x, masks=None, mod=None):\n        if isinstance(x, list):\n            raise DeprecationWarning(\"forward_features_list is deprecated, use forward_features\")\n            return self.forward_features_list(x, masks)\n\n        x = self.prepare_tokens_with_masks(x, masks)\n\n        if mod is None:\n            for blk in self.blocks:\n                x = blk(x)\n        else:\n            for blk in self.blocks:\n                x = blk(x, mod)\n\n        x_norm = self.norm(x)\n        return {\n            \"x_norm_clstoken\": x_norm[:, 0],\n            \"x_norm_regtokens\": x_norm[:, 1 : self.num_register_tokens + 1],\n            \"x_norm_patchtokens\": x_norm[:, self.num_register_tokens + 1 :],\n            \"x_prenorm\": x,\n            \"masks\": masks,\n        }\n    # ********************************************************\n\n    def _get_intermediate_layers_not_chunked(self, x, n=1):\n        x = self.prepare_tokens_with_masks(x)\n        # If n is an int, take the n last blocks. If it's a list, take them\n        output, total_block_len = [], len(self.blocks)\n        blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n\n        for i, blk in enumerate(self.blocks):\n            x = blk(x)\n            if i in blocks_to_take:\n                output.append(x)\n        assert len(output) == len(blocks_to_take), f\"only {len(output)} / {len(blocks_to_take)} blocks found\"\n        return output\n\n    def _get_intermediate_layers_chunked(self, x, n=1):\n        x = self.prepare_tokens_with_masks(x)\n        output, i, total_block_len = [], 0, len(self.blocks[-1])\n        # If n is an int, take the n last blocks. If it's a list, take them\n        blocks_to_take = range(total_block_len - n, total_block_len) if isinstance(n, int) else n\n        for block_chunk in self.blocks:\n            for blk in block_chunk[i:]:  # Passing the nn.Identity()\n                x = blk(x)\n                if i in blocks_to_take:\n                    output.append(x)\n                i += 1\n        assert len(output) == len(blocks_to_take), f\"only {len(output)} / {len(blocks_to_take)} blocks found\"\n        return output\n\n    def get_intermediate_layers(\n        self,\n        x: torch.Tensor,\n        n: Union[int, Sequence] = 1,  # Layers or n last layers to take\n        reshape: bool = False,\n        return_class_token: bool = False,\n        norm=True,\n    ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:\n        if self.chunked_blocks:\n            outputs = self._get_intermediate_layers_chunked(x, n)\n        else:\n            outputs = self._get_intermediate_layers_not_chunked(x, n)\n        if norm:\n            outputs = [self.norm(out) for out in outputs]\n        class_tokens = [out[:, 0] for out in outputs]\n        outputs = [out[:, 1 + self.num_register_tokens:] for out in outputs]\n        if reshape:\n            B, _, w, h = x.shape\n            outputs = [\n                out.reshape(B, w // self.patch_size, h // self.patch_size, -1).permute(0, 3, 1, 2).contiguous()\n                for out in outputs\n            ]\n        if return_class_token:\n            return tuple(zip(outputs, class_tokens))\n        return tuple(outputs)\n\n    def forward(self, *args, is_training=False, **kwargs):\n        ret = self.forward_features(*args, **kwargs)\n        if is_training:\n            return ret\n        else:\n            return self.head(ret[\"x_norm_clstoken\"])\n\n\ndef init_weights_vit_timm(module: nn.Module, name: str = \"\"):\n    \"\"\"ViT weight initialization, original timm impl (for reproducibility)\"\"\"\n    if isinstance(module, nn.Linear):\n        trunc_normal_(module.weight, std=0.02)\n        if module.bias is not None:\n            nn.init.zeros_(module.bias)\n\n\n# ********** Modified by Zexin He in 2023-2024 **********\n# block class selected from Block and BlockWithModulation\n\ndef _block_cls(**kwargs):\n    modulation_dim = kwargs.get(\"modulation_dim\", None)\n    if modulation_dim is None:\n        block_cls = Block\n    else:\n        block_cls = BlockWithModulation\n    return block_cls\n\n\ndef vit_small(patch_size=16, num_register_tokens=0, **kwargs):\n    model = DinoVisionTransformer(\n        patch_size=patch_size,\n        embed_dim=384,\n        depth=12,\n        num_heads=6,\n        mlp_ratio=4,\n        block_fn=partial(_block_cls(**kwargs), attn_class=MemEffAttention),\n        num_register_tokens=num_register_tokens,\n        **kwargs,\n    )\n    return model\n\n\ndef vit_base(patch_size=16, num_register_tokens=0, **kwargs):\n    model = DinoVisionTransformer(\n        patch_size=patch_size,\n        embed_dim=768,\n        depth=12,\n        num_heads=12,\n        mlp_ratio=4,\n        block_fn=partial(_block_cls(**kwargs), attn_class=MemEffAttention),\n        num_register_tokens=num_register_tokens,\n        **kwargs,\n    )\n    return model\n\n\ndef vit_large(patch_size=16, num_register_tokens=0, **kwargs):\n    model = DinoVisionTransformer(\n        patch_size=patch_size,\n        embed_dim=1024,\n        depth=24,\n        num_heads=16,\n        mlp_ratio=4,\n        block_fn=partial(_block_cls(**kwargs), attn_class=MemEffAttention),\n        num_register_tokens=num_register_tokens,\n        **kwargs,\n    )\n    return model\n\n\ndef vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):\n    \"\"\"\n    Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64\n    \"\"\"\n    model = DinoVisionTransformer(\n        patch_size=patch_size,\n        embed_dim=1536,\n        depth=40,\n        num_heads=24,\n        mlp_ratio=4,\n        block_fn=partial(_block_cls(**kwargs), attn_class=MemEffAttention),\n        num_register_tokens=num_register_tokens,\n        **kwargs,\n    )\n    return model\n\n# ********************************************************\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport logging\n\nfrom . import vision_transformer as vits\n\n\nlogger = logging.getLogger(\"dinov2\")\n\n\ndef build_model(args, only_teacher=False, img_size=224):\n    args.arch = args.arch.removesuffix(\"_memeff\")\n    if \"vit\" in args.arch:\n        vit_kwargs = dict(\n            img_size=img_size,\n            patch_size=args.patch_size,\n            init_values=args.layerscale,\n            ffn_layer=args.ffn_layer,\n            block_chunks=args.block_chunks,\n            qkv_bias=args.qkv_bias,\n            proj_bias=args.proj_bias,\n            ffn_bias=args.ffn_bias,\n            num_register_tokens=args.num_register_tokens,\n            interpolate_offset=args.interpolate_offset,\n            interpolate_antialias=args.interpolate_antialias,\n        )\n        teacher = vits.__dict__[args.arch](**vit_kwargs)\n        if only_teacher:\n            return teacher, teacher.embed_dim\n        student = vits.__dict__[args.arch](\n            **vit_kwargs,\n            drop_path_rate=args.drop_path_rate,\n            drop_path_uniform=args.drop_path_uniform,\n        )\n        embed_dim = student.embed_dim\n    return student, teacher, embed_dim\n\n\ndef build_model_from_cfg(cfg, only_teacher=False):\n    return build_model(cfg.student, only_teacher=only_teacher, img_size=cfg.crops.global_crops_size)\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/master/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py\n\nimport logging\nimport os\nimport warnings\n\nfrom torch import Tensor\nfrom torch import nn\n\n\nlogger = logging.getLogger(\"dinov2\")\n\n\nXFORMERS_ENABLED = os.environ.get(\"XFORMERS_DISABLED\") is None\ntry:\n    if XFORMERS_ENABLED:\n        from xformers.ops import memory_efficient_attention, unbind\n\n        XFORMERS_AVAILABLE = True\n        warnings.warn(\"xFormers is available (Attention)\")\n    else:\n        warnings.warn(\"xFormers is disabled (Attention)\")\n        raise ImportError\nexcept ImportError:\n    XFORMERS_AVAILABLE = False\n    warnings.warn(\"xFormers is not available (Attention)\")\n\n\nclass Attention(nn.Module):\n    def __init__(\n        self,\n        dim: int,\n        num_heads: int = 8,\n        qkv_bias: bool = False,\n        proj_bias: bool = True,\n        attn_drop: float = 0.0,\n        proj_drop: float = 0.0,\n    ) -> None:\n        super().__init__()\n        self.num_heads = num_heads\n        head_dim = dim // num_heads\n        self.scale = head_dim**-0.5\n\n        self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)\n        self.attn_drop = nn.Dropout(attn_drop)\n        self.proj = nn.Linear(dim, dim, bias=proj_bias)\n        self.proj_drop = nn.Dropout(proj_drop)\n\n    def forward(self, x: Tensor) -> Tensor:\n        B, N, C = x.shape\n        qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)\n\n        q, k, v = qkv[0] * self.scale, qkv[1], qkv[2]\n        attn = q @ k.transpose(-2, -1)\n\n        attn = attn.softmax(dim=-1)\n        attn = self.attn_drop(attn)\n\n        x = (attn @ v).transpose(1, 2).reshape(B, N, C)\n        x = self.proj(x)\n        x = self.proj_drop(x)\n        return x\n\n\nclass MemEffAttention(Attention):\n    def forward(self, x: Tensor, attn_bias=None) -> Tensor:\n        if not XFORMERS_AVAILABLE:\n            if attn_bias is not None:\n                raise AssertionError(\"xFormers is required for using nested tensors\")\n            return super().forward(x)\n\n        B, N, C = x.shape\n        qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)\n\n        q, k, v = unbind(qkv, 2)\n\n        x = memory_efficient_attention(q, k, v, attn_bias=attn_bias)\n        x = x.reshape([B, N, C])\n\n        x = self.proj(x)\n        x = self.proj_drop(x)\n        return x\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# Modified from: https://github.com/huggingface/pytorch-image-models/blob/main/timm/models/vision_transformer.py#L103-L110\n\nfrom typing import Union\n\nimport torch\nfrom torch import Tensor\nfrom torch import nn\n\n\nclass LayerScale(nn.Module):\n    def __init__(\n        self,\n        dim: int,\n        init_values: Union[float, Tensor] = 1e-5,\n        inplace: bool = False,\n    ) -> None:\n        super().__init__()\n        self.inplace = inplace\n        self.gamma = nn.Parameter(init_values * torch.ones(dim))\n\n    def forward(self, x: Tensor) -> Tensor:\n        return x.mul_(self.gamma) if self.inplace else x * self.gamma\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport torch\nimport torch.nn as nn\nfrom torch.nn.init import trunc_normal_\nfrom torch.nn.utils import weight_norm\n\n\nclass DINOHead(nn.Module):\n    def __init__(\n        self,\n        in_dim,\n        out_dim,\n        use_bn=False,\n        nlayers=3,\n        hidden_dim=2048,\n        bottleneck_dim=256,\n        mlp_bias=True,\n    ):\n        super().__init__()\n        nlayers = max(nlayers, 1)\n        self.mlp = _build_mlp(nlayers, in_dim, bottleneck_dim, hidden_dim=hidden_dim, use_bn=use_bn, bias=mlp_bias)\n        self.apply(self._init_weights)\n        self.last_layer = weight_norm(nn.Linear(bottleneck_dim, out_dim, bias=False))\n        self.last_layer.weight_g.data.fill_(1)\n\n    def _init_weights(self, m):\n        if isinstance(m, nn.Linear):\n            trunc_normal_(m.weight, std=0.02)\n            if isinstance(m, nn.Linear) and m.bias is not None:\n                nn.init.constant_(m.bias, 0)\n\n    def forward(self, x):\n        x = self.mlp(x)\n        eps = 1e-6 if x.dtype == torch.float16 else 1e-12\n        x = nn.functional.normalize(x, dim=-1, p=2, eps=eps)\n        x = self.last_layer(x)\n        return x\n\n\ndef _build_mlp(nlayers, in_dim, bottleneck_dim, hidden_dim=None, use_bn=False, bias=True):\n    if nlayers == 1:\n        return nn.Linear(in_dim, bottleneck_dim, bias=bias)\n    else:\n        layers = [nn.Linear(in_dim, hidden_dim, bias=bias)]\n        if use_bn:\n            layers.append(nn.BatchNorm1d(hidden_dim))\n        layers.append(nn.GELU())\n        for _ in range(nlayers - 2):\n            layers.append(nn.Linear(hidden_dim, hidden_dim, bias=bias))\n            if use_bn:\n                layers.append(nn.BatchNorm1d(hidden_dim))\n            layers.append(nn.GELU())\n        layers.append(nn.Linear(hidden_dim, bottleneck_dim, bias=bias))\n        return nn.Sequential(*layers)\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/master/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/mlp.py\n\n\nfrom typing import Callable, Optional\n\nfrom torch import Tensor, nn\n\n\nclass Mlp(nn.Module):\n    def __init__(\n        self,\n        in_features: int,\n        hidden_features: Optional[int] = None,\n        out_features: Optional[int] = None,\n        act_layer: Callable[..., nn.Module] = nn.GELU,\n        drop: float = 0.0,\n        bias: bool = True,\n    ) -> None:\n        super().__init__()\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n        self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)\n        self.act = act_layer()\n        self.fc2 = nn.Linear(hidden_features, out_features, bias=bias)\n        self.drop = nn.Dropout(drop)\n\n    def forward(self, x: Tensor) -> Tensor:\n        x = self.fc1(x)\n        x = self.act(x)\n        x = self.drop(x)\n        x = self.fc2(x)\n        x = self.drop(x)\n        return x\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/master/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py\n\nfrom typing import Callable, Optional, Tuple, Union\n\nfrom torch import Tensor\nimport torch.nn as nn\n\n\ndef make_2tuple(x):\n    if isinstance(x, tuple):\n        assert len(x) == 2\n        return x\n\n    assert isinstance(x, int)\n    return (x, x)\n\n\nclass PatchEmbed(nn.Module):\n    \"\"\"\n    2D image to patch embedding: (B,C,H,W) -> (B,N,D)\n\n    Args:\n        img_size: Image size.\n        patch_size: Patch token size.\n        in_chans: Number of input image channels.\n        embed_dim: Number of linear projection output channels.\n        norm_layer: Normalization layer.\n    \"\"\"\n\n    def __init__(\n        self,\n        img_size: Union[int, Tuple[int, int]] = 224,\n        patch_size: Union[int, Tuple[int, int]] = 16,\n        in_chans: int = 3,\n        embed_dim: int = 768,\n        norm_layer: Optional[Callable] = None,\n        flatten_embedding: bool = True,\n    ) -> None:\n        super().__init__()\n\n        image_HW = make_2tuple(img_size)\n        patch_HW = make_2tuple(patch_size)\n        patch_grid_size = (\n            image_HW[0] // patch_HW[0],\n            image_HW[1] // patch_HW[1],\n        )\n\n        self.img_size = image_HW\n        self.patch_size = patch_HW\n        self.patches_resolution = patch_grid_size\n        self.num_patches = patch_grid_size[0] * patch_grid_size[1]\n\n        self.in_chans = in_chans\n        self.embed_dim = embed_dim\n\n        self.flatten_embedding = flatten_embedding\n\n        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_HW, stride=patch_HW)\n        self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()\n\n    def forward(self, x: Tensor) -> Tensor:\n        _, _, H, W = x.shape\n        patch_H, patch_W = self.patch_size\n\n        assert H % patch_H == 0, f\"Input image height {H} is not a multiple of patch height {patch_H}\"\n        assert W % patch_W == 0, f\"Input image width {W} is not a multiple of patch width: {patch_W}\"\n\n        x = self.proj(x)  # B C H W\n        H, W = x.size(2), x.size(3)\n        x = x.flatten(2).transpose(1, 2)  # B HW C\n        x = self.norm(x)\n        if not self.flatten_embedding:\n            x = x.reshape(-1, H, W, self.embed_dim)  # B H W C\n        return x\n\n    def flops(self) -> float:\n        Ho, Wo = self.patches_resolution\n        flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1])\n        if self.norm is not None:\n            flops += Ho * Wo * self.embed_dim\n        return flops\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport os\nfrom typing import Callable, Optional\nimport warnings\n\nfrom torch import Tensor, nn\nimport torch.nn.functional as F\n\n\nclass SwiGLUFFN(nn.Module):\n    def __init__(\n        self,\n        in_features: int,\n        hidden_features: Optional[int] = None,\n        out_features: Optional[int] = None,\n        act_layer: Callable[..., nn.Module] = None,\n        drop: float = 0.0,\n        bias: bool = True,\n    ) -> None:\n        super().__init__()\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n        self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias)\n        self.w3 = nn.Linear(hidden_features, out_features, bias=bias)\n\n    def forward(self, x: Tensor) -> Tensor:\n        x12 = self.w12(x)\n        x1, x2 = x12.chunk(2, dim=-1)\n        hidden = F.silu(x1) * x2\n        return self.w3(hidden)\n\n\nXFORMERS_ENABLED = os.environ.get(\"XFORMERS_DISABLED\") is None\ntry:\n    if XFORMERS_ENABLED:\n        from xformers.ops import SwiGLU\n\n        XFORMERS_AVAILABLE = True\n        warnings.warn(\"xFormers is available (SwiGLU)\")\n    else:\n        warnings.warn(\"xFormers is disabled (SwiGLU)\")\n        raise ImportError\nexcept ImportError:\n    SwiGLU = SwiGLUFFN\n    XFORMERS_AVAILABLE = False\n\n    warnings.warn(\"xFormers is not available (SwiGLU)\")\n\n\nclass SwiGLUFFNFused(SwiGLU):\n    def __init__(\n        self,\n        in_features: int,\n        hidden_features: Optional[int] = None,\n        out_features: Optional[int] = None,\n        act_layer: Callable[..., nn.Module] = None,\n        drop: float = 0.0,\n        bias: bool = True,\n    ) -> None:\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n        hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8\n        super().__init__(\n            in_features=in_features,\n            hidden_features=hidden_features,\n            out_features=out_features,\n            bias=bias,\n        )\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/master/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/patch_embed.py\n\n# ******************************************************************************\n#   Code modified by Zexin He in 2023-2024.\n#   Modifications are marked with clearly visible comments\n#   licensed under the Apache License, Version 2.0.\n# ******************************************************************************\n\nimport logging\nimport os\nfrom typing import Callable, List, Any, Tuple, Dict\nimport warnings\n\nimport torch\nfrom torch import nn, Tensor\n\nfrom .attention import Attention, MemEffAttention\nfrom .drop_path import DropPath\nfrom .layer_scale import LayerScale\nfrom .mlp import Mlp\n\n\nlogger = logging.getLogger(\"dinov2\")\n\n\nXFORMERS_ENABLED = os.environ.get(\"XFORMERS_DISABLED\") is None\ntry:\n    if XFORMERS_ENABLED:\n        from xformers.ops import fmha, scaled_index_add, index_select_cat\n\n        XFORMERS_AVAILABLE = True\n        warnings.warn(\"xFormers is available (Block)\")\n    else:\n        warnings.warn(\"xFormers is disabled (Block)\")\n        raise ImportError\nexcept ImportError:\n    XFORMERS_AVAILABLE = False\n\n    warnings.warn(\"xFormers is not available (Block)\")\n\n\nclass Block(nn.Module):\n    def __init__(\n        self,\n        dim: int,\n        num_heads: int,\n        mlp_ratio: float = 4.0,\n        qkv_bias: bool = False,\n        proj_bias: bool = True,\n        ffn_bias: bool = True,\n        drop: float = 0.0,\n        attn_drop: float = 0.0,\n        init_values=None,\n        drop_path: float = 0.0,\n        act_layer: Callable[..., nn.Module] = nn.GELU,\n        norm_layer: Callable[..., nn.Module] = nn.LayerNorm,\n        attn_class: Callable[..., nn.Module] = Attention,\n        ffn_layer: Callable[..., nn.Module] = Mlp,\n    ) -> None:\n        super().__init__()\n        # print(f\"biases: qkv: {qkv_bias}, proj: {proj_bias}, ffn: {ffn_bias}\")\n        self.norm1 = norm_layer(dim)\n        self.attn = attn_class(\n            dim,\n            num_heads=num_heads,\n            qkv_bias=qkv_bias,\n            proj_bias=proj_bias,\n            attn_drop=attn_drop,\n            proj_drop=drop,\n        )\n        self.ls1 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()\n        self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()\n\n        self.norm2 = norm_layer(dim)\n        mlp_hidden_dim = int(dim * mlp_ratio)\n        self.mlp = ffn_layer(\n            in_features=dim,\n            hidden_features=mlp_hidden_dim,\n            act_layer=act_layer,\n            drop=drop,\n            bias=ffn_bias,\n        )\n        self.ls2 = LayerScale(dim, init_values=init_values) if init_values else nn.Identity()\n        self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()\n\n        self.sample_drop_ratio = drop_path\n\n    def forward(self, x: Tensor) -> Tensor:\n        def attn_residual_func(x: Tensor) -> Tensor:\n            return self.ls1(self.attn(self.norm1(x)))\n\n        def ffn_residual_func(x: Tensor) -> Tensor:\n            return self.ls2(self.mlp(self.norm2(x)))\n\n        if self.training and self.sample_drop_ratio > 0.1:\n            # the overhead is compensated only for a drop path rate larger than 0.1\n            x = drop_add_residual_stochastic_depth(\n                x,\n                residual_func=attn_residual_func,\n                sample_drop_ratio=self.sample_drop_ratio,\n            )\n            x = drop_add_residual_stochastic_depth(\n                x,\n                residual_func=ffn_residual_func,\n                sample_drop_ratio=self.sample_drop_ratio,\n            )\n        elif self.training and self.sample_drop_ratio > 0.0:\n            x = x + self.drop_path1(attn_residual_func(x))\n            x = x + self.drop_path1(ffn_residual_func(x))  # FIXME: drop_path2\n        else:\n            x = x + attn_residual_func(x)\n            x = x + ffn_residual_func(x)\n        return x\n\n\n# ********** Modified by Zexin He in 2023-2024 **********\n# Override forward with modulation input\nclass BlockWithModulation(Block):\n    def __init__(self, *args, **kwargs) -> None:\n        super().__init__(*args, **kwargs)\n\n    def forward(self, x: Tensor, mod: Tensor) -> Tensor:\n        def attn_residual_func(x: Tensor, mod: Tensor) -> Tensor:\n            return self.ls1(self.attn(self.norm1(x, mod)))\n\n        def ffn_residual_func(x: Tensor, mod: Tensor) -> Tensor:\n            return self.ls2(self.mlp(self.norm2(x, mod)))\n\n        if self.training and self.sample_drop_ratio > 0.1:\n            raise NotImplementedError(\"Modulation with drop path ratio larger than 0.1 is not supported yet\")\n        elif self.training and self.sample_drop_ratio > 0.0:\n            x = x + self.drop_path1(attn_residual_func(x, mod))\n            x = x + self.drop_path1(ffn_residual_func(x, mod))  # FIXME: drop_path2\n        else:\n            x = x + attn_residual_func(x, mod)\n            x = x + ffn_residual_func(x, mod)\n        return x\n# ********************************************************\n\n\ndef drop_add_residual_stochastic_depth(\n    x: Tensor,\n    residual_func: Callable[[Tensor], Tensor],\n    sample_drop_ratio: float = 0.0,\n) -> Tensor:\n    # 1) extract subset using permutation\n    b, n, d = x.shape\n    sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)\n    brange = (torch.randperm(b, device=x.device))[:sample_subset_size]\n    x_subset = x[brange]\n\n    # 2) apply residual_func to get residual\n    residual = residual_func(x_subset)\n\n    x_flat = x.flatten(1)\n    residual = residual.flatten(1)\n\n    residual_scale_factor = b / sample_subset_size\n\n    # 3) add the residual\n    x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)\n    return x_plus_residual.view_as(x)\n\n\ndef get_branges_scales(x, sample_drop_ratio=0.0):\n    b, n, d = x.shape\n    sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1)\n    brange = (torch.randperm(b, device=x.device))[:sample_subset_size]\n    residual_scale_factor = b / sample_subset_size\n    return brange, residual_scale_factor\n\n\ndef add_residual(x, brange, residual, residual_scale_factor, scaling_vector=None):\n    if scaling_vector is None:\n        x_flat = x.flatten(1)\n        residual = residual.flatten(1)\n        x_plus_residual = torch.index_add(x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor)\n    else:\n        x_plus_residual = scaled_index_add(\n            x, brange, residual.to(dtype=x.dtype), scaling=scaling_vector, alpha=residual_scale_factor\n        )\n    return x_plus_residual\n\n\nattn_bias_cache: Dict[Tuple, Any] = {}\n\n\ndef get_attn_bias_and_cat(x_list, branges=None):\n    \"\"\"\n    this will perform the index select, cat the tensors, and provide the attn_bias from cache\n    \"\"\"\n    batch_sizes = [b.shape[0] for b in branges] if branges is not None else [x.shape[0] for x in x_list]\n    all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list))\n    if all_shapes not in attn_bias_cache.keys():\n        seqlens = []\n        for b, x in zip(batch_sizes, x_list):\n            for _ in range(b):\n                seqlens.append(x.shape[1])\n        attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens)\n        attn_bias._batch_sizes = batch_sizes\n        attn_bias_cache[all_shapes] = attn_bias\n\n    if branges is not None:\n        cat_tensors = index_select_cat([x.flatten(1) for x in x_list], branges).view(1, -1, x_list[0].shape[-1])\n    else:\n        tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list)\n        cat_tensors = torch.cat(tensors_bs1, dim=1)\n\n    return attn_bias_cache[all_shapes], cat_tensors\n\n\ndef drop_add_residual_stochastic_depth_list(\n    x_list: List[Tensor],\n    residual_func: Callable[[Tensor, Any], Tensor],\n    sample_drop_ratio: float = 0.0,\n    scaling_vector=None,\n) -> Tensor:\n    # 1) generate random set of indices for dropping samples in the batch\n    branges_scales = [get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list]\n    branges = [s[0] for s in branges_scales]\n    residual_scale_factors = [s[1] for s in branges_scales]\n\n    # 2) get attention bias and index+concat the tensors\n    attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges)\n\n    # 3) apply residual_func to get residual, and split the result\n    residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias))  # type: ignore\n\n    outputs = []\n    for x, brange, residual, residual_scale_factor in zip(x_list, branges, residual_list, residual_scale_factors):\n        outputs.append(add_residual(x, brange, residual, residual_scale_factor, scaling_vector).view_as(x))\n    return outputs\n\n\nclass NestedTensorBlock(Block):\n\n    # ********** Modified by Zexin He in 2023-2024 **********\n    warnings.warn(\"NestedTensorBlock is deprecated for now!\", DeprecationWarning)\n    # ********************************************************\n\n    def forward_nested(self, x_list: List[Tensor]) -> List[Tensor]:\n        \"\"\"\n        x_list contains a list of tensors to nest together and run\n        \"\"\"\n        assert isinstance(self.attn, MemEffAttention)\n\n        if self.training and self.sample_drop_ratio > 0.0:\n\n            def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:\n                return self.attn(self.norm1(x), attn_bias=attn_bias)\n\n            def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:\n                return self.mlp(self.norm2(x))\n\n            x_list = drop_add_residual_stochastic_depth_list(\n                x_list,\n                residual_func=attn_residual_func,\n                sample_drop_ratio=self.sample_drop_ratio,\n                scaling_vector=self.ls1.gamma if isinstance(self.ls1, LayerScale) else None,\n            )\n            x_list = drop_add_residual_stochastic_depth_list(\n                x_list,\n                residual_func=ffn_residual_func,\n                sample_drop_ratio=self.sample_drop_ratio,\n                scaling_vector=self.ls2.gamma if isinstance(self.ls1, LayerScale) else None,\n            )\n            return x_list\n        else:\n\n            def attn_residual_func(x: Tensor, attn_bias=None) -> Tensor:\n                return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias))\n\n            def ffn_residual_func(x: Tensor, attn_bias=None) -> Tensor:\n                return self.ls2(self.mlp(self.norm2(x)))\n\n            attn_bias, x = get_attn_bias_and_cat(x_list)\n            x = x + attn_residual_func(x, attn_bias=attn_bias)\n            x = x + ffn_residual_func(x)\n            return attn_bias.split(x)\n\n    def forward(self, x_or_x_list):\n        if isinstance(x_or_x_list, Tensor):\n            return super().forward(x_or_x_list)\n        elif isinstance(x_or_x_list, list):\n            if not XFORMERS_AVAILABLE:\n                raise AssertionError(\"xFormers is required for using nested tensors\")\n            return self.forward_nested(x_or_x_list)\n        else:\n            raise AssertionError\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# ******************************************************************************\n#   Code modified by Zexin He in 2023-2024.\n#   Modifications are marked with clearly visible comments\n#   licensed under the Apache License, Version 2.0.\n# ******************************************************************************\n\nfrom .dino_head import DINOHead\nfrom .mlp import Mlp\nfrom .patch_embed import PatchEmbed\nfrom .swiglu_ffn import SwiGLUFFN, SwiGLUFFNFused\n# ********** Modified by Zexin He in 2023-2024 **********\n# Avoid using nested tensor for now, deprecating usage of NestedTensorBlock\nfrom .block import Block, BlockWithModulation\n# ********************************************************\nfrom .attention import MemEffAttention\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n# References:\n#   https://github.com/facebookresearch/dino/blob/master/vision_transformer.py\n#   https://github.com/rwightman/pytorch-image-models/tree/master/timm/layers/drop.py\n\n\nfrom torch import nn\n\n\ndef drop_path(x, drop_prob: float = 0.0, training: bool = False):\n    if drop_prob == 0.0 or not training:\n        return x\n    keep_prob = 1 - drop_prob\n    shape = (x.shape[0],) + (1,) * (x.ndim - 1)  # work with diff dim tensors, not just 2D ConvNets\n    random_tensor = x.new_empty(shape).bernoulli_(keep_prob)\n    if keep_prob > 0.0:\n        random_tensor.div_(keep_prob)\n    output = x * random_tensor\n    return output\n\n\nclass DropPath(nn.Module):\n    \"\"\"Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).\"\"\"\n\n    def __init__(self, drop_prob=None):\n        super(DropPath, self).__init__()\n        self.drop_prob = drop_prob\n\n    def forward(self, x):\n        return drop_path(x, self.drop_prob, self.training)\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport itertools\nimport math\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\n_DINOV2_BASE_URL = \"https://dl.fbaipublicfiles.com/dinov2\"\n\n\ndef _make_dinov2_model_name(arch_name: str, patch_size: int, num_register_tokens: int = 0) -> str:\n    compact_arch_name = arch_name.replace(\"_\", \"\")[:4]\n    registers_suffix = f\"_reg{num_register_tokens}\" if num_register_tokens else \"\"\n    return f\"dinov2_{compact_arch_name}{patch_size}{registers_suffix}\"\n\n\nclass CenterPadding(nn.Module):\n    def __init__(self, multiple):\n        super().__init__()\n        self.multiple = multiple\n\n    def _get_pad(self, size):\n        new_size = math.ceil(size / self.multiple) * self.multiple\n        pad_size = new_size - size\n        pad_size_left = pad_size // 2\n        pad_size_right = pad_size - pad_size_left\n        return pad_size_left, pad_size_right\n\n    @torch.inference_mode()\n    def forward(self, x):\n        pads = list(itertools.chain.from_iterable(self._get_pad(m) for m in x.shape[:1:-1]))\n        output = F.pad(x, pads)\n        return output\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nfrom enum import Enum\nfrom functools import partial\nfrom typing import Optional, Tuple, Union\n\nimport torch\n\nfrom .backbones import _make_dinov2_model\nfrom .depth import BNHead, DepthEncoderDecoder, DPTHead\nfrom .utils import _DINOV2_BASE_URL, _make_dinov2_model_name, CenterPadding\n\n\nclass Weights(Enum):\n    NYU = \"NYU\"\n    KITTI = \"KITTI\"\n\n\ndef _get_depth_range(pretrained: bool, weights: Weights = Weights.NYU) -> Tuple[float, float]:\n    if not pretrained:  # Default\n        return (0.001, 10.0)\n\n    # Pretrained, set according to the training dataset for the provided weights\n    if weights == Weights.KITTI:\n        return (0.001, 80.0)\n\n    if weights == Weights.NYU:\n        return (0.001, 10.0)\n\n    return (0.001, 10.0)\n\n\ndef _make_dinov2_linear_depth_head(\n    *,\n    embed_dim: int,\n    layers: int,\n    min_depth: float,\n    max_depth: float,\n    **kwargs,\n):\n    if layers not in (1, 4):\n        raise AssertionError(f\"Unsupported number of layers: {layers}\")\n\n    if layers == 1:\n        in_index = [0]\n    else:\n        assert layers == 4\n        in_index = [0, 1, 2, 3]\n\n    return BNHead(\n        classify=True,\n        n_bins=256,\n        bins_strategy=\"UD\",\n        norm_strategy=\"linear\",\n        upsample=4,\n        in_channels=[embed_dim] * len(in_index),\n        in_index=in_index,\n        input_transform=\"resize_concat\",\n        channels=embed_dim * len(in_index) * 2,\n        align_corners=False,\n        min_depth=0.001,\n        max_depth=80,\n        loss_decode=(),\n    )\n\n\ndef _make_dinov2_linear_depther(\n    *,\n    arch_name: str = \"vit_large\",\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.NYU,\n    depth_range: Optional[Tuple[float, float]] = None,\n    **kwargs,\n):\n    if layers not in (1, 4):\n        raise AssertionError(f\"Unsupported number of layers: {layers}\")\n    if isinstance(weights, str):\n        try:\n            weights = Weights[weights]\n        except KeyError:\n            raise AssertionError(f\"Unsupported weights: {weights}\")\n\n    if depth_range is None:\n        depth_range = _get_depth_range(pretrained, weights)\n    min_depth, max_depth = depth_range\n\n    backbone = _make_dinov2_model(arch_name=arch_name, pretrained=pretrained, **kwargs)\n\n    embed_dim = backbone.embed_dim\n    patch_size = backbone.patch_size\n    model_name = _make_dinov2_model_name(arch_name, patch_size)\n    linear_depth_head = _make_dinov2_linear_depth_head(\n        embed_dim=embed_dim,\n        layers=layers,\n        min_depth=min_depth,\n        max_depth=max_depth,\n    )\n\n    layer_count = {\n        \"vit_small\": 12,\n        \"vit_base\": 12,\n        \"vit_large\": 24,\n        \"vit_giant2\": 40,\n    }[arch_name]\n\n    if layers == 4:\n        out_index = {\n            \"vit_small\": [2, 5, 8, 11],\n            \"vit_base\": [2, 5, 8, 11],\n            \"vit_large\": [4, 11, 17, 23],\n            \"vit_giant2\": [9, 19, 29, 39],\n        }[arch_name]\n    else:\n        assert layers == 1\n        out_index = [layer_count - 1]\n\n    model = DepthEncoderDecoder(backbone=backbone, decode_head=linear_depth_head)\n    model.backbone.forward = partial(\n        backbone.get_intermediate_layers,\n        n=out_index,\n        reshape=True,\n        return_class_token=True,\n        norm=False,\n    )\n    model.backbone.register_forward_pre_hook(lambda _, x: CenterPadding(patch_size)(x[0]))\n\n    if pretrained:\n        layers_str = str(layers) if layers == 4 else \"\"\n        weights_str = weights.value.lower()\n        url = _DINOV2_BASE_URL + f\"/{model_name}/{model_name}_{weights_str}_linear{layers_str}_head.pth\"\n        checkpoint = torch.hub.load_state_dict_from_url(url, map_location=\"cpu\")\n        if \"state_dict\" in checkpoint:\n            state_dict = checkpoint[\"state_dict\"]\n        model.load_state_dict(state_dict, strict=False)\n\n    return model\n\n\ndef dinov2_vits14_ld(*, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_linear_depther(\n        arch_name=\"vit_small\", layers=layers, pretrained=pretrained, weights=weights, **kwargs\n    )\n\n\ndef dinov2_vitb14_ld(*, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_linear_depther(\n        arch_name=\"vit_base\", layers=layers, pretrained=pretrained, weights=weights, **kwargs\n    )\n\n\ndef dinov2_vitl14_ld(*, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_linear_depther(\n        arch_name=\"vit_large\", layers=layers, pretrained=pretrained, weights=weights, **kwargs\n    )\n\n\ndef dinov2_vitg14_ld(*, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_linear_depther(\n        arch_name=\"vit_giant2\", layers=layers, ffn_layer=\"swiglufused\", pretrained=pretrained, weights=weights, **kwargs\n    )\n\n\ndef _make_dinov2_dpt_depth_head(*, embed_dim: int, min_depth: float, max_depth: float):\n    return DPTHead(\n        in_channels=[embed_dim] * 4,\n        channels=256,\n        embed_dims=embed_dim,\n        post_process_channels=[embed_dim // 2 ** (3 - i) for i in range(4)],\n        readout_type=\"project\",\n        min_depth=min_depth,\n        max_depth=max_depth,\n        loss_decode=(),\n    )\n\n\ndef _make_dinov2_dpt_depther(\n    *,\n    arch_name: str = \"vit_large\",\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.NYU,\n    depth_range: Optional[Tuple[float, float]] = None,\n    **kwargs,\n):\n    if isinstance(weights, str):\n        try:\n            weights = Weights[weights]\n        except KeyError:\n            raise AssertionError(f\"Unsupported weights: {weights}\")\n\n    if depth_range is None:\n        depth_range = _get_depth_range(pretrained, weights)\n    min_depth, max_depth = depth_range\n\n    backbone = _make_dinov2_model(arch_name=arch_name, pretrained=pretrained, **kwargs)\n\n    model_name = _make_dinov2_model_name(arch_name, backbone.patch_size)\n    dpt_depth_head = _make_dinov2_dpt_depth_head(embed_dim=backbone.embed_dim, min_depth=min_depth, max_depth=max_depth)\n\n    out_index = {\n        \"vit_small\": [2, 5, 8, 11],\n        \"vit_base\": [2, 5, 8, 11],\n        \"vit_large\": [4, 11, 17, 23],\n        \"vit_giant2\": [9, 19, 29, 39],\n    }[arch_name]\n\n    model = DepthEncoderDecoder(backbone=backbone, decode_head=dpt_depth_head)\n    model.backbone.forward = partial(\n        backbone.get_intermediate_layers,\n        n=out_index,\n        reshape=True,\n        return_class_token=True,\n        norm=False,\n    )\n    model.backbone.register_forward_pre_hook(lambda _, x: CenterPadding(backbone.patch_size)(x[0]))\n\n    if pretrained:\n        weights_str = weights.value.lower()\n        url = _DINOV2_BASE_URL + f\"/{model_name}/{model_name}_{weights_str}_dpt_head.pth\"\n        checkpoint = torch.hub.load_state_dict_from_url(url, map_location=\"cpu\")\n        if \"state_dict\" in checkpoint:\n            state_dict = checkpoint[\"state_dict\"]\n        model.load_state_dict(state_dict, strict=False)\n\n    return model\n\n\ndef dinov2_vits14_dd(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_dpt_depther(arch_name=\"vit_small\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitb14_dd(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_dpt_depther(arch_name=\"vit_base\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitl14_dd(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_dpt_depther(arch_name=\"vit_large\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitg14_dd(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.NYU, **kwargs):\n    return _make_dinov2_dpt_depther(\n        arch_name=\"vit_giant2\", ffn_layer=\"swiglufused\", pretrained=pretrained, weights=weights, **kwargs\n    )\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nfrom enum import Enum\nfrom typing import Union\n\nimport torch\nimport torch.nn as nn\n\nfrom .backbones import _make_dinov2_model\nfrom .utils import _DINOV2_BASE_URL, _make_dinov2_model_name\n\n\nclass Weights(Enum):\n    IMAGENET1K = \"IMAGENET1K\"\n\n\ndef _make_dinov2_linear_classification_head(\n    *,\n    arch_name: str = \"vit_large\",\n    patch_size: int = 14,\n    embed_dim: int = 1024,\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    num_register_tokens: int = 0,\n    **kwargs,\n):\n    if layers not in (1, 4):\n        raise AssertionError(f\"Unsupported number of layers: {layers}\")\n    if isinstance(weights, str):\n        try:\n            weights = Weights[weights]\n        except KeyError:\n            raise AssertionError(f\"Unsupported weights: {weights}\")\n\n    linear_head = nn.Linear((1 + layers) * embed_dim, 1_000)\n\n    if pretrained:\n        model_base_name = _make_dinov2_model_name(arch_name, patch_size)\n        model_full_name = _make_dinov2_model_name(arch_name, patch_size, num_register_tokens)\n        layers_str = str(layers) if layers == 4 else \"\"\n        url = _DINOV2_BASE_URL + f\"/{model_base_name}/{model_full_name}_linear{layers_str}_head.pth\"\n        state_dict = torch.hub.load_state_dict_from_url(url, map_location=\"cpu\")\n        linear_head.load_state_dict(state_dict, strict=True)\n\n    return linear_head\n\n\nclass _LinearClassifierWrapper(nn.Module):\n    def __init__(self, *, backbone: nn.Module, linear_head: nn.Module, layers: int = 4):\n        super().__init__()\n        self.backbone = backbone\n        self.linear_head = linear_head\n        self.layers = layers\n\n    def forward(self, x):\n        if self.layers == 1:\n            x = self.backbone.forward_features(x)\n            cls_token = x[\"x_norm_clstoken\"]\n            patch_tokens = x[\"x_norm_patchtokens\"]\n            # fmt: off\n            linear_input = torch.cat([\n                cls_token,\n                patch_tokens.mean(dim=1),\n            ], dim=1)\n            # fmt: on\n        elif self.layers == 4:\n            x = self.backbone.get_intermediate_layers(x, n=4, return_class_token=True)\n            # fmt: off\n            linear_input = torch.cat([\n                x[0][1],\n                x[1][1],\n                x[2][1],\n                x[3][1],\n                x[3][0].mean(dim=1),\n            ], dim=1)\n            # fmt: on\n        else:\n            assert False, f\"Unsupported number of layers: {self.layers}\"\n        return self.linear_head(linear_input)\n\n\ndef _make_dinov2_linear_classifier(\n    *,\n    arch_name: str = \"vit_large\",\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    num_register_tokens: int = 0,\n    interpolate_antialias: bool = False,\n    interpolate_offset: float = 0.1,\n    **kwargs,\n):\n    backbone = _make_dinov2_model(\n        arch_name=arch_name,\n        pretrained=pretrained,\n        num_register_tokens=num_register_tokens,\n        interpolate_antialias=interpolate_antialias,\n        interpolate_offset=interpolate_offset,\n        **kwargs,\n    )\n\n    embed_dim = backbone.embed_dim\n    patch_size = backbone.patch_size\n    linear_head = _make_dinov2_linear_classification_head(\n        arch_name=arch_name,\n        patch_size=patch_size,\n        embed_dim=embed_dim,\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=num_register_tokens,\n    )\n\n    return _LinearClassifierWrapper(backbone=backbone, linear_head=linear_head, layers=layers)\n\n\ndef dinov2_vits14_lc(\n    *,\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    **kwargs,\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-S/14 backbone (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_small\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        **kwargs,\n    )\n\n\ndef dinov2_vitb14_lc(\n    *,\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    **kwargs,\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-B/14 backbone (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_base\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        **kwargs,\n    )\n\n\ndef dinov2_vitl14_lc(\n    *,\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    **kwargs,\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-L/14 backbone (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_large\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        **kwargs,\n    )\n\n\ndef dinov2_vitg14_lc(\n    *,\n    layers: int = 4,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.IMAGENET1K,\n    **kwargs,\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-g/14 backbone (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_giant2\",\n        layers=layers,\n        ffn_layer=\"swiglufused\",\n        pretrained=pretrained,\n        weights=weights,\n        **kwargs,\n    )\n\n\ndef dinov2_vits14_reg_lc(\n    *, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.IMAGENET1K, **kwargs\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-S/14 backbone with registers (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_small\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitb14_reg_lc(\n    *, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.IMAGENET1K, **kwargs\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-B/14 backbone with registers (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_base\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitl14_reg_lc(\n    *, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.IMAGENET1K, **kwargs\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-L/14 backbone with registers (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_large\",\n        layers=layers,\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitg14_reg_lc(\n    *, layers: int = 4, pretrained: bool = True, weights: Union[Weights, str] = Weights.IMAGENET1K, **kwargs\n):\n    \"\"\"\n    Linear classifier (1 or 4 layers) on top of a DINOv2 ViT-g/14 backbone with registers (optionally) pretrained on the LVD-142M dataset and trained on ImageNet-1k.\n    \"\"\"\n    return _make_dinov2_linear_classifier(\n        arch_name=\"vit_giant2\",\n        layers=layers,\n        ffn_layer=\"swiglufused\",\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nfrom enum import Enum\nfrom typing import Union\n\nimport torch\n\nfrom .utils import _DINOV2_BASE_URL, _make_dinov2_model_name\n\n\nclass Weights(Enum):\n    LVD142M = \"LVD142M\"\n\n\ndef _make_dinov2_model(\n    *,\n    arch_name: str = \"vit_large\",\n    img_size: int = 518,\n    patch_size: int = 14,\n    init_values: float = 1.0,\n    ffn_layer: str = \"mlp\",\n    block_chunks: int = 0,\n    num_register_tokens: int = 0,\n    interpolate_antialias: bool = False,\n    interpolate_offset: float = 0.1,\n    pretrained: bool = True,\n    weights: Union[Weights, str] = Weights.LVD142M,\n    **kwargs,\n):\n    from ..models import vision_transformer as vits\n\n    if isinstance(weights, str):\n        try:\n            weights = Weights[weights]\n        except KeyError:\n            raise AssertionError(f\"Unsupported weights: {weights}\")\n\n    model_base_name = _make_dinov2_model_name(arch_name, patch_size)\n    vit_kwargs = dict(\n        img_size=img_size,\n        patch_size=patch_size,\n        init_values=init_values,\n        ffn_layer=ffn_layer,\n        block_chunks=block_chunks,\n        num_register_tokens=num_register_tokens,\n        interpolate_antialias=interpolate_antialias,\n        interpolate_offset=interpolate_offset,\n    )\n    vit_kwargs.update(**kwargs)\n    model = vits.__dict__[arch_name](**vit_kwargs)\n\n    if pretrained:\n        model_full_name = _make_dinov2_model_name(arch_name, patch_size, num_register_tokens)\n        url = _DINOV2_BASE_URL + f\"/{model_base_name}/{model_full_name}_pretrain.pth\"\n        state_dict = torch.hub.load_state_dict_from_url(url, map_location=\"cpu\")\n        # ********** Modified by Zexin He in 2023-2024 **********\n        state_dict = {k: v for k, v in state_dict.items() if 'mask_token' not in k}  # DDP concern\n        if vit_kwargs.get(\"modulation_dim\") is not None:\n            state_dict = {\n                k.replace('norm1', 'norm1.norm').replace('norm2', 'norm2.norm'): v\n                for k, v in state_dict.items()\n            }\n            model.load_state_dict(state_dict, strict=False)\n        else:\n            model.load_state_dict(state_dict, strict=True)\n        # ********************************************************\n\n    return model\n\n\ndef dinov2_vits14(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-S/14 model (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(arch_name=\"vit_small\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitb14(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-B/14 model (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(arch_name=\"vit_base\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitl14(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-L/14 model (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(arch_name=\"vit_large\", pretrained=pretrained, weights=weights, **kwargs)\n\n\ndef dinov2_vitg14(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-g/14 model (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(\n        arch_name=\"vit_giant2\",\n        ffn_layer=\"swiglufused\",\n        weights=weights,\n        pretrained=pretrained,\n        **kwargs,\n    )\n\n\ndef dinov2_vits14_reg(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-S/14 model with registers (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(\n        arch_name=\"vit_small\",\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitb14_reg(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-B/14 model with registers (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(\n        arch_name=\"vit_base\",\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitl14_reg(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-L/14 model with registers (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(\n        arch_name=\"vit_large\",\n        pretrained=pretrained,\n        weights=weights,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\ndef dinov2_vitg14_reg(*, pretrained: bool = True, weights: Union[Weights, str] = Weights.LVD142M, **kwargs):\n    \"\"\"\n    DINOv2 ViT-g/14 model with registers (optionally) pretrained on the LVD-142M dataset.\n    \"\"\"\n    return _make_dinov2_model(\n        arch_name=\"vit_giant2\",\n        ffn_layer=\"swiglufused\",\n        weights=weights,\n        pretrained=pretrained,\n        num_register_tokens=4,\n        interpolate_antialias=True,\n        interpolate_offset=0.0,\n        **kwargs,\n    )\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nfrom collections import OrderedDict\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom .ops import resize\n\n\ndef add_prefix(inputs, prefix):\n    \"\"\"Add prefix for dict.\n\n    Args:\n        inputs (dict): The input dict with str keys.\n        prefix (str): The prefix to add.\n\n    Returns:\n\n        dict: The dict with keys updated with ``prefix``.\n    \"\"\"\n\n    outputs = dict()\n    for name, value in inputs.items():\n        outputs[f\"{prefix}.{name}\"] = value\n\n    return outputs\n\n\nclass DepthEncoderDecoder(nn.Module):\n    \"\"\"Encoder Decoder depther.\n\n    EncoderDecoder typically consists of backbone and decode_head.\n    \"\"\"\n\n    def __init__(self, backbone, decode_head):\n        super(DepthEncoderDecoder, self).__init__()\n\n        self.backbone = backbone\n        self.decode_head = decode_head\n        self.align_corners = self.decode_head.align_corners\n\n    def extract_feat(self, img):\n        \"\"\"Extract features from images.\"\"\"\n        return self.backbone(img)\n\n    def encode_decode(self, img, img_metas, rescale=True, size=None):\n        \"\"\"Encode images with backbone and decode into a depth estimation\n        map of the same size as input.\"\"\"\n        x = self.extract_feat(img)\n        out = self._decode_head_forward_test(x, img_metas)\n        # crop the pred depth to the certain range.\n        out = torch.clamp(out, min=self.decode_head.min_depth, max=self.decode_head.max_depth)\n        if rescale:\n            if size is None:\n                if img_metas is not None:\n                    size = img_metas[0][\"ori_shape\"][:2]\n                else:\n                    size = img.shape[2:]\n            out = resize(input=out, size=size, mode=\"bilinear\", align_corners=self.align_corners)\n        return out\n\n    def _decode_head_forward_train(self, img, x, img_metas, depth_gt, **kwargs):\n        \"\"\"Run forward function and calculate loss for decode head in\n        training.\"\"\"\n        losses = dict()\n        loss_decode = self.decode_head.forward_train(img, x, img_metas, depth_gt, **kwargs)\n        losses.update(add_prefix(loss_decode, \"decode\"))\n        return losses\n\n    def _decode_head_forward_test(self, x, img_metas):\n        \"\"\"Run forward function and calculate loss for decode head in\n        inference.\"\"\"\n        depth_pred = self.decode_head.forward_test(x, img_metas)\n        return depth_pred\n\n    def forward_dummy(self, img):\n        \"\"\"Dummy forward function.\"\"\"\n        depth = self.encode_decode(img, None)\n\n        return depth\n\n    def forward_train(self, img, img_metas, depth_gt, **kwargs):\n        \"\"\"Forward function for training.\n\n        Args:\n            img (Tensor): Input images.\n            img_metas (list[dict]): List of image info dict where each dict\n                has: 'img_shape', 'scale_factor', 'flip', and may also contain\n                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n                For details on the values of these keys see\n                `depth/datasets/pipelines/formatting.py:Collect`.\n            depth_gt (Tensor): Depth gt\n                used if the architecture supports depth estimation task.\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n\n        x = self.extract_feat(img)\n\n        losses = dict()\n\n        # the last of x saves the info from neck\n        loss_decode = self._decode_head_forward_train(img, x, img_metas, depth_gt, **kwargs)\n\n        losses.update(loss_decode)\n\n        return losses\n\n    def whole_inference(self, img, img_meta, rescale, size=None):\n        \"\"\"Inference with full image.\"\"\"\n        return self.encode_decode(img, img_meta, rescale, size=size)\n\n    def slide_inference(self, img, img_meta, rescale, stride, crop_size):\n        \"\"\"Inference by sliding-window with overlap.\n\n        If h_crop > h_img or w_crop > w_img, the small patch will be used to\n        decode without padding.\n        \"\"\"\n\n        h_stride, w_stride = stride\n        h_crop, w_crop = crop_size\n        batch_size, _, h_img, w_img = img.size()\n        h_grids = max(h_img - h_crop + h_stride - 1, 0) // h_stride + 1\n        w_grids = max(w_img - w_crop + w_stride - 1, 0) // w_stride + 1\n        preds = img.new_zeros((batch_size, 1, h_img, w_img))\n        count_mat = img.new_zeros((batch_size, 1, h_img, w_img))\n        for h_idx in range(h_grids):\n            for w_idx in range(w_grids):\n                y1 = h_idx * h_stride\n                x1 = w_idx * w_stride\n                y2 = min(y1 + h_crop, h_img)\n                x2 = min(x1 + w_crop, w_img)\n                y1 = max(y2 - h_crop, 0)\n                x1 = max(x2 - w_crop, 0)\n                crop_img = img[:, :, y1:y2, x1:x2]\n                depth_pred = self.encode_decode(crop_img, img_meta, rescale)\n                preds += F.pad(depth_pred, (int(x1), int(preds.shape[3] - x2), int(y1), int(preds.shape[2] - y2)))\n\n                count_mat[:, :, y1:y2, x1:x2] += 1\n        assert (count_mat == 0).sum() == 0\n        if torch.onnx.is_in_onnx_export():\n            # cast count_mat to constant while exporting to ONNX\n            count_mat = torch.from_numpy(count_mat.cpu().detach().numpy()).to(device=img.device)\n        preds = preds / count_mat\n        return preds\n\n    def inference(self, img, img_meta, rescale, size=None, mode=\"whole\"):\n        \"\"\"Inference with slide/whole style.\n\n        Args:\n            img (Tensor): The input image of shape (N, 3, H, W).\n            img_meta (dict): Image info dict where each dict has: 'img_shape',\n                'scale_factor', 'flip', and may also contain\n                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n                For details on the values of these keys see\n                `depth/datasets/pipelines/formatting.py:Collect`.\n            rescale (bool): Whether rescale back to original shape.\n\n        Returns:\n            Tensor: The output depth map.\n        \"\"\"\n\n        assert mode in [\"slide\", \"whole\"]\n        ori_shape = img_meta[0][\"ori_shape\"]\n        assert all(_[\"ori_shape\"] == ori_shape for _ in img_meta)\n        if mode == \"slide\":\n            depth_pred = self.slide_inference(img, img_meta, rescale)\n        else:\n            depth_pred = self.whole_inference(img, img_meta, rescale, size=size)\n        output = depth_pred\n        flip = img_meta[0][\"flip\"]\n        if flip:\n            flip_direction = img_meta[0][\"flip_direction\"]\n            assert flip_direction in [\"horizontal\", \"vertical\"]\n            if flip_direction == \"horizontal\":\n                output = output.flip(dims=(3,))\n            elif flip_direction == \"vertical\":\n                output = output.flip(dims=(2,))\n\n        return output\n\n    def simple_test(self, img, img_meta, rescale=True):\n        \"\"\"Simple test with single image.\"\"\"\n        depth_pred = self.inference(img, img_meta, rescale)\n        if torch.onnx.is_in_onnx_export():\n            # our inference backend only support 4D output\n            depth_pred = depth_pred.unsqueeze(0)\n            return depth_pred\n        depth_pred = depth_pred.cpu().numpy()\n        # unravel batch dim\n        depth_pred = list(depth_pred)\n        return depth_pred\n\n    def aug_test(self, imgs, img_metas, rescale=True):\n        \"\"\"Test with augmentations.\n\n        Only rescale=True is supported.\n        \"\"\"\n        # aug_test rescale all imgs back to ori_shape for now\n        assert rescale\n        # to save memory, we get augmented depth logit inplace\n        depth_pred = self.inference(imgs[0], img_metas[0], rescale)\n        for i in range(1, len(imgs)):\n            cur_depth_pred = self.inference(imgs[i], img_metas[i], rescale, size=depth_pred.shape[-2:])\n            depth_pred += cur_depth_pred\n        depth_pred /= len(imgs)\n        depth_pred = depth_pred.cpu().numpy()\n        # unravel batch dim\n        depth_pred = list(depth_pred)\n        return depth_pred\n\n    def forward_test(self, imgs, img_metas, **kwargs):\n        \"\"\"\n        Args:\n            imgs (List[Tensor]): the outer list indicates test-time\n                augmentations and inner Tensor should have a shape NxCxHxW,\n                which contains all images in the batch.\n            img_metas (List[List[dict]]): the outer list indicates test-time\n                augs (multiscale, flip, etc.) and the inner list indicates\n                images in a batch.\n        \"\"\"\n        for var, name in [(imgs, \"imgs\"), (img_metas, \"img_metas\")]:\n            if not isinstance(var, list):\n                raise TypeError(f\"{name} must be a list, but got \" f\"{type(var)}\")\n        num_augs = len(imgs)\n        if num_augs != len(img_metas):\n            raise ValueError(f\"num of augmentations ({len(imgs)}) != \" f\"num of image meta ({len(img_metas)})\")\n        # all images in the same aug batch all of the same ori_shape and pad\n        # shape\n        for img_meta in img_metas:\n            ori_shapes = [_[\"ori_shape\"] for _ in img_meta]\n            assert all(shape == ori_shapes[0] for shape in ori_shapes)\n            img_shapes = [_[\"img_shape\"] for _ in img_meta]\n            assert all(shape == img_shapes[0] for shape in img_shapes)\n            pad_shapes = [_[\"pad_shape\"] for _ in img_meta]\n            assert all(shape == pad_shapes[0] for shape in pad_shapes)\n\n        if num_augs == 1:\n            return self.simple_test(imgs[0], img_metas[0], **kwargs)\n        else:\n            return self.aug_test(imgs, img_metas, **kwargs)\n\n    def forward(self, img, img_metas, return_loss=True, **kwargs):\n        \"\"\"Calls either :func:`forward_train` or :func:`forward_test` depending\n        on whether ``return_loss`` is ``True``.\n\n        Note this setting will change the expected inputs. When\n        ``return_loss=True``, img and img_meta are single-nested (i.e. Tensor\n        and List[dict]), and when ``resturn_loss=False``, img and img_meta\n        should be double nested (i.e.  List[Tensor], List[List[dict]]), with\n        the outer list indicating test time augmentations.\n        \"\"\"\n        if return_loss:\n            return self.forward_train(img, img_metas, **kwargs)\n        else:\n            return self.forward_test(img, img_metas, **kwargs)\n\n    def train_step(self, data_batch, optimizer, **kwargs):\n        \"\"\"The iteration step during training.\n\n        This method defines an iteration step during training, except for the\n        back propagation and optimizer updating, which are done in an optimizer\n        hook. Note that in some complicated cases or models, the whole process\n        including back propagation and optimizer updating is also defined in\n        this method, such as GAN.\n\n        Args:\n            data (dict): The output of dataloader.\n            optimizer (:obj:`torch.optim.Optimizer` | dict): The optimizer of\n                runner is passed to ``train_step()``. This argument is unused\n                and reserved.\n\n        Returns:\n            dict: It should contain at least 3 keys: ``loss``, ``log_vars``,\n                ``num_samples``.\n                ``loss`` is a tensor for back propagation, which can be a\n                weighted sum of multiple losses.\n                ``log_vars`` contains all the variables to be sent to the\n                logger.\n                ``num_samples`` indicates the batch size (when the model is\n                DDP, it means the batch size on each GPU), which is used for\n                averaging the logs.\n        \"\"\"\n        losses = self(**data_batch)\n\n        # split losses and images\n        real_losses = {}\n        log_imgs = {}\n        for k, v in losses.items():\n            if \"img\" in k:\n                log_imgs[k] = v\n            else:\n                real_losses[k] = v\n\n        loss, log_vars = self._parse_losses(real_losses)\n\n        outputs = dict(loss=loss, log_vars=log_vars, num_samples=len(data_batch[\"img_metas\"]), log_imgs=log_imgs)\n\n        return outputs\n\n    def val_step(self, data_batch, **kwargs):\n        \"\"\"The iteration step during validation.\n\n        This method shares the same signature as :func:`train_step`, but used\n        during val epochs. Note that the evaluation after training epochs is\n        not implemented with this method, but an evaluation hook.\n        \"\"\"\n        output = self(**data_batch, **kwargs)\n        return output\n\n    @staticmethod\n    def _parse_losses(losses):\n        import torch.distributed as dist\n\n        \"\"\"Parse the raw outputs (losses) of the network.\n\n        Args:\n            losses (dict): Raw output of the network, which usually contain\n                losses and other necessary information.\n\n        Returns:\n            tuple[Tensor, dict]: (loss, log_vars), loss is the loss tensor\n                which may be a weighted sum of all losses, log_vars contains\n                all the variables to be sent to the logger.\n        \"\"\"\n        log_vars = OrderedDict()\n        for loss_name, loss_value in losses.items():\n            if isinstance(loss_value, torch.Tensor):\n                log_vars[loss_name] = loss_value.mean()\n            elif isinstance(loss_value, list):\n                log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value)\n            else:\n                raise TypeError(f\"{loss_name} is not a tensor or list of tensors\")\n\n        loss = sum(_value for _key, _value in log_vars.items() if \"loss\" in _key)\n\n        log_vars[\"loss\"] = loss\n        for loss_name, loss_value in log_vars.items():\n            # reduce loss when distributed training\n            if dist.is_available() and dist.is_initialized():\n                loss_value = loss_value.data.clone()\n                dist.all_reduce(loss_value.div_(dist.get_world_size()))\n            log_vars[loss_name] = loss_value.item()\n\n        return loss, log_vars\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport copy\nfrom functools import partial\nimport math\nimport warnings\n\nimport torch\nimport torch.nn as nn\n\nfrom .ops import resize\n\n\n# XXX: (Untested) replacement for mmcv.imdenormalize()\ndef _imdenormalize(img, mean, std, to_bgr=True):\n    import numpy as np\n\n    mean = mean.reshape(1, -1).astype(np.float64)\n    std = std.reshape(1, -1).astype(np.float64)\n    img = (img * std) + mean\n    if to_bgr:\n        img = img[::-1]\n    return img\n\n\nclass DepthBaseDecodeHead(nn.Module):\n    \"\"\"Base class for BaseDecodeHead.\n\n    Args:\n        in_channels (List): Input channels.\n        channels (int): Channels after modules, before conv_depth.\n        conv_layer (nn.Module): Conv layers. Default: None.\n        act_layer (nn.Module): Activation layers. Default: nn.ReLU.\n        loss_decode (dict): Config of decode loss.\n            Default: ().\n        sampler (dict|None): The config of depth map sampler.\n            Default: None.\n        align_corners (bool): align_corners argument of F.interpolate.\n            Default: False.\n        min_depth (int): Min depth in dataset setting.\n            Default: 1e-3.\n        max_depth (int): Max depth in dataset setting.\n            Default: None.\n        norm_layer (dict|None): Norm layers.\n            Default: None.\n        classify (bool): Whether predict depth in a cls.-reg. manner.\n            Default: False.\n        n_bins (int): The number of bins used in cls. step.\n            Default: 256.\n        bins_strategy (str): The discrete strategy used in cls. step.\n            Default: 'UD'.\n        norm_strategy (str): The norm strategy on cls. probability\n            distribution. Default: 'linear'\n        scale_up (str): Whether predict depth in a scale-up manner.\n            Default: False.\n    \"\"\"\n\n    def __init__(\n        self,\n        in_channels,\n        conv_layer=None,\n        act_layer=nn.ReLU,\n        channels=96,\n        loss_decode=(),\n        sampler=None,\n        align_corners=False,\n        min_depth=1e-3,\n        max_depth=None,\n        norm_layer=None,\n        classify=False,\n        n_bins=256,\n        bins_strategy=\"UD\",\n        norm_strategy=\"linear\",\n        scale_up=False,\n    ):\n        super(DepthBaseDecodeHead, self).__init__()\n\n        self.in_channels = in_channels\n        self.channels = channels\n        self.conf_layer = conv_layer\n        self.act_layer = act_layer\n        self.loss_decode = loss_decode\n        self.align_corners = align_corners\n        self.min_depth = min_depth\n        self.max_depth = max_depth\n        self.norm_layer = norm_layer\n        self.classify = classify\n        self.n_bins = n_bins\n        self.scale_up = scale_up\n\n        if self.classify:\n            assert bins_strategy in [\"UD\", \"SID\"], \"Support bins_strategy: UD, SID\"\n            assert norm_strategy in [\"linear\", \"softmax\", \"sigmoid\"], \"Support norm_strategy: linear, softmax, sigmoid\"\n\n            self.bins_strategy = bins_strategy\n            self.norm_strategy = norm_strategy\n            self.softmax = nn.Softmax(dim=1)\n            self.conv_depth = nn.Conv2d(channels, n_bins, kernel_size=3, padding=1, stride=1)\n        else:\n            self.conv_depth = nn.Conv2d(channels, 1, kernel_size=3, padding=1, stride=1)\n\n        self.relu = nn.ReLU()\n        self.sigmoid = nn.Sigmoid()\n\n    def forward(self, inputs, img_metas):\n        \"\"\"Placeholder of forward function.\"\"\"\n        pass\n\n    def forward_train(self, img, inputs, img_metas, depth_gt):\n        \"\"\"Forward function for training.\n        Args:\n            inputs (list[Tensor]): List of multi-level img features.\n            img_metas (list[dict]): List of image info dict where each dict\n                has: 'img_shape', 'scale_factor', 'flip', and may also contain\n                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n                For details on the values of these keys see\n                `depth/datasets/pipelines/formatting.py:Collect`.\n            depth_gt (Tensor): GT depth\n\n        Returns:\n            dict[str, Tensor]: a dictionary of loss components\n        \"\"\"\n        depth_pred = self.forward(inputs, img_metas)\n        losses = self.losses(depth_pred, depth_gt)\n\n        log_imgs = self.log_images(img[0], depth_pred[0], depth_gt[0], img_metas[0])\n        losses.update(**log_imgs)\n\n        return losses\n\n    def forward_test(self, inputs, img_metas):\n        \"\"\"Forward function for testing.\n        Args:\n            inputs (list[Tensor]): List of multi-level img features.\n            img_metas (list[dict]): List of image info dict where each dict\n                has: 'img_shape', 'scale_factor', 'flip', and may also contain\n                'filename', 'ori_shape', 'pad_shape', and 'img_norm_cfg'.\n                For details on the values of these keys see\n                `depth/datasets/pipelines/formatting.py:Collect`.\n\n        Returns:\n            Tensor: Output depth map.\n        \"\"\"\n        return self.forward(inputs, img_metas)\n\n    def depth_pred(self, feat):\n        \"\"\"Prediction each pixel.\"\"\"\n        if self.classify:\n            logit = self.conv_depth(feat)\n\n            if self.bins_strategy == \"UD\":\n                bins = torch.linspace(self.min_depth, self.max_depth, self.n_bins, device=feat.device)\n            elif self.bins_strategy == \"SID\":\n                bins = torch.logspace(self.min_depth, self.max_depth, self.n_bins, device=feat.device)\n\n            # following Adabins, default linear\n            if self.norm_strategy == \"linear\":\n                logit = torch.relu(logit)\n                eps = 0.1\n                logit = logit + eps\n                logit = logit / logit.sum(dim=1, keepdim=True)\n            elif self.norm_strategy == \"softmax\":\n                logit = torch.softmax(logit, dim=1)\n            elif self.norm_strategy == \"sigmoid\":\n                logit = torch.sigmoid(logit)\n                logit = logit / logit.sum(dim=1, keepdim=True)\n\n            output = torch.einsum(\"ikmn,k->imn\", [logit, bins]).unsqueeze(dim=1)\n\n        else:\n            if self.scale_up:\n                output = self.sigmoid(self.conv_depth(feat)) * self.max_depth\n            else:\n                output = self.relu(self.conv_depth(feat)) + self.min_depth\n        return output\n\n    def losses(self, depth_pred, depth_gt):\n        \"\"\"Compute depth loss.\"\"\"\n        loss = dict()\n        depth_pred = resize(\n            input=depth_pred, size=depth_gt.shape[2:], mode=\"bilinear\", align_corners=self.align_corners, warning=False\n        )\n        if not isinstance(self.loss_decode, nn.ModuleList):\n            losses_decode = [self.loss_decode]\n        else:\n            losses_decode = self.loss_decode\n        for loss_decode in losses_decode:\n            if loss_decode.loss_name not in loss:\n                loss[loss_decode.loss_name] = loss_decode(depth_pred, depth_gt)\n            else:\n                loss[loss_decode.loss_name] += loss_decode(depth_pred, depth_gt)\n        return loss\n\n    def log_images(self, img_path, depth_pred, depth_gt, img_meta):\n        import numpy as np\n\n        show_img = copy.deepcopy(img_path.detach().cpu().permute(1, 2, 0))\n        show_img = show_img.numpy().astype(np.float32)\n        show_img = _imdenormalize(\n            show_img,\n            img_meta[\"img_norm_cfg\"][\"mean\"],\n            img_meta[\"img_norm_cfg\"][\"std\"],\n            img_meta[\"img_norm_cfg\"][\"to_rgb\"],\n        )\n        show_img = np.clip(show_img, 0, 255)\n        show_img = show_img.astype(np.uint8)\n        show_img = show_img[:, :, ::-1]\n        show_img = show_img.transpose(0, 2, 1)\n        show_img = show_img.transpose(1, 0, 2)\n\n        depth_pred = depth_pred / torch.max(depth_pred)\n        depth_gt = depth_gt / torch.max(depth_gt)\n\n        depth_pred_color = copy.deepcopy(depth_pred.detach().cpu())\n        depth_gt_color = copy.deepcopy(depth_gt.detach().cpu())\n\n        return {\"img_rgb\": show_img, \"img_depth_pred\": depth_pred_color, \"img_depth_gt\": depth_gt_color}\n\n\nclass BNHead(DepthBaseDecodeHead):\n    \"\"\"Just a batchnorm.\"\"\"\n\n    def __init__(self, input_transform=\"resize_concat\", in_index=(0, 1, 2, 3), upsample=1, **kwargs):\n        super().__init__(**kwargs)\n        self.input_transform = input_transform\n        self.in_index = in_index\n        self.upsample = upsample\n        # self.bn = nn.SyncBatchNorm(self.in_channels)\n        if self.classify:\n            self.conv_depth = nn.Conv2d(self.channels, self.n_bins, kernel_size=1, padding=0, stride=1)\n        else:\n            self.conv_depth = nn.Conv2d(self.channels, 1, kernel_size=1, padding=0, stride=1)\n\n    def _transform_inputs(self, inputs):\n        \"\"\"Transform inputs for decoder.\n        Args:\n            inputs (list[Tensor]): List of multi-level img features.\n        Returns:\n            Tensor: The transformed inputs\n        \"\"\"\n\n        if \"concat\" in self.input_transform:\n            inputs = [inputs[i] for i in self.in_index]\n            if \"resize\" in self.input_transform:\n                inputs = [\n                    resize(\n                        input=x,\n                        size=[s * self.upsample for s in inputs[0].shape[2:]],\n                        mode=\"bilinear\",\n                        align_corners=self.align_corners,\n                    )\n                    for x in inputs\n                ]\n            inputs = torch.cat(inputs, dim=1)\n        elif self.input_transform == \"multiple_select\":\n            inputs = [inputs[i] for i in self.in_index]\n        else:\n            inputs = inputs[self.in_index]\n\n        return inputs\n\n    def _forward_feature(self, inputs, img_metas=None, **kwargs):\n        \"\"\"Forward function for feature maps before classifying each pixel with\n        ``self.cls_seg`` fc.\n        Args:\n            inputs (list[Tensor]): List of multi-level img features.\n        Returns:\n            feats (Tensor): A tensor of shape (batch_size, self.channels,\n                H, W) which is feature map for last layer of decoder head.\n        \"\"\"\n        # accept lists (for cls token)\n        inputs = list(inputs)\n        for i, x in enumerate(inputs):\n            if len(x) == 2:\n                x, cls_token = x[0], x[1]\n                if len(x.shape) == 2:\n                    x = x[:, :, None, None]\n                cls_token = cls_token[:, :, None, None].expand_as(x)\n                inputs[i] = torch.cat((x, cls_token), 1)\n            else:\n                x = x[0]\n                if len(x.shape) == 2:\n                    x = x[:, :, None, None]\n                inputs[i] = x\n        x = self._transform_inputs(inputs)\n        # feats = self.bn(x)\n        return x\n\n    def forward(self, inputs, img_metas=None, **kwargs):\n        \"\"\"Forward function.\"\"\"\n        output = self._forward_feature(inputs, img_metas=img_metas, **kwargs)\n        output = self.depth_pred(output)\n        return output\n\n\nclass ConvModule(nn.Module):\n    \"\"\"A conv block that bundles conv/norm/activation layers.\n\n    This block simplifies the usage of convolution layers, which are commonly\n    used with a norm layer (e.g., BatchNorm) and activation layer (e.g., ReLU).\n    It is based upon three build methods: `build_conv_layer()`,\n    `build_norm_layer()` and `build_activation_layer()`.\n\n    Besides, we add some additional features in this module.\n    1. Automatically set `bias` of the conv layer.\n    2. Spectral norm is supported.\n    3. More padding modes are supported. Before PyTorch 1.5, nn.Conv2d only\n    supports zero and circular padding, and we add \"reflect\" padding mode.\n\n    Args:\n        in_channels (int): Number of channels in the input feature map.\n            Same as that in ``nn._ConvNd``.\n        out_channels (int): Number of channels produced by the convolution.\n            Same as that in ``nn._ConvNd``.\n        kernel_size (int | tuple[int]): Size of the convolving kernel.\n            Same as that in ``nn._ConvNd``.\n        stride (int | tuple[int]): Stride of the convolution.\n            Same as that in ``nn._ConvNd``.\n        padding (int | tuple[int]): Zero-padding added to both sides of\n            the input. Same as that in ``nn._ConvNd``.\n        dilation (int | tuple[int]): Spacing between kernel elements.\n            Same as that in ``nn._ConvNd``.\n        groups (int): Number of blocked connections from input channels to\n            output channels. Same as that in ``nn._ConvNd``.\n        bias (bool | str): If specified as `auto`, it will be decided by the\n            norm_layer. Bias will be set as True if `norm_layer` is None, otherwise\n            False. Default: \"auto\".\n        conv_layer (nn.Module): Convolution layer. Default: None,\n            which means using conv2d.\n        norm_layer (nn.Module): Normalization layer. Default: None.\n        act_layer (nn.Module): Activation layer. Default: nn.ReLU.\n        inplace (bool): Whether to use inplace mode for activation.\n            Default: True.\n        with_spectral_norm (bool): Whether use spectral norm in conv module.\n            Default: False.\n        padding_mode (str): If the `padding_mode` has not been supported by\n            current `Conv2d` in PyTorch, we will use our own padding layer\n            instead. Currently, we support ['zeros', 'circular'] with official\n            implementation and ['reflect'] with our own implementation.\n            Default: 'zeros'.\n        order (tuple[str]): The order of conv/norm/activation layers. It is a\n            sequence of \"conv\", \"norm\" and \"act\". Common examples are\n            (\"conv\", \"norm\", \"act\") and (\"act\", \"conv\", \"norm\").\n            Default: ('conv', 'norm', 'act').\n    \"\"\"\n\n    _abbr_ = \"conv_block\"\n\n    def __init__(\n        self,\n        in_channels,\n        out_channels,\n        kernel_size,\n        stride=1,\n        padding=0,\n        dilation=1,\n        groups=1,\n        bias=\"auto\",\n        conv_layer=nn.Conv2d,\n        norm_layer=None,\n        act_layer=nn.ReLU,\n        inplace=True,\n        with_spectral_norm=False,\n        padding_mode=\"zeros\",\n        order=(\"conv\", \"norm\", \"act\"),\n    ):\n        super(ConvModule, self).__init__()\n        official_padding_mode = [\"zeros\", \"circular\"]\n        self.conv_layer = conv_layer\n        self.norm_layer = norm_layer\n        self.act_layer = act_layer\n        self.inplace = inplace\n        self.with_spectral_norm = with_spectral_norm\n        self.with_explicit_padding = padding_mode not in official_padding_mode\n        self.order = order\n        assert isinstance(self.order, tuple) and len(self.order) == 3\n        assert set(order) == set([\"conv\", \"norm\", \"act\"])\n\n        self.with_norm = norm_layer is not None\n        self.with_activation = act_layer is not None\n        # if the conv layer is before a norm layer, bias is unnecessary.\n        if bias == \"auto\":\n            bias = not self.with_norm\n        self.with_bias = bias\n\n        if self.with_explicit_padding:\n            if padding_mode == \"zeros\":\n                padding_layer = nn.ZeroPad2d\n            else:\n                raise AssertionError(f\"Unsupported padding mode: {padding_mode}\")\n            self.pad = padding_layer(padding)\n\n        # reset padding to 0 for conv module\n        conv_padding = 0 if self.with_explicit_padding else padding\n        # build convolution layer\n        self.conv = self.conv_layer(\n            in_channels,\n            out_channels,\n            kernel_size,\n            stride=stride,\n            padding=conv_padding,\n            dilation=dilation,\n            groups=groups,\n            bias=bias,\n        )\n        # export the attributes of self.conv to a higher level for convenience\n        self.in_channels = self.conv.in_channels\n        self.out_channels = self.conv.out_channels\n        self.kernel_size = self.conv.kernel_size\n        self.stride = self.conv.stride\n        self.padding = padding\n        self.dilation = self.conv.dilation\n        self.transposed = self.conv.transposed\n        self.output_padding = self.conv.output_padding\n        self.groups = self.conv.groups\n\n        if self.with_spectral_norm:\n            self.conv = nn.utils.spectral_norm(self.conv)\n\n        # build normalization layers\n        if self.with_norm:\n            # norm layer is after conv layer\n            if order.index(\"norm\") > order.index(\"conv\"):\n                norm_channels = out_channels\n            else:\n                norm_channels = in_channels\n            norm = partial(norm_layer, num_features=norm_channels)\n            self.add_module(\"norm\", norm)\n            if self.with_bias:\n                from torch.nnModules.batchnorm import _BatchNorm\n                from torch.nnModules.instancenorm import _InstanceNorm\n\n                if isinstance(norm, (_BatchNorm, _InstanceNorm)):\n                    warnings.warn(\"Unnecessary conv bias before batch/instance norm\")\n        else:\n            self.norm_name = None\n\n        # build activation layer\n        if self.with_activation:\n            # nn.Tanh has no 'inplace' argument\n            # (nn.Tanh, nn.PReLU, nn.Sigmoid, nn.HSigmoid, nn.Swish, nn.GELU)\n            if not isinstance(act_layer, (nn.Tanh, nn.PReLU, nn.Sigmoid, nn.GELU)):\n                act_layer = partial(act_layer, inplace=inplace)\n            self.activate = act_layer()\n\n        # Use msra init by default\n        self.init_weights()\n\n    @property\n    def norm(self):\n        if self.norm_name:\n            return getattr(self, self.norm_name)\n        else:\n            return None\n\n    def init_weights(self):\n        # 1. It is mainly for customized conv layers with their own\n        #    initialization manners by calling their own ``init_weights()``,\n        #    and we do not want ConvModule to override the initialization.\n        # 2. For customized conv layers without their own initialization\n        #    manners (that is, they don't have their own ``init_weights()``)\n        #    and PyTorch's conv layers, they will be initialized by\n        #    this method with default ``kaiming_init``.\n        # Note: For PyTorch's conv layers, they will be overwritten by our\n        #    initialization implementation using default ``kaiming_init``.\n        if not hasattr(self.conv, \"init_weights\"):\n            if self.with_activation and isinstance(self.act_layer, nn.LeakyReLU):\n                nonlinearity = \"leaky_relu\"\n                a = 0.01  # XXX: default negative_slope\n            else:\n                nonlinearity = \"relu\"\n                a = 0\n            if hasattr(self.conv, \"weight\") and self.conv.weight is not None:\n                nn.init.kaiming_normal_(self.conv.weight, a=a, mode=\"fan_out\", nonlinearity=nonlinearity)\n            if hasattr(self.conv, \"bias\") and self.conv.bias is not None:\n                nn.init.constant_(self.conv.bias, 0)\n        if self.with_norm:\n            if hasattr(self.norm, \"weight\") and self.norm.weight is not None:\n                nn.init.constant_(self.norm.weight, 1)\n            if hasattr(self.norm, \"bias\") and self.norm.bias is not None:\n                nn.init.constant_(self.norm.bias, 0)\n\n    def forward(self, x, activate=True, norm=True):\n        for layer in self.order:\n            if layer == \"conv\":\n                if self.with_explicit_padding:\n                    x = self.pad(x)\n                x = self.conv(x)\n            elif layer == \"norm\" and norm and self.with_norm:\n                x = self.norm(x)\n            elif layer == \"act\" and activate and self.with_activation:\n                x = self.activate(x)\n        return x\n\n\nclass Interpolate(nn.Module):\n    def __init__(self, scale_factor, mode, align_corners=False):\n        super(Interpolate, self).__init__()\n        self.interp = nn.functional.interpolate\n        self.scale_factor = scale_factor\n        self.mode = mode\n        self.align_corners = align_corners\n\n    def forward(self, x):\n        x = self.interp(x, scale_factor=self.scale_factor, mode=self.mode, align_corners=self.align_corners)\n        return x\n\n\nclass HeadDepth(nn.Module):\n    def __init__(self, features):\n        super(HeadDepth, self).__init__()\n        self.head = nn.Sequential(\n            nn.Conv2d(features, features // 2, kernel_size=3, stride=1, padding=1),\n            Interpolate(scale_factor=2, mode=\"bilinear\", align_corners=True),\n            nn.Conv2d(features // 2, 32, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(32, 1, kernel_size=1, stride=1, padding=0),\n        )\n\n    def forward(self, x):\n        x = self.head(x)\n        return x\n\n\nclass ReassembleBlocks(nn.Module):\n    \"\"\"ViTPostProcessBlock, process cls_token in ViT backbone output and\n    rearrange the feature vector to feature map.\n    Args:\n        in_channels (int): ViT feature channels. Default: 768.\n        out_channels (List): output channels of each stage.\n            Default: [96, 192, 384, 768].\n        readout_type (str): Type of readout operation. Default: 'ignore'.\n        patch_size (int): The patch size. Default: 16.\n    \"\"\"\n\n    def __init__(self, in_channels=768, out_channels=[96, 192, 384, 768], readout_type=\"ignore\", patch_size=16):\n        super(ReassembleBlocks, self).__init__()\n\n        assert readout_type in [\"ignore\", \"add\", \"project\"]\n        self.readout_type = readout_type\n        self.patch_size = patch_size\n\n        self.projects = nn.ModuleList(\n            [\n                ConvModule(\n                    in_channels=in_channels,\n                    out_channels=out_channel,\n                    kernel_size=1,\n                    act_layer=None,\n                )\n                for out_channel in out_channels\n            ]\n        )\n\n        self.resize_layers = nn.ModuleList(\n            [\n                nn.ConvTranspose2d(\n                    in_channels=out_channels[0], out_channels=out_channels[0], kernel_size=4, stride=4, padding=0\n                ),\n                nn.ConvTranspose2d(\n                    in_channels=out_channels[1], out_channels=out_channels[1], kernel_size=2, stride=2, padding=0\n                ),\n                nn.Identity(),\n                nn.Conv2d(\n                    in_channels=out_channels[3], out_channels=out_channels[3], kernel_size=3, stride=2, padding=1\n                ),\n            ]\n        )\n        if self.readout_type == \"project\":\n            self.readout_projects = nn.ModuleList()\n            for _ in range(len(self.projects)):\n                self.readout_projects.append(nn.Sequential(nn.Linear(2 * in_channels, in_channels), nn.GELU()))\n\n    def forward(self, inputs):\n        assert isinstance(inputs, list)\n        out = []\n        for i, x in enumerate(inputs):\n            assert len(x) == 2\n            x, cls_token = x[0], x[1]\n            feature_shape = x.shape\n            if self.readout_type == \"project\":\n                x = x.flatten(2).permute((0, 2, 1))\n                readout = cls_token.unsqueeze(1).expand_as(x)\n                x = self.readout_projects[i](torch.cat((x, readout), -1))\n                x = x.permute(0, 2, 1).reshape(feature_shape)\n            elif self.readout_type == \"add\":\n                x = x.flatten(2) + cls_token.unsqueeze(-1)\n                x = x.reshape(feature_shape)\n            else:\n                pass\n            x = self.projects[i](x)\n            x = self.resize_layers[i](x)\n            out.append(x)\n        return out\n\n\nclass PreActResidualConvUnit(nn.Module):\n    \"\"\"ResidualConvUnit, pre-activate residual unit.\n    Args:\n        in_channels (int): number of channels in the input feature map.\n        act_layer (nn.Module): activation layer.\n        norm_layer (nn.Module): norm layer.\n        stride (int): stride of the first block. Default: 1\n        dilation (int): dilation rate for convs layers. Default: 1.\n    \"\"\"\n\n    def __init__(self, in_channels, act_layer, norm_layer, stride=1, dilation=1):\n        super(PreActResidualConvUnit, self).__init__()\n\n        self.conv1 = ConvModule(\n            in_channels,\n            in_channels,\n            3,\n            stride=stride,\n            padding=dilation,\n            dilation=dilation,\n            norm_layer=norm_layer,\n            act_layer=act_layer,\n            bias=False,\n            order=(\"act\", \"conv\", \"norm\"),\n        )\n\n        self.conv2 = ConvModule(\n            in_channels,\n            in_channels,\n            3,\n            padding=1,\n            norm_layer=norm_layer,\n            act_layer=act_layer,\n            bias=False,\n            order=(\"act\", \"conv\", \"norm\"),\n        )\n\n    def forward(self, inputs):\n        inputs_ = inputs.clone()\n        x = self.conv1(inputs)\n        x = self.conv2(x)\n        return x + inputs_\n\n\nclass FeatureFusionBlock(nn.Module):\n    \"\"\"FeatureFusionBlock, merge feature map from different stages.\n    Args:\n        in_channels (int): Input channels.\n        act_layer (nn.Module): activation layer for ResidualConvUnit.\n        norm_layer (nn.Module): normalization layer.\n        expand (bool): Whether expand the channels in post process block.\n            Default: False.\n        align_corners (bool): align_corner setting for bilinear upsample.\n            Default: True.\n    \"\"\"\n\n    def __init__(self, in_channels, act_layer, norm_layer, expand=False, align_corners=True):\n        super(FeatureFusionBlock, self).__init__()\n\n        self.in_channels = in_channels\n        self.expand = expand\n        self.align_corners = align_corners\n\n        self.out_channels = in_channels\n        if self.expand:\n            self.out_channels = in_channels // 2\n\n        self.project = ConvModule(self.in_channels, self.out_channels, kernel_size=1, act_layer=None, bias=True)\n\n        self.res_conv_unit1 = PreActResidualConvUnit(\n            in_channels=self.in_channels, act_layer=act_layer, norm_layer=norm_layer\n        )\n        self.res_conv_unit2 = PreActResidualConvUnit(\n            in_channels=self.in_channels, act_layer=act_layer, norm_layer=norm_layer\n        )\n\n    def forward(self, *inputs):\n        x = inputs[0]\n        if len(inputs) == 2:\n            if x.shape != inputs[1].shape:\n                res = resize(inputs[1], size=(x.shape[2], x.shape[3]), mode=\"bilinear\", align_corners=False)\n            else:\n                res = inputs[1]\n            x = x + self.res_conv_unit1(res)\n        x = self.res_conv_unit2(x)\n        x = resize(x, scale_factor=2, mode=\"bilinear\", align_corners=self.align_corners)\n        x = self.project(x)\n        return x\n\n\nclass DPTHead(DepthBaseDecodeHead):\n    \"\"\"Vision Transformers for Dense Prediction.\n    This head is implemented of `DPT <https://arxiv.org/abs/2103.13413>`_.\n    Args:\n        embed_dims (int): The embed dimension of the ViT backbone.\n            Default: 768.\n        post_process_channels (List): Out channels of post process conv\n            layers. Default: [96, 192, 384, 768].\n        readout_type (str): Type of readout operation. Default: 'ignore'.\n        patch_size (int): The patch size. Default: 16.\n        expand_channels (bool): Whether expand the channels in post process\n            block. Default: False.\n    \"\"\"\n\n    def __init__(\n        self,\n        embed_dims=768,\n        post_process_channels=[96, 192, 384, 768],\n        readout_type=\"ignore\",\n        patch_size=16,\n        expand_channels=False,\n        **kwargs,\n    ):\n        super(DPTHead, self).__init__(**kwargs)\n\n        self.in_channels = self.in_channels\n        self.expand_channels = expand_channels\n        self.reassemble_blocks = ReassembleBlocks(embed_dims, post_process_channels, readout_type, patch_size)\n\n        self.post_process_channels = [\n            channel * math.pow(2, i) if expand_channels else channel for i, channel in enumerate(post_process_channels)\n        ]\n        self.convs = nn.ModuleList()\n        for channel in self.post_process_channels:\n            self.convs.append(ConvModule(channel, self.channels, kernel_size=3, padding=1, act_layer=None, bias=False))\n        self.fusion_blocks = nn.ModuleList()\n        for _ in range(len(self.convs)):\n            self.fusion_blocks.append(FeatureFusionBlock(self.channels, self.act_layer, self.norm_layer))\n        self.fusion_blocks[0].res_conv_unit1 = None\n        self.project = ConvModule(self.channels, self.channels, kernel_size=3, padding=1, norm_layer=self.norm_layer)\n        self.num_fusion_blocks = len(self.fusion_blocks)\n        self.num_reassemble_blocks = len(self.reassemble_blocks.resize_layers)\n        self.num_post_process_channels = len(self.post_process_channels)\n        assert self.num_fusion_blocks == self.num_reassemble_blocks\n        assert self.num_reassemble_blocks == self.num_post_process_channels\n        self.conv_depth = HeadDepth(self.channels)\n\n    def forward(self, inputs, img_metas):\n        assert len(inputs) == self.num_reassemble_blocks\n        x = [inp for inp in inputs]\n        x = self.reassemble_blocks(x)\n        x = [self.convs[i](feature) for i, feature in enumerate(x)]\n        out = self.fusion_blocks[0](x[-1])\n        for i in range(1, len(self.fusion_blocks)):\n            out = self.fusion_blocks[i](out, x[-(i + 1)])\n        out = self.project(out)\n        out = self.depth_pred(out)\n        return out\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nimport warnings\n\nimport torch.nn.functional as F\n\n\ndef resize(input, size=None, scale_factor=None, mode=\"nearest\", align_corners=None, warning=False):\n    if warning:\n        if size is not None and align_corners:\n            input_h, input_w = tuple(int(x) for x in input.shape[2:])\n            output_h, output_w = tuple(int(x) for x in size)\n            if output_h > input_h or output_w > output_h:\n                if (\n                    (output_h > 1 and output_w > 1 and input_h > 1 and input_w > 1)\n                    and (output_h - 1) % (input_h - 1)\n                    and (output_w - 1) % (input_w - 1)\n                ):\n                    warnings.warn(\n                        f\"When align_corners={align_corners}, \"\n                        \"the output would more aligned if \"\n                        f\"input size {(input_h, input_w)} is `x+1` and \"\n                        f\"out size {(output_h, output_w)} is `nx+1`\"\n                    )\n    return F.interpolate(input, size, scale_factor, mode, align_corners)\n\n\n# Copyright (c) Meta Platforms, Inc. and affiliates.\n#\n# This source code is licensed under the Apache License, Version 2.0\n# found in the LICENSE file in the root directory of this source tree.\n\nfrom .decode_heads import BNHead, DPTHead\nfrom .encoder_decoder import DepthEncoderDecoder\n\n\n# ORIGINAL LICENSE\n# SPDX-FileCopyrightText: Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: LicenseRef-NvidiaProprietary\n#\n# Modified by Zexin He in 2023-2024.\n# The modifications are subject to the same license as the original.\n\n\nimport itertools\nimport torch\nimport torch.nn as nn\n\nfrom .utils.renderer import ImportanceRenderer\nfrom .utils.ray_sampler import RaySampler\n\n\nclass ShiftedSoftplus(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n    def forward(self, x):\n        return nn.functional.softplus(x - 1)\n\n\nclass OSGDecoder(nn.Module):\n    \"\"\"\n    Triplane decoder that gives RGB and sigma values from sampled features.\n    Using ReLU here instead of Softplus in the original implementation.\n    \n    Reference:\n    EG3D: https://github.com/NVlabs/eg3d/blob/main/eg3d/training/triplane.py#L112\n    \"\"\"\n    def __init__(self, n_features: int,\n                 hidden_dim: int = 64, num_layers: int = 4, activation: nn.Module = nn.ReLU):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(3 * n_features, hidden_dim),\n            activation(),\n            *itertools.chain(*[[\n                nn.Linear(hidden_dim, hidden_dim),\n                activation(),\n            ] for _ in range(num_layers - 2)]),\n            nn.Linear(hidden_dim, 1 + 3),\n        )\n        # init all bias to zero\n        for m in self.modules():\n            if isinstance(m, nn.Linear):\n                nn.init.zeros_(m.bias)\n\n    @torch.compile\n    def forward(self, sampled_features, ray_directions):\n        # Aggregate features by mean\n        # sampled_features = sampled_features.mean(1)\n        # Aggregate features by concatenation\n        _N, n_planes, _M, _C = sampled_features.shape\n        sampled_features = sampled_features.permute(0, 2, 1, 3).reshape(_N, _M, n_planes*_C)\n        x = sampled_features\n\n        N, M, C = x.shape\n        x = x.contiguous().view(N*M, C)\n\n        x = self.net(x)\n        x = x.view(N, M, -1)\n        rgb = torch.sigmoid(x[..., 1:])*(1 + 2*0.001) - 0.001  # Uses sigmoid clamping from MipNeRF\n        sigma = x[..., 0:1]\n\n        return {'rgb': rgb, 'sigma': sigma}\n\n\nclass TriplaneSynthesizer(nn.Module):\n    \"\"\"\n    Synthesizer that renders a triplane volume with planes and a camera.\n    \n    Reference:\n    EG3D: https://github.com/NVlabs/eg3d/blob/main/eg3d/training/triplane.py#L19\n    \"\"\"\n\n    DEFAULT_RENDERING_KWARGS = {\n        'ray_start': 'auto',\n        'ray_end': 'auto',\n        'box_warp': 2.,\n        'white_back': False,\n        'disparity_space_sampling': False,\n        'clamp_mode': 'softplus',\n        'sampler_bbox_min': -1.,\n        'sampler_bbox_max': 1.,\n    }\n\n    def __init__(self, triplane_dim: int, samples_per_ray: int):\n        super().__init__()\n\n        # attributes\n        self.triplane_dim = triplane_dim\n        self.rendering_kwargs = {\n            **self.DEFAULT_RENDERING_KWARGS,\n            'depth_resolution': samples_per_ray // 2,\n            'depth_resolution_importance': samples_per_ray // 2,\n        }\n\n        # renderings\n        self.renderer = ImportanceRenderer()\n        self.ray_sampler = RaySampler()\n\n        # modules\n        self.decoder = OSGDecoder(n_features=triplane_dim)\n\n    def forward(self, planes, cameras, anchors, resolutions, bg_colors, region_size: int):\n        # planes: (N, 3, D', H', W')\n        # cameras: (N, M, D_cam)\n        # anchors: (N, M, 2)\n        # resolutions: (N, M, 1)\n        # bg_colors: (N, M, 1)\n        # region_size: int\n        assert planes.shape[0] == cameras.shape[0], \"Batch size mismatch for planes and cameras\"\n        assert planes.shape[0] == anchors.shape[0], \"Batch size mismatch for planes and anchors\"\n        assert cameras.shape[1] == anchors.shape[1], \"Number of views mismatch for cameras and anchors\"\n        N, M = cameras.shape[:2]\n        \n        cam2world_matrix = cameras[..., :16].view(N, M, 4, 4)\n        intrinsics = cameras[..., 16:25].view(N, M, 3, 3)\n\n        # Create a batch of rays for volume rendering\n        ray_origins, ray_directions = self.ray_sampler(\n            cam2world_matrix=cam2world_matrix.reshape(-1, 4, 4),\n            intrinsics=intrinsics.reshape(-1, 3, 3),\n            resolutions=resolutions.reshape(-1, 1),\n            anchors=anchors.reshape(-1, 2),\n            region_size=region_size,\n        )\n        assert N*M == ray_origins.shape[0], \"Batch size mismatch for ray_origins\"\n        assert ray_origins.dim() == 3, \"ray_origins should be 3-dimensional\"\n\n        # Perform volume rendering\n        rgb_samples, depth_samples, weights_samples = self.renderer(\n            planes.repeat_interleave(M, dim=0), self.decoder, ray_origins, ray_directions, self.rendering_kwargs,\n            bg_colors=bg_colors.reshape(-1, 1),\n        )\n\n        # Reshape into 'raw' neural-rendered image\n        Himg = Wimg = region_size\n        rgb_images = rgb_samples.permute(0, 2, 1).reshape(N, M, rgb_samples.shape[-1], Himg, Wimg).contiguous()\n        depth_images = depth_samples.permute(0, 2, 1).reshape(N, M, 1, Himg, Wimg)\n        weight_images = weights_samples.permute(0, 2, 1).reshape(N, M, 1, Himg, Wimg)\n\n        return {\n            'images_rgb': rgb_images,\n            'images_depth': depth_images,\n            'images_weight': weight_images,\n        }\n\n    def forward_grid(self, planes, grid_size: int, aabb: torch.Tensor = None):\n        # planes: (N, 3, D', H', W')\n        # grid_size: int\n        # aabb: (N, 2, 3)\n        if aabb is None:\n            aabb = torch.tensor([\n                [self.rendering_kwargs['sampler_bbox_min']] * 3,\n                [self.rendering_kwargs['sampler_bbox_max']] * 3,\n            ], device=planes.device, dtype=planes.dtype).unsqueeze(0).repeat(planes.shape[0], 1, 1)\n        assert planes.shape[0] == aabb.shape[0], \"Batch size mismatch for planes and aabb\"\n        N = planes.shape[0]\n\n        # create grid points for triplane query\n        grid_points = []\n        for i in range(N):\n            grid_points.append(torch.stack(torch.meshgrid(\n                torch.linspace(aabb[i, 0, 0], aabb[i, 1, 0], grid_size, device=planes.device),\n                torch.linspace(aabb[i, 0, 1], aabb[i, 1, 1], grid_size, device=planes.device),\n                torch.linspace(aabb[i, 0, 2], aabb[i, 1, 2], grid_size, device=planes.device),\n                indexing='ij',\n            ), dim=-1).reshape(-1, 3))\n        cube_grid = torch.stack(grid_points, dim=0).to(planes.device)\n\n        features = self.forward_points(planes, cube_grid)\n\n        # reshape into grid\n        features = {\n            k: v.reshape(N, grid_size, grid_size, grid_size, -1)\n            for k, v in features.items()\n        }\n        return features\n\n    def forward_points(self, planes, points: torch.Tensor, chunk_size: int = 2**20):\n        # planes: (N, 3, D', H', W')\n        # points: (N, P, 3)\n        N, P = points.shape[:2]\n\n        # query triplane in chunks\n        outs = []\n        for i in range(0, points.shape[1], chunk_size):\n            chunk_points = points[:, i:i+chunk_size]\n\n            # query triplane\n            chunk_out = self.renderer.run_model_activated(\n                planes=planes,\n                decoder=self.decoder,\n                sample_coordinates=chunk_points,\n                sample_directions=torch.zeros_like(chunk_points),\n                options=self.rendering_kwargs,\n            )\n            outs.append(chunk_out)\n\n        # concatenate the outputs\n        point_features = {\n            k: torch.cat([out[k] for out in outs], dim=1)\n            for k in outs[0].keys()\n        }\n        return point_features\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Empty\n\n\n# SPDX-FileCopyrightText: Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: LicenseRef-NvidiaProprietary\n#\n# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual\n# property and proprietary rights in and to this material, related\n# documentation and any modifications thereto. Any use, reproduction,\n# disclosure or distribution of this material and related documentation\n# without an express license agreement from NVIDIA CORPORATION or\n# its affiliates is strictly prohibited.\n#\n# Modified by Zexin He in 2023-2024.\n# The modifications are subject to the same license as the original.\n\n\n\"\"\"\nThe ray sampler is a module that takes in camera matrices and resolution and batches of rays.\nExpects cam2world matrices that use the OpenCV camera coordinate system conventions.\n\"\"\"\n\nimport torch\n\nclass RaySampler(torch.nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.ray_origins_h, self.ray_directions, self.depths, self.image_coords, self.rendering_options = None, None, None, None, None\n\n    @torch.compile\n    def forward(self, cam2world_matrix, intrinsics, resolutions, anchors, region_size):\n        \"\"\"\n        Create batches of rays and return origins and directions.\n\n        cam2world_matrix: (N, 4, 4)\n        intrinsics: (N, 3, 3)\n        resolutions: (N, 1)\n        anchors: (N, 2)\n        region_size: int\n\n        ray_origins: (N, M, 3)\n        ray_dirs: (N, M, 2)\n        \"\"\"\n\n        N, M = cam2world_matrix.shape[0], region_size**2\n        cam_locs_world = cam2world_matrix[:, :3, 3]\n        fx = intrinsics[:, 0, 0]\n        fy = intrinsics[:, 1, 1]\n        cx = intrinsics[:, 0, 2]\n        cy = intrinsics[:, 1, 2]\n        sk = intrinsics[:, 0, 1]\n\n        uv = torch.stack(torch.meshgrid(\n            torch.arange(region_size, dtype=torch.float32, device=cam2world_matrix.device),\n            torch.arange(region_size, dtype=torch.float32, device=cam2world_matrix.device),\n            indexing='ij',\n        ))\n        uv = uv.flip(0).reshape(2, -1).transpose(1, 0)\n        uv = uv.unsqueeze(0).repeat(cam2world_matrix.shape[0], 1, 1)\n\n        # anchors are indexed as normal (row, col) but uv is indexed as (x, y)\n        x_cam = (uv[:, :, 0].view(N, -1) + anchors[:, 1].unsqueeze(-1)) * (1./resolutions) + (0.5/resolutions)\n        y_cam = (uv[:, :, 1].view(N, -1) + anchors[:, 0].unsqueeze(-1)) * (1./resolutions) + (0.5/resolutions)\n        z_cam = torch.ones((N, M), device=cam2world_matrix.device)\n\n        x_lift = (x_cam - cx.unsqueeze(-1) + cy.unsqueeze(-1)*sk.unsqueeze(-1)/fy.unsqueeze(-1) - sk.unsqueeze(-1)*y_cam/fy.unsqueeze(-1)) / fx.unsqueeze(-1) * z_cam\n        y_lift = (y_cam - cy.unsqueeze(-1)) / fy.unsqueeze(-1) * z_cam\n\n        cam_rel_points = torch.stack((x_lift, y_lift, z_cam, torch.ones_like(z_cam)), dim=-1)\n\n        _opencv2blender = torch.tensor([\n            [1, 0, 0, 0],\n            [0, -1, 0, 0],\n            [0, 0, -1, 0],\n            [0, 0, 0, 1],\n        ], dtype=torch.float32, device=cam2world_matrix.device).unsqueeze(0).repeat(N, 1, 1)\n\n        cam2world_matrix = torch.bmm(cam2world_matrix, _opencv2blender)\n\n        world_rel_points = torch.bmm(cam2world_matrix, cam_rel_points.permute(0, 2, 1)).permute(0, 2, 1)[:, :, :3]\n\n        ray_dirs = world_rel_points - cam_locs_world[:, None, :]\n        ray_dirs = torch.nn.functional.normalize(ray_dirs, dim=2)\n\n        ray_origins = cam_locs_world.unsqueeze(1).repeat(1, ray_dirs.shape[1], 1)\n\n        return ray_origins, ray_dirs\n\n\n# SPDX-FileCopyrightText: Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: LicenseRef-NvidiaProprietary\n#\n# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual\n# property and proprietary rights in and to this material, related\n# documentation and any modifications thereto. Any use, reproduction,\n# disclosure or distribution of this material and related documentation\n# without an express license agreement from NVIDIA CORPORATION or\n# its affiliates is strictly prohibited.\n#\n# Modified by Zexin He in 2023-2024.\n# The modifications are subject to the same license as the original.\n\n\n\"\"\"\nThe ray marcher takes the raw output of the implicit representation and uses the volume rendering equation to produce composited colors and depths.\nBased off of the implementation in MipNeRF (this one doesn't do any cone tracing though!)\n\"\"\"\n\nimport torch\nimport torch.nn as nn\n\n\nclass MipRayMarcher2(nn.Module):\n    def __init__(self, activation_factory):\n        super().__init__()\n        self.activation_factory = activation_factory\n\n    def run_forward(self, colors, densities, depths, rendering_options, bg_colors=None):\n        deltas = depths[:, :, 1:] - depths[:, :, :-1]\n        colors_mid = (colors[:, :, :-1] + colors[:, :, 1:]) / 2\n        densities_mid = (densities[:, :, :-1] + densities[:, :, 1:]) / 2\n        depths_mid = (depths[:, :, :-1] + depths[:, :, 1:]) / 2\n\n        # using factory mode for better usability\n        densities_mid = self.activation_factory(rendering_options)(densities_mid)\n\n        density_delta = densities_mid * deltas\n\n        alpha = 1 - torch.exp(-density_delta)\n\n        alpha_shifted = torch.cat([torch.ones_like(alpha[:, :, :1]), 1-alpha + 1e-10], -2)\n        weights = alpha * torch.cumprod(alpha_shifted, -2)[:, :, :-1]\n\n        composite_rgb = torch.sum(weights * colors_mid, -2)\n        weight_total = weights.sum(2)\n        composite_depth = torch.sum(weights * depths_mid, -2) / weight_total\n\n        # clip the composite to min/max range of depths\n        composite_depth = torch.nan_to_num(composite_depth, float('inf'))\n        composite_depth = torch.clamp(composite_depth, torch.min(depths), torch.max(depths))\n\n        if rendering_options.get('white_back', False):\n            composite_rgb = composite_rgb + 1 - weight_total\n        else:\n            assert bg_colors is not None, \"Must provide bg_colors if white_back is False\"\n            composite_rgb = composite_rgb + bg_colors.unsqueeze(-1) * (1 - weight_total)\n\n        # rendered value scale is 0-1, comment out original mipnerf scaling\n        # composite_rgb = composite_rgb * 2 - 1 # Scale to (-1, 1)\n\n        return composite_rgb, composite_depth, weights\n\n\n    def forward(self, colors, densities, depths, rendering_options, bg_colors=None):\n        composite_rgb, composite_depth, weights = self.run_forward(colors, densities, depths, rendering_options, bg_colors=bg_colors)\n\n        return composite_rgb, composite_depth, weights\n\n\n# MIT License\n\n# Copyright (c) 2022 Petr Kellnhofer\n\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n\nimport torch\n\ndef transform_vectors(matrix: torch.Tensor, vectors4: torch.Tensor) -> torch.Tensor:\n    \"\"\"\n    Left-multiplies MxM @ NxM. Returns NxM.\n    \"\"\"\n    res = torch.matmul(vectors4, matrix.T)\n    return res\n\n\ndef normalize_vecs(vectors: torch.Tensor) -> torch.Tensor:\n    \"\"\"\n    Normalize vector lengths.\n    \"\"\"\n    return vectors / (torch.norm(vectors, dim=-1, keepdim=True))\n\ndef torch_dot(x: torch.Tensor, y: torch.Tensor):\n    \"\"\"\n    Dot product of two tensors.\n    \"\"\"\n    return (x * y).sum(-1)\n\n\ndef get_ray_limits_box(rays_o: torch.Tensor, rays_d: torch.Tensor, box_side_length):\n    \"\"\"\n    Author: Petr Kellnhofer\n    Intersects rays with the [-1, 1] NDC volume.\n    Returns min and max distance of entry.\n    Returns -1 for no intersection.\n    https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection\n    \"\"\"\n    o_shape = rays_o.shape\n    rays_o = rays_o.detach().reshape(-1, 3)\n    rays_d = rays_d.detach().reshape(-1, 3)\n\n\n    bb_min = [-1*(box_side_length/2), -1*(box_side_length/2), -1*(box_side_length/2)]\n    bb_max = [1*(box_side_length/2), 1*(box_side_length/2), 1*(box_side_length/2)]\n    bounds = torch.tensor([bb_min, bb_max], dtype=rays_o.dtype, device=rays_o.device)\n    is_valid = torch.ones(rays_o.shape[:-1], dtype=bool, device=rays_o.device)\n\n    # Precompute inverse for stability.\n    invdir = 1 / rays_d\n    sign = (invdir < 0).long()\n\n    # Intersect with YZ plane.\n    tmin = (bounds.index_select(0, sign[..., 0])[..., 0] - rays_o[..., 0]) * invdir[..., 0]\n    tmax = (bounds.index_select(0, 1 - sign[..., 0])[..., 0] - rays_o[..., 0]) * invdir[..., 0]\n\n    # Intersect with XZ plane.\n    tymin = (bounds.index_select(0, sign[..., 1])[..., 1] - rays_o[..., 1]) * invdir[..., 1]\n    tymax = (bounds.index_select(0, 1 - sign[..., 1])[..., 1] - rays_o[..., 1]) * invdir[..., 1]\n\n    # Resolve parallel rays.\n    is_valid[torch.logical_or(tmin > tymax, tymin > tmax)] = False\n\n    # Use the shortest intersection.\n    tmin = torch.max(tmin, tymin)\n    tmax = torch.min(tmax, tymax)\n\n    # Intersect with XY plane.\n    tzmin = (bounds.index_select(0, sign[..., 2])[..., 2] - rays_o[..., 2]) * invdir[..., 2]\n    tzmax = (bounds.index_select(0, 1 - sign[..., 2])[..., 2] - rays_o[..., 2]) * invdir[..., 2]\n\n    # Resolve parallel rays.\n    is_valid[torch.logical_or(tmin > tzmax, tzmin > tmax)] = False\n\n    # Use the shortest intersection.\n    tmin = torch.max(tmin, tzmin)\n    tmax = torch.min(tmax, tzmax)\n\n    # Mark invalid.\n    tmin[torch.logical_not(is_valid)] = -1\n    tmax[torch.logical_not(is_valid)] = -2\n\n    return tmin.reshape(*o_shape[:-1], 1), tmax.reshape(*o_shape[:-1], 1)\n\n\ndef linspace(start: torch.Tensor, stop: torch.Tensor, num: int):\n    \"\"\"\n    Creates a tensor of shape [num, *start.shape] whose values are evenly spaced from start to end, inclusive.\n    Replicates but the multi-dimensional bahaviour of numpy.linspace in PyTorch.\n    \"\"\"\n    # create a tensor of 'num' steps from 0 to 1\n    steps = torch.arange(num, dtype=torch.float32, device=start.device) / (num - 1)\n\n    # reshape the 'steps' tensor to [-1, *([1]*start.ndim)] to allow for broadcastings\n    # - using 'steps.reshape([-1, *([1]*start.ndim)])' would be nice here but torchscript\n    #   \"cannot statically infer the expected size of a list in this contex\", hence the code below\n    for i in range(start.ndim):\n        steps = steps.unsqueeze(-1)\n\n    # the output starts at 'start' and increments until 'stop' in each dimension\n    out = start[None] + steps * (stop - start)[None]\n\n    return out\n\n\n# SPDX-FileCopyrightText: Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: LicenseRef-NvidiaProprietary\n#\n# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual\n# property and proprietary rights in and to this material, related\n# documentation and any modifications thereto. Any use, reproduction,\n# disclosure or distribution of this material and related documentation\n# without an express license agreement from NVIDIA CORPORATION or\n# its affiliates is strictly prohibited.\n#\n# Modified by Zexin He in 2023-2024.\n# The modifications are subject to the same license as the original.\n\n\n\"\"\"\nThe renderer is a module that takes in rays, decides where to sample along each\nray, and computes pixel colors using the volume rendering equation.\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom .ray_marcher import MipRayMarcher2\nfrom . import math_utils\n\ndef generate_planes():\n    \"\"\"\n    Defines planes by the three vectors that form the \"axes\" of the\n    plane. Should work with arbitrary number of planes and planes of\n    arbitrary orientation.\n\n    Bugfix reference: https://github.com/NVlabs/eg3d/issues/67\n    \"\"\"\n    return torch.tensor([[[1, 0, 0],\n                            [0, 1, 0],\n                            [0, 0, 1]],\n                            [[1, 0, 0],\n                            [0, 0, 1],\n                            [0, 1, 0]],\n                            [[0, 0, 1],\n                            [0, 1, 0],\n                            [1, 0, 0]]], dtype=torch.float32)\n\ndef project_onto_planes(planes, coordinates):\n    \"\"\"\n    Does a projection of a 3D point onto a batch of 2D planes,\n    returning 2D plane coordinates.\n\n    Takes plane axes of shape n_planes, 3, 3\n    # Takes coordinates of shape N, M, 3\n    # returns projections of shape N*n_planes, M, 2\n    \"\"\"\n    N, M, C = coordinates.shape\n    n_planes, _, _ = planes.shape\n    coordinates = coordinates.unsqueeze(1).expand(-1, n_planes, -1, -1).reshape(N*n_planes, M, 3)\n    inv_planes = torch.linalg.inv(planes).unsqueeze(0).expand(N, -1, -1, -1).reshape(N*n_planes, 3, 3)\n    projections = torch.bmm(coordinates, inv_planes)\n    return projections[..., :2]\n\ndef sample_from_planes(plane_axes, plane_features, coordinates, mode='bilinear', padding_mode='zeros', box_warp=None):\n    assert padding_mode == 'zeros'\n    N, n_planes, C, H, W = plane_features.shape\n    _, M, _ = coordinates.shape\n    plane_features = plane_features.view(N*n_planes, C, H, W)\n\n    coordinates = (2/box_warp) * coordinates # add specific box bounds\n\n    projected_coordinates = project_onto_planes(plane_axes, coordinates).unsqueeze(1)\n    output_features = torch.nn.functional.grid_sample(plane_features, projected_coordinates.float(), mode=mode, padding_mode=padding_mode, align_corners=False).permute(0, 3, 2, 1).reshape(N, n_planes, M, C)\n    return output_features\n\ndef sample_from_3dgrid(grid, coordinates):\n    \"\"\"\n    Expects coordinates in shape (batch_size, num_points_per_batch, 3)\n    Expects grid in shape (1, channels, H, W, D)\n    (Also works if grid has batch size)\n    Returns sampled features of shape (batch_size, num_points_per_batch, feature_channels)\n    \"\"\"\n    batch_size, n_coords, n_dims = coordinates.shape\n    sampled_features = torch.nn.functional.grid_sample(grid.expand(batch_size, -1, -1, -1, -1),\n                                                       coordinates.reshape(batch_size, 1, 1, -1, n_dims),\n                                                       mode='bilinear', padding_mode='zeros', align_corners=False)\n    N, C, H, W, D = sampled_features.shape\n    sampled_features = sampled_features.permute(0, 4, 3, 2, 1).reshape(N, H*W*D, C)\n    return sampled_features\n\nclass ImportanceRenderer(torch.nn.Module):\n    \"\"\"\n    Modified original version to filter out-of-box samples as TensoRF does.\n    \n    Reference:\n    TensoRF: https://github.com/apchenstu/TensoRF/blob/main/models/tensorBase.py#L277\n    \"\"\"\n    def __init__(self):\n        super().__init__()\n        self.activation_factory = self._build_activation_factory()\n        self.ray_marcher = MipRayMarcher2(self.activation_factory)\n        self.plane_axes = generate_planes()\n\n    def _build_activation_factory(self):\n        def activation_factory(options: dict):\n            if options['clamp_mode'] == 'softplus':\n                return lambda x: F.softplus(x - 1)  # activation bias of -1 makes things initialize better\n            else:\n                assert False, \"Renderer only supports `clamp_mode`=`softplus`!\"\n        return activation_factory\n\n    def _forward_pass(self, depths: torch.Tensor, ray_directions: torch.Tensor, ray_origins: torch.Tensor,\n                        planes: torch.Tensor, decoder: nn.Module, rendering_options: dict):\n        \"\"\"\n        Additional filtering is applied to filter out-of-box samples.\n        Modifications made by Zexin He.\n        \"\"\"\n\n        # context related variables\n        batch_size, num_rays, samples_per_ray, _ = depths.shape\n        device = depths.device\n\n        # define sample points with depths\n        sample_directions = ray_directions.unsqueeze(-2).expand(-1, -1, samples_per_ray, -1).reshape(batch_size, -1, 3)\n        sample_coordinates = (ray_origins.unsqueeze(-2) + depths * ray_directions.unsqueeze(-2)).reshape(batch_size, -1, 3)\n\n        # filter out-of-box samples\n        mask_inbox = \\\n            (rendering_options['sampler_bbox_min'] <= sample_coordinates) & \\\n                (sample_coordinates <= rendering_options['sampler_bbox_max'])\n        mask_inbox = mask_inbox.all(-1)\n\n        # forward model according to all samples\n        _out = self.run_model(planes, decoder, sample_coordinates, sample_directions, rendering_options)\n\n        # set out-of-box samples to zeros(rgb) & -inf(sigma)\n        SAFE_GUARD = 8\n        DATA_TYPE = _out['sigma'].dtype\n        colors_pass = torch.zeros(batch_size, num_rays * samples_per_ray, 3, device=device, dtype=DATA_TYPE)\n        densities_pass = torch.nan_to_num(torch.full((batch_size, num_rays * samples_per_ray, 1), -float('inf'), device=device, dtype=DATA_TYPE)) / SAFE_GUARD\n        colors_pass[mask_inbox], densities_pass[mask_inbox] = _out['rgb'][mask_inbox], _out['sigma'][mask_inbox]\n\n        # reshape back\n        colors_pass = colors_pass.reshape(batch_size, num_rays, samples_per_ray, colors_pass.shape[-1])\n        densities_pass = densities_pass.reshape(batch_size, num_rays, samples_per_ray, densities_pass.shape[-1])\n\n        return colors_pass, densities_pass\n\n    def forward(self, planes, decoder, ray_origins, ray_directions, rendering_options, bg_colors=None):\n        # self.plane_axes = self.plane_axes.to(ray_origins.device)\n\n        if rendering_options['ray_start'] == rendering_options['ray_end'] == 'auto':\n            ray_start, ray_end = math_utils.get_ray_limits_box(ray_origins, ray_directions, box_side_length=rendering_options['box_warp'])\n            is_ray_valid = ray_end > ray_start\n            if torch.any(is_ray_valid).item():\n                ray_start[~is_ray_valid] = ray_start[is_ray_valid].min()\n                ray_end[~is_ray_valid] = ray_start[is_ray_valid].max()\n            depths_coarse = self.sample_stratified(ray_origins, ray_start, ray_end, rendering_options['depth_resolution'], rendering_options['disparity_space_sampling'])\n        else:\n            # Create stratified depth samples\n            depths_coarse = self.sample_stratified(ray_origins, rendering_options['ray_start'], rendering_options['ray_end'], rendering_options['depth_resolution'], rendering_options['disparity_space_sampling'])\n\n        # Coarse Pass\n        colors_coarse, densities_coarse = self._forward_pass(\n            depths=depths_coarse, ray_directions=ray_directions, ray_origins=ray_origins,\n            planes=planes, decoder=decoder, rendering_options=rendering_options)\n\n        # Fine Pass\n        N_importance = rendering_options['depth_resolution_importance']\n        if N_importance > 0:\n            _, _, weights = self.ray_marcher(colors_coarse, densities_coarse, depths_coarse, rendering_options, bg_colors=bg_colors)\n\n            depths_fine = self.sample_importance(depths_coarse, weights, N_importance)\n\n            colors_fine, densities_fine = self._forward_pass(\n                depths=depths_fine, ray_directions=ray_directions, ray_origins=ray_origins,\n                planes=planes, decoder=decoder, rendering_options=rendering_options)\n\n            all_depths, all_colors, all_densities = self.unify_samples(depths_coarse, colors_coarse, densities_coarse,\n                                                                  depths_fine, colors_fine, densities_fine)\n\n            # Aggregate\n            rgb_final, depth_final, weights = self.ray_marcher(all_colors, all_densities, all_depths, rendering_options, bg_colors=bg_colors)\n        else:\n            rgb_final, depth_final, weights = self.ray_marcher(colors_coarse, densities_coarse, depths_coarse, rendering_options, bg_colors=bg_colors)\n\n        return rgb_final, depth_final, weights.sum(2)\n\n    def run_model(self, planes, decoder, sample_coordinates, sample_directions, options):\n        plane_axes = self.plane_axes.to(planes.device)\n        sampled_features = sample_from_planes(plane_axes, planes, sample_coordinates, padding_mode='zeros', box_warp=options['box_warp'])\n\n        out = decoder(sampled_features, sample_directions)\n        if options.get('density_noise', 0) > 0:\n            out['sigma'] += torch.randn_like(out['sigma']) * options['density_noise']\n        return out\n\n    def run_model_activated(self, planes, decoder, sample_coordinates, sample_directions, options):\n        out = self.run_model(planes, decoder, sample_coordinates, sample_directions, options)\n        out['sigma'] = self.activation_factory(options)(out['sigma'])\n        return out\n\n    def sort_samples(self, all_depths, all_colors, all_densities):\n        _, indices = torch.sort(all_depths, dim=-2)\n        all_depths = torch.gather(all_depths, -2, indices)\n        all_colors = torch.gather(all_colors, -2, indices.expand(-1, -1, -1, all_colors.shape[-1]))\n        all_densities = torch.gather(all_densities, -2, indices.expand(-1, -1, -1, 1))\n        return all_depths, all_colors, all_densities\n\n    def unify_samples(self, depths1, colors1, densities1, depths2, colors2, densities2):\n        all_depths = torch.cat([depths1, depths2], dim = -2)\n        all_colors = torch.cat([colors1, colors2], dim = -2)\n        all_densities = torch.cat([densities1, densities2], dim = -2)\n\n        _, indices = torch.sort(all_depths, dim=-2)\n        all_depths = torch.gather(all_depths, -2, indices)\n        all_colors = torch.gather(all_colors, -2, indices.expand(-1, -1, -1, all_colors.shape[-1]))\n        all_densities = torch.gather(all_densities, -2, indices.expand(-1, -1, -1, 1))\n\n        return all_depths, all_colors, all_densities\n\n    def sample_stratified(self, ray_origins, ray_start, ray_end, depth_resolution, disparity_space_sampling=False):\n        \"\"\"\n        Return depths of approximately uniformly spaced samples along rays.\n        \"\"\"\n        N, M, _ = ray_origins.shape\n        if disparity_space_sampling:\n            depths_coarse = torch.linspace(0,\n                                    1,\n                                    depth_resolution,\n                                    device=ray_origins.device).reshape(1, 1, depth_resolution, 1).repeat(N, M, 1, 1)\n            depth_delta = 1/(depth_resolution - 1)\n            depths_coarse += torch.rand_like(depths_coarse) * depth_delta\n            depths_coarse = 1./(1./ray_start * (1. - depths_coarse) + 1./ray_end * depths_coarse)\n        else:\n            if type(ray_start) == torch.Tensor:\n                depths_coarse = math_utils.linspace(ray_start, ray_end, depth_resolution).permute(1,2,0,3)\n                depth_delta = (ray_end - ray_start) / (depth_resolution - 1)\n                depths_coarse += torch.rand_like(depths_coarse) * depth_delta[..., None]\n            else:\n                depths_coarse = torch.linspace(ray_start, ray_end, depth_resolution, device=ray_origins.device).reshape(1, 1, depth_resolution, 1).repeat(N, M, 1, 1)\n                depth_delta = (ray_end - ray_start)/(depth_resolution - 1)\n                depths_coarse += torch.rand_like(depths_coarse) * depth_delta\n\n        return depths_coarse\n\n    def sample_importance(self, z_vals, weights, N_importance):\n        \"\"\"\n        Return depths of importance sampled points along rays. See NeRF importance sampling for more.\n        \"\"\"\n        with torch.no_grad():\n            batch_size, num_rays, samples_per_ray, _ = z_vals.shape\n\n            z_vals = z_vals.reshape(batch_size * num_rays, samples_per_ray)\n            weights = weights.reshape(batch_size * num_rays, -1) # -1 to account for loss of 1 sample in MipRayMarcher\n\n            # smooth weights\n            weights = torch.nn.functional.max_pool1d(weights.unsqueeze(1).float(), 2, 1, padding=1)\n            weights = torch.nn.functional.avg_pool1d(weights, 2, 1).squeeze()\n            weights = weights + 0.01\n\n            z_vals_mid = 0.5 * (z_vals[: ,:-1] + z_vals[: ,1:])\n            importance_z_vals = self.sample_pdf(z_vals_mid, weights[:, 1:-1],\n                                             N_importance).detach().reshape(batch_size, num_rays, N_importance, 1)\n        return importance_z_vals\n\n    def sample_pdf(self, bins, weights, N_importance, det=False, eps=1e-5):\n        \"\"\"\n        Sample @N_importance samples from @bins with distribution defined by @weights.\n        Inputs:\n            bins: (N_rays, N_samples_+1) where N_samples_ is \"the number of coarse samples per ray - 2\"\n            weights: (N_rays, N_samples_)\n            N_importance: the number of samples to draw from the distribution\n            det: deterministic or not\n            eps: a small number to prevent division by zero\n        Outputs:\n            samples: the sampled samples\n        \"\"\"\n        N_rays, N_samples_ = weights.shape\n        weights = weights + eps # prevent division by zero (don't do inplace op!)\n        pdf = weights / torch.sum(weights, -1, keepdim=True) # (N_rays, N_samples_)\n        cdf = torch.cumsum(pdf, -1) # (N_rays, N_samples), cumulative distribution function\n        cdf = torch.cat([torch.zeros_like(cdf[: ,:1]), cdf], -1)  # (N_rays, N_samples_+1)\n                                                                   # padded to 0~1 inclusive\n\n        if det:\n            u = torch.linspace(0, 1, N_importance, device=bins.device)\n            u = u.expand(N_rays, N_importance)\n        else:\n            u = torch.rand(N_rays, N_importance, device=bins.device)\n        u = u.contiguous()\n\n        inds = torch.searchsorted(cdf, u, right=True)\n        below = torch.clamp_min(inds-1, 0)\n        above = torch.clamp_max(inds, N_samples_)\n\n        inds_sampled = torch.stack([below, above], -1).view(N_rays, 2*N_importance)\n        cdf_g = torch.gather(cdf, 1, inds_sampled).view(N_rays, N_importance, 2)\n        bins_g = torch.gather(bins, 1, inds_sampled).view(N_rays, N_importance, 2)\n\n        denom = cdf_g[...,1]-cdf_g[...,0]\n        denom[denom<eps] = 1 # denom equals 0 means a bin has weight 0, in which case it will not be sampled\n                             # anyway, therefore any value for it is fine (set to 1 here)\n\n        samples = bins_g[...,0] + (u-cdf_g[...,0])/denom * (bins_g[...,1]-bins_g[...,0])\n        return samples\n\n\n# SPDX-FileCopyrightText: Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n# SPDX-License-Identifier: LicenseRef-NvidiaProprietary\n#\n# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual\n# property and proprietary rights in and to this material, related\n# documentation and any modifications thereto. Any use, reproduction,\n# disclosure or distribution of this material and related documentation\n# without an express license agreement from NVIDIA CORPORATION or\n# its affiliates is strictly prohibited.\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nfrom typing import Union\nimport random\nimport numpy as np\nimport torch\nfrom megfile import smart_path_join, smart_open\n\nfrom .base import BaseDataset\nfrom .cam_utils import build_camera_standard, build_camera_principle, camera_normalization_objaverse\nfrom ..utils.proxy import no_proxy\n\n__all__ = ['ObjaverseDataset']\n\n\nclass ObjaverseDataset(BaseDataset):\n\n    def __init__(self, root_dirs: list[str], meta_path: str,\n                 sample_side_views: int,\n                 render_image_res_low: int, render_image_res_high: int, render_region_size: int,\n                 source_image_res: int, normalize_camera: bool,\n                 normed_dist_to_center: Union[float, str] = None, num_all_views: int = 32):\n        super().__init__(root_dirs, meta_path)\n        self.sample_side_views = sample_side_views\n        self.render_image_res_low = render_image_res_low\n        self.render_image_res_high = render_image_res_high\n        self.render_region_size = render_region_size\n        self.source_image_res = source_image_res\n        self.normalize_camera = normalize_camera\n        self.normed_dist_to_center = normed_dist_to_center\n        self.num_all_views = num_all_views\n\n    @staticmethod\n    def _load_pose(file_path):\n        pose = np.load(smart_open(file_path, 'rb'))\n        pose = torch.from_numpy(pose).float()\n        return pose\n\n    @no_proxy\n    def inner_get_item(self, idx):\n        \"\"\"\n        Loaded contents:\n            rgbs: [M, 3, H, W]\n            poses: [M, 3, 4], [R|t]\n            intrinsics: [3, 2], [[fx, fy], [cx, cy], [weight, height]]\n        \"\"\"\n        uid = self.uids[idx]\n        root_dir = self._locate_datadir(self.root_dirs, uid, locator=\"intrinsics.npy\")\n        \n        pose_dir = os.path.join(root_dir, uid, 'pose')\n        rgba_dir = os.path.join(root_dir, uid, 'rgba')\n        intrinsics_path = os.path.join(root_dir, uid, 'intrinsics.npy')\n\n        # load intrinsics\n        intrinsics = np.load(smart_open(intrinsics_path, 'rb'))\n        intrinsics = torch.from_numpy(intrinsics).float()\n\n        # sample views (incl. source view and side views)\n        sample_views = np.random.choice(range(self.num_all_views), self.sample_side_views + 1, replace=False)\n        poses, rgbs, bg_colors = [], [], []\n        source_image = None\n        for view in sample_views:\n            pose_path = smart_path_join(pose_dir, f'{view:03d}.npy')\n            rgba_path = smart_path_join(rgba_dir, f'{view:03d}.png')\n            pose = self._load_pose(pose_path)\n            bg_color = random.choice([0.0, 0.5, 1.0])\n            rgb = self._load_rgba_image(rgba_path, bg_color=bg_color)\n            poses.append(pose)\n            rgbs.append(rgb)\n            bg_colors.append(bg_color)\n            if source_image is None:\n                source_image = self._load_rgba_image(rgba_path, bg_color=1.0)\n        assert source_image is not None, \"Really bad luck!\"\n        poses = torch.stack(poses, dim=0)\n        rgbs = torch.cat(rgbs, dim=0)\n\n        if self.normalize_camera:\n            poses = camera_normalization_objaverse(self.normed_dist_to_center, poses)\n\n        # build source and target camera features\n        source_camera = build_camera_principle(poses[:1], intrinsics.unsqueeze(0)).squeeze(0)\n        render_camera = build_camera_standard(poses, intrinsics.repeat(poses.shape[0], 1, 1))\n\n        # adjust source image resolution\n        source_image = torch.nn.functional.interpolate(\n            source_image, size=(self.source_image_res, self.source_image_res), mode='bicubic', align_corners=True).squeeze(0)\n        source_image = torch.clamp(source_image, 0, 1)\n\n        # adjust render image resolution and sample intended rendering region\n        render_image_res = np.random.randint(self.render_image_res_low, self.render_image_res_high + 1)\n        render_image = torch.nn.functional.interpolate(\n            rgbs, size=(render_image_res, render_image_res), mode='bicubic', align_corners=True)\n        render_image = torch.clamp(render_image, 0, 1)\n        anchors = torch.randint(\n            0, render_image_res - self.render_region_size + 1, size=(self.sample_side_views + 1, 2))\n        crop_indices = torch.arange(0, self.render_region_size, device=render_image.device)\n        index_i = (anchors[:, 0].unsqueeze(1) + crop_indices).view(-1, self.render_region_size, 1)\n        index_j = (anchors[:, 1].unsqueeze(1) + crop_indices).view(-1, 1, self.render_region_size)\n        batch_indices = torch.arange(self.sample_side_views + 1, device=render_image.device).view(-1, 1, 1)\n        cropped_render_image = render_image[batch_indices, :, index_i, index_j].permute(0, 3, 1, 2)\n\n        return {\n            'uid': uid,\n            'source_camera': source_camera,\n            'render_camera': render_camera,\n            'source_image': source_image,\n            'render_image': cropped_render_image,\n            'render_anchors': anchors,\n            'render_full_resolutions': torch.tensor([[render_image_res]], dtype=torch.float32).repeat(self.sample_side_views + 1, 1),\n            'render_bg_colors': torch.tensor(bg_colors, dtype=torch.float32).unsqueeze(-1),\n        }\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport math\nfrom functools import partial\nimport torch\n\n__all__ = ['MixerDataset']\n\n\nclass MixerDataset(torch.utils.data.Dataset):\n\n    def __init__(self, \n                 split: str,\n                 subsets: list[dict],\n                 **dataset_kwargs,\n                 ):\n        self.subsets = [\n            self._dataset_fn(subset, split)(**dataset_kwargs)\n            for subset in subsets\n        ]\n        self.virtual_lens = [\n            math.ceil(subset_config['sample_rate'] * len(subset_obj))\n            for subset_config, subset_obj in zip(subsets, self.subsets)\n        ]\n\n    @staticmethod\n    def _dataset_fn(subset_config: dict, split: str):\n        name = subset_config['name']\n\n        dataset_cls = None\n        if name == \"objaverse\":\n            from .objaverse import ObjaverseDataset\n            dataset_cls = ObjaverseDataset\n        # elif name == 'mvimgnet':\n        #     from .mvimgnet import MVImgNetDataset\n        #     dataset_cls = MVImgNetDataset\n        else:\n            raise NotImplementedError(f\"Dataset {name} not implemented\")\n\n        return partial(\n            dataset_cls,\n            root_dirs=subset_config['root_dirs'],\n            meta_path=subset_config['meta_path'][split],\n        )\n\n    def __len__(self):\n        return sum(self.virtual_lens)\n\n    def __getitem__(self, idx):\n        subset_idx = 0\n        virtual_idx = idx\n        while virtual_idx >= self.virtual_lens[subset_idx]:\n            virtual_idx -= self.virtual_lens[subset_idx]\n            subset_idx += 1\n        real_idx = virtual_idx % len(self.subsets[subset_idx])\n        return self.subsets[subset_idx][real_idx]\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport math\nimport torch\n\n\"\"\"\nR: (N, 3, 3)\nT: (N, 3)\nE: (N, 4, 4)\nvector: (N, 3)\n\"\"\"\n\n\ndef compose_extrinsic_R_T(R: torch.Tensor, T: torch.Tensor):\n    \"\"\"\n    Compose the standard form extrinsic matrix from R and T.\n    Batched I/O.\n    \"\"\"\n    RT = torch.cat((R, T.unsqueeze(-1)), dim=-1)\n    return compose_extrinsic_RT(RT)\n\n\ndef compose_extrinsic_RT(RT: torch.Tensor):\n    \"\"\"\n    Compose the standard form extrinsic matrix from RT.\n    Batched I/O.\n    \"\"\"\n    return torch.cat([\n        RT,\n        torch.tensor([[[0, 0, 0, 1]]], dtype=RT.dtype, device=RT.device).repeat(RT.shape[0], 1, 1)\n        ], dim=1)\n\n\ndef decompose_extrinsic_R_T(E: torch.Tensor):\n    \"\"\"\n    Decompose the standard extrinsic matrix into R and T.\n    Batched I/O.\n    \"\"\"\n    RT = decompose_extrinsic_RT(E)\n    return RT[:, :, :3], RT[:, :, 3]\n\n\ndef decompose_extrinsic_RT(E: torch.Tensor):\n    \"\"\"\n    Decompose the standard extrinsic matrix into RT.\n    Batched I/O.\n    \"\"\"\n    return E[:, :3, :]\n\n\ndef camera_normalization_objaverse(normed_dist_to_center, poses: torch.Tensor, ret_transform: bool = False):\n    assert normed_dist_to_center is not None\n    pivotal_pose = compose_extrinsic_RT(poses[:1])\n    dist_to_center = pivotal_pose[:, :3, 3].norm(dim=-1, keepdim=True).item() \\\n        if normed_dist_to_center == 'auto' else normed_dist_to_center\n\n    # compute camera norm (new version)\n    canonical_camera_extrinsics = torch.tensor([[\n        [1, 0, 0, 0],\n        [0, 0, -1, -dist_to_center],\n        [0, 1, 0, 0],\n        [0, 0, 0, 1],\n    ]], dtype=torch.float32)\n    pivotal_pose_inv = torch.inverse(pivotal_pose)\n    camera_norm_matrix = torch.bmm(canonical_camera_extrinsics, pivotal_pose_inv)\n\n    # normalize all views\n    poses = compose_extrinsic_RT(poses)\n    poses = torch.bmm(camera_norm_matrix.repeat(poses.shape[0], 1, 1), poses)\n    poses = decompose_extrinsic_RT(poses)\n\n    if ret_transform:\n        return poses, camera_norm_matrix.squeeze(dim=0)\n    return poses\n\n\ndef get_normalized_camera_intrinsics(intrinsics: torch.Tensor):\n    \"\"\"\n    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]\n    Return batched fx, fy, cx, cy\n    \"\"\"\n    fx, fy = intrinsics[:, 0, 0], intrinsics[:, 0, 1]\n    cx, cy = intrinsics[:, 1, 0], intrinsics[:, 1, 1]\n    width, height = intrinsics[:, 2, 0], intrinsics[:, 2, 1]\n    fx, fy = fx / width, fy / height\n    cx, cy = cx / width, cy / height\n    return fx, fy, cx, cy\n\n\ndef build_camera_principle(RT: torch.Tensor, intrinsics: torch.Tensor):\n    \"\"\"\n    RT: (N, 3, 4)\n    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]\n    \"\"\"\n    fx, fy, cx, cy = get_normalized_camera_intrinsics(intrinsics)\n    return torch.cat([\n        RT.reshape(-1, 12),\n        fx.unsqueeze(-1), fy.unsqueeze(-1), cx.unsqueeze(-1), cy.unsqueeze(-1),\n    ], dim=-1)\n\n\ndef build_camera_standard(RT: torch.Tensor, intrinsics: torch.Tensor):\n    \"\"\"\n    RT: (N, 3, 4)\n    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]\n    \"\"\"\n    E = compose_extrinsic_RT(RT)\n    fx, fy, cx, cy = get_normalized_camera_intrinsics(intrinsics)\n    I = torch.stack([\n        torch.stack([fx, torch.zeros_like(fx), cx], dim=-1),\n        torch.stack([torch.zeros_like(fy), fy, cy], dim=-1),\n        torch.tensor([[0, 0, 1]], dtype=torch.float32, device=RT.device).repeat(RT.shape[0], 1),\n    ], dim=1)\n    return torch.cat([\n        E.reshape(-1, 16),\n        I.reshape(-1, 9),\n    ], dim=-1)\n\n\ndef center_looking_at_camera_pose(\n    camera_position: torch.Tensor, look_at: torch.Tensor = None, up_world: torch.Tensor = None,\n    device: torch.device = torch.device('cpu'),\n    ):\n    \"\"\"\n    camera_position: (M, 3)\n    look_at: (3)\n    up_world: (3)\n    return: (M, 3, 4)\n    \"\"\"\n    # by default, looking at the origin and world up is pos-z\n    if look_at is None:\n        look_at = torch.tensor([0, 0, 0], dtype=torch.float32, device=device)\n    if up_world is None:\n        up_world = torch.tensor([0, 0, 1], dtype=torch.float32, device=device)\n    look_at = look_at.unsqueeze(0).repeat(camera_position.shape[0], 1)\n    up_world = up_world.unsqueeze(0).repeat(camera_position.shape[0], 1)\n\n    z_axis = camera_position - look_at\n    z_axis = z_axis / z_axis.norm(dim=-1, keepdim=True)\n    x_axis = torch.cross(up_world, z_axis)\n    x_axis = x_axis / x_axis.norm(dim=-1, keepdim=True)\n    y_axis = torch.cross(z_axis, x_axis)\n    y_axis = y_axis / y_axis.norm(dim=-1, keepdim=True)\n    extrinsics = torch.stack([x_axis, y_axis, z_axis, camera_position], dim=-1)\n    return extrinsics\n\n\ndef surrounding_views_linspace(n_views: int, radius: float = 2.0, height: float = 0.8, device: torch.device = torch.device('cpu')):\n    \"\"\"\n    n_views: number of surrounding views\n    radius: camera dist to center\n    height: height of the camera\n    return: (M, 3, 4)\n    \"\"\"\n    assert n_views > 0\n    assert radius > 0\n\n    theta = torch.linspace(-torch.pi / 2, 3 * torch.pi / 2, n_views, device=device)\n    projected_radius = math.sqrt(radius ** 2 - height ** 2)\n    x = torch.cos(theta) * projected_radius\n    y = torch.sin(theta) * projected_radius\n    z = torch.full((n_views,), height, device=device)\n\n    camera_positions = torch.stack([x, y, z], dim=1)\n    extrinsics = center_looking_at_camera_pose(camera_positions, device=device)\n\n    return extrinsics\n\n\ndef create_intrinsics(\n    f: float,\n    c: float = None, cx: float = None, cy: float = None,\n    w: float = 1., h: float = 1.,\n    dtype: torch.dtype = torch.float32,\n    device: torch.device = torch.device('cpu'),\n    ):\n    \"\"\"\n    return: (3, 2)\n    \"\"\"\n    fx = fy = f\n    if c is not None:\n        assert cx is None and cy is None, \"c and cx/cy cannot be used together\"\n        cx = cy = c\n    else:\n        assert cx is not None and cy is not None, \"cx/cy must be provided when c is not provided\"\n    fx, fy, cx, cy, w, h = fx/w, fy/h, cx/w, cy/h, 1., 1.\n    intrinsics = torch.tensor([\n        [fx, fy],\n        [cx, cy],\n        [w, h],\n    ], dtype=dtype, device=device)\n    return intrinsics\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom .mixer import MixerDataset\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom abc import ABC, abstractmethod\nimport json\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom megfile import smart_open, smart_path_join, smart_exists\n\n\nclass BaseDataset(torch.utils.data.Dataset, ABC):\n    def __init__(self, root_dirs: list[str], meta_path: str):\n        super().__init__()\n        self.root_dirs = root_dirs\n        self.uids = self._load_uids(meta_path)\n\n    def __len__(self):\n        return len(self.uids)\n\n    @abstractmethod\n    def inner_get_item(self, idx):\n        pass\n\n    def __getitem__(self, idx):\n        try:\n            return self.inner_get_item(idx)\n        except Exception as e:\n            print(f\"[DEBUG-DATASET] Error when loading {self.uids[idx]}\")\n            # return self.__getitem__(idx+1)\n            raise e\n\n    @staticmethod\n    def _load_uids(meta_path: str):\n        # meta_path is a json file\n        with open(meta_path, 'r') as f:\n            uids = json.load(f)\n        return uids\n\n    @staticmethod\n    def _load_rgba_image(file_path, bg_color: float = 1.0):\n        ''' Load and blend RGBA image to RGB with certain background, 0-1 scaled '''\n        rgba = np.array(Image.open(smart_open(file_path, 'rb')))\n        rgba = torch.from_numpy(rgba).float() / 255.0\n        rgba = rgba.permute(2, 0, 1).unsqueeze(0)\n        rgb = rgba[:, :3, :, :] * rgba[:, 3:4, :, :] + bg_color * (1 - rgba[:, 3:, :, :])\n        rgba[:, :3, ...] * rgba[:, 3:, ...] + (1 - rgba[:, 3:, ...])\n        return rgb\n\n    @staticmethod\n    def _locate_datadir(root_dirs, uid, locator: str):\n        for root_dir in root_dirs:\n            datadir = smart_path_join(root_dir, uid, locator)\n            if smart_exists(datadir):\n                return root_dir\n        raise FileNotFoundError(f\"Cannot find valid data directory for uid {uid}\")\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom accelerate.logging import get_logger\n\n\nlogger = get_logger(__name__)\n\n\ndef configure_dynamo(config: dict):\n    try:\n        import torch._dynamo\n        logger.debug(f'Configuring torch._dynamo.config with {config}')\n        for k, v in config.items():\n            if v is None:\n                logger.debug(f'Skipping torch._dynamo.config.{k} with None')\n                continue\n            if hasattr(torch._dynamo.config, k):\n                logger.warning(f'Overriding torch._dynamo.config.{k} from {getattr(torch._dynamo.config, k)} to {v}')\n                setattr(torch._dynamo.config, k, v)\n    except ImportError:\n        logger.debug('torch._dynamo not found, skipping')\n        pass\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nfrom torch.profiler import profile\n\n\nclass DummyProfiler(profile):\n    def __init__(self):\n        pass\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, *args):\n        pass\n\n    def step(self):\n        pass\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\n\nNO_PROXY = \"OPENLRM_NO_DATA_PROXY\" in os.environ\n\ndef no_proxy(func):\n    \"\"\"Decorator to disable proxy but then restore after the function call.\"\"\"\n    def wrapper(*args, **kwargs):\n        # http_proxy, https_proxy, HTTP_PROXY, HTTPS_PROXY, all_proxy\n        http_proxy = os.environ.get('http_proxy')\n        https_proxy = os.environ.get('https_proxy')\n        HTTP_PROXY = os.environ.get('HTTP_PROXY')\n        HTTPS_PROXY = os.environ.get('HTTPS_PROXY')\n        all_proxy = os.environ.get('all_proxy')\n        os.environ['http_proxy'] = ''\n        os.environ['https_proxy'] = ''\n        os.environ['HTTP_PROXY'] = ''\n        os.environ['HTTPS_PROXY'] = ''\n        os.environ['all_proxy'] = ''\n        try:\n            return func(*args, **kwargs)\n        finally:\n            os.environ['http_proxy'] = http_proxy\n            os.environ['https_proxy'] = https_proxy\n            os.environ['HTTP_PROXY'] = HTTP_PROXY\n            os.environ['HTTPS_PROXY'] = HTTPS_PROXY\n            os.environ['all_proxy'] = all_proxy\n    if NO_PROXY:\n        return wrapper\n    else:\n        return func\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport math\nfrom torch.optim.lr_scheduler import LRScheduler\nfrom accelerate.logging import get_logger\n\n\nlogger = get_logger(__name__)\n\n\nclass CosineWarmupScheduler(LRScheduler):\n    def __init__(self, optimizer, warmup_iters: int, max_iters: int, initial_lr: float = 1e-10, last_iter: int = -1):\n        self.warmup_iters = warmup_iters\n        self.max_iters = max_iters\n        self.initial_lr = initial_lr\n        super().__init__(optimizer, last_iter)\n\n    def get_lr(self):\n        logger.debug(f\"step count: {self._step_count} | warmup iters: {self.warmup_iters} | max iters: {self.max_iters}\")\n        if self._step_count <= self.warmup_iters:\n            return [\n                self.initial_lr + (base_lr - self.initial_lr) * self._step_count / self.warmup_iters\n                for base_lr in self.base_lrs]\n        else:\n            cos_iter = self._step_count - self.warmup_iters\n            cos_max_iter = self.max_iters - self.warmup_iters\n            cos_theta = cos_iter / cos_max_iter * math.pi\n            cos_lr = [base_lr * (1 + math.cos(cos_theta)) / 2 for base_lr in self.base_lrs]\n            return cos_lr\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nclass Registry:\n    \"\"\"Registry class\"\"\"\n\n    def __init__(self):\n        self._registry = {}\n\n    def register(self, name):\n        \"\"\"Register a module\"\"\"\n        def decorator(cls):\n            assert name not in self._registry, 'Module {} already registered'.format(name)\n            self._registry[name] = cls\n            return cls\n        return decorator\n\n    def __getitem__(self, name):\n        \"\"\"Get a module\"\"\"\n        return self._registry[name]\n\n    def __contains__(self, name):\n        return name in self._registry\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport numpy as np\nimport rembg\nimport cv2\n\n\nclass Preprocessor:\n\n    \"\"\"\n    Preprocessing under cv2 conventions.\n    \"\"\"\n\n    def __init__(self):\n        self.rembg_session = rembg.new_session(\n            providers=[\"CUDAExecutionProvider\", \"CPUExecutionProvider\"],\n        )\n\n    def preprocess(self, image_path: str, save_path: str, rmbg: bool = True, recenter: bool = True, size: int = 512, border_ratio: float = 0.2):\n        image = self.step_load_to_size(image_path=image_path, size=size*2)\n        if rmbg:\n            image = self.step_rembg(image_in=image)\n        else:\n            image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)\n        if recenter:\n            image = self.step_recenter(image_in=image, border_ratio=border_ratio, square_size=size)\n        else:\n            image = cv2.resize(\n                src=image,\n                dsize=(size, size),\n                interpolation=cv2.INTER_AREA,\n            )\n        return cv2.imwrite(save_path, image)\n\n    def step_rembg(self, image_in: np.ndarray) -> np.ndarray:\n        image_out = rembg.remove(\n            data=image_in,\n            session=self.rembg_session,\n        )\n        return image_out\n\n    def step_recenter(self, image_in: np.ndarray, border_ratio: float, square_size: int) -> np.ndarray:\n        assert image_in.shape[-1] == 4, \"Image to recenter must be RGBA\"\n        mask = image_in[..., -1] > 0\n        ijs = np.nonzero(mask)\n        # find bbox\n        i_min, i_max = ijs[0].min(), ijs[0].max()\n        j_min, j_max = ijs[1].min(), ijs[1].max()\n        bbox_height, bbox_width = i_max - i_min, j_max - j_min\n        # recenter and resize\n        desired_size = int(square_size * (1 - border_ratio))\n        scale = desired_size / max(bbox_height, bbox_width)\n        desired_height, desired_width = int(bbox_height * scale), int(bbox_width * scale)\n        desired_i_min, desired_j_min = (square_size - desired_height) // 2, (square_size - desired_width) // 2\n        desired_i_max, desired_j_max = desired_i_min + desired_height, desired_j_min + desired_width\n        # create new image\n        image_out = np.zeros((square_size, square_size, 4), dtype=np.uint8)\n        image_out[desired_i_min:desired_i_max, desired_j_min:desired_j_max] = cv2.resize(\n            src=image_in[i_min:i_max, j_min:j_max],\n            dsize=(desired_width, desired_height),\n            interpolation=cv2.INTER_AREA,\n        )\n        return image_out\n\n    def step_load_to_size(self, image_path: str, size: int) -> np.ndarray:\n        image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)\n        height, width = image.shape[:2]\n        scale = size / max(height, width)\n        height, width = int(height * scale), int(width * scale)\n        image_out = cv2.resize(\n            src=image,\n            dsize=(width, height),\n            interpolation=cv2.INTER_AREA,\n        )\n        return image_out\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nimport numpy as np\nimport imageio\n\n\ndef images_to_video(images, output_path, fps, gradio_codec: bool, verbose=False):\n    # images: (T, C, H, W)\n    os.makedirs(os.path.dirname(output_path), exist_ok=True)\n    frames = []\n    for i in range(images.shape[0]):\n        frame = (images[i].permute(1, 2, 0).cpu().numpy() * 255).astype(np.uint8)\n        assert frame.shape[0] == images.shape[2] and frame.shape[1] == images.shape[3], \\\n            f\"Frame shape mismatch: {frame.shape} vs {images.shape}\"\n        assert frame.min() >= 0 and frame.max() <= 255, \\\n            f\"Frame value out of range: {frame.min()} ~ {frame.max()}\"\n        frames.append(frame)\n    frames = np.stack(frames)\n    if gradio_codec:\n        imageio.mimwrite(output_path, frames, fps=fps, quality=10)\n    else:\n        imageio.mimwrite(output_path, frames, fps=fps, codec='mpeg4', quality=10)\n    if verbose:\n        print(f\"Using gradio codec option {gradio_codec}\")\n        print(f\"Saved video to {output_path}\")\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport os\nimport logging\nfrom tqdm.auto import tqdm\n\n\nclass TqdmStreamHandler(logging.StreamHandler):\n    def emit(self, record):\n        tqdm.write(self.format(record))\n\n\ndef configure_logger(stream_level, log_level, file_path = None):\n    _stream_level = stream_level.upper()\n    _log_level = log_level.upper()\n    _project_level = _log_level\n\n    _formatter = logging.Formatter(\"[%(asctime)s] %(name)s: [%(levelname)s] %(message)s\")\n\n    _stream_handler = TqdmStreamHandler()\n    _stream_handler.setLevel(_stream_level)\n    _stream_handler.setFormatter(_formatter)\n\n    if file_path is not None:\n        os.makedirs(os.path.dirname(file_path), exist_ok=True)\n        _file_handler = logging.FileHandler(file_path)\n        _file_handler.setLevel(_log_level)\n        _file_handler.setFormatter(_formatter)\n\n    _project_logger = logging.getLogger(__name__.split('.')[0])\n    _project_logger.setLevel(_project_level)\n    _project_logger.addHandler(_stream_handler)\n    if file_path is not None:\n        _project_logger.addHandler(_file_handler)\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport torch.nn as nn\nfrom huggingface_hub import PyTorchModelHubMixin\n\n\ndef wrap_model_hub(model_cls: nn.Module):\n    class HfModel(model_cls, PyTorchModelHubMixin):\n        def __init__(self, config: dict):\n            super().__init__(**config)\n            self.config = config\n    return HfModel\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Empty\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport sys\nsys.path.append('.')\n\nimport argparse\nfrom accelerate import Accelerator\n\nfrom openlrm.utils.hf_hub import wrap_model_hub\nfrom openlrm.models import model_dict\n\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--model_type', type=str, required=True)\n    parser.add_argument('--local_ckpt', type=str, required=True)\n    parser.add_argument('--repo_id', type=str, required=True)\n    args, unknown = parser.parse_known_args()\n\n    accelerator = Accelerator()\n\n    hf_model_cls = wrap_model_hub(model_dict[args.model_type])\n    hf_model = hf_model_cls.from_pretrained(args.local_ckpt)\n    hf_model.push_to_hub(\n        repo_id=args.repo_id,\n        config=hf_model.config,\n        private=True,\n    )\n\n\n# Copyright (c) 2023-2024, Zexin He\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nimport argparse\nfrom omegaconf import OmegaConf\nfrom megfile import smart_path_join, smart_exists, smart_listdir, smart_makedirs, smart_copy\nfrom tempfile import TemporaryDirectory\nimport torch.nn as nn\nfrom accelerate import Accelerator\nimport safetensors\n\nimport sys\nsys.path.append(\".\")\n\nfrom openlrm.utils.hf_hub import wrap_model_hub\nfrom openlrm.utils.proxy import no_proxy\nfrom openlrm.models import model_dict\n\n\n@no_proxy\ndef auto_load_model(cfg, model: nn.Module) -> int:\n\n    ckpt_root = smart_path_join(\n        cfg.saver.checkpoint_root,\n        cfg.experiment.parent, cfg.experiment.child,\n    )\n    if not smart_exists(ckpt_root):\n        raise FileNotFoundError(f\"Checkpoint root not found: {ckpt_root}\")\n    ckpt_dirs = smart_listdir(ckpt_root)\n    if len(ckpt_dirs) == 0:\n        raise FileNotFoundError(f\"No checkpoint found in {ckpt_root}\")\n    ckpt_dirs.sort()\n\n    load_step = f\"{cfg.convert.global_step}\" if cfg.convert.global_step is not None else ckpt_dirs[-1]\n    load_model_path = smart_path_join(ckpt_root, load_step, 'model.safetensors')\n\n    if load_model_path.startswith(\"s3\"):\n        tmpdir = TemporaryDirectory()\n        tmp_model_path = smart_path_join(tmpdir.name, f\"tmp.safetensors\")\n        smart_copy(load_model_path, tmp_model_path)\n        load_model_path = tmp_model_path\n\n    print(f\"Loading from {load_model_path}\")\n    safetensors.torch.load_model(model, load_model_path)\n\n    return int(load_step)\n\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--config', type=str, default='./assets/config.yaml')\n    args, unknown = parser.parse_known_args()\n    cfg = OmegaConf.load(args.config)\n    cli_cfg = OmegaConf.from_cli(unknown)\n    cfg = OmegaConf.merge(cfg, cli_cfg)\n\n    \"\"\"\n    [cfg.convert]\n    global_step: int\n    save_dir: str\n    \"\"\"\n\n    accelerator = Accelerator()\n\n    hf_model_cls = wrap_model_hub(model_dict[cfg.experiment.type])\n    hf_model = hf_model_cls(OmegaConf.to_container(cfg.model))\n    loaded_step = auto_load_model(cfg, hf_model)\n    dump_path = smart_path_join(\n        f\"./exps/releases\",\n        cfg.experiment.parent, cfg.experiment.child,\n        f'step_{loaded_step:06d}',\n    )\n    print(f\"Saving locally to {dump_path}\")\n    smart_makedirs(dump_path, exist_ok=True)\n    hf_model.save_pretrained(\n        save_directory=dump_path,\n        config=hf_model.config,\n    )\n\n\n\"\"\"\nBlender script to render images of 3D models.\nThis script is designed to render data used in the [OpenLRM project](https://github.com/3DTopia/OpenLRM).\n\nModified from https://github.com/cvlab-columbia/zero123/blob/main/objaverse-rendering/scripts/blender_script.py\nOriginal script licensed under MIT, found at the root of its repository.\nModifications are licensed under Apache 2.0.\n\"\"\"\n\nimport argparse\nimport math\nimport os\nimport random\nimport sys\nimport time\nimport urllib.request\nfrom typing import Tuple\nfrom mathutils import Vector\nimport numpy as np\nimport bpy\n\n\nparser = argparse.ArgumentParser()\nparser.add_argument(\n    \"--object_path\",\n    type=str,\n    required=True,\n    help=\"Path to the object file\",\n)\nparser.add_argument(\"--output_dir\", type=str, default=\"./views\")\nparser.add_argument(\n    \"--engine\", type=str, default=\"CYCLES\", choices=[\"CYCLES\", \"BLENDER_EEVEE\"]\n)\nparser.add_argument(\"--num_images\", type=int, default=32)\nparser.add_argument(\"--resolution\", type=int, default=1024)\n    \nargv = sys.argv[sys.argv.index(\"--\") + 1 :]\nargs = parser.parse_args(argv)\n\nprint('===================', args.engine, '===================')\n\ncontext = bpy.context\nscene = context.scene\nrender = scene.render\n\nrender.engine = args.engine\nrender.image_settings.file_format = \"PNG\"\nrender.image_settings.color_mode = \"RGBA\"\nrender.resolution_x = args.resolution\nrender.resolution_y = args.resolution\nrender.resolution_percentage = 100\n\nscene.cycles.device = \"GPU\"\nscene.cycles.samples = 128\nscene.cycles.diffuse_bounces = 1\nscene.cycles.glossy_bounces = 1\nscene.cycles.transparent_max_bounces = 3\nscene.cycles.transmission_bounces = 3\nscene.cycles.filter_width = 0.01\nscene.cycles.use_denoising = True\nscene.render.film_transparent = True\n\n# Set the device_type\ncycles_preferences = bpy.context.preferences.addons[\"cycles\"].preferences\ncycles_preferences.compute_device_type = \"CUDA\"  # or \"OPENCL\"\ncuda_devices = cycles_preferences.get_devices_for_type(\"CUDA\")\nfor device in cuda_devices:\n    device.use = True\n\ndef compose_RT(R, T):\n    return np.hstack((R, T.reshape(-1, 1)))\n\ndef sample_point_on_sphere(radius: float) -> Tuple[float, float, float]:\n    theta = random.random() * 2 * math.pi\n    phi = math.acos(2 * random.random() - 1)\n    return (\n        radius * math.sin(phi) * math.cos(theta),\n        radius * math.sin(phi) * math.sin(theta),\n        radius * math.cos(phi),\n    )\n\ndef sample_spherical(radius=3.0, maxz=3.0, minz=0.):\n    correct = False\n    while not correct:\n        vec = np.random.uniform(-1, 1, 3)\n        vec[2] = np.abs(vec[2])\n        vec = vec / np.linalg.norm(vec, axis=0) * radius\n        if maxz > vec[2] > minz:\n            correct = True\n    return vec\n\ndef sample_spherical(radius_min=1.5, radius_max=2.0, maxz=1.6, minz=-0.75):\n    correct = False\n    while not correct:\n        vec = np.random.uniform(-1, 1, 3)\n#         vec[2] = np.abs(vec[2])\n        radius = np.random.uniform(radius_min, radius_max, 1)\n        vec = vec / np.linalg.norm(vec, axis=0) * radius[0]\n        if maxz > vec[2] > minz:\n            correct = True\n    return vec\n\ndef set_camera_location(camera, option: str):\n    assert option in ['fixed', 'random', 'front']\n\n    if option == 'fixed':\n        x, y, z = 0, -2.25, 0\n    elif option == 'random':\n        # from https://blender.stackexchange.com/questions/18530/\n        x, y, z = sample_spherical(radius_min=1.9, radius_max=2.6, maxz=1.60, minz=-0.75)\n    elif option == 'front':\n        x, y, z = 0, -np.random.uniform(1.9, 2.6, 1)[0], 0\n\n    camera.location = x, y, z\n\n    # adjust orientation\n    direction = - camera.location\n    rot_quat = direction.to_track_quat('-Z', 'Y')\n    camera.rotation_euler = rot_quat.to_euler()\n    return camera\n\ndef add_lighting(option: str) -> None:\n    assert option in ['fixed', 'random']\n    \n    # delete the default light\n    bpy.data.objects[\"Light\"].select_set(True)\n    bpy.ops.object.delete()\n    \n    # add a new light\n    bpy.ops.object.light_add(type=\"AREA\")\n    light = bpy.data.lights[\"Area\"]\n\n    if option == 'fixed':\n        light.energy = 30000\n        bpy.data.objects[\"Area\"].location[0] = 0\n        bpy.data.objects[\"Area\"].location[1] = 1\n        bpy.data.objects[\"Area\"].location[2] = 0.5\n\n    elif option == 'random':\n        light.energy = random.uniform(80000, 120000)\n        bpy.data.objects[\"Area\"].location[0] = random.uniform(-2., 2.)\n        bpy.data.objects[\"Area\"].location[1] = random.uniform(-2., 2.)\n        bpy.data.objects[\"Area\"].location[2] = random.uniform(1.0, 3.0)\n\n    # set light scale\n    bpy.data.objects[\"Area\"].scale[0] = 200\n    bpy.data.objects[\"Area\"].scale[1] = 200\n    bpy.data.objects[\"Area\"].scale[2] = 200\n\n\ndef reset_scene() -> None:\n    \"\"\"Resets the scene to a clean state.\"\"\"\n    # delete everything that isn't part of a camera or a light\n    for obj in bpy.data.objects:\n        if obj.type not in {\"CAMERA\", \"LIGHT\"}:\n            bpy.data.objects.remove(obj, do_unlink=True)\n    # delete all the materials\n    for material in bpy.data.materials:\n        bpy.data.materials.remove(material, do_unlink=True)\n    # delete all the textures\n    for texture in bpy.data.textures:\n        bpy.data.textures.remove(texture, do_unlink=True)\n    # delete all the images\n    for image in bpy.data.images:\n        bpy.data.images.remove(image, do_unlink=True)\n\n\n# load the glb model\ndef load_object(object_path: str) -> None:\n    \"\"\"Loads a glb model into the scene.\"\"\"\n    if object_path.endswith(\".glb\"):\n        bpy.ops.import_scene.gltf(filepath=object_path, merge_vertices=True)\n    elif object_path.endswith(\".fbx\"):\n        bpy.ops.import_scene.fbx(filepath=object_path)\n    else:\n        raise ValueError(f\"Unsupported file type: {object_path}\")\n\n\ndef scene_bbox(single_obj=None, ignore_matrix=False):\n    bbox_min = (math.inf,) * 3\n    bbox_max = (-math.inf,) * 3\n    found = False\n    for obj in scene_meshes() if single_obj is None else [single_obj]:\n        found = True\n        for coord in obj.bound_box:\n            coord = Vector(coord)\n            if not ignore_matrix:\n                coord = obj.matrix_world @ coord\n            bbox_min = tuple(min(x, y) for x, y in zip(bbox_min, coord))\n            bbox_max = tuple(max(x, y) for x, y in zip(bbox_max, coord))\n    if not found:\n        raise RuntimeError(\"no objects in scene to compute bounding box for\")\n    return Vector(bbox_min), Vector(bbox_max)\n\n\ndef scene_root_objects():\n    for obj in bpy.context.scene.objects.values():\n        if not obj.parent:\n            yield obj\n\n\ndef scene_meshes():\n    for obj in bpy.context.scene.objects.values():\n        if isinstance(obj.data, (bpy.types.Mesh)):\n            yield obj\n\n\ndef normalize_scene(box_scale: float):\n    bbox_min, bbox_max = scene_bbox()\n    scale = box_scale / max(bbox_max - bbox_min)\n    for obj in scene_root_objects():\n        obj.scale = obj.scale * scale\n    # Apply scale to matrix_world.\n    bpy.context.view_layer.update()\n    bbox_min, bbox_max = scene_bbox()\n    offset = -(bbox_min + bbox_max) / 2\n    for obj in scene_root_objects():\n        obj.matrix_world.translation += offset\n    bpy.ops.object.select_all(action=\"DESELECT\")\n\n\ndef setup_camera():\n    cam = scene.objects[\"Camera\"]\n    cam.location = (0, 1.2, 0)\n    cam.data.lens = 24\n    cam.data.sensor_width = 32\n    cam.data.sensor_height = 32  # affects instrinsics calculation, should be set explicitly\n    cam_constraint = cam.constraints.new(type=\"TRACK_TO\")\n    cam_constraint.track_axis = \"TRACK_NEGATIVE_Z\"\n    cam_constraint.up_axis = \"UP_Y\"\n    return cam, cam_constraint\n\n\ndef save_images(object_file: str) -> None:\n    \"\"\"Saves rendered images of the object in the scene.\"\"\"\n    os.makedirs(args.output_dir, exist_ok=True)\n    reset_scene()\n\n    # load the object\n    load_object(object_file)\n    object_uid = os.path.basename(object_file).split(\".\")[0]\n    normalize_scene(box_scale=2)\n    add_lighting(option='random')\n    camera, cam_constraint = setup_camera()\n\n    # create an empty object to track\n    empty = bpy.data.objects.new(\"Empty\", None)\n    scene.collection.objects.link(empty)\n    cam_constraint.target = empty\n\n    # prepare to save\n    img_dir = os.path.join(args.output_dir, object_uid, 'rgba')\n    pose_dir = os.path.join(args.output_dir, object_uid, 'pose')\n    os.makedirs(img_dir, exist_ok=True)\n    os.makedirs(pose_dir, exist_ok=True)\n\n    for i in range(args.num_images):\n        # set the camera position\n        camera_option = 'random' if i > 0 else 'front'\n        camera = set_camera_location(camera, option=camera_option)\n\n        # render the image\n        render_path = os.path.join(img_dir, f\"{i:03d}.png\")\n        scene.render.filepath = render_path\n        bpy.ops.render.render(write_still=True)\n\n        # save camera RT matrix (C2W)\n        location, rotation = camera.matrix_world.decompose()[0:2]\n        RT = compose_RT(rotation.to_matrix(), np.array(location))\n        RT_path = os.path.join(pose_dir, f\"{i:03d}.npy\")\n        np.save(RT_path, RT)\n    \n    # save the camera intrinsics\n    intrinsics = get_calibration_matrix_K_from_blender(camera.data, return_principles=True)\n    with open(os.path.join(args.output_dir, object_uid,'intrinsics.npy'), 'wb') as f_intrinsics:\n        np.save(f_intrinsics, intrinsics)\n\n\ndef download_object(object_url: str) -> str:\n    \"\"\"Download the object and return the path.\"\"\"\n    # uid = uuid.uuid4()\n    uid = object_url.split(\"/\")[-1].split(\".\")[0]\n    tmp_local_path = os.path.join(\"tmp-objects\", f\"{uid}.glb\" + \".tmp\")\n    local_path = os.path.join(\"tmp-objects\", f\"{uid}.glb\")\n    # wget the file and put it in local_path\n    os.makedirs(os.path.dirname(tmp_local_path), exist_ok=True)\n    urllib.request.urlretrieve(object_url, tmp_local_path)\n    os.rename(tmp_local_path, local_path)\n    # get the absolute path\n    local_path = os.path.abspath(local_path)\n    return local_path\n\n\ndef get_calibration_matrix_K_from_blender(camera, return_principles=False):\n    \"\"\"\n        Get the camera intrinsic matrix from Blender camera.\n        Return also numpy array of principle parameters if specified.\n        \n        Intrinsic matrix K has the following structure in pixels:\n            [fx  0 cx]\n            [0  fy cy]\n            [0   0  1]\n        \n        Specified principle parameters are:\n            [fx, fy] - focal lengths in pixels\n            [cx, cy] - optical centers in pixels\n            [width, height] - image resolution in pixels\n        \n    \"\"\"\n    # Render resolution\n    render = bpy.context.scene.render\n    width = render.resolution_x * render.pixel_aspect_x\n    height = render.resolution_y * render.pixel_aspect_y\n\n    # Camera parameters\n    focal_length = camera.lens  # Focal length in millimeters\n    sensor_width = camera.sensor_width  # Sensor width in millimeters\n    sensor_height = camera.sensor_height  # Sensor height in millimeters\n\n    # Calculate the focal length in pixel units\n    focal_length_x = width * (focal_length / sensor_width)\n    focal_length_y = height * (focal_length / sensor_height)\n\n    # Assuming the optical center is at the center of the sensor\n    optical_center_x = width / 2\n    optical_center_y = height / 2\n\n    # Constructing the intrinsic matrix\n    K = np.array([[focal_length_x, 0, optical_center_x],\n                [0, focal_length_y, optical_center_y],\n                [0, 0, 1]])\n    \n    if return_principles:\n        return np.array([\n            [focal_length_x, focal_length_y],\n            [optical_center_x, optical_center_y],\n            [width, height],\n        ])\n    else:\n        return K\n\n\nif __name__ == \"__main__\":\n    try:\n        start_i = time.time()\n        if args.object_path.startswith(\"http\"):\n            local_path = download_object(args.object_path)\n        else:\n            local_path = args.object_path\n        save_images(local_path)\n        end_i = time.time()\n        print(\"Finished\", local_path, \"in\", end_i - start_i, \"seconds\")\n        # delete the object if it was downloaded\n        if args.object_path.startswith(\"http\"):\n            os.remove(local_path)\n    except Exception as e:\n        print(\"Failed to render\", args.object_path)\n        print(e)\n\n\nLRM: LARGE RECONSTRUCTION MODEL FOR\nSINGLE IMAGE TO 3D\nYicong Hong12˚ Kai Zhang1\nJiuxiang Gu1\nSai Bi1\nYang Zhou1\nDifan Liu1\nFeng Liu1\nKalyan Sunkavalli1\nTrung Bui1\nHao Tan1\n1Adobe Research\n2Australian National Univeristy\nmr.yiconghong@gmail.com\n{kaiz,jigu,sbi,yazhou,diliu,fengl,sunkaval,bui,hatan}@adobe.com\nABSTRACT\nWe propose the first Large Reconstruction Model (LRM) that predicts the 3D\nmodel of an object from a single input image within just 5 seconds. In contrast to\nmany previous methods that are trained on small-scale datasets such as ShapeNet\nin a category-specific fashion, LRM adopts a highly scalable transformer-based\narchitecture with 500 million learnable parameters to directly predict a neural ra-\ndiance field (NeRF) from the input image. We train our model in an end-to-end\nmanner on massive multi-view data containing around 1 million objects, includ-\ning both synthetic renderings from Objaverse and real captures from MVImgNet.\nThis combination of a high-capacity model and large-scale training data empowers\nour model to be highly generalizable and produce high-quality 3D reconstructions\nfrom various testing inputs, including real-world in-the-wild captures and images\ncreated by generative models. Video demos and interactable 3D meshes can be\nfound on our LRM project webpage: https://yiconghong.me/LRM.\n1\nINTRODUCTION\nImagine if we could instantly create a 3D shape from a single image of an arbitrary object. Broad\napplications in industrial design, animation, gaming, and AR/VR have strongly motivated relevant\nresearch in seeking a generic and efficient approach towards this long-standing goal. Due to the\nunderlying ambiguity of 3D geometry in a single view, early learning-based methods usually per-\nform well on specific categories, utilizing the category data prior to infer the overall shape (Yu et al.,\n2021). Recently, advances in image generation, such as DALL-E (Ramesh et al., 2021) and Stable\nDiffusion (Rombach et al., 2022), have inspired research that leverages the remarkable generaliza-\ntion capability of 2D diffusion models to enable multi-view supervision (Liu et al., 2023b; Tang\net al., 2023). However, many of these methods require delicate parameter tuning and regularization,\nand their results are limited by the pre-trained 2D generative models. Meanwhile, there are many\napproaches that rely on per-shape optimization (e.g. optimize a NeRF (Mildenhall et al., 2021;\nChan et al., 2022; Chen et al., 2022a; M¨\nuller et al., 2022; Sun et al., 2022)) to construct a consistent\ngeometry; this process is often slow and impractical.\nOn the other hand, the great success in natural language processing (Devlin et al., 2018; Brown\net al., 2020; Chowdhery et al., 2022) and image processing (Caron et al., 2021; Radford et al.,\n2021; Alayrac et al., 2022; Ramesh et al., 2022) can be largely credited to three critical factors:\n(1) using highly scalable and effective neural networks, such as the Transformers (Vaswani et al.,\n2017), for modeling the data distribution, (2) enormous datasets for learning generic priors, as well\nas (3) self-supervised-like training objectives that encourage the model to discover the underlying\ndata structure while maintaining high scalability. For instance, the GPT (generative pre-trained\ntransformer) series (Radford et al., 2019; Brown et al., 2020; OpenAI, 2023) build large language\nmodels with huge transformer networks, large-scale data, and the simple next-word prediction task.\nIn light of this, we pose the same question for 3D: given sufficient 3D data and a large-scale training\nframework, is it possible to learn a generic 3D prior for reconstructing an object from a single\nimage?\n˚Intern at Adobe Research.\n1\n\n\nIn this paper, we propose a Large Reconstruction Model (LRM) for single-image to 3D. Our method\nadopts a large transformer-based encoder-decoder architecture for learning 3D representations of ob-\njects from a single image in a data-driven manner. Our method takes an image as input and regresses\na NeRF in the form of a triplane representation (Chan et al., 2022). Specifically, LRM utilizes the\npre-trained visual transformer DINO (Caron et al., 2021) as the image encoder to generate the image\nfeatures, and learns an image-to-triplane transformer decoder to project the 2D image features onto\nthe 3D triplane via cross-attention and model the relations among the spatially-structured triplane\ntokens via self-attention. The output tokens from the decoder are reshaped and upsampled to the\nfinal triplane feature maps. Afterwards, we can render the images at an arbitrary view by decoding\nthe triplane feature of each point with an additional shared multi-layer perception (MLP) to get its\ncolor and density and performing volume rendering.\nThe overall design of LRM maintains high scalability and efficiency. In addition to the use of a fully\ntransformer-based pipeline, a triplane NeRF is a concise and scalable 3D representation since it is\ncomputationally friendly compared to other representations such as volumes and point clouds. It\nalso has a better locality with respect to the image input compared to tokenizing the NeRF’s model\nweights as in Shap-E (Jun & Nichol, 2023). Moreover, our LRM is trained by simply minimizing the\ndifference between the rendered images and ground truth images at novel views, without excessive\n3D-aware regularization or delicate hyper-parameter tuning, allowing the model to be very efficient\nin training and adaptable to a wide range of multi-view image datasets.\nTo the best of our knowledge, LRM is the first large-scale 3D reconstruction model; it contains more\nthan 500 million learnable parameters, and it is trained on approximately one million 3D shapes and\nvideo data across diverse categories (Deitke et al., 2023; Yu et al., 2023); this is substantially larger\nthan recent methods that apply relatively shallower networks and smaller datasets (Chang et al.,\n2015; Reizenstein et al., 2021; Downs et al., 2022). Through experiments, we show that LRM can\nreconstruct high-fidelity 3D shapes from a wide range of images captured in the real world, as well\nas images created by generative models. LRM is also a highly practical solution for downstream\napplications since it can produce a 3D shape in just five seconds1 without post-optimization.\n2\nRELATED WORK\nSingle Image to 3D Reconstruction\nExtensive efforts have been devoted to address this prob-\nlem, including early learning-based methods that explore point clouds (Fan et al., 2017; Wu et al.,\n2020), voxels (Choy et al., 2016; Tulsiani et al., 2017; Chen & Zhang, 2019), and meshes (Wang\net al., 2018; Gkioxari et al., 2019), as well as various approaches that learn implicit representa-\ntions such as SDFs (Park et al., 2019; Mittal et al., 2022), occupancy networks (Mescheder et al.,\n2019), and NeRF (Jang & Agapito, 2021; M¨\nuller et al., 2022). Leveraging 3D templates (Roth et al.,\n2016; Goel et al., 2020; Kanazawa et al., 2018; Kulkarni et al., 2020), semantics (Li et al., 2020),\nand poses (Bogo et al., 2016; Novotny et al., 2019) as shape priors have also been widely studied\nin category-specific reconstruction. Category-agnostic methods show great generalization poten-\ntial (Yan et al., 2016; Niemeyer et al., 2020), but they often unable to produce fine-grained details\neven when exploiting spatially-aligned local image features (Xu et al., 2019; Yu et al., 2021).\nVery recently, there is an emerging trend of using pre-trained image/language models (Radford et al.,\n2021; Li et al., 2022; 2023b; Saharia et al., 2022; Rombach et al., 2022), to introduce semantics and\nmulti-view guidance for image-to-3D reconstruction (Liu et al., 2023b; Tang et al., 2023; Deng et al.,\n2023; Shen et al., 2023b; Anciukeviˇ\ncius et al., 2023; Melas-Kyriazi et al., 2023; Metzer et al., 2023;\nXu et al., 2023; Qian et al., 2023; Li et al., 2023a). For instance, Zero-1-to-3 fine-tunes the Stable\nDiffusion model to generate novel views by conditioning on the input image and camera poses (Liu\net al., 2023b); its view consistency and reconstruction efficiency have been further improved by Liu\net al. (2023a). Make-It-3D (Tang et al., 2023) uses BLIP to generate text descriptions for the input\nimage (which is applied to guide the text-to-image diffusion) and trains the model with score distilla-\ntion sampling loss (Poole et al., 2022) and CLIP image loss to create geometrically and semantically\nplausible shapes.\n1Five seconds per shape on a single NVIDIA A100 GPU, including around 1.14 seconds image-to-triplane\nfeed-forward time, 1.14 seconds to query resolution of 384ˆ384ˆ384 points from the triplane-NeRF, and 1.91\nseconds mesh extraction time using Marching Cubes (Lorensen & Cline, 1998).\n2\n\n\nIn contrast to all these methods, our LRM is a purely data-driven approach that learns to reconstruct\narbitrary objects in the wild. It is trained with minimal and extensible 3D supervision (i.e., rendered\nor captured 2D images of 3D objects) and does not rely on any guidance from pre-trained vision-\nlanguage contrastive or generative models.\nLearning 3D Representations from Images\n3D reconstruction from a single image is an ill-posed\nproblem that has been frequently addressed by models with generative properties. Many previous\nworks apply an encoder-decoder framework to model the image-to-3D data distribution (Choy et al.,\n2016; Yan et al., 2016; Dai et al., 2017; Xu et al., 2019; Wu et al., 2020; M¨\nuller et al., 2022; Sajjadi\net al., 2022; Goel et al., 2023), where a compact latent code is trained to carry the texture, geometry,\nand pose details of the target. However, learning such an expressive representation usually requires\na capable network and abundant 3D data which is very expensive to acquire. Hence most of these\nmethods only focus on a few categories and produce very coarse results. GINA-3D (Shen et al.,\n2023a) implements a model that applies a visual transformer encoder and cross-attention (instead\nof a transformer decoder as in LRM) to translate images to triplane representations. However, the\nmodel and training are much smaller in scale, and their work has a different focus on category-\nspecific generation. Recent data-driven approach MCC (Wu et al., 2023) trains a generalizable\ntransformer-based decoder with CO3D-v2 data (Reizenstein et al., 2021) to predict occupancy and\ncolor from the input image and its unprojected point cloud. Although MCC can handle real and\ngenerated images and scenes, the results are usually over-smooth and lose details.\nMultimodal 3D\nMotivated by the great advances in 2D multimodal learning (Tan & Bansal, 2019;\nChen et al., 2020; 2022b; Yu et al., 2022; Singh et al., 2022; Wang et al., 2022; Alayrac et al., 2022;\nGirdhar et al., 2023), LRM considers 3D as a new modality and directly grounds 2D feature maps\nonto 3D triplane via cross-attention. There are early attempts in this direction that minimize the dif-\nference between encoded image and 3D representations (Girdhar et al., 2016; Mandikal et al., 2018),\nas well as recent research, ULIP (Xue et al., 2023) and CLIP2 (Zeng et al., 2023), which bridges\n3D, language, and images via contrastive learning. LERF (Kerr et al., 2023) learns a language\nfield inside NeRF by rendering CLIP embeddings along training rays. In contrast, our method fo-\ncuses on generic single image-to-3D reconstruction. We would like to mention the concurrent work\nCap3D (Luo et al., 2023) that produces descriptions for 3D shapes by applying BLIP (Li et al.,\n2023b) to generate captions of different views, uses GPT-4 (OpenAI, 2023) to summarize them, and\nthen employs these language-3D pairs for training text-to-3D generative models (Nichol et al., 2022;\nPoole et al., 2022; Jun & Nichol, 2023). There are also recent works in connecting 3D and large\nlanguage models, such as 3D-LLM (Hong et al., 2023) and LLM-Grounder (Yang et al., 2023).\n3\nMETHOD\nIn this section, we detail the proposed LRM architecture (Fig. 1). LRM contains an image encoder\nthat encodes the input image to patch-wise feature tokens (Sec. 3.1), followed by an image-to-\ntriplane decoder that projects image features onto triplane tokens via cross-attention (Sec. 3.2). The\noutput triplane tokens are upsampled and reshaped into the final triplane representation, which is\nused to query 3D point features. Lastly, the 3D point features are passed to a multi-layer perception\nto predict RGB and density for volumetric rendering (Sec. 3.3). The training objectives and data are\ndescribed in Sec. 3.4 and Sec. 4.1.\n3.1\nIMAGE ENCODER\nGiven an RGB image as input, LRM first applies a pre-trained visual transformer (ViT) (Dosovitskiy\net al., 2020) to encode the image to patch-wise feature tokens thiun\ni“1 P RdE, where i denotes the\ni-th image patch, n is the total number of patches, and dE is the latent dimension of the encoder.\nSpecifically, we use DINO (Caron et al., 2021), a model trained with self-distillation that learns inter-\npretable attention over the structure and texture of the salient content in images. Compared to other\nsemantic-oriented representations such as the visual features from ImageNet-pretrained ResNet (He\net al., 2016) or CLIP (Radford et al., 2021), the detailed structural and texture information in DINO is\nmore important in our case since LRM can use it to reconstruct the geometry and color in 3D space.\n3\n\n\nSelf\nMLP\n+\n+\nMLP\n+\nSelf\n+\nCross\n+\nLearnable \npositional embeddings\nDim: (3 x 32 x 32) x 1024\nImage encoder\n12 Layers, Dim: 768, ViT (DINO)\nImage-to-Triplane Decoder\n16 Layers, Dim: 1024  \nReshape\nTriplane | Dim: 3 x (64 x 64) x 80\nSingle input image\nDim: 512 x 512 x 3\nImage features\nDim: (32 x 32) x 768\nPoint features\nDim: 3 x 80\nMLP\nRGB, 𝜎\nVolumetric Rendering\nRendered \nnovel image\nCamera features, Dim: 20\nNeural Radiance Field (NeRF)\n10 layers, Dim 64\nDeConv\nTriplane  tokens\nDim: (3 x 32 x 32) x 1024\nRes: (3 x 32 x 32) → (3 x 64 x 64)\nDim: 1024 → 80\nM\nM\nM\nM\n+\nModulation with \ncamera features\nResidual connection\nConv\nFigure 1: The overall architecture of LRM, a fully-differentiable transformer-based encoder-decoder\nframework for single-image to NeRF reconstruction.\nLRM applies a pre-trained vision model\n(DINO) to encode the input image (Sec. 3.1), where the image features are projected to a 3D tri-\nplane representation by a large transformer decoder via cross-attention (Sec. 3.2), followed by a\nmulti-layer perceptron to predict the point color and density for volumetric rendering (Sec. 3.3).\nThe entire network is trained end-to-end on around a million of 3D data (Sec. 4.1) with simple im-\nage reconstruction losses (Sec. 3.4).\nAs a result, instead of only using the ViT pre-defined class token [CLS] that aggregates patch-wise\nfeatures, we also utilize the entire feature sequence thiun\ni“1 to better preserve this information2.\n3.2\nIMAGE-TO-TRIPLANE DECODER\nWe implement a transformer decoder to project image and camera features onto learnable spatial-\npositional embeddings and translate them to triplane representations. This decoder can be considered\nas a prior network that is trained with large-scale data to provide necessary geometric and appearance\ninformation to compensate for the ambiguities of single-image reconstruction.\nCamera Features\nWe construct the camera feature c P R20 of the input image by flattening out the\n4-by-4 camera extrinsic matrix E (that represents the camera-to-world transformation) and concate-\nnate it with the camera focal length foc and principal point pp as c “ rE1ˆ16, focx, focy, ppx, ppys.\nMoreover, we normalize the camera extrinsic E by similarity transformations so that all the input\ncameras are aligned on the same axis (with the lookup direction aligned with the z-axis). Note that,\nLRM does not depend on a canonical pose of the object, and the ground truth c is only applied in\ntraining. Conditioning on normalized camera parameters greatly reduces the optimization space of\ntriplane features and facilitates model convergence (see details in Sec. 4.2). To embed the camera\nfeature, we further implement a multi-layer perceptron (MLP) to map the camera feature to a high-\ndimensional camera embedding ˜\nc. The intrinsics (focal and principal point) are normalized by the\nimage’s height and width before sending to the MLP layer.\nTriplane Representation\nWe follow previous works (Chan et al., 2022; Gao et al., 2022) to apply\ntriplane as a compact and expressive feature representation of the reconstruction subject. A triplane\nT contains three axis-aligned feature planes TXY , TYZ and TXZ. In our implementation, each plane\nis of dimension p64ˆ64qˆdT where 64ˆ64 is the spatial resolution, and dT is the number of feature\nchannels. For any 3D point in the NeRF object bounding box r´1, 1s3, we can project it onto each\n2For simplicity, we use thiun\ni“1 in the following to denote the concatenated sequence of the encoded [CLS]\ntoken and patch-wise features.\n4\n\n\nof the planes and query the corresponding point features pTxy, Tyz, Txzq via bilinear interpolation,\nwhich is then decoded by an MLPnerf into the NeRF color and density (Sec. 3.3).\nTo obtain the triplane representation T , we define learnable spatial-positional embeddings f init\nof dimension p3ˆ32ˆ32qˆdD which guide the image-to-3D projection and are used to query the\nimage features via cross-attention, where dD is the hidden dimension of the transformer decoder.\nThe number of tokens in f init is smaller than the number of final triplane tokens (3ˆ64ˆ64); we\nwill upsample the output of the transformer f out to the final T . In the forward pass, conditioning on\nthe camera features ˜\nc and image features thiun\ni“1, each layer of our image-to-triplane transformer\ndecoder gradually updates the initial positional embedding f init to the final triplane features via\nmodulation and cross-attention, respectively. The reason for applying two different conditional\noperations is that the camera controls the orientation and distortion of the whole shape, whereas\nthe image features carry the fine-grained geometric and color information that need to be embedded\nonto the triplane. Details of the two operations are explained below.\nModulation with Camera Features\nOur camera modulation is inspired by DiT (Peebles & Xie,\n2022) which implements an adaptive layer norm (adaLN) to modulate image latents with denoising\ntimesteps and class labels. Suppose tfju is a sequence of vectors in transformer, we define our\nmodulation function ModLNcpfjq with camera feature c as\nγ, β “ MLPmodp˜\ncq\n(1)\nModLNcpfjq “ LNpfjq ¨ p1 ` γq ` β\n(2)\nwhere γ and β are the scale and shift (Huang & Belongie, 2017) output by MLPmod and LN is the\nLayer Normalization (Ba et al., 2016). Such modulation is applied to each attention sub-layer which\nwill be specified next.\nTransformer Layers\nEach transformer layer contains a cross-attention sub-layer, a self-attention\nsub-layer, and a multi-layer perceptron sub-layer (MLP), where the input tokens to each sub-layer\nare modulated by the camera features. Suppose feature sequence f in is the input of an transformer\nlayer, we can consider f in as the triplane hidden features since they are corresponding to the final\ntriplane features T . As shown in the decoder part of Fig. 1, the cross-attention module firstly attends\nfrom the triplane hidden features f in to the image features thiun\ni“1, which can help linking image\ninformation to the triplane. Note that we here do not explicitly define any spatial alignment between\nthe 2D images and 3D triplane hidden features, but consider 3D as an independent modality and ask\nthe model to learn the 2D-to-3D correspondence by itself. The updated triplane hidden features will\nbe passed to a self-attention sub-layer that further models the intra-modal relationships across the\nspatially-structured triplane entries. Then, a multi-layer perceptron sub-layer (MLPtfm) follows as\nin the original Transformer (Vaswani et al., 2017) design. Lastly, the output triplane features f out\nwill become the input to the next transformer layer.\nSuch a design is similar to the Perceiver network (Jaegle et al., 2021) while our model maintains a\nhigh-dimensional representation across the attention layers instead of projecting the input to a latent\nbottleneck. Overall, we can express this process for each j-th triplane entry in each layer as\nf cross\nj\n“ CrossAttnpModLNcpf in\nj q; thiun\ni“1q ` f in\nj\n(3)\nf self\nj\n“ SelfAttnpModLNcpf cross\nj\nq; tModLNcpf cross\nj\nqujq ` f cross\nj\n(4)\nf out\nj\n“ MLPtfmpModLNcpf self\nj\nqq ` f self\nj\n(5)\nThe ModLN operators in sub-layers (i.e., CrossAttn, SelfAttn, MLPtfm) use different set of learn-\nable parameters in the layer normalization and the modulation MLPmod. We do not add additional\nsuperscript to differentiate them for clarity.\nThe transformer layers are processed sequentially. After all the transformer layers, we obtain the\noutput triplane features f out from the last layer as the output of the decoder. This final output is\nupsampled by a learnable de-convolution layer and reshaped to the final triplane representation T .\n3.3\nTRIPLANE-NERF\nWe employ the triplane-NeRF formulation (Chan et al., 2022) and implement an MLPnerf to pre-\ndict RGB and density σ from the point features queried from the triplane representation T . The\n5\n\n\nMLPnerf contains multiple linear layers with ReLU (Nair & Hinton, 2010) activation. The output\ndimension of the MLPnerf is 4 where the first three dimensions are RGB colors and the last di-\nmension corresponds to the density of the field. We refer to the Appendix for the details of NeRF\nvolumetric rendering.\n3.4\nTRAINING OBJECTIVES\nLRM produces the 3D shape from a single input image and leverages additional side views to guide\nthe reconstruction during training. For each shape in the training data, we consider pV ´1q randomly\nchosen side views for supervision; we apply simple image reconstruction objectives between the V\nrendered views ˆ\nx and the ground-truth views xGT (include the input view and side views). More\nprecisely, for every input image x, we minimize:\nLreconpxq “ 1\nV\nV\nÿ\nv“1\n`\nLMSEpˆ\nxv, xGT\nv\nq ` λLLPIPSpˆ\nxv, xGT\nv\nq\n˘\n(6)\nwhere LMSE is the normalized pixel-wise L2 loss, LLPIPS is the perceptual image patch similar-\nity (Zhang et al., 2018) and λ is a customized weight coefficient.\n4\nEXPERIMENTS\n4.1\nDATA\nLRM relies on abundant 3D data from Objaverse (Deitke et al., 2023) and MVImgNet (Yu et al.,\n2023), consisting of synthetic 3D assets and videos of objects in the real world, respectively, to learn\na generalizable cross-shape 3D prior. For each 3D asset in Objaverse, we normalize the shape to\nthe box r´1, 1s3 in world space and render 32 random views with the same camera pointing toward\nthe shape at arbitrary poses. The rendered images are of resolution 1024ˆ1024, and the camera\nposes are sampled from a ball of radius r1.5, 3.0s and with height in range r´0.75, 1.60s3. For each\nvideo, we utilize the extracted frames from the dataset. Since the target shape in those frames can\nbe at random positions, we crop and resize all of them using the predicted object mask4 so that the\nobject is at the center of the resulting frames; we adjust the camera parameters accordingly. Note\nthat our method does not model background, hence we render images from Objaverse with a pure\nwhite background, and use an off-the-shelf package4 to remove the background of video frames. In\ntotal, we pre-processed 730,648 3D assets and 220,219 videos for training.\nTo evaluate the performance of LRM on arbitrary images, we collected novel images from Ob-\njaverse (Deitke et al., 2023), MvImgNet (Yu et al., 2023), ImageNet (Deng et al., 2009), Google\nScanned Objects (Downs et al., 2022), Amazon Berkeley Objects (Collins et al., 2022), captured\nnew images in the real world, and generated images with Adobe Firefly5 for reconstruction. We\nvisualize their results in Sec. 4.3.1 and Appendix. To numerically study the design choices of our\napproach, we randomly acquired 50 unseen 3D shapes from the Objaverse and 50 unseen videos\nfrom the MvImgNet dataset, respectively. For each shape, we pre-process 15 reference views and\npass five of them to our model one by one to reconstruct the same object, and evaluate the rendered\nimages using all 15 reference views (see analyses in Appendix).\n4.2\nIMPLEMENTATION DETAILS\nCamera Normalization\nWe normalize the camera poses corresponding to the input images to\nfacilitate the image-to-triplane modeling. Specifically, for the images rendered from synthetic 3D\nassets in Objaverse, regardless of the corresponding positions of the cameras, we normalize the input\ncamera poses to position r0, ´2, 0s with the camera vertical axis aligned with the upward z-axis in\nthe world frame. For the video data, since the camera can be at an arbitrary distance from the target\nand the object is not at the image center, we only normalize the camera pose to r0, ´dis, 0s where\ndis is the original distance between world origin and camera origin.\n3Most of Objaverse assets have consistent z-axis up.\n4Rembg package, a tool to remove image background: https://pypi.org/project/rembg\n5Adobe Firefly, a text-to-image generation tool: https://firefly.adobe.com\n6\n\n\nPhone \nCaptured\nPhone \nCaptured\nPhone \nCaptured\nPhone \nCaptured\nGenerated\nGenerated\nGenerated\nGenerated\nInput Image\nRendered Novel Views\nInput Image\nRendered Novel Views\nImageNet\nImageNet\nInput Image\nRendered\nInput Image\nGT\nRendered\nGT\nRendered\nGT\nRendered\nGT\nObjaverse\nObjaverse\nObjaverse\nObjaverse\nFigure 2: Rendered novel views (RGB and depth) of shapes reconstructed by our LRM from single\nimages. None of the images are observed by the model during training. Generated images are\ncreated using Adobe Firefly. The last two rows compare our results to the rendered ground truth\nimages of Objaverse objects (GT). Please zoom in for clearer visualization.\n7\n\n\nInput Image\nOurs\nOne-2-3-45\nInput Image\nOurs\nOne-2-3-45\nFigure 3: Comparison to One-2-3-45 (Liu et al., 2023a). To avoid cherry-picking, input images in the\nfirst three rows are selected from the examples provided in One-2-3-45’s paper or demo page. None\nof the images are observed by our model during training. Please zoom in for clearer visualization.\nInput Image\nRendered Novel Views\nInput Image\nRendered Novel Views\nInput Image\nRendered Novel Views\nFigure 4: Failure cases of our method. All three examples show blurry textures for occluded regions,\nand distortion due to the largely inaccurate assumption of the camera parameters.\nNetwork Architecture\nWe apply the ViT-B/16 model of pre-trained DINO as the image encoder,\nwhich takes 512ˆ512 RGB images as input and produces 1025 feature tokens (1024 patch-wise\nfeatures plus one [CLS] features) of dimension 768 (dE) (Caron et al., 2021). The image-to-\ntriplane decoder and the MLPnerf are of 16 and 10 layers with hidden dimensions 1024 (dD) and\n64, respectively. The triplane dimension is 80 (dT ). For neural rendering, LRM uniformly samples\n128 points for each ray and renders 128ˆ128 resolution images for supervision. We also use the\ndeferred back-propagation introduced in ARF (Zhang et al., 2022) to save GPU memory.\nTraining\nWe train LRM on 128 NVIDIA (40G) A100 GPUs with batch size 1024 (1024 different\nshapes per iteration) for 30 epochs, taking about 3 days to complete. Each epoch contains one\ncopy of the rendered image data from Objaverse and three copies of the video frame data from\nMvImgNet to balance the amount of synthetic and real data. For each sample, we use 3 randomly\nchosen side views (i.e., the total views V “ 4) to supervise the shape reconstruction, and we set the\ncoefficient λ“2.0 for LLPIPS. We apply the AdamW optimizer (Loshchilov & Hutter, 2017) and set\nthe learning rate to 4ˆ10´4 with a cosine schedule (Loshchilov & Hutter, 2016). We numerically\nanalyze the influence of data, training, and model hyper-parameters in the Appendix.\nInference\nDuring inference, LRM takes an arbitrary image as input (squared and background re-\nmoved) and assumes the unknown camera parameters to be the normalized cameras that we applied\nto train the Objaverse data. We query a resolution of 384ˆ384ˆ384 points from the reconstructed\ntriplane-NeRF and extract the mesh using Marching Cubes (Lorensen & Cline, 1998). This entire\nprocess only takes less than 5 seconds to complete on a single NVIDIA A100 GPU.\n4.3\nRESULTS\nWe visualize the novel views of shapes reconstructed from real, generated, and rendered images from\nvarious datasets (Fig. 2), compare our method with a concurrent work (Liu et al., 2023a) (Fig. 3), and\nsummarize some failure cases of our method (Sec. 4.3.2). Numerical comparisons to other methods,\nand analyses of data, model architecture, and supervision can be found in the Appendix.\n8\n\n\n4.3.1\nVISUALIZATION\nFigure 2 visualizes some examples of the shapes reconstructed from single images. Overall, the\nresults show very high fidelity for diverse inputs, including real, generated, and rendered images of\nvarious subjects with distinct textures. Not only is complex geometry correctly modeled (e.g. flower,\nflagon, and wipe), but also the high-frequency details, such as the texture of the wood peafowl, are\npreserved, both reflecting the great generalization ability of our model. From the asymmetric exam-\nples, giraffe, penguin, and bear, we can see that LRM can infer semantically reasonable occluded\nportion of the shapes, which implies effective cross-shape priors have been learned.\nIn Figure 3, we compare LRM with One-2-3-45, a concurrent work to ours that achieves state-\nof-the-art single image to 3D reconstruction by generating multi-view images with 2D diffusion\nmodels (Liu et al., 2023a). To avoid cherry-picking, we directly test our method on the example\nimages provided in their paper or demo page6. We can see that our method produces much sharper\ndetails and consistent surfaces. In the last row of the figure, we test One-2-3-45 with two examples\nused in Figure 2, showing much worse reconstruction results.\n4.3.2\nLIMITATIONS\nDespite the high-quality single-image-to-3D results we have shown, our method still has a few lim-\nitations. First, our LRM tends to produce blurry textures for occluded regions, as shown in Figure\n4. We conjecture that this is due to the fact that the single-image-to-3D problem is inherently prob-\nabilistic, i.e., multiple plausible solutions exist for the unseen region, but our model is deterministic\nand is likely producing averaged modes of the unseens. Second, during inference time, we assign a\nset of fixed camera intrinsics and extrinsics (same as our Objaverse training data) to the test images.\nThese camera parameters may not align well with the ground truth, especially when the images are\ncropped and resized, causing large changes to Field-of-View (FoV) and principal points. Figure 4\nshows that incorrect assumptions of the camera parameters can lead to distorted shape reconstruc-\ntion. Third, we only address images of objects without background; handling the background (Zhang\net al., 2020; Barron et al., 2022), as well as complex scenes, is beyond the scope of this work. Finally,\nwe assume Lambertian objects and omit the view-dependent modelling (Mildenhall et al., 2021) in\nour predicted NeRF. Therefore, we cannot faithfully reconstruct the view-dependent appearance of\nsome real-world materials, e.g., shiny metals, glossy ceramics, etc.\n5\nCONCLUSION\nIn this paper, we propose LRM, the first large transformer-based framework to learn an expressive\n3D prior from a million 3D data to reconstruct objects from single images. LRM is very efficient\nin training and inference; it is a fully-differentiable network that can be trained end-to-end with\nsimple image reconstruction losses and only takes five seconds to render a high-fidelity 3D shape,\nthus enabling a wide range of real-world applications. In the era of large-scale learning, we hope our\nidea can inspire future research to explore data-driven 3D large reconstruction models that generalize\nwell to arbitrary in-the-wild images.\nFuture Directions\nIn addition to addressing the limitations mentioned in Sec. 4.3.2, we suggest\ntwo future directions of our research; (1) Scaling up the model and training data: with the simplest\ntransformer-based design and minimal regularization, LRM can be easily scaled to a larger and more\ncapable network, including but not limited to applying a larger image encoder, adding more attention\nlayers to the image-to-triplane decoder, and increasing the resolution of triplane representations. On\nthe other hand, LRM only requires multi-view images for supervision, hence a wide range of 3D,\nvideo, and image datasets can be exploited in training. We expect both approaches to be promising\nin improving the model’s generalization ability and the quality of reconstruction. (2) Extension to\nmultimodal 3D generative models: LRM model builds a pathway for generating novel 3D shapes\nfrom language by leveraging a text-to-image generation model to first create 2D images. But more\ninterestingly, we suggest the learned expressive triplane representations could be applied to directly\nbridge language descriptions and 3D to enable efficient text-to-3D generation and editing (e.g., via\nlatent diffusion (Rombach et al., 2022)). We will explore these ideas in our future research.\n6One-2-3-45 demo page: https://huggingface.co/spaces/One-2-3-45/One-2-3-45.\n9\n\n\nETHICS STATEMENT\nLRM proposed in this paper is a deterministic model in which, given the same image as input, the\nmodel will infer the identical 3D shape. Unlike generative models that can be used to easily synthe-\nsize various undesirable contents (e.g., from language inputs), LRM requests the specific 2D content\nto exist in the first place. LRM is trained on Objaverse (Deitke et al., 2023) and MvImgNet (Yu et al.,\n2023) data, which mostly contain ethical content. However, given an unethical or misleading image,\nLRM could produce unethical 3D objects or 3D disinformation that may be more convincing than\nthe 2D input images (although the reconstructed objects are less realistic than real-world objects).\nImage-to-3D reconstruction models like LRM hold the potential to automate tasks currently per-\nformed by 3D designers. However, it’s worth noting that these tools also have the capacity to foster\ngrowth and enhance accessibility within the creative industry.\nREPRODUCIBILITY STATEMENT\nOur LRM is built by integrating the publicly available codebases of threestudio7 (Guo et al., 2023),\nx-transformers8, and DINO9 (Caron et al., 2021), and the model is trained using publicly available\ndata from Objaverse (Deitke et al., 2023) and MvImgNet (Yu et al., 2023). We include very compre-\nhensive data pre-processing, network architecture, and training details in this paper, which greatly\nfacilitate reproducing our LRM.\nACKNOWLEDGMENT\nWe want to thank Nathan Carr, Scott Cohen, Hailin Jin, Aseem Agarwala, Tong Sun for their sup-\nport, and thank Duygu Ceylan, Zexiang Xu, Paul Guerrero, Chun-Hao Huang, Niloy Mitra, Radomir\nMech, Vova Kim, Thibault Groueix for constructive feedback on this project. Hao wants to thank\nXin for the inspiration as he ran on this road. Yicong wants to thank Prof. Stephen Gould and Ms.\nZiwei Wang for their great advice.\nREFERENCES\nJean-Baptiste Alayrac, Jeff Donahue, Pauline Luc, Antoine Miech, Iain Barr, Yana Hasson, Karel\nLenc, Arthur Mensch, Katherine Millican, Malcolm Reynolds, et al. Flamingo: a visual language\nmodel for few-shot learning. Advances in Neural Information Processing Systems, 35:23716–\n23736, 2022.\nTitas Anciukeviˇ\ncius, Zexiang Xu, Matthew Fisher, Paul Henderson, Hakan Bilen, Niloy J Mitra, and\nPaul Guerrero. Renderdiffusion: Image diffusion for 3d reconstruction, inpainting and generation.\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp.\n12608–12618, 2023.\nJimmy Lei Ba, Jamie Ryan Kiros, and Geoffrey E Hinton. Layer normalization. arXiv preprint\narXiv:1607.06450, 2016.\nJonathan T. Barron, Ben Mildenhall, Dor Verbin, Pratul P. Srinivasan, and Peter Hedman. Mip-nerf\n360: Unbounded anti-aliased neural radiance fields. CVPR, 2022.\nFederica Bogo, Angjoo Kanazawa, Christoph Lassner, Peter Gehler, Javier Romero, and Michael J\nBlack. Keep it smpl: Automatic estimation of 3d human pose and shape from a single image. In\nComputer Vision–ECCV 2016: 14th European Conference, Amsterdam, The Netherlands, Octo-\nber 11-14, 2016, Proceedings, Part V 14, pp. 561–578. Springer, 2016.\nTom Brown, Benjamin Mann, Nick Ryder, Melanie Subbiah, Jared D Kaplan, Prafulla Dhariwal,\nArvind Neelakantan, Pranav Shyam, Girish Sastry, Amanda Askell, et al. Language models are\nfew-shot learners. Advances in neural information processing systems, 33:1877–1901, 2020.\n7threestudio’s GitHub page: https://github.com/threestudio-project/threestudio.\n8x-transformers’s GitHub page: https://github.com/lucidrains/x-transformers.\n9DINO’s GitHub page: https://github.com/facebookresearch/dino.\n10\n\n\nMathilde Caron, Hugo Touvron, Ishan Misra, Herv´\ne J´\negou, Julien Mairal, Piotr Bojanowski, and\nArmand Joulin. Emerging properties in self-supervised vision transformers. In Proceedings of\nthe IEEE/CVF international conference on computer vision, pp. 9650–9660, 2021.\nEric R Chan, Connor Z Lin, Matthew A Chan, Koki Nagano, Boxiao Pan, Shalini De Mello, Orazio\nGallo, Leonidas J Guibas, Jonathan Tremblay, Sameh Khamis, et al. Efficient geometry-aware\n3d generative adversarial networks. In Proceedings of the IEEE/CVF Conference on Computer\nVision and Pattern Recognition, pp. 16123–16133, 2022.\nAngel X Chang, Thomas Funkhouser, Leonidas Guibas, Pat Hanrahan, Qixing Huang, Zimo Li,\nSilvio Savarese, Manolis Savva, Shuran Song, Hao Su, et al. Shapenet: An information-rich 3d\nmodel repository. arXiv preprint arXiv:1512.03012, 2015.\nAnpei Chen, Zexiang Xu, Andreas Geiger, Jingyi Yu, and Hao Su. Tensorf: Tensorial radiance\nfields. In European Conference on Computer Vision (ECCV), 2022a.\nJun Chen, Han Guo, Kai Yi, Boyang Li, and Mohamed Elhoseiny. Visualgpt: Data-efficient adap-\ntation of pretrained language models for image captioning. In Proceedings of the IEEE/CVF\nConference on Computer Vision and Pattern Recognition, pp. 18030–18040, 2022b.\nYen-Chun Chen, Linjie Li, Licheng Yu, Ahmed El Kholy, Faisal Ahmed, Zhe Gan, Yu Cheng, and\nJingjing Liu. Uniter: Universal image-text representation learning. In European conference on\ncomputer vision, pp. 104–120. Springer, 2020.\nZhiqin Chen and Hao Zhang. Learning implicit fields for generative shape modeling. In Proceedings\nof the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 5939–5948, 2019.\nAakanksha Chowdhery, Sharan Narang, Jacob Devlin, Maarten Bosma, Gaurav Mishra, Adam\nRoberts, Paul Barham, Hyung Won Chung, Charles Sutton, Sebastian Gehrmann, et al. Palm:\nScaling language modeling with pathways. arXiv preprint arXiv:2204.02311, 2022.\nChristopher B Choy, Danfei Xu, JunYoung Gwak, Kevin Chen, and Silvio Savarese. 3d-r2n2: A\nunified approach for single and multi-view 3d object reconstruction. In Computer Vision–ECCV\n2016: 14th European Conference, Amsterdam, The Netherlands, October 11-14, 2016, Proceed-\nings, Part VIII 14, pp. 628–644. Springer, 2016.\nJasmine Collins, Shubham Goel, Kenan Deng, Achleshwar Luthra, Leon Xu, Erhan Gundogdu,\nXi Zhang, Tomas F Yago Vicente, Thomas Dideriksen, Himanshu Arora, et al. Abo: Dataset and\nbenchmarks for real-world 3d object understanding. In Proceedings of the IEEE/CVF Conference\non Computer Vision and Pattern Recognition, pp. 21126–21136, 2022.\nAngela Dai, Charles Ruizhongtai Qi, and Matthias Nießner. Shape completion using 3d-encoder-\npredictor cnns and shape synthesis. In Proceedings of the IEEE conference on computer vision\nand pattern recognition, pp. 5868–5877, 2017.\nMatt Deitke, Dustin Schwenk, Jordi Salvador, Luca Weihs, Oscar Michel, Eli VanderBilt, Ludwig\nSchmidt, Kiana Ehsani, Aniruddha Kembhavi, and Ali Farhadi. Objaverse: A universe of anno-\ntated 3d objects. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 13142–13153, 2023.\nCongyue Deng, Chiyu Jiang, Charles R Qi, Xinchen Yan, Yin Zhou, Leonidas Guibas, Dragomir\nAnguelov, et al. Nerdi: Single-view nerf synthesis with language-guided diffusion as general\nimage priors.\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 20637–20647, 2023.\nJia Deng, Wei Dong, Richard Socher, Li-Jia Li, Kai Li, and Li Fei-Fei. Imagenet: A large-scale hi-\nerarchical image database. In 2009 IEEE conference on computer vision and pattern recognition,\npp. 248–255. Ieee, 2009.\nJacob Devlin, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova. Bert: Pre-training of deep\nbidirectional transformers for language understanding. arXiv preprint arXiv:1810.04805, 2018.\n11\n\n\nAlexey Dosovitskiy, Lucas Beyer, Alexander Kolesnikov, Dirk Weissenborn, Xiaohua Zhai, Thomas\nUnterthiner, Mostafa Dehghani, Matthias Minderer, Georg Heigold, Sylvain Gelly, et al. An\nimage is worth 16x16 words: Transformers for image recognition at scale.\narXiv preprint\narXiv:2010.11929, 2020.\nLaura Downs, Anthony Francis, Nate Koenig, Brandon Kinman, Ryan Hickman, Krista Reymann,\nThomas B McHugh, and Vincent Vanhoucke. Google scanned objects: A high-quality dataset\nof 3d scanned household items. In 2022 International Conference on Robotics and Automation\n(ICRA), pp. 2553–2560. IEEE, 2022.\nHaoqiang Fan, Hao Su, and Leonidas J Guibas. A point set generation network for 3d object recon-\nstruction from a single image. In Proceedings of the IEEE conference on computer vision and\npattern recognition, pp. 605–613, 2017.\nJun Gao, Tianchang Shen, Zian Wang, Wenzheng Chen, Kangxue Yin, Daiqing Li, Or Litany, Zan\nGojcic, and Sanja Fidler. Get3d: A generative model of high quality 3d textured shapes learned\nfrom images. Advances In Neural Information Processing Systems, 35:31841–31854, 2022.\nRohit Girdhar, David F Fouhey, Mikel Rodriguez, and Abhinav Gupta. Learning a predictable and\ngenerative vector representation for objects. In Computer Vision–ECCV 2016: 14th European\nConference, Amsterdam, The Netherlands, October 11-14, 2016, Proceedings, Part VI 14, pp.\n484–499. Springer, 2016.\nRohit Girdhar, Alaaeldin El-Nouby, Zhuang Liu, Mannat Singh, Kalyan Vasudev Alwala, Armand\nJoulin, and Ishan Misra. Imagebind: One embedding space to bind them all. In Proceedings of\nthe IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 15180–15190, 2023.\nGeorgia Gkioxari, Jitendra Malik, and Justin Johnson. Mesh r-cnn. In Proceedings of the IEEE/CVF\ninternational conference on computer vision, pp. 9785–9795, 2019.\nShubham Goel, Angjoo Kanazawa, and Jitendra Malik. Shape and viewpoint without keypoints. In\nComputer Vision–ECCV 2020: 16th European Conference, Glasgow, UK, August 23–28, 2020,\nProceedings, Part XV 16, pp. 88–104. Springer, 2020.\nShubham Goel, Georgios Pavlakos, Jathushan Rajasegaran, Angjoo Kanazawa, and Jitendra Malik.\nHumans in 4d: Reconstructing and tracking humans with transformers. In Proceedings of the\nIEEE/CVF International Conference on Computer Vision, pp. 14783–14794, 2023.\nYuan-Chen Guo, Ying-Tian Liu, Ruizhi Shao, Christian Laforte, Vikram Voleti, Guan Luo, Chia-\nHao Chen, Zi-Xin Zou, Chen Wang, Yan-Pei Cao, and Song-Hai Zhang. threestudio: A unified\nframework for 3d content generation. https://github.com/threestudio-project/\nthreestudio, 2023.\nKaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. Deep residual learning for image recog-\nnition. In Proceedings of the IEEE conference on computer vision and pattern recognition, pp.\n770–778, 2016.\nDan Hendrycks and Kevin Gimpel. Gaussian error linear units (gelus), 2023.\nYining Hong, Haoyu Zhen, Peihao Chen, Shuhong Zheng, Yilun Du, Zhenfang Chen, and Chuang\nGan. 3d-llm: Injecting the 3d world into large language models. arXiv, 2023.\nXun Huang and Serge Belongie. Arbitrary style transfer in real-time with adaptive instance normal-\nization. In Proceedings of the IEEE international conference on computer vision, pp. 1501–1510,\n2017.\nAndrew Jaegle, Felix Gimeno, Andy Brock, Oriol Vinyals, Andrew Zisserman, and Joao Carreira.\nPerceiver: General perception with iterative attention. In International conference on machine\nlearning, pp. 4651–4664. PMLR, 2021.\nWonbong Jang and Lourdes Agapito.\nCodenerf: Disentangled neural radiance fields for object\ncategories. In Proceedings of the IEEE/CVF International Conference on Computer Vision, pp.\n12949–12958, 2021.\n12\n\n\nHeewoo Jun and Alex Nichol. Shap-e: Generating conditional 3d implicit functions. arXiv preprint\narXiv:2305.02463, 2023.\nAngjoo Kanazawa, Shubham Tulsiani, Alexei A Efros, and Jitendra Malik. Learning category-\nspecific mesh reconstruction from image collections. In Proceedings of the European Conference\non Computer Vision (ECCV), pp. 371–386, 2018.\nJustin Kerr, Chung Min Kim, Ken Goldberg, Angjoo Kanazawa, and Matthew Tancik. Lerf: Lan-\nguage embedded radiance fields. arXiv preprint arXiv:2303.09553, 2023.\nNilesh Kulkarni, Abhinav Gupta, David F Fouhey, and Shubham Tulsiani.\nArticulation-aware\ncanonical surface mapping. In Proceedings of the IEEE/CVF Conference on Computer Vision\nand Pattern Recognition, pp. 452–461, 2020.\nJiahao Li, Hao Tan, Kai Zhang, Zexiang Xu, Fujun Luan, Yinghao Xu, Yicong Hong, Kalyan\nSunkavalli, Greg Shakhnarovich, and Sai Bi. Instant3d: Fast text-to-3d with sparse-view gen-\neration and large reconstruction model. arXiv preprint arXiv:2311.06214, 2023a.\nJunnan Li, Dongxu Li, Caiming Xiong, and Steven Hoi. Blip: Bootstrapping language-image pre-\ntraining for unified vision-language understanding and generation. In International Conference\non Machine Learning, pp. 12888–12900. PMLR, 2022.\nJunnan Li, Dongxu Li, Silvio Savarese, and Steven Hoi.\nBlip-2:\nBootstrapping language-\nimage pre-training with frozen image encoders and large language models.\narXiv preprint\narXiv:2301.12597, 2023b.\nXueting Li, Sifei Liu, Kihwan Kim, Shalini De Mello, Varun Jampani, Ming-Hsuan Yang, and\nJan Kautz. Self-supervised single-view 3d reconstruction via semantic consistency. In Computer\nVision–ECCV 2020: 16th European Conference, Glasgow, UK, August 23–28, 2020, Proceedings,\nPart XIV 16, pp. 677–693. Springer, 2020.\nMinghua Liu, Chao Xu, Haian Jin, Linghao Chen, Zexiang Xu, Hao Su, et al.\nOne-2-3-45:\nAny single image to 3d mesh in 45 seconds without per-shape optimization.\narXiv preprint\narXiv:2306.16928, 2023a.\nRuoshi Liu, Rundi Wu, Basile Van Hoorick, Pavel Tokmakov, Sergey Zakharov, and Carl Vondrick.\nZero-1-to-3: Zero-shot one image to 3d object. arXiv preprint arXiv:2303.11328, 2023b.\nWilliam E Lorensen and Harvey E Cline. Marching cubes: A high resolution 3d surface construction\nalgorithm. In Seminal graphics: pioneering efforts that shaped the field, pp. 347–353. 1998.\nIlya Loshchilov and Frank Hutter. Sgdr: Stochastic gradient descent with warm restarts. arXiv\npreprint arXiv:1608.03983, 2016.\nIlya Loshchilov and Frank Hutter.\nDecoupled weight decay regularization.\narXiv preprint\narXiv:1711.05101, 2017.\nTiange Luo, Chris Rockwell, Honglak Lee, and Justin Johnson. Scalable 3d captioning with pre-\ntrained models. arXiv preprint arXiv:2306.07279, 2023.\nPriyanka Mandikal, KL Navaneet, Mayank Agarwal, and R Venkatesh Babu. 3d-lmnet: Latent\nembedding matching for accurate and diverse 3d point cloud reconstruction from a single image.\narXiv preprint arXiv:1807.07796, 2018.\nLuke Melas-Kyriazi, Iro Laina, Christian Rupprecht, and Andrea Vedaldi.\nRealfusion: 360deg\nreconstruction of any object from a single image. In Proceedings of the IEEE/CVF Conference\non Computer Vision and Pattern Recognition, pp. 8446–8455, 2023.\nLars Mescheder, Michael Oechsle, Michael Niemeyer, Sebastian Nowozin, and Andreas Geiger. Oc-\ncupancy networks: Learning 3d reconstruction in function space. In Proceedings of the IEEE/CVF\nconference on computer vision and pattern recognition, pp. 4460–4470, 2019.\nGal Metzer, Elad Richardson, Or Patashnik, Raja Giryes, and Daniel Cohen-Or. Latent-nerf for\nshape-guided generation of 3d shapes and textures. In Proceedings of the IEEE/CVF Conference\non Computer Vision and Pattern Recognition, pp. 12663–12673, 2023.\n13\n\n\nBen Mildenhall, Pratul P Srinivasan, Matthew Tancik, Jonathan T Barron, Ravi Ramamoorthi, and\nRen Ng. Nerf: Representing scenes as neural radiance fields for view synthesis. Communications\nof the ACM, 65(1):99–106, 2021.\nParitosh Mittal, Yen-Chi Cheng, Maneesh Singh, and Shubham Tulsiani. Autosdf: Shape priors for\n3d completion, reconstruction and generation. In Proceedings of the IEEE/CVF Conference on\nComputer Vision and Pattern Recognition, pp. 306–315, 2022.\nNorman M¨\nuller, Andrea Simonelli, Lorenzo Porzi, Samuel Rota Bulo, Matthias Nießner, and Peter\nKontschieder. Autorf: Learning 3d object radiance fields from single view observations. In Pro-\nceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 3971–\n3980, 2022.\nThomas M¨\nuller, Alex Evans, Christoph Schied, and Alexander Keller. Instant neural graphics prim-\nitives with a multiresolution hash encoding.\nACM Trans. Graph., 41(4):102:1–102:15, July\n2022.\ndoi: 10.1145/3528223.3530127.\nURL https://doi.org/10.1145/3528223.\n3530127.\nVinod Nair and Geoffrey E Hinton. Rectified linear units improve restricted boltzmann machines. In\nProceedings of the 27th international conference on machine learning (ICML-10), pp. 807–814,\n2010.\nAlex Nichol, Heewoo Jun, Prafulla Dhariwal, Pamela Mishkin, and Mark Chen. Point-e: A system\nfor generating 3d point clouds from complex prompts. arXiv preprint arXiv:2212.08751, 2022.\nMichael Niemeyer, Lars Mescheder, Michael Oechsle, and Andreas Geiger. Differentiable volumet-\nric rendering: Learning implicit 3d representations without 3d supervision. In Proceedings of the\nIEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 3504–3515, 2020.\nDavid Novotny, Nikhila Ravi, Benjamin Graham, Natalia Neverova, and Andrea Vedaldi. C3dpo:\nCanonical 3d pose networks for non-rigid structure from motion. In Proceedings of the IEEE/CVF\nInternational Conference on Computer Vision, pp. 7688–7697, 2019.\nOpenAI. Gpt-4 technical report, 2023.\nJeong Joon Park, Peter Florence, Julian Straub, Richard Newcombe, and Steven Lovegrove.\nDeepsdf: Learning continuous signed distance functions for shape representation. In Proceedings\nof the IEEE/CVF conference on computer vision and pattern recognition, pp. 165–174, 2019.\nAdam Paszke, Sam Gross, Francisco Massa, Adam Lerer, James Bradbury, Gregory Chanan, Trevor\nKilleen, Zeming Lin, Natalia Gimelshein, Luca Antiga, et al. Pytorch: An imperative style, high-\nperformance deep learning library. Advances in neural information processing systems, 32, 2019.\nWilliam Peebles and Saining Xie. Scalable diffusion models with transformers. arXiv preprint\narXiv:2212.09748, 2022.\nBen Poole, Ajay Jain, Jonathan T Barron, and Ben Mildenhall. Dreamfusion: Text-to-3d using 2d\ndiffusion. arXiv preprint arXiv:2209.14988, 2022.\nGuocheng Qian, Jinjie Mai, Abdullah Hamdi, Jian Ren, Aliaksandr Siarohin, Bing Li, Hsin-\nYing Lee, Ivan Skorokhodov, Peter Wonka, Sergey Tulyakov, et al.\nMagic123: One image\nto high-quality 3d object generation using both 2d and 3d diffusion priors.\narXiv preprint\narXiv:2306.17843, 2023.\nAlec Radford, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei, Ilya Sutskever, et al. Language\nmodels are unsupervised multitask learners. OpenAI blog, 1(8):9, 2019.\nAlec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal,\nGirish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, et al. Learning transferable visual\nmodels from natural language supervision. In International conference on machine learning, pp.\n8748–8763. PMLR, 2021.\n14\n\n\nAditya Ramesh, Mikhail Pavlov, Gabriel Goh, Scott Gray, Chelsea Voss, Alec Radford, Mark Chen,\nand Ilya Sutskever. Zero-shot text-to-image generation. In International Conference on Machine\nLearning, pp. 8821–8831. PMLR, 2021.\nAditya Ramesh, Prafulla Dhariwal, Alex Nichol, Casey Chu, and Mark Chen. Hierarchical text-\nconditional image generation with clip latents. arXiv preprint arXiv:2204.06125, 2022.\nJeremy Reizenstein, Roman Shapovalov, Philipp Henzler, Luca Sbordone, Patrick Labatut, and\nDavid Novotny. Common objects in 3d: Large-scale learning and evaluation of real-life 3d cat-\negory reconstruction. In Proceedings of the IEEE/CVF International Conference on Computer\nVision, pp. 10901–10911, 2021.\nRobin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser, and Bj¨\norn Ommer. High-\nresolution image synthesis with latent diffusion models. In Proceedings of the IEEE/CVF confer-\nence on computer vision and pattern recognition, pp. 10684–10695, 2022.\nJoseph Roth, Yiying Tong, and Xiaoming Liu. Adaptive 3d face reconstruction from unconstrained\nphoto collections. In Proceedings of the IEEE conference on computer vision and pattern recog-\nnition, pp. 4197–4206, 2016.\nChitwan Saharia, William Chan, Saurabh Saxena, Lala Li, Jay Whang, Emily L Denton, Kamyar\nGhasemipour, Raphael Gontijo Lopes, Burcu Karagol Ayan, Tim Salimans, et al. Photorealistic\ntext-to-image diffusion models with deep language understanding. Advances in Neural Informa-\ntion Processing Systems, 35:36479–36494, 2022.\nMehdi SM Sajjadi, Henning Meyer, Etienne Pot, Urs Bergmann, Klaus Greff, Noha Radwan, Suhani\nVora, Mario Luˇ\nci´\nc, Daniel Duckworth, Alexey Dosovitskiy, et al. Scene representation trans-\nformer: Geometry-free novel view synthesis through set-latent scene representations. In Proceed-\nings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 6229–6238,\n2022.\nBokui Shen, Xinchen Yan, Charles R Qi, Mahyar Najibi, Boyang Deng, Leonidas Guibas, Yin\nZhou, and Dragomir Anguelov. Gina-3d: Learning to generate implicit neural assets in the wild.\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp.\n4913–4926, 2023a.\nQiuhong Shen, Xingyi Yang, and Xinchao Wang.\nAnything-3d: Towards single-view anything\nreconstruction in the wild. arXiv preprint arXiv:2304.10261, 2023b.\nAmanpreet Singh, Ronghang Hu, Vedanuj Goswami, Guillaume Couairon, Wojciech Galuba, Mar-\ncus Rohrbach, and Douwe Kiela. Flava: A foundational language and vision alignment model.\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp.\n15638–15650, 2022.\nCheng Sun, Min Sun, and Hwann-Tzong Chen. Direct voxel grid optimization: Super-fast conver-\ngence for radiance fields reconstruction. In CVPR, 2022.\nHao Tan and Mohit Bansal. Lxmert: Learning cross-modality encoder representations from trans-\nformers. arXiv preprint arXiv:1908.07490, 2019.\nJunshu Tang, Tengfei Wang, Bo Zhang, Ting Zhang, Ran Yi, Lizhuang Ma, and Dong Chen.\nMake-it-3d: High-fidelity 3d creation from a single image with diffusion prior. arXiv preprint\narXiv:2303.14184, 2023.\nHugo Touvron, Thibaut Lavril, Gautier Izacard, Xavier Martinet, Marie-Anne Lachaux, Timoth´\nee\nLacroix, Baptiste Rozi`\nere, Naman Goyal, Eric Hambro, Faisal Azhar, et al. Llama: Open and\nefficient foundation language models. arXiv preprint arXiv:2302.13971, 2023.\nShubham Tulsiani, Tinghui Zhou, Alexei A Efros, and Jitendra Malik.\nMulti-view supervision\nfor single-view reconstruction via differentiable ray consistency. In Proceedings of the IEEE\nconference on computer vision and pattern recognition, pp. 2626–2634, 2017.\n15\n\n\nAshish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez,\nŁukasz Kaiser, and Illia Polosukhin. Attention is all you need. Advances in neural informa-\ntion processing systems, 30, 2017.\nNanyang Wang, Yinda Zhang, Zhuwen Li, Yanwei Fu, Wei Liu, and Yu-Gang Jiang. Pixel2mesh:\nGenerating 3d mesh models from single rgb images. In Proceedings of the European conference\non computer vision (ECCV), pp. 52–67, 2018.\nYi Wang, Kunchang Li, Yizhuo Li, Yinan He, Bingkun Huang, Zhiyu Zhao, Hongjie Zhang, Jilan\nXu, Yi Liu, Zun Wang, et al. Internvideo: General video foundation models via generative and\ndiscriminative learning. arXiv preprint arXiv:2212.03191, 2022.\nZhou Wang, Alan C Bovik, Hamid R Sheikh, and Eero P Simoncelli. Image quality assessment:\nfrom error visibility to structural similarity. IEEE transactions on image processing, 13(4):600–\n612, 2004.\nChao-Yuan Wu, Justin Johnson, Jitendra Malik, Christoph Feichtenhofer, and Georgia Gkioxari.\nMultiview compressive coding for 3d reconstruction. In Proceedings of the IEEE/CVF Confer-\nence on Computer Vision and Pattern Recognition, pp. 9065–9075, 2023.\nRundi Wu, Yixin Zhuang, Kai Xu, Hao Zhang, and Baoquan Chen. Pq-net: A generative part\nseq2seq network for 3d shapes. In Proceedings of the IEEE/CVF Conference on Computer Vision\nand Pattern Recognition, pp. 829–838, 2020.\nDejia Xu, Yifan Jiang, Peihao Wang, Zhiwen Fan, Yi Wang, and Zhangyang Wang. Neurallift-360:\nLifting an in-the-wild 2d photo to a 3d object with 360deg views. In Proceedings of the IEEE/CVF\nConference on Computer Vision and Pattern Recognition, pp. 4479–4489, 2023.\nQiangeng Xu, Weiyue Wang, Duygu Ceylan, Radomir Mech, and Ulrich Neumann. Disn: Deep\nimplicit surface network for high-quality single-view 3d reconstruction. Advances in neural in-\nformation processing systems, 32, 2019.\nLe Xue, Mingfei Gao, Chen Xing, Roberto Mart´\nın-Mart´\nın, Jiajun Wu, Caiming Xiong, Ran Xu,\nJuan Carlos Niebles, and Silvio Savarese. Ulip: Learning a unified representation of language,\nimages, and point clouds for 3d understanding. In Proceedings of the IEEE/CVF Conference on\nComputer Vision and Pattern Recognition, pp. 1179–1189, 2023.\nXinchen Yan, Jimei Yang, Ersin Yumer, Yijie Guo, and Honglak Lee.\nPerspective transformer\nnets: Learning single-view 3d object reconstruction without 3d supervision. Advances in neural\ninformation processing systems, 29, 2016.\nJianing Yang, Xuweiyi Chen, Shengyi Qian, Nikhil Madaan, Madhavan Iyengar, David F. Fouhey,\nand Joyce Chai. Llm-grounder: Open-vocabulary 3d visual grounding with large language model\nas an agent, 2023.\nAlex Yu, Vickie Ye, Matthew Tancik, and Angjoo Kanazawa. pixelnerf: Neural radiance fields from\none or few images. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 4578–4587, 2021.\nJiahui Yu, Zirui Wang, Vijay Vasudevan, Legg Yeung, Mojtaba Seyedhosseini, and Yonghui\nWu.\nCoca:\nContrastive captioners are image-text foundation models.\narXiv preprint\narXiv:2205.01917, 2022.\nXianggang Yu, Mutian Xu, Yidan Zhang, Haolin Liu, Chongjie Ye, Yushuang Wu, Zizheng Yan,\nChenming Zhu, Zhangyang Xiong, Tianyou Liang, et al. Mvimgnet: A large-scale dataset of\nmulti-view images. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 9150–9161, 2023.\nYihan Zeng, Chenhan Jiang, Jiageng Mao, Jianhua Han, Chaoqiang Ye, Qingqiu Huang, Dit-Yan\nYeung, Zhen Yang, Xiaodan Liang, and Hang Xu.\nClip2: Contrastive language-image-point\npretraining from real-world point cloud data. In Proceedings of the IEEE/CVF Conference on\nComputer Vision and Pattern Recognition, pp. 15244–15253, 2023.\n16\n\n\nKai Zhang, Gernot Riegler, Noah Snavely, and Vladlen Koltun. Nerf++: Analyzing and improving\nneural radiance fields. arXiv preprint arXiv:2010.07492, 2020.\nKai Zhang, Nick Kolkin, Sai Bi, Fujun Luan, Zexiang Xu, Eli Shechtman, and Noah Snavely. Arf:\nArtistic radiance fields. In European Conference on Computer Vision, pp. 717–733. Springer,\n2022.\nRichard Zhang, Phillip Isola, Alexei A Efros, Eli Shechtman, and Oliver Wang. The unreasonable\neffectiveness of deep features as a perceptual metric. In CVPR, 2018.\n17\n\n\nAPPENDICES\nA\nBACKGROUND OF MODEL COMPONENTS\nA.1\nNERF\nWe adopt NeRF (Mildenhall et al., 2021), specifically the compact triplane NeRF variant (Chan\net al., 2022), as our 3D representation to predict in LRM. NeRF, when coupled with differentiable\nvolume rendering, can be optimized with just image reconstruction losses.\nAt the core of NeRF (Mildenhall et al., 2021) and its variants (Chan et al., 2022; Chen et al., 2022a;\nM¨\nuller et al., 2022; Sun et al., 2022) is a spatially-varying color (modeling appearance) and density\n(modeling geometry) field function. 10 Given a 3D point p, the color and density field pu, σq can be\nwritten as:\npu, σq “ MLPnerf pfθppqq,\n(7)\nwhere the spatial encoding fθ is used to facilitate the MLPnerf to learn high-frequency signals.\nDifferent NeRF variants (Chan et al., 2022; Chen et al., 2022a; M¨\nuller et al., 2022; Sun et al., 2022)\ntypically differ from each other in terms of the choice of the spatial encoding and the size of the\nMLP. In this work, we use the triplane spatial encoding function proposed by EG3D (Chan et al.,\n2022), because of its low tokenization complexity (OpN 2q as opposed to a voxel grid’s OpN 3q\ncomplexity, where N is spatial resolution).\nImages are rendered from NeRF using volume rendering that’s trivially differentiable. In detail, for\neach pixel to render, we cast a ray r through a NeRF, and use finite point samples pi along the ray\nto compute the volume rendering integral to get the rendered color uprq:\nuprq “\nÿ\ni\nTip1 ´ expp´σiδiqqui,\n(8)\nTi “ expp´\ni´1\nÿ\nj“1\nσjδjq,\n(9)\nwhere pui, σiq “ MLPϕpfθppiqq and δi is the distance between point pi and pi`1.\nA.2\nTRANSFORMER LAYERS\nIn this subsection, we provide the details of the layers used in the transformer decoder (Vaswani\net al., 2017) as a background. For the Vision Transformer encoder, please refer to the original DINO\npaper (Caron et al., 2021) for implementation details.\nAttention operator\nAttention operator is an expressive neural operator which converts an input\nfeature x with condition to a sequence of other features tyiu. It first computes the attention score αi\nby using the dot product between the input x and each condition feature yi. An additional softmax\nis added after the dot products to normalize the weights to a summation of 1. This attention score\nmeasures the relationship between input and conditions. Then the output is the weighted summation\nof the conditions tyiu with respect to the attention score αi.\nαi “ softmaxitxJyiu\n(10)\nAttnpx; tyiuiq “\nÿ\ni\nαiyi\n(11)\nFor some specific cases (e.g., in the transformer attention layer below), the attention operator wants\nto differentiate the vectors used in calculating the attention score and the vectors for final outputs.\nThus it will introduce another set of ‘value’ vectors tziui, and treat the tyiui as corresponding ‘key’\nvectors. Taking this into consideration, the formula would become\nαi “ softmaxitxJyiu\n(12)\nAttnpx; tyiui, tziuiq “\nÿ\ni\nαizi\n(13)\n10To simplify the discussion, we ignore the view-dependent modeling in NeRF (Mildenhall et al., 2021).\n18\n\n\n(Q, K, V)\n…\nCross-Attention\nImage features  \n𝒉𝑖𝑖=1\n𝑛\nTriplane hidden \nfeatures\n(K, V)\n(K, V)\n(K, V)\n(K, V)\n(Q)\n(Q)\n(Q)\n(Q)\n…\n…\n(Q, K, V)\n(Q, K, V)\n(Q, K, V)\n…\nSelf-attended \nfeatures 𝒇𝑠𝑒𝑙𝑓\nSelf-Attention\nFigure 5: Visual illustration of the cross-attention and self-attention in LRM’s image-to-triplane\ndecoder.\nMulti-head Attention\nThe attention operator described above only attends to the condition fea-\ntures once to get the attention vector. However, the actual attention might contain multiple modes.\nThus, the multi-head attention (Vaswani et al., 2017) is proposed. The multi-head attention is im-\nplemented by first splitting the input features into smaller queries.\nrx1, . . . , xnhs “ x\n(14)\nwhere nh is the number of heads. Meanwhile, yi and zi are split into tyk\ni uk and tzk\ni uk in a sim-\nilar way. After that, the output of each head is computed independently and the final output is a\nconcatenation of heads’ outputs.\noutk “ Attnpxk; tyk\ni ui, tzk\ni uiq\n(15)\nMultiHeadAttnpx; tyiui, tziuiq “ rout1, . . . , outnhs\n(16)\nAttention Layers in Transformer\nThe detailed attention layers in transformer utilize the above\nmulti-head attention with more linear layers. Here are the formulas for the self-attention layer (see\nthe right yellow ‘Self-Attention’ block in Fig. 5). The layer first projects the input feature sequence\nf “ tfjuj to query q, key k, and value v vectors with linear layers. Then the multi-head attention is\napplied. There is one more linear layer over the output. We also follow the recent papers (Chowdhery\net al., 2022; Touvron et al., 2023) to remove the bias terms in the attention layers.\nqj “ Wqfj\n(17)\nki “ Wkfi\n(18)\nvi “ Wvfi\n(19)\noj “ MultiHeadAttnpqj; tkiui, tviuiq\n(20)\nSelfAttnpfj; tfjujq “ Woutoj\n(21)\n(22)\nThe cross-attention layer is defined similarly (see the left blue ‘Cross-Attention’ block in Fig. 5).\nThe only difference to the self-attention layer is that the Wk and Wv is applied to the condition\nvectors (e.g., the image features h in our example).\nMLP layers in Transformer\nThe Transformer model architecture applies the MLP layer (multi-\nlayer perceptron) to do channel mixing (i.e., mix the information from different feature dimensions).\nWe follow the original transformer paper (Vaswani et al., 2017) for the implementation. The MLP\nlayer contains two linear layers with a GELU (Hendrycks & Gimpel, 2023) activation in between.\nThe intermediate hidden dimension is 4 times of the model dimension.\n19\n\n\nLayer Normalization\nWe take the default LayerNorm (LN) implementation in PyTorch (Paszke\net al., 2019). Besides the LN layers in ModLN as in Sec. 3.2, we follow the Pre-LN architecture\nto also apply LN to the final output of transformers, e.g., the output of ViT and also the output of\ntransformer decoder.\nPositional Encoding\nThe positional embedding in ViT (Dosovitskiy et al., 2020) is bilinearly up-\nsampled from its original resolution (14ˆ14 for input 224ˆ224) to match our higher input resolution\n(32ˆ32 for input 512ˆ512).\nB\nTRAINING SETUP\nWe specify the training setup of our LRM. Apart from the information that we provided in Sec. 4.2,\nwe apply a cosine schedule (Loshchilov & Hutter, 2016) with 3000 warm-up iterations. We set the\nsecond beta parameter (β2) of the AdamW optimizer (Loshchilov & Hutter, 2017) to be 0.95. We\napply a gradient clipping of 1.0 and a weight decay of 0.05. The weight decay are only applied on\nthe weights that are not bias and not in the layer normalization layer. We use BF16 precision in in\nthe mixed precision training. To save computational cost in training, we resize the reference novel\nviews from 512ˆ512 to a randomly chosen resolution between 128ˆ128 and 384ˆ384 and only ask\nthe model to reconstruct a randomly selected 128ˆ128 region. With this design, we can possibly\nincrease the effective resolution of the model.\nC\nCOMPARISON WITH SOTA\nWe provide a quantitative comparison to the stat-of-the-art methods Point-E (Nichol et al., 2022),\nShap-E (Jun & Nichol, 2023), and One-2-3-45 (Liu et al., 2023a). Point-E trains an image-to-3D\npoint cloud diffusion model, Shap-E encodes point clouds to latent representations and trains a\ndiffusion model on the latents to generate parameters of a 3D implicit function, and One-2-3-45\nreconstructs multi-view images generated with a 2D diffusion model. We randomly selected 100\nobjects from the Google Scanned Objects (GSO) dataset (Downs et al., 2022) and measured the\nnovel view synthetic quality of 20 reference views (FID, CLIP-Similarity (Radford et al., 2021),\nPSNR, LPIPS (Zhang et al., 2018)) and the geometric quality (Chamfer Distance), as shown in the\nTable below. We can see that our LRM consistently outperforms previous approaches in all metrics.\nTable 1: Comparison between LRM and state-of-the-art 3D generative models on Google Scanned\nObjects dataset (100 randomly selected objects and 20 reference views).\nModels\nGSO Evaluation\nFIDÓ\nCLIP-SimilarityÒ\nPSNRÒ\nLPIPSÓ\nChamfer DistanceÓ\nPoint-E\n123.70\n0.741\n15.60\n0.308\n0.099\nShap-E\n97.05\n0.805\n14.36\n0.289\n0.085\nOne-2-3-45\n139.24\n0.713\n12.42\n0.448\n0.123\nLRM (ours)\n31.44\n0.902\n19.60\n0.163\n0.053\nWe would like to discuss further the difference between LRM and the large-scale approaches Point-\nE and Shap-E. The models of Point-E and Shap-E contain hundreds of millions of learnable pa-\nrameters and are trained with several million 3D assets (unknown data source and unknown com-\nputational cost from their papers). In terms of the network and dataset sizes, our LRM has 500\nmillion learnable parameters, and it is trained on 1 million 3D data (publicly accessible), which\ndoes not show an advantage. In terms of the network architecture, Point-E, Shap-E, and LRM all\nuse transformer-based models and apply cross-attention for inter-modality modeling (i.e., image-to-\npoint cloud, point cloud+image-to-3D latents, and image-to-triplane, respectively). We hypothesize\nit is the choice of very compact and expressive triplane representation together with an end-to-end\ntrainable framework that enables the effective scaling of LRM and its adequate learning on large\ndatasets (Objaverse and MvImgNet). Compared to the unstructured point cloud representation ap-\nplied in Point-E and Shap-E, LRM applies the structured triplane representation that is aligned with\nthe world frame, which naturally facilitates 2D-to-3D projection. It is also worth mentioning that\nPoint-E uses 4K points (as tokens) and Shap-E uses 16K points (as tokens), but our LRM only uses\n20\n\n\n3ˆ32ˆ32“3072 triplane tokens, which largely reduce the modeling complexity. Additionally, com-\npared to the two-stage approach in Shape-E, which attempts to generate latents that can produce the\nparameters of implicit 3D functions through a diffusion model, our LRM directly maps 2D images\nto triplanes, which should be much more stable and efficient to learn. Overall, we suggest that LRM\nis a more data-friendly and efficient model than Point-E and Shap-E.\nD\nANALYSES\nWe evaluate the effect of data, model hyper-parameters, and training methods on the performance\nof LRM, measuring by PSNR, CLIP-Similarity (Radford et al., 2021), SSIM (Wang et al., 2004)\nand LPIPS (Zhang et al., 2018) of the rendered novel views. Note that due to the large training cost\nof our final model, the following analytic experiments use a much smaller version of LRM model\nas the baseline (indicated by orange shaded rows in the tables). Specifically, we scale down the\nimage-to-triplane decoder to 12 cross-attention layers, change the input image resolution to 256,\ntriplane latent dimension to 32, rendering resolution in training to 64, and use 96 samples per ray for\nrendering 64ˆ64 images for supervision. We only train each model on 32 NVIDIA A100 GPUs for\n15 epochs, and the resulting difference can be seen in Table 2. We are aware that some observations\nmight change if we scale up the model, but most of the conclusions should be general and consistent.\nTable 2: Comparison between the final model and the baseline for analysis.\nModels\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\nFinal\n20.1\n91.0\n79.7\n16.0\nBaseline\n19.0\n87.8\n77.4\n19.1\nD.1\nSYNTHETIC VS. REAL DATA\nTable 3 compares the influence of using synthetic 3D data from the Objaverse (Deitke et al., 2023)\nand real video data from the MvImgNet (Yu et al., 2023) in training. Results show that removing real\ndata causes an obvious drop for all the metrics, despite the fact our synthetic 3D dataset contains 3ˆ\nmore shapes than MvImgNet. One potential reason is that the real data have much more variation\nin the lighting, the size of the target, and the camera poses, which effectively benefits the learning.\nFuture work could augment the rendering of synthetic shapes to adequately utilize those abundant\ndata. Nevertheless, combining the two datasets leads to substantially better results than training on\nany one of them alone.\nTable 3: Influence of training datasets.\nData\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\nSynthetic (Objaverse)\n15.5\n84.7\n70.3\n29.3\nReal (MvImgNet)\n17.5\n85.7\n75.7\n22.0\nSynthetic+Real\n19.0\n87.8\n77.4\n19.1\nD.2\nNUMBER OF VIEWS IN TRAINING DATA\nIn Table 4, we conduct experiments with all data but limit the number of training views per shape.\nFor example, for Train Views“8, we use only a random subset of 8 views per shape and keep\nrandomly sampling 4 views from the above subset at each training step. The results show that more\nviews can lead to better results, possibly because of more diverse data. While the growth is saturated\nat 16 views, adding more views does not lead to worse results.\nD.3\nMODEL HYPER-PARAMETERS\nTable 5 presents the results of having a different number of cross-attention layers in the image-to-\ntriplane decoder. There is a slight trend indicating that the scores can be improved by having a\n21\n\n\nTable 4: Effect of the number of different views per shape in training. 32+ indicates some video\ndata in MvImgNet contain more than 32 views per shape, which we apply all of them in training.\nTrain Views\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n4\n18.8\n86.7\n77.5\n19.8\n8\n18.9\n87.3\n77.5\n19.4\n16\n19.1\n87.9\n77.6\n19.0\n32+\n19.0\n87.8\n77.4\n19.1\ndeeper model, especially for the latent semantic and perceptual similarity measurements CLIP and\nLPIPS, implying that the network models better representations for reconstructing higher-quality\nimages.\nWe also evaluate the influence of the number of MLP layers in NeRF (Table 6). Results show that\nit is unnecessary to have a very large network, and there seems to be a sweet spot around two to\nfour layers. This observation is consistent with EG3D (Chan et al., 2022) where the information\nof shapes is encoded by the triplane and such MLP is only a shallow model for projecting triplane\nfeatures to color and density.\nAs shown in Table 7, we found that increasing the triplane resolution leads to better image quality.\nNote that, in this experiment, we only use a deconvolution layer to upsample the 32ˆ32ˆ32 triplane\nproduced by LRM’s decoder, whereas we suspect a large improvement could be seen by increasing\nthe quantity of input spatial-positional embeddings to query more fine-grained image details. How-\never, such an approach will dramatically increase the computational cost, we leave this exploration\nto future research.\nTable 5: Effect of the number of cross-attention layers in image-to-triplane decoder.\nCrossAttn\nLayers\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n6\n19.0\n87.7\n77.6\n19.1\n16\n19.0\n87.8\n77.4\n19.1\n24\n19.1\n88.0\n77.6\n18.9\nTable 6: Effect of the number of MLP layers in NeRF.\nNeRF MLP\nLayers\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n2\n19.2\n87.7\n77.8\n18.9\n6\n19.1\n88.0\n77.6\n19.0\n12\n19.0\n87.8\n77.4\n19.1\n14\n19.1\n87.2\n77.6\n19.0\nTable 7: Effect of the resolution of triplane. For 64up and 128up, we apply additional 2ˆ2 and 4ˆ4\ndeconvolution layers, respectively, to upsample a Res. 32 triplane.\nTriplane Res.\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n32\n18.9\n86.3\n77.2\n19.7\n64up\n19.0\n87.8\n77.4\n19.1\n128up\n19.0\n88.3\n77.5\n19.0\nD.4\nCAMERA POSE\nAs we have discussed in the Main Paper, normalizing camera poses in training has a huge impact\non the generalization of input views. We can see from Table 8 that when no modification is ap-\nplied (None), LRM produces the worst results. Augmenting camera poses with a Random rotation\n22\n\n\ngreatly improves the results since the model learns a more general image-to-triplane projection via\ndecoupled views and camera poses. However, such unconstrained projection is very difficult to\nlearn. We therefore Normalized all camera poses so that all images are projected onto the triplane\nfrom the same direction, allowing the model to adequately learn and utilize the cross-shape prior for\nreconstruction.\nTable 8: Effect of camera pose normalization.\nCamera Pose\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\nNone\n15.3\n83.4\n70.1\n28.9\nRandom\n18.0\n85.6\n75.7\n21.1\nNormalized\n19.0\n87.8\n77.4\n19.1\nD.5\nIMAGE QUANTITY AND RESOLUTION\nTable 9 and Table 10 study the influence of the number of side views supervision for each sample\nand the effect of image rendering resolution in training. Results indicate that as the quantity of side\nviews increases, the reconstructed image quality improves. Having more views allows the model\nto better correlate the appearance and geometry of different parts of the same shape, and facilitates\ninferring multi-view consistent results. Moreover, using a higher rendering resolution of images\nin training largely improves the results, as the model is encouraged to learn more high-frequency\ndetails.\nTable 9: Influence of the number of side views applied for each training sample.\nSide Views\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n1\n18.7\n87.7\n77.2\n19.7\n2\n18.7\n87.5\n77.2\n19.6\n3\n19.0\n87.8\n77.4\n19.1\n4\n19.1\n87.8\n77.6\n18.9\nTable 10: Influence of the rendering resolution of images in training.\nRender Res.\nUnseen Evaluation\nPSNRÒ\nCLIP-SimilarityÒ\nSSIMÒ\nLPIPSÓ\n32\n18.8\n86.3\n77.0\n20.1\n64\n19.0\n87.8\n77.4\n19.1\n128\n19.4\n89.0\n78.3\n18.0\nD.6\nLPIPS LOSS\nLastly, we found that our LPIPS objective (Zhang et al., 2018) has a huge impact on the results.\nRemoving it from training will decrease the CLIP-Similarity, SSIM, and LPIPS scores to 74.7, 76.4,\nand 29.4, respectively.\nE\nVISUALIZATIONS\nWe present more visualizations of the reconstructed 3D shapes in the following pages. The in-\nput images include photos captured by our phone camera, images from Objaverse (Deitke et al.,\n2023), MvImgNet (Yu et al., 2023), ImageNet (Deng et al., 2009), Google Scanned Objects (Downs\net al., 2022), Amazon Berkeley Objects (Collins et al., 2022), and images generated by the Adobe\nFirefly11. We implement a heuristic function to pre-process the camera-captured images, generated\n11Adobe Firefly, a text-to-image generation tool: https://firefly.adobe.com.\n23\n\n\nimages, and images from MvImgNet and ImageNet. The function removes the image background\nwith an off-the-shelf package12, followed by cropping out the target object, rescaling the target to a\nsuitable size and centering the target on a square white figure. All input images are never seen by\nthe model in training. Please visit our project webpage https://yiconghong.me/LRM/ for video\ndemonstrations and interactable 3D meshes.\nGoogle Scanned \nObjects\nInput Image\nRendered\nGT\nRendered\nGT\nAmazon Berkeley \nObjects\nObjaverse\nMvImgNet\nInput Image\nRendered\nGT\nRendered\nGT\nMvImgNet\nGoogle Scanned \nObjects\nAmazon Berkeley \nObjects\nFigure 6: Comparison between LRM rendered novel views and the ground truth images (GT). None\nof the images are observed by the model during training. The GT depth images of Objaverse are\nrendered from the 3D models. Please zoom in for clearer visualization.\n12Rembg package, a tool to remove image background: https://pypi.org/project/rembg\n24\n\n\nPhone \nCaptured\nPhone \nCaptured\nPhone \nCaptured\nPhone \nCaptured\nImageNet\nImageNet\nImageNet\nImageNet\nInput Image\nRendered Novel Views\nInput Image\nRendered Novel Views\nImageNet\nImageNet\nGenerated\nGenerated\nGenerated\nGenerated\nFigure 7: Rendered novel views (RGB and Depth) of shapes reconstructed by our LRM from single\nimages. None of the images are observed by the model during training. Generated images are\ncreated by the Adobe Firefly. Please zoom in for clearer visualization.\n25\n\n\nUnder review as a conference paper at ICLR 2024\nINSTANT3D: FAST TEXT-TO-3D WITH SPARSE-VIEW\nGENERATION AND LARGE RECONSTRUCTION MODEL\nAnonymous authors\nPaper under double-blind review\nABSTRACT\nText-to-3D with diffusion models have achieved remarkable progress in recent\nyears. However, existing methods either rely on score distillation-based optimiza-\ntion which suffer from slow inference, low diversity and Janus problems, or are\nfeed-forward methods that generate low quality results due to the scarcity of 3D\ntraining data. In this paper, we propose Instant3D, a novel method that generates\nhigh-quality and diverse 3D assets from text prompts in a feed-forward manner.\nWe adopt a two-stage paradigm, which first generates a sparse set of four struc-\ntured and consistent views from text in one shot with a fine-tuned 2D text-to-image\ndiffusion model, and then directly regresses the NeRF from the generated images\nwith a novel transformer-based sparse-view reconstructor. Through extensive ex-\nperiments, we demonstrate that our method can generate high-quality, diverse and\nJanus-free 3D assets within 20 seconds, which is two order of magnitude faster\nthan previous optimization-based methods that can take 1 to 10 hours. Our project\nwebpage: https://instant-3d.github.io/.\n1\nINTRODUCTION\nIn recent years, remarkable progress has been achieved in the field of 2D image generation. This\nsuccess can be attributed to two key factors: the development of novel generative models such as\ndiffusion models (Song et al., 2021; Ho et al., 2020; Ramesh et al., 2022; Rombach et al., 2021),\nand the availability of large-scale datasets like Laion5B (Schuhmann et al., 2022). Transferring\nthis success in 2D image generation to 3D presents challenges, mainly due to the scarcity of avail-\nable 3D training data. While Laion5B has 5 billion text-image pairs, Objaverse-XL (Deitke et al.,\n2023a), the largest public 3D dataset, contains only 10 million 3D assets with less diversity and\npoorer annotations. As a result, previous attempts to directly train 3D diffusion models on existing\n3D datasets (Luo & Hu, 2021; Nichol et al., 2022; Jun & Nichol, 2023; Gupta et al., 2023; Chen\net al., 2023b) are limited in the visual (shape and appearance) quality, diversity and compositional\ncomplexity of the results they can produce.\nTo address this, another line of methods (Poole et al., 2022; Wang et al., 2023a; Lin et al., 2023;\nWang et al., 2023b; Chen et al., 2023c) leverage the semantic understanding and high-quality gen-\neration capabilities of pretrained 2D diffusion models. Here, 2D generators are used to calculate\ngradients on rendered images, which are then used to optimize for a 3D representation, usually a\nNeRF (Mildenhall et al., 2020). Although these methods yield better visual quality and text-3D\nalignment, they can be incredibly time-consuming, taking hours for optimizing 3D for each prompt.\nThey also suffer from artifacts such as over-saturated colors and the “multi-face” problem arising\nfrom the bias in pretrained 2D diffusion models, and struggle to generate diverse results from the\nsame text prompt, with varying the random seed leading to minor changes in geometry and texture.\nIn this paper, we propose Instant3D, a novel feed-forward method that generates high-quality and\ndiverse 3D assets conditioned on text. Instant3D, like the methods noted above, builds on top of\npretrained 2D diffusion models. However, it does so by splitting 3D generation into two stages:\n2D generation and 3D reconstruction. In the first stage, instead of generating images sequentially,\nwe fine-tune an existing text-to-image diffusion model (Podell et al., 2023) to generate a sparse set\nof four-view images in the form of a 2×2 grid in a single denoising process. This design allows\nthe multi-view images to attend to each other during generation, leading to more view-consistent\nresults. In the second stage, instead of relying on a slow optimization-based reconstruction method,\n1\n\n\nUnder review as a conference paper at ICLR 2024\na steaming basket full of dumplings\na steam engine train, high resolution\na 3d model of an adorable cottage with a thatched roof\na baby dragon drinking boba\nbeautifully carved jade heavenly Chinese guardian lion, \nhand carved nephrite jade\na ghost eating a hamburger\na hippo wearing a sweater\na panda rowing a boat in a pond\na snail on a leaf\na cracked egg with the yolk spilling out\nFigure 1: Our method generates high-quality 3D NeRF assets from the given text prompts within 20\nseconds. Here we show novel view renderings from our generated NeRFs as well as the renderings\nof the extracted meshes from their density field.\ninspired by (Anonymous, 2023), we introduce a novel sparse-view large reconstruction model with\na transformer-based architecture that can directly regress a triplane-based (Chan et al., 2022) NeRF\nfrom a sparse set of multi-view images. Our model projects sparse-view images into a set of pose-\naware image tokens using pretrained vision transformers (Caron et al., 2021), which are then fed\nto an image-to-triplane decoder that contains a sequence of transformer blocks with cross-attention\nand self-attention layers. Our proposed model has a large capacity with more than 500 million\nparameters and can robustly infer correct geometry and appearance of objects from just four images.\nBoth of these stages are fine-tuned/trained with multi-view rendered images of around 750K 3D\nobjects (Deitke et al., 2023b). Both of these stages are fine-tuned/trained with multi-view rendered\nimages of 3D objects (Deitke et al., 2023b). The second reconstruction stage takes around 750K\nmulti-view data in training while the first stage can take as little as 10K data. While we use rel-\natively smaller dataset compared to the pre-training dataset for other modalities (e.g., C4 Raffel\net al. (2020) for text and Laion5B for image), Instant3D’s two-stage approach combines it with the\npower of pretrained 2D diffusion models to generate high-quality, diverse 3D assets even for com-\nplex input prompts (see Figure 1). Due to its feed-forward architecture, Instant3D is exceptionally\nfast, requiring only about 20 seconds for a text prompt, which is about 200× faster than previous\noptimization-based methods (Poole et al., 2022; Wang et al., 2023b) while achieving comparable or\neven better quality.\n2\nRELATED WORKS\n2.1\n3D GENERATION\nFollowing the success of generative models on 2D images using GANs (Goodfellow et al., 2014;\nKarras et al., 2019; Gu et al., 2022; Kang et al., 2023) and diffusion models (Rombach et al., 2021;\nPodell et al., 2023; Ho et al., 2020; Song et al., 2021; Saharia et al., 2022), people have also explored\nthe applications of such models on 3D generation. Previously GAN-based approached have explored\ndifferent methods to generate 3D models in the form of point clouds (Wu et al., 2016; Gadelha\net al., 2017; Smith & Meger, 2017), triangle meshes (Gao et al., 2022; Pavllo et al., 2020; Chen\net al., 2019; Luo et al., 2021) or volumes (Chan et al., 2022; Or-El et al., 2022; Bergman et al.,\n2022; Skorokhodov et al., 2022) in either unconditional or text/image-conditioned manner, typically\nsupervised by adversarial losses on the 3D representations or 2D rendered images. Such methods\nusually require category-specific training and do not generalize well to novel categories.\n2\n\n\nUnder review as a conference paper at ICLR 2024\nDiffusion models open new possibilities for 3D generation. A class of methods directly train 3D\ndiffusion models on the 3D representations (Nichol et al., 2022; Liu et al., 2023c; Zhou et al., 2021)\nor project the 3D models or multi-view rendered images into latent representations (Ntavelis et al.,\n2023; Zeng et al., 2022; Gupta et al., 2023; Jun & Nichol, 2023; Chen et al., 2023b) and perform the\ndiffusion process in the latent space. For example, Shap-E (Jun & Nichol, 2023) encodes each 3D\nshape into a set of parameters of an implicit function, and then trains an conditional diffusion models\non the parameters. These approaches face challenges due to the restricted availability and diversity of\nexisting 3D data, consequently resulting in generated content with poor visual quality and inadequate\nlignment with the input prompt. Therefore, although trained on millions of 3D assets, Shap-E still\nfails to generate 3D shapes with complex compositional concepts and high-fidelity textures.\nTo resolve this, another line of works try to make use of 2D diffusion models to facilitate the 3D gen-\neration. Some works (Jain et al., 2022; Mohammad Khalid et al., 2022) optimize meshes or NeRFs\nto minimize the CLIP Radford et al. (2021) score between the rendered images and input prompt\nutilizing pretrained CLIP model. While such methods can generate diverse 3D content, they exhibit\na deficiency in achieving visual realism. More recently, some works (Poole et al., 2022; Wang et al.,\n2023b; Lin et al., 2023; Chen et al., 2023c) optimize 3D representations using score distillition loss\n(SDS) based on a pretrained 2D diffusion models. Such methods can generate high-quality results,\nbut suffer from the slow optimization, over-saturated colors and the Janus problem. For example, it\ntakes 1.5 hours for DreamFusion (Poole et al., 2022) and 10 hours for ProlificDreamer Wang et al.\n(2023b) to generate a single 3D asset, which greatly limits their practicality. In contrast, our method\nenjoys the benefits of both worlds: it’s able to borrow information from pretrained 2D diffusion\nmodels to generate diverse multi-view consistent images that are subsequently lifted to faithful 3D\nmodels, while still being fast and efficient due to its feed-forward nature.\n2.2\nSPARSE-VIEW RECONSTRUCTION\nTraditional 3D reconstruction with multi-view stereo (Agarwal et al., 2011; Sch¨\nonberger et al., 2016;\nFurukawa et al., 2015) typically require a dense set of input images that have significant overlaps\nto find correspondence across views and infer the geometry correctly. While NeRF (Mildenhall\net al., 2020) and its variants (M¨\nuller et al., 2022; Chen et al., 2022; 2023a) have further alleviated\nthe prerequisites for 3D reconstruction, they perform per-scene optimization that still necessitate a\nlot of input images. Previous methods (Wang et al., 2021; Chen et al., 2021; Long et al., 2022;\nReizenstein et al., 2021; Trevithick & Yang, 2021) have tried to learn data priors so as to infer NeRF\nfrom a sparse set of images. Typically they extract per-view features from each input image, and then\nfor each point on the camera ray, aggregate the multi-view features and decode them to the density\n(or SDF) and colors. Such methods are either trained in a category-specific manner, or only trained\non a small datasets such as ShapeNet and ScanNet; they have not been demonstrated to generalize\nbeyond these datasets especially to the complexity of text-to-2D outputs.\nMore recently, some methods utilize data priors from pretrained 2D diffusion models to lift a single\n2D image to 3D by providing supervision at novel views using SDS loss (Liu et al., 2023b; Qian\net al., 2023; Melas-Kyriazi et al., 2023) or generated multi-view images (Liu et al., 2023a). For\ninstance, One-2-3-45 Liu et al. (2023a) generates 32 images at novel views from a single input image\nusing a fine-tuned 2D diffusion model, and reconstructs a 3D model from them; this leads to issues\nwith inconsistency between the many input views. In comparison, our sparse-view reconstructor\nadopts a highly scalable transformer-based architecture and is trained on large-scale 3D data. This\ngives it the ability to accurately reconstruct 3D models of novel unseeen objects from a sparse set\nof 4 images without per-scene optimization. In a way, our work can be seen as revisiting the earlier\nattempts at 3D reconstruction from sparse views, such as space carving, visual hulls and photo\nhulls (Kutulakos & Seitz, 2000), but with the benefit of modern learning techniques, and in the\ncontext of generation rather than purely scene reconstruction.\n3\nMETHOD\nOur method Instant3D is composed of two stages: sparse-view generation and feed-forward NeRF\nreconstruction. In Section 3.1, we present our approach on generating sparse multi-view images\nconditioned on the text input. In Section 3.2, we describe our transformer-based sparse-view large\nreconstruction model.\n3\n\n\nUnder review as a conference paper at ICLR 2024\nA car made \nout of sushi\n2D diffusion model\n(fine-tuned)\nTransformer-based\nreconstructor\nGaussian blob initialization\n(inference only)\n4-view image (2 by 2 grid)\nNeRF\nFigure 2: Overview of our method. Given a text prompt (‘a car made out of sushi’), we perform\nmulti-view generation with Gaussian blobs as initialization using fine-tuned 2D diffusion model,\nproducing a 4-view image in the form of a 2 by 2 grid. Then we apply a transformer-based sparse-\nview 3D reconstructor on the 4-view image to generate the final NeRF.\n3.1\nTEXT-CONDITIONED SPARSE VIEW GENERATION\nGiven a text prompt, our goal is to generate a set of multi-view images that are aligned with the\nprompt and consistent with each other. We achieve this by fine-tuning a pretrained text-to-image\ndiffusion models to generate 2x2 grid as shown in Figure 2.\nIn the following paragraphs, we first illustrate that the large text-to-image diffusion model (i.e.,\nSDXL (Podell et al., 2023)) has the capacity to generate view-consistent images thus a light-weight\nfine-tuning is possible. We then introduces three essential techniques to realize it: the image grid,\nthe curation of the dataset, and also the Gaussian Blob noise initialization in inference. As a result\nof these observations and technical improvements, we can fine-tune the 2D diffusion model for only\n10K steps (on 10K data) to generate consistent sparse views.\nMulti-view generation with image grid. Previous methods (Liu et al., 2023b;a) on novel-view\nsynthesis show that image diffusion models are capable of understanding the multi-view consistency.\nIn light of this, we compile the images at different views into a single image in the form of an image\ngrid, as depicted in Figure 2. This image-grid design can better match the original data format of\nthe 2D diffusion model, and is suitable for simple direct fine-tuning protocol of 2D models. We\nalso observe that this simple protocol only works when the base 2D diffusion has enough capacity,\nas shown in the comparisons of Stable Diffusion v1.5 (Rombach et al., 2021) and SDXL (Podell\net al., 2023) in Section 4.3. The benefit from simplicity will also be illustrated later in unlocking the\nlight-weight fine-tuning possibility.\nRegarding the number of views in the image grid, there is a trade-off between the requirements of\nthe multi-view generation and 3D reconstructor. More generated views make the problem of 3D re-\nconstruction easier with more overlaps but increase possibility of view inconsistencies in generation.\nOn the other hand, too few views may cause insufficient coverage, requiring the reconstructor to hal-\nlucinate unseen parts, which is challenging for a deterministic 3D reconstruction model. Thanks to\nour sparse-view constructor which greatly reduces the number of views, we empirically found that\n4 views is a balance between these two needs. Moreover, 4 views can be naturally arranged in a 2x2\ngrid as detailed in Figure 2. Next, we detail how the image grid data is created and curated.\nMulti-view data creation and curation.\nTo fine-tune the text-to-image diffusion model, we create\npaired multi-view renderings and text prompts. We adopt a large-scale synthetic 3D dataset (Deitke\net al., 2023b) and render four 512x512 views of about 750K objects with Blender. We distribute the\nfour views at a fixed elevation (20 degrees) and four equidistant azimuths (0, 90, 180, 270 degrees)\nto achieve a better coverage of the object. We use the captions from Cap3D (Luo et al., 2023) ,\nwhich utilizes an off-the-shelf image captioning model BLIP-2 (Li et al., 2023). As the final pre-\nprocessing step, the four view are then assembled into a grid image in a fixed order and resized to\nthe input resolution compatible of the 2D diffusion model.\nWe find that naively using all the data for fine-tuning has a negative impact on the quality of the\nresults. Therefore, we train a simple scorer on a small amount (2000 samples) of manually labelled\ndata to predict asset quality. The model is a simple SVM on top of pretrained CLIP features extracted\nfrom multi-view renderings of a 3D object. See Appendix for details.\n4\n\n\nUnder review as a conference paper at ICLR 2024\nSelf-\nattention\nMLP\n+\n+\n12 layers, ViT (DINO)\nCamera features\nConv\nConv\nMulti-view images\n(N=2 here)\nImage encoder \n(shared)\nImage encoder\nTriplane tokens \n(learnable)\nConcat\n2D image tokens \nSelf-\nattention\nMLP\n+\n16 layers\nCross-\nattention\nreshape & \nupsample\nTriplane\nVolume\nrendering\n+\n+\nMod\nNovel view rendering\nImage-to-triplane decoder\nFigure 3: Architecture of our sparse-view reconstructor. The model applies a pretrained ViT to\nencode multi-view images into pose-aware image tokens, from which we decode a triplane rep-\nresentation of the scene using a transformer-based decoder. Finally we decode per-point triplane\nfeatures to its density and color and perform volume rendering to render novel views. We illustrate\nhere with 2 views and the actual implementation uses 4 views.\nOur model only takes the top 10K data ranked by our scorer for training by default. We provide\nquantitative study in Section 4.3 for the impact of more or less data. We also provide the results\nfrom our uncurated data. Although the difference is not big from the metric side, we found that this\ncurated data is helpful in quality.\nInference with Gaussian blob initialization.\nWhile our data are multi-view images with white\nbackground, we observed that during inference starting from standard Gaussian noise results in low-\nquality images that have cluttered backgrounds; this introduces extra difficulty for the feed-forward\nreconstructor in the second stage (Section 3.2). To guide the model toward generating images with\na clean white backgrounds, inspired by SDEdit (Meng et al., 2022), we first create a white image\nof a 2x2 grid that has the same resolution as the output images, and initialize each sub-grid with\na black 2D Gaussian blob that is placed at the center of image with a standard deviation of 0.1.\nThe visualization of this Gaussian Blob can be found in Figure 2; please refer to Appendix for the\nconcrete math formulation. This Gaussian blob image grid is fed to the autoencoder to get its the\nlatent representation. We then add diffusion noise (e.g., use t=980/1000 for 50 DDIM denoising\nsteps), and use it as the starting point for the denoising process. As seen in Figure 5, this technique\neffectively guides the model toward generating images with clean background.\nLight-weight fine-tuning.\nWith all above observations and techniques, we are able to adapt a\ntext-to-image diffusion model to a text-to-multiviews model with light-weight fine-tuning. This\nlight-weight fine-tuning share a similar spirit as the ‘instruction fine-tuning’ (Mishra et al., 2022;\nWei et al., 2021) for LLM (large language model) alignment. The assumption is that the base model\nis already capable of the task, and the fine-tuning is to unlock the base model’s ability without\nintroducing additional knowledge.\nSince we utilize image grid, the fine-tuning follows the exact same training protocol as the 2D\ndiffusion model pre-training, except that we decrease the learning rate to 10−5. We train the model\nwith a batch size of 192 for only 10K iterations on the 10K curated multi-view data. The training is\ndone on 32 NVIDIA A100 GPUs for only 3 hours. We study the impact of different training amount\nin Section 4.3. For more training details, please refer to Appendix.\n3.2\nFEED-FORWARD SPARSE-VIEW LARGE RECONSTRUCTION MODEL\nIn this stage, we aim to reconstruct a NeRF from the four-view images I = {Ii | i = 1, ..., 4}\ngenerated in the first stage. 3D reconstruction from sparse inputs with a large baseline is a challeng-\ning problem, which requires strong model priors to resolve the inherent ambiguity. Inspired by a\nrecent work LRM (Anonymous, 2023) that introduces a transformer-based model for single image\n5\n\n\nUnder review as a conference paper at ICLR 2024\nTable 1: Quantitative comparisons on CLIP scores\nagainst baseline methods.\nOur method outper-\nforms previous feed-forward method Shap-E and\noptimization-based\nmethod\nDreamFusion,\nand\nachieves competitive performance to ProlificDreamer\nwhile being 1800× faster.\nViT-L/14 ↑\nViT-bigG-14 ↑\nTime(s) ↓\nShap-E\n20.51\n32.21\n6\nDreamFusion\n23.60\n37.46\n5400\nProlificDreamer\n27.39\n42.98\n36000\nOurs\n26.87\n41.77\n20\nTable 2:\nQuantitative comparisons\nagainst previous sparse-view recon-\nstruction methods on GSO dataset.\nPSNR ↑\nSSIM ↑\nLPIPS ↓\nSparseNeus\n20.62\n0.8360\n0.1989\nOurs\n26.54\n0.8934\n0.0643\n3D reconstruction, we propose a novel approach that enables us to predict a NeRF from a sparse set\nof input views with known poses. Similar to Anonymous (2023), our model consists of an image\nencoder, a images-to-triplane decoder, and a NeRF decoder. The image encoder encodes the multi-\nview images into a set of tokens. We feed the concatenated image tokens to the image-to-triplane\ndecoder to output a triplane representation (Chan et al., 2022) for the 3D object. Finally, the triplane\nfeatures are decoded into per-point density and colors via the NeRF MLP decoder.\nIn detail, we apply a pretrained Vision Transformer (ViT) DINO (Caron et al., 2021) as our image\nencoder. To support multi-view inputs, we inject camera information in the image-encoder to make\nthe output image tokens pose-aware. This is different from Anonymous (2023) that feeds the camera\ninformation in the image-to-triplane decoder because they take single-image input. The camera\ninformation injection is done by the AdaLN (Huang & Belongie, 2017; Peebles & Xie, 2022) camera\nmodulation as described in Anonymous (2023). The final output of the image encoder is a set of\npose-aware image features f ∗\nIi, and we concatenate the per-view features together as the feature\ndescriptors for the multi-view images: fI = ⊕(f ∗\nI1, ...f ∗\nI4)\nWe use triplane as the scene representation. The triplane is flattened to a sequence of learnable to-\nkens, and the image-to-triplane decoder connects these triplane tokens with the pose-aware image\ntokens fI using cross-attention layer, followed by self-attention and MLP layers. The final out-\nput tokens are reshaped and upsampled using a de-convolution layer to the final triplane features.\nDuring training, we ray-march through the object bounding box and decode the triplane features at\neach point to its density and color using a shared MLP, and finally get the pixel color via volume\nrendering. We train the networks in an end-to-end manner with image reconstruction loss at novel\nviews using a combination of MSE loss and LPIPS (Zhang et al., 2018) loss.\nTraining Details.\nWe train the model on multi-view renderings of the Ojbaverse dataset (Deitke\net al., 2023b). Different from the first stage that performs data curation, we use all the 3D data in\nthe dataset and scale them to [−1, 1]3; then we generate multi-view renderings using Blender under\nuniform lighting with a resolution of 512 × 512. While the output images from the first stage is\ngenerated in a structured setup with fixed camera poses, we train the model using random views as a\ndata augmentation mechanism to increase the robustness. Particularly, for each object, we randomly\nsample 32 views around the object. During training, for each object we randomly select a subset\nof 4 images as input and another random set of 4 images as supervision. During inference, we will\nreuse the fixed camera poses in the first stage as the camera input to the second stage. For more\ndetails on the training, please refer to the Appendix.\n4\nEXPERIMENTS\nIn this section, we first do comparisons against previous methods on text-to-3D (Section 4.1), and\nthen perform ablation studies on different design choices of our method. By default, we report the\nresults generated with fine-tuned SDXL models, unless otherwise noted.\n4.1\nTEXT-TO-3D\nWe make comparisons to state-of-the-art methods on text-to-3D, including feed-forward methods\nShap-E (Jun & Nichol, 2023), and optimization-based methods including DreamFusion (Poole et al.,\n6\n\n\nUnder review as a conference paper at ICLR 2024\nFigure 4: Qualitative comparisons on text-to-3D compared against previous methods. We include\nmore uncurated comparison results in the supplementary material.\n2022) and ProlificDreamer (Wang et al., 2023b). We use the official code for Shap-E, and use the\nimplementation from three-studio (Guo et al., 2023) for the other two as there is no official code.\nWe keep all the hyper-parameters (number of optimization iterations, number of denoising steps) of\nthese models as default. For our own model we use the SDXL base model trained on 10K data for\n10K steps. During inference we take 100 DDIM steps.\nQualitative comparisons.\nAs shown in Figure 4, our method generates visually better results\nthan those of Shap-E, producing sharper textures, better geometry and substantially improved text-\n3D alignment. Shap-E applies a 3D diffusion model that are exclusively trained on million-level\n3D data, which might be an evidence for the need of 2D data or 2D prior models. DreamFusion\nand ProlificDreamer achieves better text-3D alignment utilizing a pretrained 2D diffusion model.\nHowever, DreamFusion generates results with over-saturated colors and over-smooth textures. Pro-\nlificDreamer results have better details. However, it still suffer from low-quality geometry (as in ‘A\nbulldozer clearing ...’) and the Janus problem (as in ”a squirrel dressed like ...”, also more detailed\nin Appendix Figure 11). In comparison, our results have more photorealistic colors with higher\ngeometric details. Please refer to the Appendix and supplementary materials for video comparisons\nand more results.\nQuantitative comparisons.\nIn Table 4, we quantitatively assess the coherence between the gen-\nerated models and the text prompts using CLIP-based scores. We perform the evaluation on re-\nsults with 400 text prompts from DreamFusion. For each model, we render 10 random views and\ncalculate the average CLIP score between the rendered images and the input text. We report the\nmetric using multiple variants of CLIP models with different model size and training data (i.e., ViT-\nL/14 from OpenAI and ViT-bigG-14 from OpenCLIP). From the result we can see that our model\nachieves higher CLIP scores than Shap-E, indicating better text-3D alignment. Our method even\nachieves consistently higher CLIP scores than optimization-based method DreamFusion and com-\npetitive scores to ProlificDreamer, from which we can see that our approach can effectively inherit\nthe great text understanding capability from pretrained SDXL models while preserve them in the\ngenerated 3D assets via consistent sparse-view generation and robust 3D reconstruction.\nInference time comparisons.\nWe present the time to generate one 3D assets in Table 1. The\ntiming is calculated using the default hyper-parameters of each method on an A100 GPU. Notably,\n7\n\n\nUnder review as a conference paper at ICLR 2024\na barbecue grill cooking sausages and burger patties 001\nFrog 004\nw/o Gaussian blobs\nw/ Gaussian blobs\nOriginal\nFinetuned\nFigure 5: Qualitative comparisons on results generated with and without Gaussian blob initialization.\nour method is significantly faster than the optimization-based methods: while it takes 1.5 hours for\nDreamFusion and 10 hours for ProlificDreamer to generate a single asset, our method can finish\nthe generation within 20 seconds, resulting in a 270× and 1800× speed up respectively. In Fig-\nure 10, we show that our inference time can be further reduced by reducing the DDIM steps without\nsignificantly sacrificing the quality.\n4.2\nCOMPARISONS ON SPARSE VIEW RECONSTRUCTION\nWe make comparison to previous sparse-view NeRF reconstruction works.\nMost of previous\nworks (Reizenstein et al., 2021; Trevithick & Yang, 2021; Yu et al., 2021) are either trained on\na small-scale dataset such as ShapeNet, or trained in a category-specific manner. Therefore, we\nmake comparison to a state-of-the-art method SparseNeus (Long et al., 2022), which is also applied\nin One2345 (Liu et al., 2023a) where they train the model on the same Objaverse dataset for sparse-\nview reconstruction. We do the comparisons on the Google Scan Object (GSO) dataset (Downs\net al., 2022), which consists of 1019 objects. For each object, we render 4-view input following the\nstructured setup and randomly select another 10 views for testing. We adopt the pretrained model\nfrom Liu et al. (2023a). Particularly, SparseNeus does not work well for 4-view inputs with such a\nlarge baseline; therefore we add another set of 4 input views in addition to the our four input views,\nfollowing the setup in Liu et al. (2023a). We report the metrics on novel view renderings in Table 2.\nFor the table, we can see that our method outperforms the baseline method even with fewer input\nimages, which demonstrates the superiority of our sparse-view reconstructor.\n4.3\nABLATION STUDY FOR SPARSE VIEW GENERATION\nWe ablate several key decisions in our method design, i.e., (1) the choice of larger 2D base model\nSDXL, (2) the use of Guassian Blob during inference, (3) the quality and size of curated dataset,\nalso lastly, (4) the need and requirements of light-weight fine-tuning. We gather the quantitative\nresults in Table 3 and put all qualitative results in Appendix. We observe that qualitative results are\nmore evident than quantitative results thus recommend a look.\nScalability with 2D text-to-image models.\nOne of the notable advantages of our method is that\nthe efficacy of our method scales positively with the potency of the underlying 2D text-to-image\nmodel. In Figure 12, we present qualitative comparisons between two distinct backbones (with\ntheir own tuned hyperparameters): SD1.5 (Rombach et al., 2021) and SDXL (Podell et al., 2023).\nIt becomes readily apparent that SDXL, which boasts a model size 3× larger than that of SD1.5,\nexhibits superior text comprehension and visual quality. We also show a quantitative comparison on\nCLIP scores in Table 3, by comparing with Exp(l, m) with Exp(d, g), we can see that the model with\nSD1.5 achieves consistently lower CLIP scores indicating worse text alignment.\nGaussian Blobs Initialization.\nIn Figure 5, we show our results generated with and without Gaus-\nsian blob initialization. From the results we can see that while our fine-tuned model can generate\nmulti-view images without Gaussian blob initialization, they tend to have cluttered backgrounds,\nwhich challenges the second-stage feed-forward reconstructor. In contrast, our proposed Gaussian\nblob initialization enables the fine-tuned model to generate images with clean white background,\nwhich better align with the image input data in the second stage.\n8\n\n\nUnder review as a conference paper at ICLR 2024\nTable 3: Comparison on CLIP scores of NeRF renderings with different variants of fine-tuning\nsettings.\nExp ID\nExp Name\nBase\n# Data\nCurated\n# Steps\nViT-L/14\nViT-bigG-14\n(a)\nCurated-1K-s1k\nSDXL\n1K\n✓\n1K\n26.33\n41.09\n(b)\nCurated-1K-s10k\nSDXL\n1K\n✓\n10k\n22.55\n35.59\n(c)\nCurated-10K-s4k\nSDXL\n10K\n✓\n4k\n26.55\n41.08\n(d)\nCurated-10K-s10k\nSDXL\n10K\n✓\n10k\n26.87\n41.77\n(e)\nCurated-10K-s20k\nSDXL\n10K\n✓\n20k\n25.96\n40.56\n(f)\nCurated-100K-s10k\nSDXL\n100K\n✓\n10k\n25.79\n40.32\n(g)\nCurated-100K-s40k\nSDXL\n100K\n✓\n40k\n26.59\n41.29\n(h)\nCurated-300K-s40k\nSDXL\n300K\n✓\n40K\n26.43\n40.72\n(i)\nRandom-10K-s10k\nSDXL\n10K\n✗\n10k\n26.87\n41.47\n(j)\nRandom-100K-s40k\nSDXL\n100K\n✗\n40k\n26.28\n40.90\n(k)\nAllData-s40k\nSDXL\n700K\n✗\n40k\n26.13\n40.60\n(l)\nCurated-10K-s10k (SD1.5)\nSD1.5\n10K\n✓\n10k\n23.50\n36.90\n(m)\nCurated-100K-s40k (SD1.5)\nSD1.5\n100K\n✓\n40k\n25.48\n39.07\nSize and quality of fine-tuning dataset.\nWe evaluate the impact of the quality and size of the\ndataset used for fine-tuning 2D text-to-image models. We first make comparisons between curated\nand uncurated randomly selected data. The CLIP alignment score raises slightly as shown in Table 3\n(i.e., comparing Exp ID d&i), while there is a substantial quality improvement as illustrated in\nAppendix Figure 7. This aligns with the observation that the data quality can dramatically impact\nthe results in the instruction fine-tuning stage for LLM (Zhou et al., 2023).\nWhen it comes to data size, we observe a double descent from Table 3 Exp ID (a, d, g) with 1K, 10K,\nand 100K data. We pick experiment (a, d, g) here because they are the best results among different\ntraining steps for the same training data size. The reason for this double descent can be spotlighted\nby the qualitatively comparisons in Appendix Figure 13, where training with 1K data can lead to\nincomplete and inconsistent images, while training with 100K data can hurt the compositionality,\nphoto realism, and also the text alignment.\nNumber of fine-tuning steps.\nWe also both quantitatively and qualitatively analyze the impact\nof fine-tuning steps. For each block Table 3 in the Table, we show the CLIP text alignment scores\nfor different training setups. Similar to the findings in instruction fine-tuning (Ouyang et al., 2022),\nthe results do not increase monotonically regarding the number of fine-tuning steps but has a peak\nin middle. For example, in our final setup trained with SDXL base model and 10K cureated data\n(i.e., Exp ID c, d, e), the results are peaked at 10K step and we also take this number to train our\nfinal model. For other data setups and number of data amount, the observations are similar. We\nalso qualitatively visualize the result comparisons at different training steps for 10K curated data in\nAppendix Figure 14. There is observable result degradation for both 4K and 20K training steps.\nAnother important observation is that the peak might move earlier when the model size goes larger.\nThis can be observed by comparing between SD1.5 (Exp ID l&m in Table 3) and SDXL (Exp ID\nd&g)) results. Note that this comparison is not conclusive yet from the Table given that SD1.5 does\nnot perform reasonbily with our direct fine-tuning protocal. More details are in Appendix.\nWe also found that Exp ID (a) with 1K steps on 1K data can achieve the best CLIP scores but the\nthe view consistency is actually disrupted. A possible reason is that the CLIP score is insensitive\nto certain noise introduced by reconstruction from inconsistent images, which also calls for a more\nreliable 3D generation evaluation metric.\n5\nCONCLUSIONS\nIn this paper we presented a novel feed-forward two-stage approach Instant3D that can generate\nhigh-quality and diverse 3D assets from text prompts within 20 seconds. Our method leverages\na fine-tuned 2D text-to-image model to generate consistent 4-view images, and lift them to 3D\nwith a robust transformer-based large reconstruction model. The experiment results show that our\nmethod outperforms previous feed-forward methods in terms of quality while being equally fast, and\nachieves comparable or better performance to previous optimization-based methods with a speed-\nup of more than 200× . Instant3D allows novice user to easily create 3D assets and enables fast\nprototyping and iteration for various applications such as 3D design and modeling.\n9\n\n\nUnder review as a conference paper at ICLR 2024\nEthics Statement.\nThe generation ability of our model is inherited from the public 2D stable dif-\nfusion model (SDXL). We only do light-weight fine-tuning over the SDXL model thus it is hard to\nintroduce extra knowledge to SDXL. Also, our model can share similar ethical and legal consid-\neration as SDXL. The curation of the data for light-weight fine-tuning does not introduce outside\nannotators. Thus the quality of the data might be biased towards the preference of authors, which\ncan lead to a potential bias on the generated results as well. The text input to the model is not further\nchecked by the model. That means that the model will try generating for every text prompt it gets,\nwithout has the ability to acknowledge unknown knowledge.\nReproducibility Statement.\nIn the main text, we highlight the essential techniques to build our\nmodel for both the first stage (Section 3.1) and the second stage (Section 3.2). We also discuss how\nour data is created and curated in Section 3. The full model configurations and training details can be\nfound in Appendix Section A.2 and Section A.4. We have detailed all the optimizer hyperparameters\nand model dimensions. We also detail our data curation process in Section A.3. Since different\npeople might have different preference thus lead to different curated dataset, we also attach our\ncurated data annotation in Supplementary Materials to enforce reproducibility.\nREFERENCES\nAdobe. Adobe Firefly. https://firefly.adobe.com/, 2023.\nSameer Agarwal, Yasutaka Furukawa, Noah Snavely, Ian Simon, Brian Curless, Steven M Seitz, and\nRichard Szeliski. Building rome in a day. Communications of the ACM, 54(10):105–112, 2011.\nAnonymous. Lrm: Large reconstruction model for single image to 3d. In Supplementary Files,\n2023.\nAlexander Bergman, Petr Kellnhofer, Wang Yifan, Eric Chan, David Lindell, and Gordon Wet-\nzstein. Generative neural articulated radiance fields. Advances in Neural Information Processing\nSystems, 35:19900–19916, 2022.\nMathilde Caron, Hugo Touvron, Ishan Misra, Herv´\ne J´\negou, Julien Mairal, Piotr Bojanowski, and\nArmand Joulin. Emerging properties in self-supervised vision transformers. In Proceedings of\nthe International Conference on Computer Vision (ICCV), 2021.\nEric R. Chan, Connor Z. Lin, Matthew A. Chan, Koki Nagano, Boxiao Pan, Shalini De Mello,\nOrazio Gallo, Leonidas Guibas, Jonathan Tremblay, Sameh Khamis, Tero Karras, and Gordon\nWetzstein. Efficient geometry-aware 3D generative adversarial networks. In CVPR, 2022.\nAnpei Chen, Zexiang Xu, Fuqiang Zhao, Xiaoshuai Zhang, Fanbo Xiang, Jingyi Yu, and Hao Su.\nMvsnerf: Fast generalizable radiance field reconstruction from multi-view stereo. In Proceedings\nof the IEEE/CVF International Conference on Computer Vision, pp. 14124–14133, 2021.\nAnpei Chen, Zexiang Xu, Andreas Geiger, Jingyi Yu, and Hao Su. Tensorf: Tensorial radiance\nfields. In European Conference on Computer Vision (ECCV), 2022.\nAnpei Chen, Zexiang Xu, Xinyue Wei, Siyu Tang, Hao Su, and Andreas Geiger. Dictionary fields:\nLearning a neural basis decomposition. ACM Trans. Graph., 2023a.\nHansheng Chen, Jiatao Gu, Anpei Chen, Wei Tian, Zhuowen Tu, Lingjie Liu, and Hao Su. Single-\nstage diffusion nerf: A unified approach to 3d generation and reconstruction. In ICCV, 2023b.\nRui Chen, Yongwei Chen, Ningxin Jiao, and Kui Jia. Fantasia3d: Disentangling geometry and\nappearance for high-quality text-to-3d content creation. arXiv preprint arXiv:2303.13873, 2023c.\nTianqi Chen, Bing Xu, Chiyuan Zhang, and Carlos Guestrin. Training deep nets with sublinear\nmemory cost. arXiv preprint arXiv:1604.06174, 2016.\nWenzheng Chen, Huan Ling, Jun Gao, Edward Smith, Jaakko Lehtinen, Alec Jacobson, and Sanja\nFidler. Learning to predict 3d objects with an interpolation-based differentiable renderer. Ad-\nvances in neural information processing systems, 32, 2019.\n10\n\n\nUnder review as a conference paper at ICLR 2024\nTri Dao, Dan Fu, Stefano Ermon, Atri Rudra, and Christopher R´\ne. Flashattention: Fast and memory-\nefficient exact attention with io-awareness. Advances in Neural Information Processing Systems,\n35:16344–16359, 2022.\nMatt Deitke, Ruoshi Liu, Matthew Wallingford, Huong Ngo, Oscar Michel, Aditya Kusupati, Alan\nFan, Christian Laforte, Vikram Voleti, Samir Yitzhak Gadre, et al. Objaverse-xl: A universe of\n10m+ 3d objects. arXiv preprint arXiv:2307.05663, 2023a.\nMatt Deitke, Dustin Schwenk, Jordi Salvador, Luca Weihs, Oscar Michel, Eli VanderBilt, Ludwig\nSchmidt, Kiana Ehsani, Aniruddha Kembhavi, and Ali Farhadi. Objaverse: A universe of anno-\ntated 3d objects. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 13142–13153, 2023b.\nLaura Downs, Anthony Francis, Nate Koenig, Brandon Kinman, Ryan Hickman, Krista Reymann,\nThomas B McHugh, and Vincent Vanhoucke. Google scanned objects: A high-quality dataset\nof 3d scanned household items. In 2022 International Conference on Robotics and Automation\n(ICRA), pp. 2553–2560. IEEE, 2022.\nYasutaka Furukawa, Carlos Hern´\nandez, et al.\nMulti-view stereo: A tutorial.\nFoundations and\nTrends® in Computer Graphics and Vision, 9(1-2):1–148, 2015.\nMatheus Gadelha, Subhransu Maji, and Rui Wang. 3d shape induction from 2d views of multiple\nobjects. In 2017 International Conference on 3D Vision (3DV), pp. 402–411. IEEE, 2017.\nJun Gao, Tianchang Shen, Zian Wang, Wenzheng Chen, Kangxue Yin, Daiqing Li, Or Litany, Zan\nGojcic, and Sanja Fidler. Get3d: A generative model of high quality 3d textured shapes learned\nfrom images. Advances In Neural Information Processing Systems, 35:31841–31854, 2022.\nIan Goodfellow, Jean Pouget-Abadie, Mehdi Mirza, Bing Xu, David Warde-Farley, Sherjil Ozair,\nAaron Courville, and Yoshua Bengio. Generative adversarial nets. Advances in neural information\nprocessing systems, 27, 2014.\nJiatao Gu, Lingjie Liu, Peng Wang, and Christian Theobalt. Stylenerf: A style-based 3d aware\ngenerator for high-resolution image synthesis. In International Conference on Learning Repre-\nsentations, 2022.\nYuan-Chen Guo, Ying-Tian Liu, Ruizhi Shao, Christian Laforte, Vikram Voleti, Guan Luo, Chia-\nHao Chen, Zi-Xin Zou, Chen Wang, Yan-Pei Cao, and Song-Hai Zhang. threestudio: A unified\nframework for 3d content generation. https://github.com/threestudio-project/\nthreestudio, 2023.\nAnchit Gupta, Wenhan Xiong, Yixin Nie, Ian Jones, and Barlas O˘\nguz.\n3dgen: Triplane latent\ndiffusion for textured mesh generation. arXiv preprint arXiv:2303.05371, 2023.\nJonathan Ho, Ajay Jain, and Pieter Abbeel. Denoising diffusion probabilistic models. Advances in\nneural information processing systems, 33:6840–6851, 2020.\nXun Huang and Serge Belongie. Arbitrary style transfer in real-time with adaptive instance normal-\nization. In Proceedings of the IEEE international conference on computer vision, pp. 1501–1510,\n2017.\nAjay Jain, Ben Mildenhall, Jonathan T Barron, Pieter Abbeel, and Ben Poole. Zero-shot text-guided\nobject generation with dream fields. In Proceedings of the IEEE/CVF Conference on Computer\nVision and Pattern Recognition, pp. 867–876, 2022.\nHeewoo Jun and Alex Nichol. Shap-e: Generating conditional 3d implicit functions. arXiv preprint\narXiv:2305.02463, 2023.\nMinguk Kang, Jun-Yan Zhu, Richard Zhang, Jaesik Park, Eli Shechtman, Sylvain Paris, and Taesung\nPark. Scaling up gans for text-to-image synthesis. In Proceedings of the IEEE Conference on\nComputer Vision and Pattern Recognition (CVPR), 2023.\n11\n\n\nUnder review as a conference paper at ICLR 2024\nTero Karras, Samuli Laine, and Timo Aila. A style-based generator architecture for generative\nadversarial networks. In Proceedings of the IEEE/CVF conference on computer vision and pattern\nrecognition, pp. 4401–4410, 2019.\nDiederik P Kingma and Jimmy Ba. Adam: A method for stochastic optimization. arXiv preprint\narXiv:1412.6980, 2014.\nKiriakos N Kutulakos and Steven M Seitz. A theory of shape by space carving. International journal\nof computer vision, 38:199–218, 2000.\nJunnan Li, Dongxu Li, Silvio Savarese, and Steven Hoi. Blip-2: Bootstrapping language-image\npre-training with frozen image encoders and large language models. 2023.\nChen-Hsuan Lin, Jun Gao, Luming Tang, Towaki Takikawa, Xiaohui Zeng, Xun Huang, Karsten\nKreis, Sanja Fidler, Ming-Yu Liu, and Tsung-Yi Lin. Magic3d: High-resolution text-to-3d con-\ntent creation.\nIn Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern\nRecognition, pp. 300–309, 2023.\nMinghua Liu, Chao Xu, Haian Jin, Linghao Chen, Mukund Varma T, Zexiang Xu, and Hao Su.\nOne-2-3-45: Any single image to 3d mesh in 45 seconds without per-shape optimization, 2023a.\nRuoshi Liu, Rundi Wu, Basile Van Hoorick, Pavel Tokmakov, Sergey Zakharov, and Carl Vondrick.\nZero-1-to-3: Zero-shot one image to 3d object, 2023b.\nZhen Liu, Yao Feng, Michael J Black, Derek Nowrouzezahrai, Liam Paull, and Weiyang Liu.\nMeshdiffusion: Score-based generative 3d mesh modeling. arXiv preprint arXiv:2303.08133,\n2023c.\nXiaoxiao Long, Cheng Lin, Peng Wang, Taku Komura, and Wenping Wang. Sparseneus: Fast gen-\neralizable neural surface reconstruction from sparse views. In European Conference on Computer\nVision, pp. 210–227. Springer, 2022.\nIlya Loshchilov and Frank Hutter.\nDecoupled weight decay regularization.\narXiv preprint\narXiv:1711.05101, 2017.\nAndrew Luo, Tianqin Li, Wen-Hao Zhang, and Tai Sing Lee. Surfgen: Adversarial 3d shape syn-\nthesis with explicit surface discriminators. In Proceedings of the IEEE/CVF International Con-\nference on Computer Vision, pp. 16238–16248, 2021.\nShitong Luo and Wei Hu. Diffusion probabilistic models for 3d point cloud generation. In Proceed-\nings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 2837–2845,\n2021.\nTiange Luo, Chris Rockwell, Honglak Lee, and Justin Johnson. Scalable 3d captioning with pre-\ntrained models. arXiv preprint arXiv:2306.07279, 2023.\nLuke Melas-Kyriazi, Christian Rupprecht, Iro Laina, and Andrea Vedaldi. Realfusion: 360 recon-\nstruction of any object from a single image. In CVPR, 2023. URL https://arxiv.org/\nabs/2302.10663.\nChenlin Meng, Yutong He, Yang Song, Jiaming Song, Jiajun Wu, Jun-Yan Zhu, and Stefano Ermon.\nSDEdit: Guided image synthesis and editing with stochastic differential equations. In Interna-\ntional Conference on Learning Representations, 2022.\nPaulius Micikevicius, Sharan Narang, Jonah Alben, Gregory Diamos, Erich Elsen, David Garcia,\nBoris Ginsburg, Michael Houston, Oleksii Kuchaiev, Ganesh Venkatesh, et al. Mixed precision\ntraining. In International Conference on Learning Representations, 2018.\nBen Mildenhall, Pratul P. Srinivasan, Matthew Tancik, Jonathan T. Barron, Ravi Ramamoorthi, and\nRen Ng. Nerf: Representing scenes as neural radiance fields for view synthesis. In ECCV, 2020.\nSwaroop Mishra, Daniel Khashabi, Chitta Baral, and Hannaneh Hajishirzi. Cross-task generalization\nvia natural language crowdsourcing instructions. In Proceedings of the 60th Annual Meeting of\nthe Association for Computational Linguistics (Volume 1: Long Papers), pp. 3470–3487, 2022.\n12\n\n\nUnder review as a conference paper at ICLR 2024\nNasir Mohammad Khalid, Tianhao Xie, Eugene Belilovsky, and Tiberiu Popa. Clip-mesh: Gener-\nating textured meshes from text using pretrained image-text models. In SIGGRAPH Asia 2022\nconference papers, pp. 1–8, 2022.\nThomas M¨\nuller, Alex Evans, Christoph Schied, and Alexander Keller. Instant neural graphics prim-\nitives with a multiresolution hash encoding.\nACM Trans. Graph., 41(4):102:1–102:15, July\n2022.\ndoi: 10.1145/3528223.3530127.\nURL https://doi.org/10.1145/3528223.\n3530127.\nAlex Nichol, Heewoo Jun, Prafulla Dhariwal, Pamela Mishkin, and Mark Chen. Point-e: A system\nfor generating 3d point clouds from complex prompts. arXiv preprint arXiv:2212.08751, 2022.\nEvangelos Ntavelis, Aliaksandr Siarohin, Kyle Olszewski, Chaoyang Wang, Luc Van Gool, and\nSergey Tulyakov. Autodecoding latent 3d diffusion models. arXiv preprint arXiv:2307.05445,\n2023.\nRoy Or-El, Xuan Luo, Mengyi Shan, Eli Shechtman, Jeong Joon Park, and Ira Kemelmacher-\nShlizerman. StyleSDF: High-Resolution 3D-Consistent Image and Geometry Generation. In\nProceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR),\npp. 13503–13513, June 2022.\nLong Ouyang, Jeffrey Wu, Xu Jiang, Diogo Almeida, Carroll Wainwright, Pamela Mishkin, Chong\nZhang, Sandhini Agarwal, Katarina Slama, Alex Ray, et al. Training language models to follow\ninstructions with human feedback.\nAdvances in Neural Information Processing Systems, 35:\n27730–27744, 2022.\nDario Pavllo, Graham Spinks, Thomas Hofmann, Marie-Francine Moens, and Aurelien Lucchi.\nConvolutional generation of textured 3d meshes. Advances in Neural Information Processing\nSystems, 33:870–882, 2020.\nF. Pedregosa, G. Varoquaux, A. Gramfort, V. Michel, B. Thirion, O. Grisel, M. Blondel, P. Pretten-\nhofer, R. Weiss, V. Dubourg, J. Vanderplas, A. Passos, D. Cournapeau, M. Brucher, M. Perrot, and\nE. Duchesnay. Scikit-learn: Machine learning in Python. Journal of Machine Learning Research,\n12:2825–2830, 2011.\nWilliam Peebles and Saining Xie. Scalable diffusion models with transformers. arXiv preprint\narXiv:2212.09748, 2022.\nDustin Podell, Zion English, Kyle Lacey, Andreas Blattmann, Tim Dockhorn, Jonas M¨\nuller, Joe\nPenna, and Robin Rombach. Sdxl: improving latent diffusion models for high-resolution image\nsynthesis. arXiv preprint arXiv:2307.01952, 2023.\nBen Poole, Ajay Jain, Jonathan T. Barron, and Ben Mildenhall. Dreamfusion: Text-to-3d using 2d\ndiffusion. arXiv, 2022.\nGuocheng Qian, Jinjie Mai, Abdullah Hamdi, Jian Ren, Aliaksandr Siarohin, Bing Li, Hsin-\nYing Lee, Ivan Skorokhodov, Peter Wonka, Sergey Tulyakov, et al.\nMagic123: One image\nto high-quality 3d object generation using both 2d and 3d diffusion priors.\narXiv preprint\narXiv:2306.17843, 2023.\nAlec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal,\nGirish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, et al. Learning transferable visual\nmodels from natural language supervision. In International conference on machine learning, pp.\n8748–8763. PMLR, 2021.\nColin Raffel, Noam Shazeer, Adam Roberts, Katherine Lee, Sharan Narang, Michael Matena, Yanqi\nZhou, Wei Li, and Peter J Liu. Exploring the limits of transfer learning with a unified text-to-text\ntransformer. The Journal of Machine Learning Research, 21(1):5485–5551, 2020.\nAditya Ramesh, Prafulla Dhariwal, Alex Nichol, Casey Chu, and Mark Chen. Hierarchical text-\nconditional image generation with clip latents. arXiv preprint arXiv:2204.06125, 1(2):3, 2022.\n13\n\n\nUnder review as a conference paper at ICLR 2024\nJeremy Reizenstein, Roman Shapovalov, Philipp Henzler, Luca Sbordone, Patrick Labatut, and\nDavid Novotny. Common objects in 3d: Large-scale learning and evaluation of real-life 3d cat-\negory reconstruction. In Proceedings of the IEEE/CVF International Conference on Computer\nVision, pp. 10901–10911, 2021.\nRobin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser, and Bj¨\norn Ommer. High-\nresolution image synthesis with latent diffusion models, 2021.\nChitwan Saharia, William Chan, Saurabh Saxena, Lala Li, Jay Whang, Emily L Denton, Kamyar\nGhasemipour, Raphael Gontijo Lopes, Burcu Karagol Ayan, Tim Salimans, et al. Photorealistic\ntext-to-image diffusion models with deep language understanding. Advances in Neural Informa-\ntion Processing Systems, 35:36479–36494, 2022.\nJohannes Lutz Sch¨\nonberger, Enliang Zheng, Marc Pollefeys, and Jan-Michael Frahm. Pixelwise\nview selection for unstructured multi-view stereo. In European Conference on Computer Vision\n(ECCV), 2016.\nChristoph Schuhmann, Romain Beaumont, Richard Vencu, Cade Gordon, Ross Wightman, Mehdi\nCherti, Theo Coombes, Aarush Katta, Clayton Mullis, Mitchell Wortsman, et al. Laion-5b: An\nopen large-scale dataset for training next generation image-text models.\nAdvances in Neural\nInformation Processing Systems, 35:25278–25294, 2022.\nIvan Skorokhodov, Sergey Tulyakov, Yiqun Wang, and Peter Wonka. Epigraf: Rethinking training\nof 3d gans. Advances in Neural Information Processing Systems, 35:24487–24501, 2022.\nEdward J Smith and David Meger.\nImproved adversarial systems for 3d object generation and\nreconstruction. In Conference on Robot Learning, pp. 87–96. PMLR, 2017.\nJiaming Song, Chenlin Meng, and Stefano Ermon. Denoising diffusion implicit models. In Interna-\ntional Conference on Learning Representations, 2021. URL https://openreview.net/\nforum?id=St1giarCHLP.\nHugo Touvron, Thibaut Lavril, Gautier Izacard, Xavier Martinet, Marie-Anne Lachaux, Timoth´\nee\nLacroix, Baptiste Rozi`\nere, Naman Goyal, Eric Hambro, Faisal Azhar, et al. Llama: Open and\nefficient foundation language models. arXiv preprint arXiv:2302.13971, 2023.\nAlex Trevithick and Bo Yang. Grf: Learning a general radiance field for 3d representation and\nrendering. In Proceedings of the IEEE/CVF International Conference on Computer Vision, pp.\n15182–15192, 2021.\nHaochen Wang, Xiaodan Du, Jiahao Li, Raymond A Yeh, and Greg Shakhnarovich. Score jaco-\nbian chaining: Lifting pretrained 2d diffusion models for 3d generation. In Proceedings of the\nIEEE/CVF Conference on Computer Vision and Pattern Recognition, pp. 12619–12629, 2023a.\nQianqian Wang, Zhicheng Wang, Kyle Genova, Pratul Srinivasan, Howard Zhou, Jonathan T. Bar-\nron, Ricardo Martin-Brualla, Noah Snavely, and Thomas Funkhouser. Ibrnet: Learning multi-\nview image-based rendering. In CVPR, 2021.\nZhengyi Wang, Cheng Lu, Yikai Wang, Fan Bao, Chongxuan Li, Hang Su, and Jun Zhu. Prolific-\ndreamer: High-fidelity and diverse text-to-3d generation with variational score distillation. arXiv\npreprint arXiv:2305.16213, 2023b.\nJason Wei, Maarten Bosma, Vincent Zhao, Kelvin Guu, Adams Wei Yu, Brian Lester, Nan Du, An-\ndrew M Dai, and Quoc V Le. Finetuned language models are zero-shot learners. In International\nConference on Learning Representations, 2021.\nJiajun Wu, Chengkai Zhang, Tianfan Xue, Bill Freeman, and Josh Tenenbaum. Learning a proba-\nbilistic latent space of object shapes via 3d generative-adversarial modeling. Advances in neural\ninformation processing systems, 29, 2016.\nAlex Yu, Vickie Ye, Matthew Tancik, and Angjoo Kanazawa. pixelNeRF: Neural radiance fields\nfrom one or few images. In CVPR, 2021.\n14\n\n\nUnder review as a conference paper at ICLR 2024\nXiaohui Zeng, Arash Vahdat, Francis Williams, Zan Gojcic, Or Litany, Sanja Fidler, and Karsten\nKreis. Lion: Latent point diffusion models for 3d shape generation. In Advances in Neural\nInformation Processing Systems (NeurIPS), 2022.\nRichard Zhang, Phillip Isola, Alexei A Efros, Eli Shechtman, and Oliver Wang. The unreasonable\neffectiveness of deep features as a perceptual metric. In CVPR, 2018.\nChunting Zhou, Pengfei Liu, Puxin Xu, Srini Iyer, Jiao Sun, Yuning Mao, Xuezhe Ma, Avia Efrat,\nPing Yu, Lili Yu, et al. Lima: Less is more for alignment. arXiv preprint arXiv:2305.11206, 2023.\nLinqi Zhou, Yilun Du, and Jiajun Wu. 3d shape generation and completion through point-voxel\ndiffusion. In Proceedings of the IEEE/CVF International Conference on Computer Vision, pp.\n5826–5835, 2021.\nA\nAPPENDIX\nA.1\nDIVERSITY OF GENERATION.\nOur method can generate diverse results from the same text prompt by using different random seeds\nin the feed-forward pass, where optimization-based methods are known As shown in Figure 6, our\napproach excels in generating diverse 3D assets featuring strikingly distinct textures and geometries\nfrom the same prompt.\nA.2\nMULTI-VIEW DIFFUSION TRAINING DETAILS\nWe use SDXL as the base model for our first-stage fine-tuning. We use the AdamW optimizer with a\nfixed learning rate 10−5, β1 = 0.9, β2 = 0.999 and a weight decay of 10−2. We fine-tune the model\nusing fp16 on 32 A100 GPUs with a total batch size of 192. No gradient accumulation is used. We\ntrain the model on 10K curated data for 40K steps, which takes around 3 hours.\nWe train the model with the standard denoising diffusion loss Ho et al. (2020)\nL(θ) = Et,x0,ϵ\n\u0002\n∥ϵ −ϵθ(√αtx0 +\n√\n1 −αtϵ, t)∥2\u0003\n(1)\nwhere ϵθ is the denoising UNet and θ are the trainable parameters.\nThe SDXL introduces image resolution and aspect ratio conditioning that allow mixing training on\nimages of different resolutions and aspect ratios. As for our training data, we render 4 views each\nwith a resolution of 512x512 and assemble them into a 1024x1024 images. Therefore we fix the\nresolution and aspect ratio conditioning to be (1024, 1024) throughout the fine-tuning procedure.\nWe don’t do random cropping in our training and fixed the crop conditioning to be (0, 0). All the\nother training setups are identical to the SDXL training.\nA.3\nDATA CURATION DETIALS\nWe adopt a quality scorer to curate high-quality data from the Objaverse dataset. To train the quality\nscorer, we first randomly sample 2000 data points from the dataset and manually label each 3D asset\nas good or bad. Good assets have realistic textures and complex geometry, while bad ones have\nsimple shapes and flat or cartoon-like texture. This criterion is subjective and imprecise, but we\nfound it to be good enough for the purpose of data filtering.\nSince the amount of annotated data is limited, we use a pretrained CLIP (Radford et al., 2021) model\nto extract high-level image features of rendered images at 5 randomly sampled camera viewpoints\nfor each asset. Then we train a simple binary SVM classifier on top of the averaged CLIP features\nfrom different views. We use the NuSVC implementation from the popular sklearn framework\nPedregosa et al. (2011), which also gives us probability estimates of the classification.\nAfter training the SVM model, we compute the scores for all the data points in the dataset. Similar\nto the training data, for each 3D object we render 5 random views, compute the averaged CLIP\nfeatures, and use the trained SVM to estimate the probability values. These probabilities are used\nas scores to rank the data from high to low quality. Finally, we use the top 10K objects as our\nfine-tuning data.\n15\n\n\nUnder review as a conference paper at ICLR 2024\nFigure 6: Our method can generate diverse results from the same text prompt.\nTo render the 4-view data, we scale the curated objects and center them at a cube [−1, 1]3. We\nrender the objects with a white background following the structured setup discussed in Section 3.1\nusing a field view of 50◦at a distance of 2.7 under uniform lighting. We use the physically-based\npath tracer Cycles in Blender for rendering.\nIn Figure 7 we show qualitative comparisons on results from models trained with curated data and\nrandom data. Models trained with random data tend to generate cartoon-like 3D assets with simple\nand flat textures. This is not surprising since a bulk of the Objaverse dataset contains simple shapes\nwith simple textures, and without curation these data will guide the model to over denoise the results,\nleading to large areas of flat color. On the contrary, models trained with curated data tend to generate\nrealistically looking objects with complex textures and shading effects.\nA.4\nSPARSE-VIEW RECONSTRUCTION DETAILS\nModel Details\nWe use the DINO-ViT-B/16 as our image encoder. This model is transformer-\nbased, which has 12 layers and the hidden dimension of the transformer is 768. The ViT begins\nwith a convolution of kernel-size 16, stride 16, and padding 0. It is essentially patchifying the input\nimage with a patch size of 16 × 16. For our final model, the image input resolution is 512, thus it\nleads to 32 × 32 = 1024 spatial tokens in the vision transformer. In ablation studies, we reduce the\ninput resolution from 512 to 256 to save some compute budget. The orignal DINO is trained with\na resolution of 224, thus the positional embedding has only a size of 14 × 14 = 196. We thus use\n2D bilinear extrapolation (with the torch.nn.functional.interpolate function) to extrapolate it to the\ndesired token size.\nSince the camera information is integrated into the image encoder via modulation, we inject mod-\nulation layer (Peebles & Xie, 2022) into each of the transformer layer (for both self-attention layer\nand the MLP layer). The modulation layer is initialized to be an identity mapping thus it is suitable\nto be added to a pre-trained vision transformer.\nAfter the image encoder, we have 1025 image feature tokens for each image, since we also include\nthe output of the [CLS] token. We concatenate the tokens from all four images to construct a\nsequence condition features of length 4100. This condition feature will be used to create the keys\nand values in the cross-attention layer of the image-to-triplane transformer decoder.\nThe image-to-triplane transformer decoder starts with a token sequence of (3 × 32 × 32) × 1024,\nwhere (3 × 32 × 32) is the number of tokens and 1024 is the hidden dimension of the transformer.\nWe use 16 layers in our transformer decoder. All attention layer has 16 attention heads and each\nhead has a dimension of 64. We remove the bias term in the attention layer as in Touvron et al.\n(2023). We take the pre-normalization architecture of the transformer where each sub-layer will be\nin the format of x + f(LayerNorm(x)).\nAfter the transformer, we apply the de-convolution layer to map the transformer output from (3 ×\n32 × 32) × 1024 to 3 × (64 × 64) × 80. It means that there are 3 planes (XY, YZ, XZ) Chan et al.\n(2022) and each plane has a size of 64 by 64. The dimension of each plane is 80 All three planes\nshare the same deconvlution layer. The deconvolution is of kernal size 2, stride 2, and pad 0.\nIn NeRF volumetric rendering, the features from the three planes are bilinearly interpolated and also\nconcatenated to get a 240 dimensional feature for each point. Then, we have 10 layers MLP with a\nhidden dimension of 64 to map this 240-dim feature to a 4-dim feature. The first three dimension\nwill be treated as RGB and normalized to (0, 1) with a sigmoid function. The last dimension will\n16\n\n\nUnder review as a conference paper at ICLR 2024\nbe treated as the density value of the NeRF field. Since density is non-negative, we use exponential\nfunction to map the MLP’s output to be non-negative.\nFor the exact formulation of the above operators, please refer to LRM (Anonymous, 2023) and\nDiT (Peebles & Xie, 2022).\nTraining Details\nWe use the AdamW (Kingma & Ba, 2014; Loshchilov & Hutter, 2017) optimizer\nto train our model. We use a peak learning rate of 4 × 10−4 with a linear warm-up (on the first 3K\nsteps) and a cosine decay. We change the β2 of the AdamW optimizer to 0.95 for better stability. We\nuse a weight-decay of 0.05 for non-bias and non-layernorm parameters. We use a gradient clipping\nof 1.\nThe training is completed with 120 epochs on our rendered Objaverse data with a training batch size\nof 1024. We use both L2 loss and LPIPS loss to supervise the model and the weight of two losses\nare L2=1 and LPIPS=2. The model is trained on 128 NVIDIA A100 GPUs and the whole training\ncan be finished in 7 days.\nFor the initialization of the image encoder, we initialize it from the official-released DINO pre-\ntraining weight. For the initialization of the triplane decoder, and NeRF MLP, we use the default\ninitializer in the PyTorch implementation. We empirically found that the pre-normalization trans-\nformer is robust to different initialization of linear layers. For the positional embedding of the\ntriplane tokens in the transformer decoder, we initialize with a Gaussian random of zero-mean and\nstd of 1/\n√\n1024.\nFor each training step, we take 4 random views for input and 4 random views as supervision. To\nallow the input views and supervision views to have more overlapping than purely independent\nrandom samples, we first sample 8 random views from all views. Then the 4 input views and 4\nsupervising views are randomly sampled from these 8 views.\nThe number of points per ray in NeRF rendering is 128, which is uniformly distributed inside the\n[−1, 1]3 object bounding box. The rendering resolution is 128 × 128. To allow higher actual super-\nvising resolution, we first resize the image to a smaller resolution (uniformly sampled from [128,\n384]) and then crop a patch of 128 × 128 from it. Thus we can go beyond the rendering resolution\nof 128.\nWe utilize the flash attention (Dao et al., 2022), mixed-precision training (with bf16 as the half-\nprecision format) (Micikevicius et al., 2018), and gradient checkpointing (Chen et al., 2016) to\nimprove the compute/memory efficiency of the training.\nA.5\nSTABLE DIFFUSION 1.5 (SD1.5) TRAINING DETAILS\nWe use 8 A100 GPUs for fine-tuning SD1.5 on 100K data with a total batch size of 64. We use the\nsame AdamW optimizer as on SDXL with exactly the same hyper-parameters. We also use gradient\naccumulation for 3 steps, which gives an effective batch size of 192. The training loss is the same as\nSDXL. We train the model for 120K steps (40K parameter updates due to gradient accumulation),\nwhich takes roughly 33 hours.\nA.6\nEXTENSION TO IMAGE-CONDITIONED GENERATION\nOur method can also be extended to support additional image conditioning to provide more fine-\ngrained control over the 3D model to be generated. In this process, the input to the model includes an\ninput text prompt that describes the object to be generated as well as a single image of the object. We\nuse the same training data as our text-conditioned model. During training, for a randomly sampled\ntime step, we aim to keep the first image (top-left quadrant) untouched and only add noise to the\nremaining three views. This allows the diffusion model to generate the other views while accounting\nfor the conditioning image. Figure 8 shows some visual results of our image-conditioned model.\nFrom the results we can see that our method is able to effectively generate the other views with\nfaithful details that are coherent with the input text prompt and image, thus giving us a high-quality\n3D model.\nDuring training, suppose we have a clean 2 × 2 grid image with four views of the same object.\nIn normal diffusion training, we add noise to the entire image and feed it to the UNet. To allow\n17\n\n\nUnder review as a conference paper at ICLR 2024\nTable 4: Ablation study of the sparse-view reconstruction model.\n#Layers\nRender\nSupervision\nPSNR ↑\nSSIM ↑\nLPIPS ↓\nexp01\n6\n64\nAll\n23.6551\n0.8616\n0.1281\nexp02\n12\n64\nAll\n23.8257\n0.8631\n0.1266\nexp03\n24\n64\nAll\n23.8351\n0.8635\n0.1258\nexp04\n12\n32\nAll\n23.1704\n0.8561\n0.1358\nexp05\n12\n64\nw/o novel\n18.2359\n0.8103\n0.2256\nexp06\n12\n64\nw/o LPIPS\n24.1699\n0.8641\n0.1934\nthe model to condition on a single image (the first view), we keep that left upper part of the image\nalways clean, and only add noise the other parts of the grid image. This kind of input is different from\nnormal inputs to the original diffusion model, but after finetuning the model can learn to adapt to\nthe distribution shift and be able to denoise conditioned on the upper left image. During inference,\nsimilarly, we replace the upper left part of every intermediate iterate with the clean conditioned\nimage.\nA.7\nGAUSSIAN BLOBS INITIALIZATION\nSince the diffusion model is finetuned with only a relatively small number of steps, it still largely\npossesses the original denoising behavior on images that are not in the form of 2×2 grids and do not\nhave white background. Naively applying the standard backward denoising process starting from\nrandom Gaussian noise will likely lead to results far from the data distribution of the finetuning data\n(see Figure 5).\nThe spatial structure of the training images is simple: four views of the same object are placed at\nthe centers of each quadrant. Also, the background is always white. Since the model is finetuned on\nsuch data with a denoising objective, it is naturally that, when presented with a noisy input whose\nunderlying clean image has these two characteristics, the model will tend to denoise the image to\na clean one where the four quadrant objects are view consistent. Following this, and inspired by\nSDEdit Meng et al. (2022), we introduce Gaussian blobs initialization to bias the model towards\ngenerating samples consistent with the distribution of the fine-tuning data.\nThe standard latent diffusion inference starts with a Gaussian noise image ϵ with the same size\nas the image latents. Instead, we modify the initial iterate to be a composition of Gaussian noise\nand an image with the aforementioned two characteristics: object quadrants and white background.\nWe construct such an image by first constructing a white image with a black Gaussian blob at the\ncenter. Specifically, we construct a H × W grayscale image I, where H and W are the height and\nwidth of the input RGB image with value range [0, 1]. For all our models H = W, and we denote\nS = H = W. For a given pixel (x, y), the value of I is computed as\nI(x, y) = 1 −exp\n\u0012\n−(x −S/2)2 + (y −S/2)2\n2σ2S2\n\u0013\n(2)\nwhere σ is a hyper-parameter controlling the width of the gaussian blob. Such an image looks like\na black ball at the center of a white image slowly fading away towards the edges of the image. We\nthen assemble four such same images into a 2 × 2 image grid. Some examples of such images with\ndifferent σ can be seen at the first row of figure 5.\nNext we construct the initial noise for the denoising step by blending a complete Gaussian noise\nlatent with the latent of the Gaussian blobs latent. We denote the latents of I as ˜\nI, and a noise latent\nimage with i.i.d. Gaussian values as ϵ. For a N step denoising inference process with timesteps\n{tN, tN−1, ..., t0}, we mix the two latents with a weighted sum\nϵtN =\np\nαtN ˜\nI +\np\n1 −αtN ϵ\n(3)\nThen ϵtN is used as the initial noise of the denoising process. E.g., the tN is 980 for a denoising\nstep with 50 (and the total time step is 1000).\nA.8\nSPARSE VIEW RECONSTRUCTION ABLATION RESULTS\nWe conduct the ablation study of our second-stage model (the sparse-view reconstruction model)\non the same training set as our final model. However, we change the training recipe to reduce the\n18\n\n\nUnder review as a conference paper at ICLR 2024\ncomputation cost to 32 A100 GPU for 1 day. The change of configuration for ablation is: (1) input\nimage resolution of 256 (2) 96 points per ray during rendering (3) 5 layers instead of 10 layers in\nNeRF MLP. (4) number of epochs decreased to 30.\nTo evaluate the performance of different variants, we test them on another 3D dataset Google\nScanned Object (GSO) (Downs et al., 2022). For each object in GSO, we render a set of 64 view\nimages with resolution 512 × 512 and white background from elevations 0◦, 20◦, 40◦, 60◦. Each\nelevation has 16 views with equidistant azimuths starting from 0. We use 4 views on elevation 20◦\ndegrees and azimuths 45◦, 135◦, 225◦, 315◦as input views, and randomly sample 5 views from the\nother 60 views as ground truth images. We fix the random seed across different runs to make sure\nthat the selected GT views are the same. A model takes in 4 input images and render 5 512 × 512\nimages according to the camera parameters of the 5 GT views. Then we compute 3 metrics between\nthe rendered images and the corresponding GT images to measure the performance including PSNR,\nSSIM and LPIPS. These metrics are averaged over all of the 1019 objects in the dataset to compute\nthe final values.\nThe results of the ablation studies are in Figure 4. First, we show that the model is robust to the\ntransformer hyperparameters (i.e., the transformer layers) in exp01, exp02, and exp03. Second, we\nshow that the LPIPS loss can largely change the results by comparing the exp02 and exp06. The\nonly difference between these two experiments is the use of LPIPS loss during training. Without\nLPIPS loss, the model drops a lot on LPIPS while got slightly improvement on PSNR and SSIM.\nHowever, we empirically find that LPIPS is much more aligned with human judgement and the\nrendered images become blurry without LPIPS. The rendering resolution is also important (as shown\nin exp04) because of the LPIPS loss, since LPIPS can be more robust and accurate for larger image\nresolution. Thus we use a rendering resolution of 128 by 128 in training our final model.\nAlso, the inclusion of novel supervising views in the training is also important as shown in exp05. All\nthree metrics got a significant drop when only supervising on the four input views. After checking\nthe results, the reason is that the coverage of four views is usually not complete for the whole space.\nThus the model will get grey floaters at the space that are not cover by these four views.\nB\nLIMITATIONS AND DISCUSSION\nWhile our model outperforms existing works in terms of speed, its efficiency is ultimately con-\nstrained by the underlying 2D diffusion models. We have also empirically observed that the fine-\ntuned multi-view generation model necessitates more inference steps compared to its original 2D\ndiffusion counterpart. Another limitation is the absence of a 3D inductive bias in the initial stage\nof the model; consequently, it is wholly dependent on a 2D model to generate 3D-consistent views,\nwhich could be a limiting factor. On the other hand, the feed-forward multi-view reconstruction\nmodel tends to blur textures, resulting in a slight degradation of the reconstructed 3D model’s qual-\nity in comparison to the original views.\n19\n\n\nUnder review as a conference paper at ICLR 2024\nCurated\nRandom\na hippo wearing a \nsweater\na panda wearing a \nnecktie and sitting \nin an office chair \na stack of pancakes \ncovered in maple syrup\na gummy bear \ndriving a convertible\nFigure 7: Comparisons on novel view renderings of NeRF assets generated from SDXL models\nfine-tuned with 10K curated and random data. We can see that that curated data enables the model\nto generate more photorealistic 3D assets with more geometric and textures details. Here curated\nand random correspond to Exp d (Curated-10K-s10K) and i (Random-10K-s10K) in Table 3.\n20\n\n\nUnder review as a conference paper at ICLR 2024\nInput  image\nOur 2x2 grid\nOurs\nZero-1-to-3\nOne-2-3-45\nFigure 8: Comparison to previous methods on single image-conditioned 3D generation. We com-\npared to previous methods Zero-1-to-3 (Liu et al., 2023b) and One-2-3-45 (Liu et al., 2023a). Our\nmethod can faithfully generate the details at the invisible regions, thus empowering us to reconstruct\n3D assets of higher quality than baseline methods. All input images are generated with a public\ntext-to-image platform Adobe Firefly (Adobe, 2023).\n21\n\n\nUnder review as a conference paper at ICLR 2024\nppendix blobs\nSigma=0.0\nSigma=0.01\nSigma=0.1\nSigma=0.2\nSigma=0.3\nFigure 9: 2x2 grid images generated with Gaussian blobs of different Sigma σ.\n22\n\n\nUnder review as a conference paper at ICLR 2024\n10 steps\n(6 seconds)\n20 steps\n(8 seconds)\n50 steps\n(15 seconds)\n100 steps\n(20 seconds)\na blue poison-dart frog sitting on a water lily\nbeautifully carved jade heavenly Chinese guardian lion, hand carved nephrite jade\na cute dragon with big eyes\nFigure 10: Comparison on the NeRF assets generated with different number of DDIM steps and\ntheir inference time. While we use 100 steps in our experiments that takes 20 seconds to generate\na NeRF asset, we find that using smaller number of steps can also give us results of similar quality\nwith much shorter inference time.\n23\n\n\nUnder review as a conference paper at ICLR 2024\na plush toy of a corgi nurse\na chimpanzee holding a cup of hot coffee\na hippo made out of chocolate\na red-eyed tree frog\nOurs\nProlificDreamer\nFigure 11: SDS optimization-based method such as ProlificDreamer (Wang et al., 2023b) can possi-\nbly suffer from the Janus problem, which greatly degrades the quality of the 3D assets. In contrast,\nour method can effectively get rid of this problem.\na tray of sushi \ncontaining pugs \na squirrel dressed \nlike a clown\nbaby elephant jumping \non a trampoline\na bear dressed as a\nlumberjack\nSDXL\nSD1.5\nFigure 12: Comparisons on the quality of the NeRF assets generated with fine-tuned SDXL and\nSD1.5 models. SDXL has a model size that is three times larger than SD1.5 thus has better text\ncomprehension. As shown in the figure, the 3D assets generated by our fine-tuned SDXL has better\nphoto realism and text alignment. The used SDXL and SD1.5 models are from Exp d (Curated-10K-\ns10K) and m (Curated-100K-s40K) in Table 3.\n24\n\n\nUnder review as a conference paper at ICLR 2024\n1K\n10K\n100K\na brightly colored \nmushroom \ngrowing on a log\na dalmatian wearing\na fireman’s hat\na teal moped\na pug wearing a bee \ncostume\nFigure 13: Comparison on the effect of different fine-tuning data size. Training on too little data such\nas 1K results in inconsistency between the generated 4 views, thus resulting in incorrect geometry.\nOn the other side, training on too much data such as 100K makes the model bias towards the fine-\ntuning dataset, thus negatively affecting the quality of generated 3D assets. Here 1K, 10K and 100K\nare corresponding to Exp a (Curated-1K-s1K), d (Curated-10K-s10K) and g (Curated-100K-s40K)\nin Table 3, respectively.\na beagle eating a \ndonut\na bear dancing \nballet\na blue motorcycle\na chihuahua \nwearing a tutu\n20K steps\n10K steps\n4K steps\nFigure 14: Comparison on different number of fine-tuning steps. 4K training steps lead to inconsis-\ntent 4-view generation, while 20K results in biasing towards the fine-tuning data. In contrast, 10K\nachieves a balance between these two. Here 4K, 10K and 20K correspond to Exp c (Curated-10K-\ns4K), d(Curated-10K-s10K) and e (Curated-10K-s20K) in Table 3.\n25","difficulty":"hard","domain":"Code Repository Understanding","length":"medium","question":"The Instant3D paper introduced significant innovations in accelerating 3D object generation by reducing the computational complexity typically seen in traditional 3D modeling methods. In contrast, OpenLRM adopts a large-scale reconstruction approach that leverages hybrid datasets like Objaverse and MVImgNet. Considering these two frameworks, how might OpenLRM’s use of hybrid datasets and a large reconstruction model introduce new challenges that were less emphasized in the Instant3D approach, especially in terms of real-time object generation and scaling?","sub_domain":"Code repo QA"}

Source: https://huggingface.co/datasets/zai-org/LongBench-v2

initial import

Posting: /agents

GET /api/v1/write?intent=publish&task_id=a6429116-9271-5977-bef8-52b58c5c8b37&body={url_encoded_text}&agent_name={optional_name}&nonce={optional_random_id}
